re-run flutter create (to add windows platform)
This commit is contained in:
parent
f126ffef46
commit
276d0ad4bf
32
.metadata
32
.metadata
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
10
build.yaml
10
build.yaml
|
@ -1,6 +1,6 @@
|
|||
targets:
|
||||
$default:
|
||||
builders:
|
||||
json_serializable:
|
||||
options:
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
json_serializable:
|
||||
options:
|
||||
explicit_to_json: true
|
60
deezcryptor/.gitignore
vendored
60
deezcryptor/.gitignore
vendored
|
@ -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/
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
|
|
|
@ -1 +1 @@
|
|||
TODO: Add your license here.
|
||||
TODO: Add your license here.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
18
deezcryptor/android/.gitignore
vendored
18
deezcryptor/android/.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
rootProject.name = 'deezcryptor'
|
||||
rootProject.name = 'deezcryptor'
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
@ -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>)>();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
34
deezcryptor/windows/.gitignore
vendored
34
deezcryptor/windows/.gitignore
vendored
|
@ -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/
|
||||
|
|
|
@ -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
68
ios/.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "Generated.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "Generated.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
14
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
14
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1 +1 @@
|
|||
#import "GeneratedPluginRegistrant.h"
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import 'package:isar/isar.dart';
|
||||
|
||||
class T {}
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class T {}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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']!);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
2
linux/.gitignore
vendored
|
@ -1 +1 @@
|
|||
flutter/ephemeral
|
||||
flutter/ephemeral
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
14
macos/.gitignore
vendored
|
@ -1,7 +1,7 @@
|
|||
# Flutter-related
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
|
||||
# Xcode-related
|
||||
**/dgph
|
||||
**/xcuserdata/
|
||||
# Flutter-related
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
|
||||
# Xcode-related
|
||||
**/dgph
|
||||
**/xcuserdata/
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
14
macos/Runner.xcworkspace/contents.xcworkspacedata
generated
14
macos/Runner.xcworkspace/contents.xcworkspacedata
generated
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#include "../../Flutter/Flutter-Debug.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
#include "../../Flutter/Flutter-Debug.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#include "../../Flutter/Flutter-Release.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
#include "../../Flutter/Flutter-Release.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
3190
pubspec.lock
3190
pubspec.lock
File diff suppressed because it is too large
Load diff
34
windows/.gitignore
vendored
34
windows/.gitignore
vendored
|
@ -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/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue