re-run flutter create (to add windows platform)

This commit is contained in:
Pato05 2023-10-19 14:34:22 +02:00
parent f126ffef46
commit 276d0ad4bf
No known key found for this signature in database
GPG Key ID: ED4C6F9C3D574FB6
104 changed files with 7331 additions and 7327 deletions

View File

@ -1,11 +1,11 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.
version:
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
channel: stable
revision: "2f708eb8396e362e280fac22cf171c2cb467343c"
channel: "stable"
project_type: app
@ -13,26 +13,20 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: android
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
- platform: ios
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
- platform: linux
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
- platform: macos
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: web
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
- platform: windows
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
# User provided section

View File

@ -1,31 +1,31 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
constant_identifier_names: false # too much
use_build_context_synchronously: false # no
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
constant_identifier_names: false # too much
use_build_context_synchronously: false # no
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -1,6 +1,6 @@
package f.f.freezer
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
package f.f.freezer
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M300,640v-60h100v-40h-60v-40h60v-40L300,460v-60h120q17,0 28.5,11.5T460,440v160q0,17 -11.5,28.5T420,640L300,640ZM540,640q-17,0 -28.5,-11.5T500,600v-160q0,-17 11.5,-28.5T540,400h80q17,0 28.5,11.5T660,440v160q0,17 -11.5,28.5T620,640h-80ZM560,580h40v-120h-40v120ZM480,880q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,520q0,-75 28.5,-140.5t77,-114q48.5,-48.5 114,-77T480,160h6l-62,-62 56,-58 160,160 -160,160 -56,-58 62,-62h-6q-117,0 -198.5,81.5T200,520q0,117 81.5,198.5T480,800q117,0 198.5,-81.5T760,520h80q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,880Z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M300,640v-60h100v-40h-60v-40h60v-40L300,460v-60h120q17,0 28.5,11.5T460,440v160q0,17 -11.5,28.5T420,640L300,640ZM540,640q-17,0 -28.5,-11.5T500,600v-160q0,-17 11.5,-28.5T540,400h80q17,0 28.5,11.5T660,440v160q0,17 -11.5,28.5T620,640h-80ZM560,580h40v-120h-40v120ZM480,880q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,520q0,-75 28.5,-140.5t77,-114q48.5,-48.5 114,-77T480,160h6l-62,-62 56,-58 160,160 -160,160 -56,-58 62,-62h-6q-117,0 -198.5,81.5T200,520q0,117 81.5,198.5T480,800q117,0 198.5,-81.5T760,520h80q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,880Z"/>
</vector>

View File

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="m480,840 l-58,-52q-101,-91 -167,-157T150,512.5Q111,460 95.5,416T80,326q0,-94 63,-157t157,-63q52,0 99,22t81,62q34,-40 81,-62t99,-22q94,0 157,63t63,157q0,46 -15.5,90T810,512.5Q771,565 705,631T538,788l-58,52ZM480,732q96,-86 158,-147.5t98,-107q36,-45.5 50,-81t14,-70.5q0,-60 -40,-100t-100,-40q-47,0 -87,26.5T518,280h-76q-15,-41 -55,-67.5T300,186q-60,0 -100,40t-40,100q0,35 14,70.5t50,81q36,45.5 98,107T480,732ZM480,459Z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="m480,840 l-58,-52q-101,-91 -167,-157T150,512.5Q111,460 95.5,416T80,326q0,-94 63,-157t157,-63q52,0 99,22t81,62q34,-40 81,-62t99,-22q94,0 157,63t63,157q0,46 -15.5,90T810,512.5Q771,565 705,631T538,788l-58,52ZM480,732q96,-86 158,-147.5t98,-107q36,-45.5 50,-81t14,-70.5q0,-60 -40,-100t-100,-40q-47,0 -87,26.5T518,280h-76q-15,-41 -55,-67.5T300,186q-60,0 -100,40t-40,100q0,35 14,70.5t50,81q36,45.5 98,107T480,732ZM480,459Z"/>
</vector>

View File

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M480,880q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,520h80q0,117 81.5,198.5T480,800q117,0 198.5,-81.5T760,520q0,-117 -81.5,-198.5T480,240h-6l62,62 -56,58 -160,-160 160,-160 56,58 -62,62h6q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,520q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,880ZM300,640v-60h100v-40h-60v-40h60v-40L300,460v-60h120q17,0 28.5,11.5T460,440v160q0,17 -11.5,28.5T420,640L300,640ZM540,640q-17,0 -28.5,-11.5T500,600v-160q0,-17 11.5,-28.5T540,400h80q17,0 28.5,11.5T660,440v160q0,17 -11.5,28.5T620,640h-80ZM560,580h40v-120h-40v120Z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M480,880q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,520h80q0,117 81.5,198.5T480,800q117,0 198.5,-81.5T760,520q0,-117 -81.5,-198.5T480,240h-6l62,62 -56,58 -160,-160 160,-160 56,58 -62,62h6q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,520q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,880ZM300,640v-60h100v-40h-60v-40h60v-40L300,460v-60h120q17,0 28.5,11.5T460,440v160q0,17 -11.5,28.5T420,640L300,640ZM540,640q-17,0 -28.5,-11.5T500,600v-160q0,-17 11.5,-28.5T540,400h80q17,0 28.5,11.5T660,440v160q0,17 -11.5,28.5T620,640h-80ZM560,580h40v-120h-40v120Z"/>
</vector>

View File

@ -1,29 +1,29 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -1,6 +1,6 @@
targets:
$default:
builders:
json_serializable:
options:
targets:
$default:
builders:
json_serializable:
options:
explicit_to_json: true

View File

@ -1,30 +1,30 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

View File

@ -1,42 +1,42 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
channel: stable
project_type: plugin_ffi
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: android
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: ios
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: linux
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: macos
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: windows
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
channel: stable
project_type: plugin_ffi
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: android
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: ios
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: linux
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: macos
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: windows
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -1,3 +1,3 @@
## 0.0.1
* TODO: Describe initial release.
## 0.0.1
* TODO: Describe initial release.

View File

@ -1 +1 @@
TODO: Add your license here.
TODO: Add your license here.

View File

@ -1,92 +1,92 @@
# deezcryptor
A new Flutter FFI plugin project.
## Getting Started
This project is a starting point for a Flutter
[FFI plugin](https://docs.flutter.dev/development/platform-integration/c-interop),
a specialized package that includes native code directly invoked with Dart FFI.
## Project structure
This template uses the following structure:
* `src`: Contains the native source code, and a CmakeFile.txt file for building
that source code into a dynamic library.
* `lib`: Contains the Dart code that defines the API of the plugin, and which
calls into the native code using `dart:ffi`.
* platform folders (`android`, `ios`, `windows`, etc.): Contains the build files
for building and bundling the native code library with the platform application.
## Building and bundling native code
The `pubspec.yaml` specifies FFI plugins as follows:
```yaml
plugin:
platforms:
some_platform:
ffiPlugin: true
```
This configuration invokes the native build for the various target platforms
and bundles the binaries in Flutter applications using these FFI plugins.
This can be combined with dartPluginClass, such as when FFI is used for the
implementation of one platform in a federated plugin:
```yaml
plugin:
implements: some_other_plugin
platforms:
some_platform:
dartPluginClass: SomeClass
ffiPlugin: true
```
A plugin can have both FFI and method channels:
```yaml
plugin:
platforms:
some_platform:
pluginClass: SomeName
ffiPlugin: true
```
The native build systems that are invoked by FFI (and method channel) plugins are:
* For Android: Gradle, which invokes the Android NDK for native builds.
* See the documentation in android/build.gradle.
* For iOS and MacOS: Xcode, via CocoaPods.
* See the documentation in ios/deezcryptor.podspec.
* See the documentation in macos/deezcryptor.podspec.
* For Linux and Windows: CMake.
* See the documentation in linux/CMakeLists.txt.
* See the documentation in windows/CMakeLists.txt.
## Binding to native code
To use the native code, bindings in Dart are needed.
To avoid writing these by hand, they are generated from the header file
(`src/deezcryptor.h`) by `package:ffigen`.
Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`.
## Invoking native code
Very short-running native functions can be directly invoked from any isolate.
For example, see `sum` in `lib/deezcryptor.dart`.
Longer-running functions should be invoked on a helper isolate to avoid
dropping frames in Flutter applications.
For example, see `sumAsync` in `lib/deezcryptor.dart`.
## Flutter help
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
# deezcryptor
A new Flutter FFI plugin project.
## Getting Started
This project is a starting point for a Flutter
[FFI plugin](https://docs.flutter.dev/development/platform-integration/c-interop),
a specialized package that includes native code directly invoked with Dart FFI.
## Project structure
This template uses the following structure:
* `src`: Contains the native source code, and a CmakeFile.txt file for building
that source code into a dynamic library.
* `lib`: Contains the Dart code that defines the API of the plugin, and which
calls into the native code using `dart:ffi`.
* platform folders (`android`, `ios`, `windows`, etc.): Contains the build files
for building and bundling the native code library with the platform application.
## Building and bundling native code
The `pubspec.yaml` specifies FFI plugins as follows:
```yaml
plugin:
platforms:
some_platform:
ffiPlugin: true
```
This configuration invokes the native build for the various target platforms
and bundles the binaries in Flutter applications using these FFI plugins.
This can be combined with dartPluginClass, such as when FFI is used for the
implementation of one platform in a federated plugin:
```yaml
plugin:
implements: some_other_plugin
platforms:
some_platform:
dartPluginClass: SomeClass
ffiPlugin: true
```
A plugin can have both FFI and method channels:
```yaml
plugin:
platforms:
some_platform:
pluginClass: SomeName
ffiPlugin: true
```
The native build systems that are invoked by FFI (and method channel) plugins are:
* For Android: Gradle, which invokes the Android NDK for native builds.
* See the documentation in android/build.gradle.
* For iOS and MacOS: Xcode, via CocoaPods.
* See the documentation in ios/deezcryptor.podspec.
* See the documentation in macos/deezcryptor.podspec.
* For Linux and Windows: CMake.
* See the documentation in linux/CMakeLists.txt.
* See the documentation in windows/CMakeLists.txt.
## Binding to native code
To use the native code, bindings in Dart are needed.
To avoid writing these by hand, they are generated from the header file
(`src/deezcryptor.h`) by `package:ffigen`.
Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`.
## Invoking native code
Very short-running native functions can be directly invoked from any isolate.
For example, see `sum` in `lib/deezcryptor.dart`.
Longer-running functions should be invoked on a helper isolate to avoid
dropping frames in Flutter applications.
For example, see `sumAsync` in `lib/deezcryptor.dart`.
## Flutter help
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -1,4 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -1,9 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View File

@ -1,59 +1,59 @@
// The Android Gradle Plugin builds the native code with the Android NDK.
group 'com.example.deezcryptor'
version '1.0'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// The Android Gradle Plugin knows how to build native code with the NDK.
classpath 'com.android.tools.build:gradle:7.3.0'
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
// Bumping the plugin compileSdkVersion requires all clients of this plugin
// to bump the version in their app.
compileSdkVersion 31
// Bumping the plugin ndkVersion requires all clients of this plugin to bump
// the version in their app and to download a newer version of the NDK.
// ndkVersion "25.2.9519653"
// Invoke the shared CMake build with the Android Gradle Plugin.
externalNativeBuild {
cmake {
path "../src/CMakeLists.txt"
// The default CMake version for the Android Gradle Plugin is 3.10.2.
// https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
//
// The Flutter tooling requires that developers have CMake 3.10 or later
// installed. You should not increase this version, as doing so will cause
// the plugin to fail to compile for some customers of the plugin.
// version "3.10.2"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 21
}
}
// The Android Gradle Plugin builds the native code with the Android NDK.
group 'com.example.deezcryptor'
version '1.0'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// The Android Gradle Plugin knows how to build native code with the NDK.
classpath 'com.android.tools.build:gradle:7.3.0'
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
// Bumping the plugin compileSdkVersion requires all clients of this plugin
// to bump the version in their app.
compileSdkVersion 31
// Bumping the plugin ndkVersion requires all clients of this plugin to bump
// the version in their app and to download a newer version of the NDK.
// ndkVersion "25.2.9519653"
// Invoke the shared CMake build with the Android Gradle Plugin.
externalNativeBuild {
cmake {
path "../src/CMakeLists.txt"
// The default CMake version for the Android Gradle Plugin is 3.10.2.
// https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
//
// The Flutter tooling requires that developers have CMake 3.10 or later
// installed. You should not increase this version, as doing so will cause
// the plugin to fail to compile for some customers of the plugin.
// version "3.10.2"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 21
}
}

View File

@ -1 +1 @@
rootProject.name = 'deezcryptor'
rootProject.name = 'deezcryptor'

View File

@ -1,3 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.deezcryptor">
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.deezcryptor">
</manifest>

View File

@ -1,19 +1,19 @@
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: DeezcryptorBindings
description: |
Bindings for `src/deezcryptor.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/deezcryptor_bindings_generated.dart'
headers:
entry-points:
- 'src/deezcryptor.h'
include-directives:
- 'src/deezcryptor.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
comments:
style: any
length: full
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: DeezcryptorBindings
description: |
Bindings for `src/deezcryptor.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/deezcryptor_bindings_generated.dart'
headers:
entry-points:
- 'src/deezcryptor.h'
include-directives:
- 'src/deezcryptor.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
comments:
style: any
length: full

View File

@ -1,3 +1,3 @@
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include "../../src/deezcryptor.c"
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include "../../src/deezcryptor.c"

View File

@ -1,28 +1,28 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint deezcryptor.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'deezcryptor'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.platform = :ios, '11.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint deezcryptor.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'deezcryptor'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.platform = :ios, '11.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
end

View File

@ -1,55 +1,55 @@
import 'dart:ffi' as ffi;
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'deezcryptor_bindings_generated.dart';
const String _libName = 'deezcryptor';
/// The dynamic library in which the symbols for [DeezcryptorBindings] can be found.
final ffi.DynamicLibrary _dylib = () {
if (Platform.isMacOS || Platform.isIOS) {
return ffi.DynamicLibrary.open('$_libName.framework/$_libName');
}
if (Platform.isAndroid || Platform.isLinux) {
return ffi.DynamicLibrary.open('lib$_libName.so');
}
if (Platform.isWindows) {
return ffi.DynamicLibrary.open('$_libName.dll');
}
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();
/// The bindings to the native functions in [_dylib].
final DeezcryptorBindings _bindings = DeezcryptorBindings(_dylib);
final decryptChunkRaw = _bindings.decryptChunk;
List<int> decryptChunk(List<int> data, List<int> key) {
final ptr = malloc<ffi.Char>(data.length);
final ptr1 = listIntToPointerChar(data);
final ptr2 = listIntToPointerChar(key);
final size = _bindings.decryptChunk(ptr, ptr1, ptr2);
malloc.free(ptr1);
malloc.free(ptr2);
final dec = pointerCharToListInt(ptr, size);
malloc.free(ptr);
return dec;
}
ffi.Pointer<ffi.Char> listIntToPointerChar<T>(List<int> list) {
final length = list.length;
final ptr = malloc<ffi.Char>(length);
for (int i = 0; i < length; i++) {
ptr[i] = list[i];
}
return ptr;
}
List<int> pointerCharToListInt(ffi.Pointer<ffi.Char> ptr, int size) {
return List<int>.unmodifiable(
Iterable<int>.generate(size, (index) => ptr[index]));
}
import 'dart:ffi' as ffi;
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'deezcryptor_bindings_generated.dart';
const String _libName = 'deezcryptor';
/// The dynamic library in which the symbols for [DeezcryptorBindings] can be found.
final ffi.DynamicLibrary _dylib = () {
if (Platform.isMacOS || Platform.isIOS) {
return ffi.DynamicLibrary.open('$_libName.framework/$_libName');
}
if (Platform.isAndroid || Platform.isLinux) {
return ffi.DynamicLibrary.open('lib$_libName.so');
}
if (Platform.isWindows) {
return ffi.DynamicLibrary.open('$_libName.dll');
}
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();
/// The bindings to the native functions in [_dylib].
final DeezcryptorBindings _bindings = DeezcryptorBindings(_dylib);
final decryptChunkRaw = _bindings.decryptChunk;
List<int> decryptChunk(List<int> data, List<int> key) {
final ptr = malloc<ffi.Char>(data.length);
final ptr1 = listIntToPointerChar(data);
final ptr2 = listIntToPointerChar(key);
final size = _bindings.decryptChunk(ptr, ptr1, ptr2);
malloc.free(ptr1);
malloc.free(ptr2);
final dec = pointerCharToListInt(ptr, size);
malloc.free(ptr);
return dec;
}
ffi.Pointer<ffi.Char> listIntToPointerChar<T>(List<int> list) {
final length = list.length;
final ptr = malloc<ffi.Char>(length);
for (int i = 0; i < length; i++) {
ptr[i] = list[i];
}
return ptr;
}
List<int> pointerCharToListInt(ffi.Pointer<ffi.Char> ptr, int size) {
return List<int>.unmodifiable(
Iterable<int>.generate(size, (index) => ptr[index]));
}

View File

@ -1,48 +1,48 @@
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Bindings for `src/deezcryptor.h`.
///
/// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
///
class DeezcryptorBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
DeezcryptorBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
DeezcryptorBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
int decryptChunk(
ffi.Pointer<ffi.Char> buffer,
ffi.Pointer<ffi.Char> encrypted,
ffi.Pointer<ffi.Char> key,
) {
return _decryptChunk(
buffer,
encrypted,
key,
);
}
late final _decryptChunkPtr = _lookup<
ffi.NativeFunction<
ffi.Int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>)>>('decryptChunk');
late final _decryptChunk = _decryptChunkPtr.asFunction<
int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>)>();
}
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Bindings for `src/deezcryptor.h`.
///
/// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
///
class DeezcryptorBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
DeezcryptorBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
DeezcryptorBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
int decryptChunk(
ffi.Pointer<ffi.Char> buffer,
ffi.Pointer<ffi.Char> encrypted,
ffi.Pointer<ffi.Char> key,
) {
return _decryptChunk(
buffer,
encrypted,
key,
);
}
late final _decryptChunkPtr = _lookup<
ffi.NativeFunction<
ffi.Int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>)>>('decryptChunk');
late final _decryptChunk = _decryptChunkPtr.asFunction<
int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>)>();
}

View File

@ -1,22 +1,22 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "deezcryptor")
project(${PROJECT_NAME} LANGUAGES CXX)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(deezcryptor_bundled_libraries
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:deezcryptor>
PARENT_SCOPE
)
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "deezcryptor")
project(${PROJECT_NAME} LANGUAGES CXX)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(deezcryptor_bundled_libraries
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:deezcryptor>
PARENT_SCOPE
)

View File

@ -1,3 +1,3 @@
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include "../../src/deezcryptor.c"
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include "../../src/deezcryptor.c"

View File

@ -1,27 +1,27 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint deezcryptor.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'deezcryptor'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint deezcryptor.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'deezcryptor'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
end

View File

@ -1,35 +1,35 @@
name: deezcryptor
description: A deezer decryptor written in C
version: 0.0.1
homepage:
environment:
sdk: '>=3.0.6 <4.0.0'
flutter: ">=3.3.0"
dependencies:
ffi: ^2.0.2
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
ffigen: ^6.1.2
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
plugin:
platforms:
android:
ffiPlugin: true
ios:
ffiPlugin: true
linux:
ffiPlugin: true
macos:
ffiPlugin: true
windows:
ffiPlugin: true
name: deezcryptor
description: A deezer decryptor written in C
version: 0.0.1
homepage:
environment:
sdk: '>=3.0.6 <4.0.0'
flutter: ">=3.3.0"
dependencies:
ffi: ^2.0.2
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
ffigen: ^6.1.2
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
plugin:
platforms:
android:
ffiPlugin: true
ios:
ffiPlugin: true
linux:
ffiPlugin: true
macos:
ffiPlugin: true
windows:
ffiPlugin: true

View File

@ -1,33 +1,33 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
project(deezcryptor VERSION 0.0.1 LANGUAGES C)
SET(OPENSSL_ROOT_DIR openssl)
SET(OPENSSL_LIBRARIES_DIR "lib/${ANDROID_ABI}")
SET(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
SET(OPENSSL_LIBRARIES "ssl" "crypto")
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
target_link_libraries(deezcryptor OpenSSL::Crypto)
LINK_DIRECTORIES(${OPENSSL_LIBRARIES_DIR})
add_library(deezcryptor SHARED
"deezcryptor.c"
)
set_target_properties(deezcryptor PROPERTIES
PUBLIC_HEADER deezcryptor.h
OUTPUT_NAME "deezcryptor"
)
TARGET_INCLUDE_DIRECTORIES(deezcryptor PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(deezcryptor ${OPENSSL_LIBRARIES})
target_compile_definitions(deezcryptor PUBLIC DART_SHARED_LIB)
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
project(deezcryptor VERSION 0.0.1 LANGUAGES C)
SET(OPENSSL_ROOT_DIR openssl)
SET(OPENSSL_LIBRARIES_DIR "lib/${ANDROID_ABI}")
SET(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
SET(OPENSSL_LIBRARIES "ssl" "crypto")
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
target_link_libraries(deezcryptor OpenSSL::Crypto)
LINK_DIRECTORIES(${OPENSSL_LIBRARIES_DIR})
add_library(deezcryptor SHARED
"deezcryptor.c"
)
set_target_properties(deezcryptor PROPERTIES
PUBLIC_HEADER deezcryptor.h
OUTPUT_NAME "deezcryptor"
)
TARGET_INCLUDE_DIRECTORIES(deezcryptor PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(deezcryptor ${OPENSSL_LIBRARIES})
target_compile_definitions(deezcryptor PUBLIC DART_SHARED_LIB)

View File

@ -1,14 +1,14 @@
#include "deezcryptor.h"
#include <openssl/blowfish.h>
#define CHUNK_SIZE 2048
FFI_PLUGIN_EXPORT int decryptChunk(unsigned char *buffer, unsigned char *encrypted, unsigned char *key)
{
BF_KEY _key;
BF_set_key(&_key, 16, key);
unsigned char IV = {0, 1, 2, 3, 4, 5, 6, 7};
BF_cbc_encrypt(encrypted, buffer, CHUNK_SIZE, &_key, IV, BF_DECRYPT);
return CHUNK_SIZE;
#include "deezcryptor.h"
#include <openssl/blowfish.h>
#define CHUNK_SIZE 2048
FFI_PLUGIN_EXPORT int decryptChunk(unsigned char *buffer, unsigned char *encrypted, unsigned char *key)
{
BF_KEY _key;
BF_set_key(&_key, 16, key);
unsigned char IV = {0, 1, 2, 3, 4, 5, 6, 7};
BF_cbc_encrypt(encrypted, buffer, CHUNK_SIZE, &_key, IV, BF_DECRYPT);
return CHUNK_SIZE;
}

View File

@ -1,7 +1,7 @@
#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif
FFI_PLUGIN_EXPORT int decryptChunk(char *buffer, char *encrypted, char *key);
#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif
FFI_PLUGIN_EXPORT int decryptChunk(char *buffer, char *encrypted, char *key);

View File

@ -1,17 +1,17 @@
flutter/
# 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/
flutter/
# 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/

View File

@ -1,23 +1,23 @@
# The Flutter tooling requires that developers have a version of Visual Studio
# installed that includes CMake 3.14 or later. You should not increase this
# version, as doing so will cause the plugin to fail to compile for some
# customers of the plugin.
cmake_minimum_required(VERSION 3.14)
# Project-level configuration.
set(PROJECT_NAME "deezcryptor")
project(${PROJECT_NAME} LANGUAGES CXX)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(deezcryptor_bundled_libraries
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:deezcryptor>
PARENT_SCOPE
)
# The Flutter tooling requires that developers have a version of Visual Studio
# installed that includes CMake 3.14 or later. You should not increase this
# version, as doing so will cause the plugin to fail to compile for some
# customers of the plugin.
cmake_minimum_required(VERSION 3.14)
# Project-level configuration.
set(PROJECT_NAME "deezcryptor")
project(${PROJECT_NAME} LANGUAGES CXX)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(deezcryptor_bundled_libraries
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:deezcryptor>
PARENT_SCOPE
)

68
ios/.gitignore vendored
View File

@ -1,34 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>

View File

@ -1 +1 @@
#include "Generated.xcconfig"
#include "Generated.xcconfig"

View File

@ -1 +1 @@
#include "Generated.xcconfig"
#include "Generated.xcconfig"

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -1,98 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -1,13 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -1,122 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -1,23 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -1,5 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@ -1,37 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -1,51 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Freezer</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>freezer</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Freezer</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>freezer</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@ -1 +1 @@
#import "GeneratedPluginRegistrant.h"
#import "GeneratedPluginRegistrant.h"

View File

@ -1,12 +1,12 @@
import Flutter
import UIKit
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.
}
}
import Flutter
import UIKit
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.
}
}

View File

@ -1,104 +1,104 @@
// ignore_for_file: implementation_imports
import 'package:dart_blowfish/dart_blowfish.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_cache_manager/src/storage/cache_object.dart';
import 'package:freezer/type_adapters/uri.dart';
import 'package:hive_flutter/hive_flutter.dart';
class FreezerCacheManager extends CacheManager {
static const key = 'freezerImageCache';
void init(String path, {String? boxName}) {
_instance =
FreezerCacheManager._(FreezerCacheInfoRepository(boxName ?? key, path));
}
static late final FreezerCacheManager _instance;
factory FreezerCacheManager() => _instance;
FreezerCacheManager._(FreezerCacheInfoRepository repo)
: super(Config(key, repo: repo));
}
class FreezerCacheInfoRepository extends CacheInfoRepository {
final String boxName;
final String path;
late final LazyBox<CacheObject> _box;
bool _isOpen;
FreezerCacheInfoRepository(this.boxName, this.path);
@override
Future<bool> exists() => Hive.boxExists(boxName, path: path);
@override
Future<bool> open() async {
if (_isOpen) return true;
_box = await Hive.openLazyBox<CacheObject>(boxName, path: path);
_isOpen = true;
return true;
}
@override
Future<dynamic> updateOrInsert(CacheObject cacheObject) {
if (cacheObject.id == null) {
return insert(cacheObject);
} else {
return update(cacheObject);
}
}
@override
Future<CacheObject> insert(CacheObject cacheObject,
{bool setTouchedToNow = true}) {
final id = await _box.add(cacheObject);
}
/// Gets a [CacheObject] by [key]
@override
Future<CacheObject?> get(String key);
/// Deletes a cache object by [id]
@override
Future<int> delete(int id);
/// Deletes items with [ids] from the repository
@override
Future<int> deleteAll(Iterable<int> ids);
/// Updates an existing [cacheObject]
@override
Future<int> update(CacheObject cacheObject, {bool setTouchedToNow = true});
/// Gets the list of all objects in the cache
@override
Future<List<CacheObject>> getAllObjects();
/// Gets the list of [CacheObject] that can be removed if the repository is over capacity.
///
/// The exact implementation is up to the repository, but implementations should
/// return a preferred list of items. For example, the least recently accessed
@override
Future<List<CacheObject>> getObjectsOverCapacity(int capacity);
/// Returns a list of [CacheObject] that are older than [maxAge]
@override
Future<List<CacheObject>> getOldObjects(Duration maxAge);
/// Close the connection to the repository. If this is the last connection
/// to the repository it will return true and the repository is truly
/// closed. If there are still open connections it will return false;
@override
Future<bool> close();
/// Deletes the cache data file including all cache data.
@override
Future<void> deleteDataFile();
}
class CacheObjectAdapter extends TypeAdapter<CacheObject> {
@override
// TODO: implement typeId
int get typeId => throw UnimplementedError();
// ignore_for_file: implementation_imports
import 'package:dart_blowfish/dart_blowfish.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_cache_manager/src/storage/cache_object.dart';
import 'package:freezer/type_adapters/uri.dart';
import 'package:hive_flutter/hive_flutter.dart';
class FreezerCacheManager extends CacheManager {
static const key = 'freezerImageCache';
void init(String path, {String? boxName}) {
_instance =
FreezerCacheManager._(FreezerCacheInfoRepository(boxName ?? key, path));
}
static late final FreezerCacheManager _instance;
factory FreezerCacheManager() => _instance;
FreezerCacheManager._(FreezerCacheInfoRepository repo)
: super(Config(key, repo: repo));
}
class FreezerCacheInfoRepository extends CacheInfoRepository {
final String boxName;
final String path;
late final LazyBox<CacheObject> _box;
bool _isOpen;
FreezerCacheInfoRepository(this.boxName, this.path);
@override
Future<bool> exists() => Hive.boxExists(boxName, path: path);
@override
Future<bool> open() async {
if (_isOpen) return true;
_box = await Hive.openLazyBox<CacheObject>(boxName, path: path);
_isOpen = true;
return true;
}
@override
Future<dynamic> updateOrInsert(CacheObject cacheObject) {
if (cacheObject.id == null) {
return insert(cacheObject);
} else {
return update(cacheObject);
}
}
@override
Future<CacheObject> insert(CacheObject cacheObject,
{bool setTouchedToNow = true}) {
final id = await _box.add(cacheObject);
}
/// Gets a [CacheObject] by [key]
@override
Future<CacheObject?> get(String key);
/// Deletes a cache object by [id]
@override
Future<int> delete(int id);
/// Deletes items with [ids] from the repository
@override
Future<int> deleteAll(Iterable<int> ids);
/// Updates an existing [cacheObject]
@override
Future<int> update(CacheObject cacheObject, {bool setTouchedToNow = true});
/// Gets the list of all objects in the cache
@override
Future<List<CacheObject>> getAllObjects();
/// Gets the list of [CacheObject] that can be removed if the repository is over capacity.
///
/// The exact implementation is up to the repository, but implementations should
/// return a preferred list of items. For example, the least recently accessed
@override
Future<List<CacheObject>> getObjectsOverCapacity(int capacity);
/// Returns a list of [CacheObject] that are older than [maxAge]
@override
Future<List<CacheObject>> getOldObjects(Duration maxAge);
/// Close the connection to the repository. If this is the last connection
/// to the repository it will return true and the repository is truly
/// closed. If there are still open connections it will return false;
@override
Future<bool> close();
/// Deletes the cache data file including all cache data.
@override
Future<void> deleteDataFile();
}
class CacheObjectAdapter extends TypeAdapter<CacheObject> {
@override
// TODO: implement typeId
int get typeId => throw UnimplementedError();
}

View File

@ -1,394 +1,394 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:encrypt/encrypt.dart';
import 'package:flutter/foundation.dart' as flutter;
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart';
import 'package:freezer/settings.dart';
import 'package:just_audio/just_audio.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:dart_blowfish/dart_blowfish.dart';
import 'package:logging/logging.dart';
import 'package:scrobblenaut/lastfm.dart';
typedef _IsolateMessage = (
Stream<List<int>> source,
int start,
String trackId,
SendPort sendPort
);
// Maybe better implementation of Blowfish CBC instead of random-ass, unpublished library from github?
// This class can be considered a rewrite in Dart of the Java backend (from the StreamServer.deezer() function and also from the Deezer class)
class DeezerAudioSource extends StreamAudioSource {
final _logger = Logger("DeezerAudioSource");
late AudioQuality Function() _getQuality;
late AudioQuality? _initialQuality;
late String _trackId;
late String _md5origin;
late String _mediaVersion;
final String trackToken;
final int trackTokenExpiration;
final StreamInfoCallback? onStreamObtained;
// some cache
AudioQuality? _currentQuality;
int? _cachedSourceLength;
String? _cachedContentType;
Uri? _downloadUrl;
DeezerAudioSource({
required AudioQuality Function() getQuality,
required String trackId,
required String md5origin,
required String mediaVersion,
required this.trackToken,
required this.trackTokenExpiration,
this.onStreamObtained,
}) {
_getQuality = getQuality;
_initialQuality = quality;
_trackId = trackId;
_md5origin = md5origin;
_mediaVersion = mediaVersion;
}
AudioQuality? get quality => _currentQuality;
String get trackId => _trackId;
String get md5origin => _md5origin;
String get mediaVersion => _mediaVersion;
static const chunkSize = 2048;
void _updateTrackData(Map trackData) {
_trackId = trackData["SNG_ID"];
_md5origin = trackData["MD5_ORIGIN"];
_mediaVersion = trackData["MEDIA_VERSION"];
}
Future<Uri> _fallbackUrl() async {
_logger.finer("called _fallbackUrl()");
try {
return await _qualityFallback();
} on QualityException {
_logger.warning("quality fallback failed! trying trackId fallback");
_currentQuality = _initialQuality;
}
Map? privateJson;
try {
// TRACK ID FALLBACK
final data = await deezerAPI
.callApi("deezer.pageTrack", params: {"sng_id": trackId});
privateJson = data["results"]["DATA"];
if (privateJson!.containsKey("FALLBACK")) {
String fallbackId = privateJson["FALLBACK"]["SNG_ID"];
if (fallbackId != trackId) {
final newPrivate =
await deezerAPI.callApi("song.getListData", params: {
"sng_ids": [fallbackId]
});
final Map trackData = newPrivate["results"]["data"][0];
_updateTrackData(trackData);
return await _fallbackUrl();
}
}
} catch (e, st) {
_logger.warning("ID fallback failed! Trying ISRC fallback!", e, st);
}
try {
final data =
await deezerAPI.callPublicApi("track/isrc:${privateJson!["ISRC"]!}");
final newId = data["id"] as int;
if (newId == int.parse(trackId)) throw Exception();
final newPrivate = await deezerAPI.callApi("song.getListData", params: {
"sng_ids": [newId]
});
final trackData = newPrivate["results"]["data"][0] as Map;
_updateTrackData(trackData);
return await _fallbackUrl();
} catch (e, st) {
_logger.severe("ISRC Fallback failed, track unavailable!", e, st);
}
throw Exception(
"Every known method of fallback failed, track unavailable!");
}
Future<Uri> _qualityFallback() async {
// only use url generation with MP3_128
_currentQuality = AudioQuality.MP3_128;
final genUri = _generateTrackUri();
final req = await http.head(genUri, headers: {
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*'
});
int rc = req.statusCode;
if (rc > 400) {
_logger.warning(
"quality fallback, response code: $rc, current quality: $quality");
switch (_currentQuality) {
case AudioQuality.FLAC:
_currentQuality = AudioQuality.MP3_320;
break;
case AudioQuality.MP3_320:
_currentQuality = AudioQuality.MP3_128;
break;
case AudioQuality.MP3_128:
default:
_currentQuality = null;
throw QualityException("No quality to fallback to!");
}
return await _qualityFallback();
}
return genUri;
}
Uri _generateTrackUri() {
int magic = 164;
// step 1
final step1 = <int>[
...md5origin.codeUnits,
magic,
...quality!.toDeezerQualityInt().toString().codeUnits,
magic,
...trackId.codeUnits,
magic,
...mediaVersion.codeUnits,
];
// get md5 hash of step1
final md5hex = md5.convert(step1).toString().toLowerCase();
// step2
final step2 = <int>[
...md5hex.codeUnits,
magic,
...step1,
magic,
];
// pad step2 with dots to get correct length
while (step2.length % 16 > 0) {
// step2.addAll(('.' * (step2.length % 16)).codeUnits);
step2.add(46);
}
// encrypt with AES ECB
final k = Uint8List.fromList('jo6aey6haid2Teih'.codeUnits);
final encrypter = Encrypter(AES(Key(k), mode: AESMode.ecb, padding: null));
final String step3 = encrypter
.encryptBytes(step2, iv: IV(Uint8List(8)))
.base16
.toLowerCase();
final uri =
Uri.https('e-cdns-proxy-${md5origin[0]}.dzcdn.net', '/mobile/1/$step3');
return uri;
}
static List<int> getKey(String id) {
final secret = utf8.encode('g4el58wc0zvf9na1');
final idmd5 =
utf8.encode(md5.convert(utf8.encode(id)).toString().toLowerCase());
final buffer = <int>[];
for (int i = 0; i < 16; i++) {
final s0 = idmd5[i];
final s1 = idmd5[i + 16];
final s2 = secret[i];
buffer.add(s0 ^ s1 ^ s2);
}
return buffer;
}
static List<int> decryptChunk(List<int> key, List<int> data) {
final Uint8List iv =
Uint8List.fromList(const [00, 01, 02, 03, 04, 05, 06, 07]);
final bf = Blowfish(
key: Uint8List.fromList(key), mode: Mode.cbc, padding: Padding.none)
..setIv(iv);
Uint8List decrypted = bf.decode(data, returnType: Type.uInt8Array);
return decrypted;
}
static Stream<List<int>> decryptionStream(Stream<List<int>> source,
{required int start, required String trackId}) async* {
var dropBytes = start % 2048;
final deezerStart = start - dropBytes;
int counter = deezerStart ~/ chunkSize;
final buffer = List<int>.empty(growable: true);
final key = getKey(trackId);
await for (var bytes in source) {
buffer.addAll(bytes);
int i;
for (i = 0; i < buffer.length; i += chunkSize) {
if (buffer.length <= i + chunkSize) {
break;
}
bytes = buffer.sublist(i, i + chunkSize);
if ((counter % 3) == 0) {
bytes = decryptChunk(key, bytes);
}
if (dropBytes > 0) {
bytes = bytes.sublist(dropBytes);
dropBytes = 0;
}
counter++;
yield bytes;
}
if (i < buffer.length) {
buffer.removeRange(0, i);
} else {
buffer.clear();
}
}
// add remaining items in buffer
if (buffer.isNotEmpty) {
if (dropBytes > 0) {
yield buffer.sublist(dropBytes);
return;
}
yield buffer;
}
}
Future<Uri?> _getUrl() async {
final String actualTrackToken;
if (DateTime.now().millisecondsSinceEpoch ~/ 1000 > trackTokenExpiration) {
if (!deezerAPI.canStreamHQ && !deezerAPI.canStreamLossless) {
_logger.fine('using old url generation, token expired.');
}
final newTrack = await deezerAPI.track(trackId);
actualTrackToken = newTrack.trackToken!;
} else {
actualTrackToken = trackToken;
}
final res = await deezerAPI.getTrackUrl(
actualTrackToken, _currentQuality!.toDeezerQualityString());
if (res.error != null) {
_logger.warning('Error while getting track url: ${res.error!}');
return null;
}
if (res.sources == null) {
_logger.warning('Error while getting track url: No sources!');
return null;
}
return Uri.parse(res.sources![0].url);
}
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
start ??= 0;
if (_cachedSourceLength != null) {
if (start == _cachedSourceLength) {
return StreamAudioResponse(
sourceLength: _cachedSourceLength,
contentLength: 0,
offset: start,
stream: const Stream<List<int>>.empty(),
contentType: _cachedContentType!);
}
}
_logger.fine("authorizing...");
if (!await deezerAPI.authorize()) {
_logger.severe("authorization failed! cannot continue!");
throw Exception("Authorization failed!");
}
// determine quality to use
final newQuality = _getQuality.call();
if (_downloadUrl != null && _currentQuality != newQuality) {
// update currentUrl to get tracks with new quality
_downloadUrl = null;
}
_currentQuality = newQuality;
if (_downloadUrl == null) {
final gottenUrl = await _getUrl();
if (gottenUrl != null) {
_downloadUrl = gottenUrl;
} else {
_logger.warning('falling back to old url generation!');
// OLD URL GENERATION
try {
_downloadUrl = await _fallbackUrl();
} on QualityException {
rethrow;
}
}
}
_logger.fine("Downloading track from ${_downloadUrl!.toString()}");
final int deezerStart = start - (start % 2048);
final req = http.Request('GET', _downloadUrl!)
..headers.addAll({
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*',
if (deezerStart > 0)
"Range": "bytes=$deezerStart-${end == null ? '' : end.toString()}"
});
final res = await req.send();
final rc = res.statusCode;
_logger
.finest("request sent! rc: $rc, content-length: ${res.contentLength}");
onStreamObtained?.call(StreamQualityInfo(
format: quality == AudioQuality.FLAC ? Format.FLAC : Format.MP3,
source: Source.stream,
size: start + res.contentLength!,
quality: quality,
));
if (rc != HttpStatus.ok && rc != HttpStatus.partialContent) {
throw Exception(await res.stream.bytesToString());
}
int dropBytes = start % chunkSize;
_logger.finest(
"deezerStart: $deezerStart (actual start: $start), end: $end, dropBytes: $dropBytes");
final stream = decryptionStream(res.stream, start: start, trackId: trackId);
final cl = res.contentLength! - dropBytes;
if (end == null) {
_cachedSourceLength = cl + start;
}
return StreamAudioResponse(
sourceLength: _cachedSourceLength,
contentLength: cl,
offset: start,
stream: stream,
contentType: _cachedContentType =
quality == AudioQuality.FLAC ? "audio/flac" : "audio/mpeg");
}
}
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:encrypt/encrypt.dart';
import 'package:flutter/foundation.dart' as flutter;
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart';
import 'package:freezer/settings.dart';
import 'package:just_audio/just_audio.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:dart_blowfish/dart_blowfish.dart';
import 'package:logging/logging.dart';
import 'package:scrobblenaut/lastfm.dart';
typedef _IsolateMessage = (
Stream<List<int>> source,
int start,
String trackId,
SendPort sendPort
);
// Maybe better implementation of Blowfish CBC instead of random-ass, unpublished library from github?
// This class can be considered a rewrite in Dart of the Java backend (from the StreamServer.deezer() function and also from the Deezer class)
class DeezerAudioSource extends StreamAudioSource {
final _logger = Logger("DeezerAudioSource");
late AudioQuality Function() _getQuality;
late AudioQuality? _initialQuality;
late String _trackId;
late String _md5origin;
late String _mediaVersion;
final String trackToken;
final int trackTokenExpiration;
final StreamInfoCallback? onStreamObtained;
// some cache
AudioQuality? _currentQuality;
int? _cachedSourceLength;
String? _cachedContentType;
Uri? _downloadUrl;
DeezerAudioSource({
required AudioQuality Function() getQuality,
required String trackId,
required String md5origin,
required String mediaVersion,
required this.trackToken,
required this.trackTokenExpiration,
this.onStreamObtained,
}) {
_getQuality = getQuality;
_initialQuality = quality;
_trackId = trackId;
_md5origin = md5origin;
_mediaVersion = mediaVersion;
}
AudioQuality? get quality => _currentQuality;
String get trackId => _trackId;
String get md5origin => _md5origin;
String get mediaVersion => _mediaVersion;
static const chunkSize = 2048;
void _updateTrackData(Map trackData) {
_trackId = trackData["SNG_ID"];
_md5origin = trackData["MD5_ORIGIN"];
_mediaVersion = trackData["MEDIA_VERSION"];
}
Future<Uri> _fallbackUrl() async {
_logger.finer("called _fallbackUrl()");
try {
return await _qualityFallback();
} on QualityException {
_logger.warning("quality fallback failed! trying trackId fallback");
_currentQuality = _initialQuality;
}
Map? privateJson;
try {
// TRACK ID FALLBACK
final data = await deezerAPI
.callApi("deezer.pageTrack", params: {"sng_id": trackId});
privateJson = data["results"]["DATA"];
if (privateJson!.containsKey("FALLBACK")) {
String fallbackId = privateJson["FALLBACK"]["SNG_ID"];
if (fallbackId != trackId) {
final newPrivate =
await deezerAPI.callApi("song.getListData", params: {
"sng_ids": [fallbackId]
});
final Map trackData = newPrivate["results"]["data"][0];
_updateTrackData(trackData);
return await _fallbackUrl();
}
}
} catch (e, st) {
_logger.warning("ID fallback failed! Trying ISRC fallback!", e, st);
}
try {
final data =
await deezerAPI.callPublicApi("track/isrc:${privateJson!["ISRC"]!}");
final newId = data["id"] as int;
if (newId == int.parse(trackId)) throw Exception();
final newPrivate = await deezerAPI.callApi("song.getListData", params: {
"sng_ids": [newId]
});
final trackData = newPrivate["results"]["data"][0] as Map;
_updateTrackData(trackData);
return await _fallbackUrl();
} catch (e, st) {
_logger.severe("ISRC Fallback failed, track unavailable!", e, st);
}
throw Exception(
"Every known method of fallback failed, track unavailable!");
}
Future<Uri> _qualityFallback() async {
// only use url generation with MP3_128
_currentQuality = AudioQuality.MP3_128;
final genUri = _generateTrackUri();
final req = await http.head(genUri, headers: {
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*'
});
int rc = req.statusCode;
if (rc > 400) {
_logger.warning(
"quality fallback, response code: $rc, current quality: $quality");
switch (_currentQuality) {
case AudioQuality.FLAC:
_currentQuality = AudioQuality.MP3_320;
break;
case AudioQuality.MP3_320:
_currentQuality = AudioQuality.MP3_128;
break;
case AudioQuality.MP3_128:
default:
_currentQuality = null;
throw QualityException("No quality to fallback to!");
}
return await _qualityFallback();
}
return genUri;
}
Uri _generateTrackUri() {
int magic = 164;
// step 1
final step1 = <int>[
...md5origin.codeUnits,
magic,
...quality!.toDeezerQualityInt().toString().codeUnits,
magic,
...trackId.codeUnits,
magic,
...mediaVersion.codeUnits,
];
// get md5 hash of step1
final md5hex = md5.convert(step1).toString().toLowerCase();
// step2
final step2 = <int>[
...md5hex.codeUnits,
magic,
...step1,
magic,
];
// pad step2 with dots to get correct length
while (step2.length % 16 > 0) {
// step2.addAll(('.' * (step2.length % 16)).codeUnits);
step2.add(46);
}
// encrypt with AES ECB
final k = Uint8List.fromList('jo6aey6haid2Teih'.codeUnits);
final encrypter = Encrypter(AES(Key(k), mode: AESMode.ecb, padding: null));
final String step3 = encrypter
.encryptBytes(step2, iv: IV(Uint8List(8)))
.base16
.toLowerCase();
final uri =
Uri.https('e-cdns-proxy-${md5origin[0]}.dzcdn.net', '/mobile/1/$step3');
return uri;
}
static List<int> getKey(String id) {
final secret = utf8.encode('g4el58wc0zvf9na1');
final idmd5 =
utf8.encode(md5.convert(utf8.encode(id)).toString().toLowerCase());
final buffer = <int>[];
for (int i = 0; i < 16; i++) {
final s0 = idmd5[i];
final s1 = idmd5[i + 16];
final s2 = secret[i];
buffer.add(s0 ^ s1 ^ s2);
}
return buffer;
}
static List<int> decryptChunk(List<int> key, List<int> data) {
final Uint8List iv =
Uint8List.fromList(const [00, 01, 02, 03, 04, 05, 06, 07]);
final bf = Blowfish(
key: Uint8List.fromList(key), mode: Mode.cbc, padding: Padding.none)
..setIv(iv);
Uint8List decrypted = bf.decode(data, returnType: Type.uInt8Array);
return decrypted;
}
static Stream<List<int>> decryptionStream(Stream<List<int>> source,
{required int start, required String trackId}) async* {
var dropBytes = start % 2048;
final deezerStart = start - dropBytes;
int counter = deezerStart ~/ chunkSize;
final buffer = List<int>.empty(growable: true);
final key = getKey(trackId);
await for (var bytes in source) {
buffer.addAll(bytes);
int i;
for (i = 0; i < buffer.length; i += chunkSize) {
if (buffer.length <= i + chunkSize) {
break;
}
bytes = buffer.sublist(i, i + chunkSize);
if ((counter % 3) == 0) {
bytes = decryptChunk(key, bytes);
}
if (dropBytes > 0) {
bytes = bytes.sublist(dropBytes);
dropBytes = 0;
}
counter++;
yield bytes;
}
if (i < buffer.length) {
buffer.removeRange(0, i);
} else {
buffer.clear();
}
}
// add remaining items in buffer
if (buffer.isNotEmpty) {
if (dropBytes > 0) {
yield buffer.sublist(dropBytes);
return;
}
yield buffer;
}
}
Future<Uri?> _getUrl() async {
final String actualTrackToken;
if (DateTime.now().millisecondsSinceEpoch ~/ 1000 > trackTokenExpiration) {
if (!deezerAPI.canStreamHQ && !deezerAPI.canStreamLossless) {
_logger.fine('using old url generation, token expired.');
}
final newTrack = await deezerAPI.track(trackId);
actualTrackToken = newTrack.trackToken!;
} else {
actualTrackToken = trackToken;
}
final res = await deezerAPI.getTrackUrl(
actualTrackToken, _currentQuality!.toDeezerQualityString());
if (res.error != null) {
_logger.warning('Error while getting track url: ${res.error!}');
return null;
}
if (res.sources == null) {
_logger.warning('Error while getting track url: No sources!');
return null;
}
return Uri.parse(res.sources![0].url);
}
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
start ??= 0;
if (_cachedSourceLength != null) {
if (start == _cachedSourceLength) {
return StreamAudioResponse(
sourceLength: _cachedSourceLength,
contentLength: 0,
offset: start,
stream: const Stream<List<int>>.empty(),
contentType: _cachedContentType!);
}
}
_logger.fine("authorizing...");
if (!await deezerAPI.authorize()) {
_logger.severe("authorization failed! cannot continue!");
throw Exception("Authorization failed!");
}
// determine quality to use
final newQuality = _getQuality.call();
if (_downloadUrl != null && _currentQuality != newQuality) {
// update currentUrl to get tracks with new quality
_downloadUrl = null;
}
_currentQuality = newQuality;
if (_downloadUrl == null) {
final gottenUrl = await _getUrl();
if (gottenUrl != null) {
_downloadUrl = gottenUrl;
} else {
_logger.warning('falling back to old url generation!');
// OLD URL GENERATION
try {
_downloadUrl = await _fallbackUrl();
} on QualityException {
rethrow;
}
}
}
_logger.fine("Downloading track from ${_downloadUrl!.toString()}");
final int deezerStart = start - (start % 2048);
final req = http.Request('GET', _downloadUrl!)
..headers.addAll({
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*',
if (deezerStart > 0)
"Range": "bytes=$deezerStart-${end == null ? '' : end.toString()}"
});
final res = await req.send();
final rc = res.statusCode;
_logger
.finest("request sent! rc: $rc, content-length: ${res.contentLength}");
onStreamObtained?.call(StreamQualityInfo(
format: quality == AudioQuality.FLAC ? Format.FLAC : Format.MP3,
source: Source.stream,
size: start + res.contentLength!,
quality: quality,
));
if (rc != HttpStatus.ok && rc != HttpStatus.partialContent) {
throw Exception(await res.stream.bytesToString());
}
int dropBytes = start % chunkSize;
_logger.finest(
"deezerStart: $deezerStart (actual start: $start), end: $end, dropBytes: $dropBytes");
final stream = decryptionStream(res.stream, start: start, trackId: trackId);
final cl = res.contentLength! - dropBytes;
if (end == null) {
_cachedSourceLength = cl + start;
}
return StreamAudioResponse(
sourceLength: _cachedSourceLength,
contentLength: cl,
offset: start,
stream: stream,
contentType: _cachedContentType =
quality == AudioQuality.FLAC ? "audio/flac" : "audio/mpeg");
}
}

View File

@ -1,3 +1,3 @@
import 'package:isar/isar.dart';
class T {}
import 'package:isar/isar.dart';
class T {}

View File

@ -1,30 +1,30 @@
import 'dart:io';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:freezer/api/download_manager/download_service.dart';
import 'package:freezer/translations.i18n.dart';
class DownloadManager {
Future<bool> startService() {
if (Platform.isAndroid) {
return FlutterBackgroundService().configure(
iosConfiguration: IosConfiguration(), // fuck ios
androidConfiguration: AndroidConfiguration(
onStart: _startService,
isForegroundMode: false,
autoStart: false,
autoStartOnBoot: false,
foregroundServiceNotificationId: DownloadService.NOTIFICATION_ID,
notificationChannelId: DownloadService.NOTIFICATION_CHANNEL_ID,
initialNotificationTitle: 'Freezer'.i18n,
initialNotificationContent: 'Starting download service...'.i18n,
));
}
_startService(null);
return Future.value(true);
}
static void _startService(ServiceInstance? service) =>
DownloadService(service).run();
}
import 'dart:io';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:freezer/api/download_manager/download_service.dart';
import 'package:freezer/translations.i18n.dart';
class DownloadManager {
Future<bool> startService() {
if (Platform.isAndroid) {
return FlutterBackgroundService().configure(
iosConfiguration: IosConfiguration(), // fuck ios
androidConfiguration: AndroidConfiguration(
onStart: _startService,
isForegroundMode: false,
autoStart: false,
autoStartOnBoot: false,
foregroundServiceNotificationId: DownloadService.NOTIFICATION_ID,
notificationChannelId: DownloadService.NOTIFICATION_CHANNEL_ID,
initialNotificationTitle: 'Freezer'.i18n,
initialNotificationContent: 'Starting download service...'.i18n,
));
}
_startService(null);
return Future.value(true);
}
static void _startService(ServiceInstance? service) =>
DownloadService(service).run();
}

View File

@ -1,11 +1,11 @@
import 'package:flutter_background_service/flutter_background_service.dart';
class DownloadService {
static const NOTIFICATION_ID = 6969;
static const NOTIFICATION_CHANNEL_ID = "freezerdownloads";
final ServiceInstance? service;
DownloadService(this.service);
void run() {}
}
import 'package:flutter_background_service/flutter_background_service.dart';
class DownloadService {
static const NOTIFICATION_ID = 6969;
static const NOTIFICATION_CHANNEL_ID = "freezerdownloads";
final ServiceInstance? service;
DownloadService(this.service);
void run() {}
}

View File

@ -1,34 +1,34 @@
import 'dart:io';
import 'package:freezer/api/definitions.dart';
import 'package:just_audio/just_audio.dart';
class OfflineAudioSource extends StreamAudioSource {
final File file;
final StreamInfoCallback? onStreamObtained;
OfflineAudioSource(this.file, {this.onStreamObtained});
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
// determine content type
final first = await file.openRead(0, 4).join();
bool isFlac = false;
if (first == 'fLaC') isFlac = true;
final length = await file.length();
final stream = file.openRead(start, end);
onStreamObtained?.call(StreamQualityInfo(
format: isFlac ? Format.FLAC : Format.MP3,
source: Source.offline,
quality: null,
size: length));
return StreamAudioResponse(
sourceLength: length,
contentLength: (end ?? length) - (start ?? 0),
offset: start,
stream: stream,
contentType: isFlac ? 'audio/flac' : 'audio/mpeg');
}
}
import 'dart:io';
import 'package:freezer/api/definitions.dart';
import 'package:just_audio/just_audio.dart';
class OfflineAudioSource extends StreamAudioSource {
final File file;
final StreamInfoCallback? onStreamObtained;
OfflineAudioSource(this.file, {this.onStreamObtained});
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
// determine content type
final first = await file.openRead(0, 4).join();
bool isFlac = false;
if (first == 'fLaC') isFlac = true;
final length = await file.length();
final stream = file.openRead(start, end);
onStreamObtained?.call(StreamQualityInfo(
format: isFlac ? Format.FLAC : Format.MP3,
source: Source.offline,
quality: null,
size: length));
return StreamAudioResponse(
sourceLength: length,
contentLength: (end ?? length) - (start ?? 0),
offset: start,
stream: stream,
contentType: isFlac ? 'audio/flac' : 'audio/mpeg');
}
}

View File

@ -1,49 +1,54 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
class Paths {
static Future<String> dataDirectory() async {
switch (defaultTargetPlatform) {
case TargetPlatform.linux:
final home = Platform.environment['HOME'];
if (home == null) return path.dirname(Platform.resolvedExecutable);
if (await Directory(path.join(home, '.local', 'share')).exists()) {
final target =
await Directory(path.join(home, '.local', 'share', 'freezer'))
.create();
return target.path;
}
return path.dirname(Platform.resolvedExecutable);
case TargetPlatform.windows:
String? home = Platform.environment['USERPROFILE'];
if (home == null) {
final drive = Platform.environment['HOMEDRIVE'];
final homepath = Platform.environment['HOMEPATH'];
if (drive == null || homepath == null) {
return path.dirname(Platform.resolvedExecutable);
}
home = drive + homepath;
}
final target =
await Directory(path.join(home, 'AppData', 'Freezer')).create();
return target.path;
default:
return (await getApplicationDocumentsDirectory()).path;
}
}
static Future<String> cacheDir() async {
if (Platform.isLinux || Platform.isWindows) {
final dataDir = await dataDirectory();
final target = await Directory(path.join(dataDir, 'cache')).create();
return target.path;
}
return (await getTemporaryDirectory()).path;
}
}
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
class Paths {
static Future<String> dataDirectory() async {
switch (defaultTargetPlatform) {
case TargetPlatform.linux:
final home = Platform.environment['HOME'];
if (home == null) return path.dirname(Platform.resolvedExecutable);
if (await Directory(path.join(home, '.local', 'share')).exists()) {
final target =
await Directory(path.join(home, '.local', 'share', 'freezer'))
.create();
return target.path;
}
return path.dirname(Platform.resolvedExecutable);
case TargetPlatform.windows:
final String? localAppData = Platform.environment['LOCALAPPDATA'];
if (localAppData != null) {
final target = await Directory(path.join(localAppData, 'Freezer')).create();
return target.path;
}
String? home = Platform.environment['USERPROFILE'];
if (home == null) {
final drive = Platform.environment['HOMEDRIVE'];
final homepath = Platform.environment['HOMEPATH'];
if (drive == null || homepath == null) {
return path.dirname(Platform.resolvedExecutable);
}
home = drive + homepath;
}
final target =
await Directory(path.join(home, 'AppData', 'Local', 'Freezer')).create();
return target.path;
default:
return (await getApplicationDocumentsDirectory()).path;
}
}
static Future<String> cacheDir() async {
if (Platform.isLinux || Platform.isWindows) {
final dataDir = await dataDirectory();
final target = await Directory(path.join(dataDir, 'cache')).create();
return target.path;
}
return (await getTemporaryDirectory()).path;
}
}

View File

@ -1,304 +1,304 @@
import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:equalizer/equalizer.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:freezer/api/cache.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart';
import 'package:freezer/api/player/audio_handler.dart';
import 'package:freezer/settings.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
class PlayerHelper {
late StreamSubscription _customEventSubscription;
late StreamSubscription _mediaItemSubscription;
late StreamSubscription _playbackStateStreamSubscription;
QueueSource? queueSource;
AudioServiceRepeatMode repeatType = AudioServiceRepeatMode.none;
int? audioSession;
int? _prevAudioSession;
bool equalizerOpen = false;
bool _shuffleEnabled = false;
int _queueIndex = 0;
bool _started = false;
/// Whether this system supports the [Connectivity] plugin or not
bool _isConnectivityPluginAvailable = true;
bool get isConnectivityPluginAvailable => _isConnectivityPluginAvailable;
//Visualizer
// StreamController _visualizerController = StreamController.broadcast();
// Stream get visualizerStream => _visualizerController.stream;
final _streamInfoSubject = BehaviorSubject<StreamQualityInfo>();
ValueStream<StreamQualityInfo> get streamInfo => _streamInfoSubject.stream;
final _bufferPositionSubject = BehaviorSubject<Duration>();
ValueStream<Duration> get bufferPosition => _bufferPositionSubject.stream;
/// Find queue index by id
///
/// The function gets more expensive the longer the queue is and the further the element is from the beginning.
int getQueueIndexFromId() => audioHandler.mediaItem.value == null
? -1
: audioHandler.queue.value
.indexWhere((mi) => mi.id == audioHandler.mediaItem.value!.id);
int getQueueIndex() =>
audioHandler.playbackState.value.queueIndex ?? getQueueIndexFromId();
int get queueIndex => _queueIndex;
Future<void> initAudioHandler() async {
final initArgs = AudioPlayerTaskInitArguments.from(
settings: settings, deezerAPI: deezerAPI);
// initialize our audiohandler instance
audioHandler = await AudioService.init(
builder: () => AudioPlayerTask(initArgs),
config: AudioServiceConfig(
notificationColor: settings.primaryColor,
androidStopForegroundOnPause: false,
androidNotificationOngoing: false,
androidNotificationClickStartsActivity: true,
androidNotificationChannelDescription: 'Freezer',
androidNotificationChannelName: 'Freezer',
androidNotificationIcon: 'drawable/ic_logo',
preloadArtwork: false,
),
);
}
Future<void> start() async {
if (_started) return;
_started = true;
//Subscribe to custom events
_customEventSubscription = audioHandler.customEvent.listen((event) async {
if (event is! Map) return;
Logger('PlayerHelper').fine("event received: ${event['action']}");
switch (event['action']) {
case 'onLoad':
//After audio_service is loaded, load queue, set quality
await settings.updateAudioServiceQuality();
break;
case 'onRestore':
//Load queueSource from isolate
queueSource = event['queueSource'] as QueueSource;
repeatType = event['repeatMode'] as AudioServiceRepeatMode;
_queueIndex = getQueueIndex();
break;
case 'audioSession':
if (!settings.enableEqualizer) break;
//Save
_prevAudioSession = audioSession;
audioSession = event['id'];
if (audioSession == null) break;
//Open EQ
if (!equalizerOpen) {
Equalizer.open(event['id']);
equalizerOpen = true;
break;
}
//Change session id
if (_prevAudioSession != audioSession) {
if (_prevAudioSession != null) {
Equalizer.removeAudioSessionId(_prevAudioSession!);
}
Equalizer.setAudioSessionId(audioSession!);
}
break;
//Visualizer data
// case 'visualizer':
// _visualizerController.add(event['data']);
// break;
case 'streamInfo':
Logger('PlayerHelper').fine("streamInfo received");
_streamInfoSubject.add(event['data'] as StreamQualityInfo);
break;
case 'bufferPosition':
_bufferPositionSubject.add(event['data'] as Duration);
break;
case 'connectivityPlugin':
_isConnectivityPluginAvailable = event['available'] as bool;
break;
}
});
_mediaItemSubscription = audioHandler.mediaItem.listen((mediaItem) async {
if (mediaItem == null) return;
_queueIndex = getQueueIndex();
//Load more flow if last song (not using .last since it iterates through previous elements first)
//Save queue
await audioHandler.customAction('saveQueue', {});
//Add to history
if (cache.history.isNotEmpty && cache.history.last.id == mediaItem.id) {
return;
}
cache.history.add(Track.fromMediaItem(mediaItem));
cache.save();
});
//Start audio_service
// await startService(); it is already ready, there is no need to start it
}
Future<void> authorizeLastFM() async {
if (settings.lastFMUsername == null || settings.lastFMPassword == null) {
return;
}
await audioHandler.customAction('authorizeLastFM', {
'username': settings.lastFMUsername,
'password': settings.lastFMPassword
});
}
Future<bool> toggleShuffle() async {
_shuffleEnabled = !_shuffleEnabled;
await audioHandler.setShuffleMode(_shuffleEnabled
? AudioServiceShuffleMode.all
: AudioServiceShuffleMode.none);
return _shuffleEnabled;
}
bool get shuffleEnabled => _shuffleEnabled;
//Repeat toggle
Future changeRepeat() async {
//Change to next repeat type
repeatType = repeatType == AudioServiceRepeatMode.all
? AudioServiceRepeatMode.none
: repeatType == AudioServiceRepeatMode.none
? AudioServiceRepeatMode.one
: AudioServiceRepeatMode.all;
//Set repeat type
await audioHandler.setRepeatMode(repeatType);
}
//Executed before exit
Future onExit() async {
_customEventSubscription.cancel();
_playbackStateStreamSubscription.cancel();
_mediaItemSubscription.cancel();
}
//Replace queue, play specified track id
Future<void> _loadQueuePlay(List<MediaItem> queue, String? trackId) async {
await settings.updateAudioServiceQuality();
await audioHandler.customAction('setIndex', {
'index': trackId == null ? 0 : queue.indexWhere((m) => m.id == trackId)
});
await audioHandler.updateQueue(queue);
// if (queue[0].id != trackId)
// await AudioService.skipToQueueItem(trackId);
if (!audioHandler.playbackState.value.playing) audioHandler.play();
}
//Play track from album
Future playFromAlbum(Album album, [String? trackId]) async {
await playFromTrackList(album.tracks!, trackId,
QueueSource(id: album.id, text: album.title, source: 'album'));
}
//Play mix by track
Future playMix(String trackId, String trackTitle) async {
List<Track> tracks = (await deezerAPI.playMix(trackId))!;
playFromTrackList(
tracks,
tracks[0].id,
QueueSource(
id: trackId,
text: '${'Mix based on'.i18n} $trackTitle',
source: 'mix'));
}
//Play from artist top tracks
Future playFromTopTracks(
List<Track> tracks, String trackId, Artist artist) async {
await playFromTrackList(
tracks,
trackId,
QueueSource(
id: artist.id, text: 'Top ${artist.name}', source: 'topTracks'));
}
Future playFromPlaylist(Playlist playlist, [String? trackId]) async {
await playFromTrackList(playlist.tracks!, trackId,
QueueSource(id: playlist.id, text: playlist.title, source: 'playlist'));
}
//Play episode from show, load whole show as queue
Future<void> playShowEpisode(Show show, List<ShowEpisode> episodes,
{int index = 0}) async {
QueueSource queueSource =
QueueSource(id: show.id, text: show.name, source: 'show');
//Generate media items
List<MediaItem> queue =
episodes.map<MediaItem>((e) => e.toMediaItem(show)).toList();
//Load and play
// await startService(); // audioservice is ready
await settings.updateAudioServiceQuality();
await setQueueSource(queueSource);
await audioHandler.customAction('setIndex', {'index': index});
await audioHandler.updateQueue(queue);
if (!audioHandler.playbackState.value.playing) audioHandler.play();
}
//Load tracks as queue, play track id, set queue source
Future playFromTrackList(
List<Track?> tracks, String? trackId, QueueSource queueSource) async {
final queue = await Future.wait(tracks
.map<Future<MediaItem>>((track) => track!.toMediaItem())
.toList());
await setQueueSource(queueSource);
await _loadQueuePlay(queue, trackId);
}
//Load smart track list as queue, start from beginning
Future playFromSmartTrackList(SmartTrackList stl) async {
//Load from API if no tracks
if (stl.tracks == null || stl.tracks!.isEmpty) {
if (settings.offlineMode) {
Fluttertoast.showToast(
msg: "Offline mode, can't play flow or smart track lists.".i18n,
gravity: ToastGravity.BOTTOM,
toastLength: Toast.LENGTH_SHORT);
return;
}
//Flow songs cannot be accessed by smart track list call
if (stl.id! == 'flow') {
stl.tracks = await deezerAPI.flow(stl.flowConfig);
} else {
stl = await deezerAPI.smartTrackList(stl.id);
}
}
QueueSource queueSource = QueueSource(
id: stl.flowConfig ?? stl.id,
source: (stl.id == 'flow') ? 'flow' : 'smarttracklist',
text: stl.title ??
((stl.id == 'flow') ? 'Flow'.i18n : 'Smart track list'.i18n));
await playFromTrackList(stl.tracks!, stl.tracks![0].id, queueSource);
}
Future setQueueSource(QueueSource queueSource) async {
this.queueSource = queueSource;
await audioHandler.customAction('queueSource', queueSource.toJson());
}
//Reorder tracks in queue
Future reorder(int oldIndex, int newIndex) => audioHandler
.customAction('reorder', {'oldIndex': oldIndex, 'newIndex': newIndex});
//Start visualizer
// Future startVisualizer() async {
// await audioHandler.customAction('startVisualizer');
// }
//Stop visualizer
// Future stopVisualizer() async {
// await audioHandler.customAction('stopVisualizer');
// }
}
import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:equalizer/equalizer.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:freezer/api/cache.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart';
import 'package:freezer/api/player/audio_handler.dart';
import 'package:freezer/settings.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
class PlayerHelper {
late StreamSubscription _customEventSubscription;
late StreamSubscription _mediaItemSubscription;
late StreamSubscription _playbackStateStreamSubscription;
QueueSource? queueSource;
AudioServiceRepeatMode repeatType = AudioServiceRepeatMode.none;
int? audioSession;
int? _prevAudioSession;
bool equalizerOpen = false;
bool _shuffleEnabled = false;
int _queueIndex = 0;
bool _started = false;
/// Whether this system supports the [Connectivity] plugin or not
bool _isConnectivityPluginAvailable = true;
bool get isConnectivityPluginAvailable => _isConnectivityPluginAvailable;
//Visualizer
// StreamController _visualizerController = StreamController.broadcast();
// Stream get visualizerStream => _visualizerController.stream;
final _streamInfoSubject = BehaviorSubject<StreamQualityInfo>();
ValueStream<StreamQualityInfo> get streamInfo => _streamInfoSubject.stream;
final _bufferPositionSubject = BehaviorSubject<Duration>();
ValueStream<Duration> get bufferPosition => _bufferPositionSubject.stream;
/// Find queue index by id
///
/// The function gets more expensive the longer the queue is and the further the element is from the beginning.
int getQueueIndexFromId() => audioHandler.mediaItem.value == null
? -1
: audioHandler.queue.value
.indexWhere((mi) => mi.id == audioHandler.mediaItem.value!.id);
int getQueueIndex() =>
audioHandler.playbackState.value.queueIndex ?? getQueueIndexFromId();
int get queueIndex => _queueIndex;
Future<void> initAudioHandler() async {
final initArgs = AudioPlayerTaskInitArguments.from(
settings: settings, deezerAPI: deezerAPI);
// initialize our audiohandler instance
audioHandler = await AudioService.init(
builder: () => AudioPlayerTask(initArgs),
config: AudioServiceConfig(
notificationColor: settings.primaryColor,
androidStopForegroundOnPause: false,
androidNotificationOngoing: false,
androidNotificationClickStartsActivity: true,
androidNotificationChannelDescription: 'Freezer',
androidNotificationChannelName: 'Freezer',
androidNotificationIcon: 'drawable/ic_logo',
preloadArtwork: false,
),
);
}
Future<void> start() async {
if (_started) return;
_started = true;
//Subscribe to custom events
_customEventSubscription = audioHandler.customEvent.listen((event) async {
if (event is! Map) return;
Logger('PlayerHelper').fine("event received: ${event['action']}");
switch (event['action']) {
case 'onLoad':
//After audio_service is loaded, load queue, set quality
await settings.updateAudioServiceQuality();
break;
case 'onRestore':
//Load queueSource from isolate
queueSource = event['queueSource'] as QueueSource;
repeatType = event['repeatMode'] as AudioServiceRepeatMode;
_queueIndex = getQueueIndex();
break;
case 'audioSession':
if (!settings.enableEqualizer) break;
//Save
_prevAudioSession = audioSession;
audioSession = event['id'];
if (audioSession == null) break;
//Open EQ
if (!equalizerOpen) {
Equalizer.open(event['id']);
equalizerOpen = true;
break;
}
//Change session id
if (_prevAudioSession != audioSession) {
if (_prevAudioSession != null) {
Equalizer.removeAudioSessionId(_prevAudioSession!);
}
Equalizer.setAudioSessionId(audioSession!);
}
break;
//Visualizer data
// case 'visualizer':
// _visualizerController.add(event['data']);
// break;
case 'streamInfo':
Logger('PlayerHelper').fine("streamInfo received");
_streamInfoSubject.add(event['data'] as StreamQualityInfo);
break;
case 'bufferPosition':
_bufferPositionSubject.add(event['data'] as Duration);
break;
case 'connectivityPlugin':
_isConnectivityPluginAvailable = event['available'] as bool;
break;
}
});
_mediaItemSubscription = audioHandler.mediaItem.listen((mediaItem) async {
if (mediaItem == null) return;
_queueIndex = getQueueIndex();
//Load more flow if last song (not using .last since it iterates through previous elements first)
//Save queue
await audioHandler.customAction('saveQueue', {});
//Add to history
if (cache.history.isNotEmpty && cache.history.last.id == mediaItem.id) {
return;
}
cache.history.add(Track.fromMediaItem(mediaItem));
cache.save();
});
//Start audio_service
// await startService(); it is already ready, there is no need to start it
}
Future<void> authorizeLastFM() async {
if (settings.lastFMUsername == null || settings.lastFMPassword == null) {
return;
}
await audioHandler.customAction('authorizeLastFM', {
'username': settings.lastFMUsername,
'password': settings.lastFMPassword
});
}
Future<bool> toggleShuffle() async {
_shuffleEnabled = !_shuffleEnabled;
await audioHandler.setShuffleMode(_shuffleEnabled
? AudioServiceShuffleMode.all
: AudioServiceShuffleMode.none);
return _shuffleEnabled;
}
bool get shuffleEnabled => _shuffleEnabled;
//Repeat toggle
Future changeRepeat() async {
//Change to next repeat type
repeatType = repeatType == AudioServiceRepeatMode.all
? AudioServiceRepeatMode.none
: repeatType == AudioServiceRepeatMode.none
? AudioServiceRepeatMode.one
: AudioServiceRepeatMode.all;
//Set repeat type
await audioHandler.setRepeatMode(repeatType);
}
//Executed before exit
Future onExit() async {
_customEventSubscription.cancel();
_playbackStateStreamSubscription.cancel();
_mediaItemSubscription.cancel();
}
//Replace queue, play specified track id
Future<void> _loadQueuePlay(List<MediaItem> queue, String? trackId) async {
await settings.updateAudioServiceQuality();
await audioHandler.customAction('setIndex', {
'index': trackId == null ? 0 : queue.indexWhere((m) => m.id == trackId)
});
await audioHandler.updateQueue(queue);
// if (queue[0].id != trackId)
// await AudioService.skipToQueueItem(trackId);
if (!audioHandler.playbackState.value.playing) audioHandler.play();
}
//Play track from album
Future playFromAlbum(Album album, [String? trackId]) async {
await playFromTrackList(album.tracks!, trackId,
QueueSource(id: album.id, text: album.title, source: 'album'));
}
//Play mix by track
Future playMix(String trackId, String trackTitle) async {
List<Track> tracks = (await deezerAPI.playMix(trackId))!;
playFromTrackList(
tracks,
tracks[0].id,
QueueSource(
id: trackId,
text: '${'Mix based on'.i18n} $trackTitle',
source: 'mix'));
}
//Play from artist top tracks
Future playFromTopTracks(
List<Track> tracks, String trackId, Artist artist) async {
await playFromTrackList(
tracks,
trackId,
QueueSource(
id: artist.id, text: 'Top ${artist.name}', source: 'topTracks'));
}
Future playFromPlaylist(Playlist playlist, [String? trackId]) async {
await playFromTrackList(playlist.tracks!, trackId,
QueueSource(id: playlist.id, text: playlist.title, source: 'playlist'));
}
//Play episode from show, load whole show as queue
Future<void> playShowEpisode(Show show, List<ShowEpisode> episodes,
{int index = 0}) async {
QueueSource queueSource =
QueueSource(id: show.id, text: show.name, source: 'show');
//Generate media items
List<MediaItem> queue =
episodes.map<MediaItem>((e) => e.toMediaItem(show)).toList();
//Load and play
// await startService(); // audioservice is ready
await settings.updateAudioServiceQuality();
await setQueueSource(queueSource);
await audioHandler.customAction('setIndex', {'index': index});
await audioHandler.updateQueue(queue);
if (!audioHandler.playbackState.value.playing) audioHandler.play();
}
//Load tracks as queue, play track id, set queue source
Future playFromTrackList(
List<Track?> tracks, String? trackId, QueueSource queueSource) async {
final queue = await Future.wait(tracks
.map<Future<MediaItem>>((track) => track!.toMediaItem())
.toList());
await setQueueSource(queueSource);
await _loadQueuePlay(queue, trackId);
}
//Load smart track list as queue, start from beginning
Future playFromSmartTrackList(SmartTrackList stl) async {
//Load from API if no tracks
if (stl.tracks == null || stl.tracks!.isEmpty) {
if (settings.offlineMode) {
Fluttertoast.showToast(
msg: "Offline mode, can't play flow or smart track lists.".i18n,
gravity: ToastGravity.BOTTOM,
toastLength: Toast.LENGTH_SHORT);
return;
}
//Flow songs cannot be accessed by smart track list call
if (stl.id! == 'flow') {
stl.tracks = await deezerAPI.flow(stl.flowConfig);
} else {
stl = await deezerAPI.smartTrackList(stl.id);
}
}
QueueSource queueSource = QueueSource(
id: stl.flowConfig ?? stl.id,
source: (stl.id == 'flow') ? 'flow' : 'smarttracklist',
text: stl.title ??
((stl.id == 'flow') ? 'Flow'.i18n : 'Smart track list'.i18n));
await playFromTrackList(stl.tracks!, stl.tracks![0].id, queueSource);
}
Future setQueueSource(QueueSource queueSource) async {
this.queueSource = queueSource;
await audioHandler.customAction('queueSource', queueSource.toJson());
}
//Reorder tracks in queue
Future reorder(int oldIndex, int newIndex) => audioHandler
.customAction('reorder', {'oldIndex': oldIndex, 'newIndex': newIndex});
//Start visualizer
// Future startVisualizer() async {
// await audioHandler.customAction('startVisualizer');
// }
//Stop visualizer
// Future stopVisualizer() async {
// await audioHandler.customAction('stopVisualizer');
// }
}

View File

@ -1,39 +1,39 @@
import 'package:freezer/api/definitions.dart';
import 'package:just_audio/just_audio.dart';
import 'package:http/http.dart' as http;
import 'deezer.dart';
class UrlAudioSource extends StreamAudioSource {
final Uri uri;
final StreamInfoCallback? onStreamObtained;
UrlAudioSource(this.uri, {this.onStreamObtained});
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
final req = http.Request('GET', uri)
..headers.addAll({
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*',
if (start != null || end != null)
"Range":
"bytes=${start == null ? '' : start.toString()}-${end == null ? '' : end.toString()}"
});
final res = await req.send();
onStreamObtained?.call(StreamQualityInfo(
format: Format.MP3,
source: Source.stream,
quality: null,
size: res.contentLength! + (start ?? 0)));
return StreamAudioResponse(
sourceLength: res.contentLength! + (start ?? 0),
contentLength: res.contentLength!,
offset: start,
stream: res.stream,
contentType: res.headers['content-type']!);
}
}
import 'package:freezer/api/definitions.dart';
import 'package:just_audio/just_audio.dart';
import 'package:http/http.dart' as http;
import 'deezer.dart';
class UrlAudioSource extends StreamAudioSource {
final Uri uri;
final StreamInfoCallback? onStreamObtained;
UrlAudioSource(this.uri, {this.onStreamObtained});
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
final req = http.Request('GET', uri)
..headers.addAll({
'User-Agent': deezerAPI.headers['User-Agent']!,
'Accept-Language': '*',
'Accept': '*/*',
if (start != null || end != null)
"Range":
"bytes=${start == null ? '' : start.toString()}-${end == null ? '' : end.toString()}"
});
final res = await req.send();
onStreamObtained?.call(StreamQualityInfo(
format: Format.MP3,
source: Source.stream,
quality: null,
size: res.contentLength! + (start ?? 0)));
return StreamAudioResponse(
sourceLength: res.contentLength! + (start ?? 0),
contentLength: res.contentLength!,
offset: start,
stream: res.stream,
contentType: res.headers['content-type']!);
}
}

View File

@ -1,195 +1,195 @@
import 'package:flutter/material.dart';
class FancyScaffold extends StatefulWidget {
final Widget bottomPanel;
final double bottomPanelHeight;
final Widget expandedPanel;
final Widget? bottomNavigationBar;
final Widget? drawer;
final Widget? navigationRail;
final Widget body;
final void Function(AnimationStatus)? onAnimationStatusChange;
const FancyScaffold({
required this.bottomPanel,
required this.bottomPanelHeight,
required this.expandedPanel,
required this.body,
this.onAnimationStatusChange,
this.bottomNavigationBar,
this.navigationRail,
this.drawer,
super.key,
});
static FancyScaffoldState? of(BuildContext context) =>
context.findAncestorStateOfType<FancyScaffoldState>();
@override
FancyScaffoldState createState() => FancyScaffoldState();
}
class FancyScaffoldState extends State<FancyScaffold>
with TickerProviderStateMixin {
// goes from 0 to 1 (double)
// 0 = preview, 1 = expanded
late final AnimationController dragController;
final statusNotifier =
ValueNotifier<AnimationStatus>(AnimationStatus.dismissed);
@override
void initState() {
dragController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
dragController.addStatusListener((status) => statusNotifier.value = status);
statusNotifier.addListener(
() => widget.onAnimationStatusChange?.call(statusNotifier.value));
super.initState();
}
@override
void dispose() {
dragController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final systemPadding = MediaQuery.of(context).viewPadding;
final defaultBottomPadding =
(widget.bottomNavigationBar == null ? 0 : 80.0) + systemPadding.bottom;
final screenHeight = MediaQuery.of(context).size.height;
final sizeAnimation = Tween<double>(
begin: widget.bottomPanelHeight / MediaQuery.of(context).size.height,
end: 1.0,
).animate(dragController);
return WillPopScope(
onWillPop: () {
if (statusNotifier.value == AnimationStatus.completed ||
statusNotifier.value == AnimationStatus.reverse) {
dragController.fling(velocity: -1.0);
return Future.value(false);
}
return Future.value(true);
},
child: Stack(
children: [
Positioned.fill(
child: Scaffold(
body: widget.navigationRail != null
? Row(children: [
widget.navigationRail!,
const VerticalDivider(
indent: 0.0,
endIndent: 0.0,
width: 2.0,
),
Expanded(child: widget.body)
])
: widget.body,
drawer: widget.drawer,
bottomNavigationBar: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: widget.bottomPanelHeight),
if (widget.bottomNavigationBar != null)
SizeTransition(
axisAlignment: -1.0,
sizeFactor:
Tween(begin: 1.0, end: 0.0).animate(sizeAnimation),
child: widget.bottomNavigationBar,
),
],
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: AnimatedBuilder(
animation: sizeAnimation,
builder: (context, child) {
final x = 1.0 - sizeAnimation.value;
return Padding(
padding: EdgeInsets.only(
bottom: (defaultBottomPadding /*+ 8.0*/) * x,
//right: 8.0 * x,
//left: 8.0 * x,
),
child: child,
);
},
child: ValueListenableBuilder(
valueListenable: statusNotifier,
builder: (context, state, child) {
return GestureDetector(
onVerticalDragEnd: _onVerticalDragEnd,
onVerticalDragUpdate: _onVerticalDragUpdate,
child: child,
);
},
child: SizeTransition(
sizeFactor: sizeAnimation,
axisAlignment: -1.0,
axis: Axis.vertical,
child: SizedBox(
height: screenHeight,
width: MediaQuery.of(context).size.width,
child: ValueListenableBuilder(
valueListenable: statusNotifier,
builder: (context, state, _) => Stack(
children: [
if (state != AnimationStatus.dismissed)
Positioned.fill(
key: const Key('player_screen'),
child: widget.expandedPanel,
),
if (state != AnimationStatus.completed)
Positioned(
top: 0,
right: 0,
left: 0,
key: const Key('player_bar'),
child: FadeTransition(
opacity: Tween(begin: 1.0, end: 0.0)
.animate(dragController),
child: SizedBox(
height: widget.bottomPanelHeight,
child: widget.bottomPanel),
),
),
],
),
),
)),
),
),
),
],
),
);
}
void _onVerticalDragUpdate(DragUpdateDetails details) {
dragController.value -=
details.delta.dy / MediaQuery.of(context).size.height;
}
void _onVerticalDragEnd(DragEndDetails details) {
// snap widget to size
// this should be also handled by drag velocity and not only with bare size.
const double minFlingVelocity = 365.0;
if (details.velocity.pixelsPerSecond.dy.abs() > minFlingVelocity) {
dragController.fling(
velocity: -details.velocity.pixelsPerSecond.dy /
MediaQuery.of(context).size.height);
return;
}
dragController.fling(velocity: dragController.value > 0.5 ? 1.0 : -1.0);
}
}
import 'package:flutter/material.dart';
class FancyScaffold extends StatefulWidget {
final Widget bottomPanel;
final double bottomPanelHeight;
final Widget expandedPanel;
final Widget? bottomNavigationBar;
final Widget? drawer;
final Widget? navigationRail;
final Widget body;
final void Function(AnimationStatus)? onAnimationStatusChange;
const FancyScaffold({
required this.bottomPanel,
required this.bottomPanelHeight,
required this.expandedPanel,
required this.body,
this.onAnimationStatusChange,
this.bottomNavigationBar,
this.navigationRail,
this.drawer,
super.key,
});
static FancyScaffoldState? of(BuildContext context) =>
context.findAncestorStateOfType<FancyScaffoldState>();
@override
FancyScaffoldState createState() => FancyScaffoldState();
}
class FancyScaffoldState extends State<FancyScaffold>
with TickerProviderStateMixin {
// goes from 0 to 1 (double)
// 0 = preview, 1 = expanded
late final AnimationController dragController;
final statusNotifier =
ValueNotifier<AnimationStatus>(AnimationStatus.dismissed);
@override
void initState() {
dragController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
dragController.addStatusListener((status) => statusNotifier.value = status);
statusNotifier.addListener(
() => widget.onAnimationStatusChange?.call(statusNotifier.value));
super.initState();
}
@override
void dispose() {
dragController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final systemPadding = MediaQuery.of(context).viewPadding;
final defaultBottomPadding =
(widget.bottomNavigationBar == null ? 0 : 80.0) + systemPadding.bottom;
final screenHeight = MediaQuery.of(context).size.height;
final sizeAnimation = Tween<double>(
begin: widget.bottomPanelHeight / MediaQuery.of(context).size.height,
end: 1.0,
).animate(dragController);
return WillPopScope(
onWillPop: () {
if (statusNotifier.value == AnimationStatus.completed ||
statusNotifier.value == AnimationStatus.reverse) {
dragController.fling(velocity: -1.0);
return Future.value(false);
}
return Future.value(true);
},
child: Stack(
children: [
Positioned.fill(
child: Scaffold(
body: widget.navigationRail != null
? Row(children: [
widget.navigationRail!,
const VerticalDivider(
indent: 0.0,
endIndent: 0.0,
width: 2.0,
),
Expanded(child: widget.body)
])
: widget.body,
drawer: widget.drawer,
bottomNavigationBar: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: widget.bottomPanelHeight),
if (widget.bottomNavigationBar != null)
SizeTransition(
axisAlignment: -1.0,
sizeFactor:
Tween(begin: 1.0, end: 0.0).animate(sizeAnimation),
child: widget.bottomNavigationBar,
),
],
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: AnimatedBuilder(
animation: sizeAnimation,
builder: (context, child) {
final x = 1.0 - sizeAnimation.value;
return Padding(
padding: EdgeInsets.only(
bottom: (defaultBottomPadding /*+ 8.0*/) * x,
//right: 8.0 * x,
//left: 8.0 * x,
),
child: child,
);
},
child: ValueListenableBuilder(
valueListenable: statusNotifier,
builder: (context, state, child) {
return GestureDetector(
onVerticalDragEnd: _onVerticalDragEnd,
onVerticalDragUpdate: _onVerticalDragUpdate,
child: child,
);
},
child: SizeTransition(
sizeFactor: sizeAnimation,
axisAlignment: -1.0,
axis: Axis.vertical,
child: SizedBox(
height: screenHeight,
width: MediaQuery.of(context).size.width,
child: ValueListenableBuilder(
valueListenable: statusNotifier,
builder: (context, state, _) => Stack(
children: [
if (state != AnimationStatus.dismissed)
Positioned.fill(
key: const Key('player_screen'),
child: widget.expandedPanel,
),
if (state != AnimationStatus.completed)
Positioned(
top: 0,
right: 0,
left: 0,
key: const Key('player_bar'),
child: FadeTransition(
opacity: Tween(begin: 1.0, end: 0.0)
.animate(dragController),
child: SizedBox(
height: widget.bottomPanelHeight,
child: widget.bottomPanel),
),
),
],
),
),
)),
),
),
),
],
),
);
}
void _onVerticalDragUpdate(DragUpdateDetails details) {
dragController.value -=
details.delta.dy / MediaQuery.of(context).size.height;
}
void _onVerticalDragEnd(DragEndDetails details) {
// snap widget to size
// this should be also handled by drag velocity and not only with bare size.
const double minFlingVelocity = 365.0;
if (details.velocity.pixelsPerSecond.dy.abs() > minFlingVelocity) {
dragController.fling(
velocity: -details.velocity.pixelsPerSecond.dy /
MediaQuery.of(context).size.height);
return;
}
dragController.fling(velocity: dragController.value > 0.5 ? 1.0 : -1.0);
}
}

2
linux/.gitignore vendored
View File

@ -1 +1 @@
flutter/ephemeral
flutter/ephemeral

View File

@ -1,142 +1,142 @@
# 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 "freezer")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "f.f.freezer")
# 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 "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>: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)
# add app icon
install(FILES "app_icon.ico" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}")
# 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()
# 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 "freezer")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "f.f.freezer")
# 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 "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>: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)
# add app icon
install(FILES "app_icon.ico" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}")
# 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()

View File

@ -1,88 +1,88 @@
# 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}
)
# 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}
)

View File

@ -1,6 +1,6 @@
#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);
}
#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);
}

View File

@ -1,118 +1,118 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#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
// set icon
gtk_window_set_icon_from_file(window, "data/app_icon.ico", NULL);
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, "Freezer");
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, "Freezer");
}
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));
}
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#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
// set icon
gtk_window_set_icon_from_file(window, "data/app_icon.ico", NULL);
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, "Freezer");
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, "Freezer");
}
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));
}

View File

@ -1,18 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
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_
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
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_

14
macos/.gitignore vendored
View File

@ -1,7 +1,7 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/

View File

@ -1 +1 @@
#include "ephemeral/Flutter-Generated.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -1 +1 @@
#include "ephemeral/Flutter-Generated.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,98 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "freezer.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,9 +1,9 @@
import Cocoa
import FlutterMacOS
@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
import Cocoa
import FlutterMacOS
@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}

View File

@ -1,68 +1,68 @@
{
"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"
}
}
{
"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"
}
}

View File

@ -1,343 +1,343 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
<connections>
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="APP_NAME" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About APP_NAME" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="EPT-qC-fAb">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
</menuItem>
</items>
<point key="canvasLocation" x="142" y="-258"/>
</menu>
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="335" y="390" width="800" height="600"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
<connections>
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="APP_NAME" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About APP_NAME" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="EPT-qC-fAb">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
</menuItem>
</items>
<point key="canvasLocation" x="142" y="-258"/>
</menu>
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="335" y="390" width="800" height="600"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>

View File

@ -1,14 +1,14 @@
// 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 = freezer
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = f.f.freezer
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 f.f. All rights reserved.
// 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 = freezer
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = f.f.freezer
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 f.f. All rights reserved.

View File

@ -1,2 +1,2 @@
#include "../../Flutter/Flutter-Debug.xcconfig"
#include "Warnings.xcconfig"
#include "../../Flutter/Flutter-Debug.xcconfig"
#include "Warnings.xcconfig"

View File

@ -1,2 +1,2 @@
#include "../../Flutter/Flutter-Release.xcconfig"
#include "Warnings.xcconfig"
#include "../../Flutter/Flutter-Release.xcconfig"
#include "Warnings.xcconfig"

View File

@ -1,13 +1,13 @@
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
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

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View File

@ -1,32 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -1,15 +1,15 @@
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()
}
}
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()
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>

View File

@ -1,12 +1,12 @@
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.
}
}
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.
}
}

File diff suppressed because it is too large Load Diff

34
windows/.gitignore vendored
View File

@ -1,17 +1,17 @@
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/
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/

View File

@ -1,102 +1,102 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(freezer 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 "freezer")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# 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 "$<$<CONFIG:Debug>:_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 "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# 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)
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(freezer 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 "freezer")
# 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 "$<$<CONFIG:Debug>:_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 "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# 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)

View File

@ -1,104 +1,104 @@
# 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 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)
# 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 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

View File

@ -1,40 +1,40 @@
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)
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)

View File

@ -1,121 +1,121 @@
// 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", "f.f" "\0"
VALUE "FileDescription", "freezer" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "freezer" "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 f.f. All rights reserved." "\0"
VALUE "OriginalFilename", "freezer.exe" "\0"
VALUE "ProductName", "freezer" "\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
// 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", "f.f" "\0"
VALUE "FileDescription", "freezer" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "freezer" "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 f.f. All rights reserved." "\0"
VALUE "OriginalFilename", "freezer.exe" "\0"
VALUE "ProductName", "freezer" "\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

View File

@ -1,66 +1,71 @@
#include "flutter_window.h"
#include <optional>
#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<flutter::FlutterViewController>(
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();
});
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<LRESULT> 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);
}
#include "flutter_window.h"
#include <optional>
#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<flutter::FlutterViewController>(
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<LRESULT> 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);
}

View File

@ -1,33 +1,33 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#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::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#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::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_

View File

@ -1,47 +1,47 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#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<std::string> 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"Freezer", origin, size))
{
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#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<std::string> 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"Freezer", origin, size))
{
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}

View File

@ -1,16 +1,16 @@
//{{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
//{{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

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

Some files were not shown because too many files have changed in this diff Show More