feat: flutter ui integration

This commit is contained in:
levi authored and monkeyWie committed 2022-10-27 13:58:43 +08:00
1 parent 0fc133e46d
commit c4a30dc4c2
170 files changed
+7783 -1

No files matched your search

+3 -1
View File
@@ -22,4 +22,6 @@ bin/
.torrent.db-wal
*.data
*.db
*.db
.DS_Store
+49
View File
@@ -0,0 +1,49 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# 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
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# Libgopeed
android/app/libs/libgopeed.aar
macos/Frameworks/libgopeed.h
macos/Frameworks/libgopeed.dylib
+45
View File
@@ -0,0 +1,45 @@
# 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: f1875d570e39de09040c8f79aa13cc56baab8db1
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: android
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: ios
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: linux
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: macos
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: web
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
- platform: windows
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
# 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'
+16
View File
@@ -0,0 +1,16 @@
# gopeed
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+29
View File
@@ -0,0 +1,29 @@
# 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
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+13
View File
@@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
+78
View File
@@ -0,0 +1,78 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 33
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.gopeed"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
repositories {
flatDir {
dirs 'libs'
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation(name: 'libgopeed', ext: 'aar')
}
@@ -0,0 +1,38 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:label="gopeed">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
@@ -0,0 +1,48 @@
package com.gopeed
import androidx.annotation.NonNull
import com.gopeed.libgopeed.Libgopeed
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.StandardMethodCodec
class MainActivity : FlutterActivity() {
private val CHANNEL = "gopeed.com/libgopeed"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val taskQueue =
flutterEngine.dartExecutor.binaryMessenger.makeBackgroundTaskQueue()
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL,
StandardMethodCodec.INSTANCE,
taskQueue
).setMethodCallHandler { call, result ->
when (call.method) {
"start" -> {
val cfg = call.argument<String>("cfg")
try {
val port = Libgopeed.start(cfg)
result.success(port)
} catch (e: Exception) {
result.error("ERROR", e.message, null)
}
}
"stop" -> {
try {
Libgopeed.stop()
result.success(null)
} catch (e: Exception) {
result.error("ERROR", e.message, null)
}
}
else -> {
result.notImplemented()
}
}
}
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>
+31
View File
@@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+3
View File
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
@@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
+11
View File
@@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+19
View File
@@ -0,0 +1,19 @@
targets:
$default:
builders:
json_serializable:
options:
# Options configure how source code is generated for every
# `@JsonSerializable`-annotated class in the package.
#
# The default value for each is listed.
any_map: false
checked: false
create_factory: true
create_to_json: true
disallow_unrecognized_keys: false
explicit_to_json: false
field_rename: none
generic_argument_factories: false
ignore_unannotated: false
include_if_null: false
+88
View File
@@ -0,0 +1,88 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package github.com/monkeyWie/gopeed-core/bind/desktop */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
/* Return type for Start */
struct Start_return {
GoInt r0;
char* r1;
};
extern struct Start_return Start(char* cfg);
extern void Stop();
#ifdef __cplusplus
}
#endif
+34
View File
@@ -0,0 +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
@@ -0,0 +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>9.0</string>
</dict>
</plist>
+2
View File
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
+2
View File
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
+41
View File
@@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
@@ -0,0 +1,481 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +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>
@@ -0,0 +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>
@@ -0,0 +1,87 @@
<?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>
</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>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +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>
@@ -0,0 +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>
+13
View File
@@ -0,0 +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)
}
}
@@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "[email protected]",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

@@ -0,0 +1,5 @@
# 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.
@@ -0,0 +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>
@@ -0,0 +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>
+49
View File
@@ -0,0 +1,49 @@
<?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>Gopeed</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>gopeed</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/>
</dict>
</plist>
@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"
+107
View File
@@ -0,0 +1,107 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:gopeed/api/model/create_task.dart';
import 'package:gopeed/api/model/request.dart';
import 'package:gopeed/api/model/resource.dart';
import 'package:gopeed/api/model/result.dart';
import 'package:gopeed/api/model/task.dart';
import '../core/libgopeed_boot.dart';
import 'model/server_config.dart';
class _Client {
static _Client? _instance;
late Dio dio;
_Client._internal();
factory _Client() {
if (_instance == null) {
_instance = _Client._internal();
var dio = Dio();
dio.options.baseUrl = "http://127.0.0.1:${LibgopeedBoot.instance.port}";
_instance!.dio = dio;
(_instance!.dio.httpClientAdapter as DefaultHttpClientAdapter)
.onHttpClientCreate = (client) {
if (LibgopeedBoot.instance.network == "unix") {
client.connectionFactory =
(Uri uri, String? proxyHost, int? proxyPort) {
var address = InternetAddress(LibgopeedBoot.instance.address,
type: InternetAddressType.unix);
return Socket.startConnect(address, 0);
};
}
return client;
};
}
return _instance!;
}
}
var _client = _Client();
Future<T> _parse<T>(
Future<Response> Function() fetch,
T Function(dynamic json)? fromJsonT,
) async {
try {
var resp = await fetch();
if (fromJsonT != null) {
return Result<T>.fromJson(jsonDecode(resp.data), fromJsonT).data as T;
} else {
return null as T;
}
} on DioError catch (e) {
if (e.response == null) {
throw Exception("Server error");
}
throw Exception(
Result.fromJson(jsonDecode(e.response?.data), (_) => null).msg);
}
}
Future<Resource> resolve(Request request) async {
return _parse<Resource>(
() => _client.dio.post("/api/v1/resolve", data: request),
(data) => Resource.fromJson(data));
}
Future<String> createTask(CreateTask createTask) async {
print(jsonEncode(createTask));
return _parse<String>(
() => _client.dio.post("/api/v1/tasks", data: createTask),
(data) => data as String);
}
Future<List<Task>> getTasks(List<Status> statuses) async {
return _parse<List<Task>>(
() => _client.dio
.get("/api/v1/tasks?status=${statuses.map((e) => e.name).join(",")}"),
(data) => (data as List).map((e) => Task.fromJson(e)).toList());
}
Future<void> pauseTask(String id) async {
return _parse(() => _client.dio.put("/api/v1/tasks/$id/pause"), null);
}
Future<void> continueTask(String id) async {
return _parse(() => _client.dio.put("/api/v1/tasks/$id/continue"), null);
}
Future<void> deleteTask(String id, bool force) async {
return _parse(
() => _client.dio.delete("/api/v1/tasks/$id?force=$force"), null);
}
Future<ServerConfig> getConfig() async {
return _parse(() => _client.dio.get("/api/v1/config"),
(data) => ServerConfig.fromJson(data));
}
Future<void> putConfig(ServerConfig config) async {
return _parse(() => _client.dio.put("/api/v1/config", data: config), null);
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';
import 'options.dart';
import 'resource.dart';
part 'create_task.g.dart';
@JsonSerializable(explicitToJson: true)
class CreateTask {
Resource res;
Options? opts;
CreateTask({
required this.res,
this.opts,
});
factory CreateTask.fromJson(Map<String, dynamic> json) =>
_$CreateTaskFromJson(json);
Map<String, dynamic> toJson() => _$CreateTaskToJson(this);
}
@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'create_task.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CreateTask _$CreateTaskFromJson(Map<String, dynamic> json) => CreateTask(
res: Resource.fromJson(json['res'] as Map<String, dynamic>),
opts: json['opts'] == null
? null
: Options.fromJson(json['opts'] as Map<String, dynamic>),
);
Map<String, dynamic> _$CreateTaskToJson(CreateTask instance) {
final val = <String, dynamic>{
'res': instance.res.toJson(),
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('opts', instance.opts?.toJson());
return val;
}
+23
View File
@@ -0,0 +1,23 @@
import 'package:json_annotation/json_annotation.dart';
part 'options.g.dart';
@JsonSerializable()
class Options {
String name;
String path;
int connections;
List<int> selectFiles;
Options({
required this.name,
required this.path,
required this.connections,
required this.selectFiles,
});
factory Options.fromJson(Map<String, dynamic> json) =>
_$OptionsFromJson(json);
Map<String, dynamic> toJson() => _$OptionsToJson(this);
}
+22
View File
@@ -0,0 +1,22 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'options.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
name: json['name'] as String,
path: json['path'] as String,
connections: json['connections'] as int,
selectFiles:
(json['selectFiles'] as List<dynamic>).map((e) => e as int).toList(),
);
Map<String, dynamic> _$OptionsToJson(Options instance) => <String, dynamic>{
'name': instance.name,
'path': instance.path,
'connections': instance.connections,
'selectFiles': instance.selectFiles,
};
+18
View File
@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
part 'request.g.dart';
@JsonSerializable()
class Request {
String url;
Map<String, dynamic>? extra;
Request({
required this.url,
this.extra,
});
factory Request.fromJson(Map<String, dynamic> json) =>
_$RequestFromJson(json);
Map<String, dynamic> toJson() => _$RequestToJson(this);
}
+27
View File
@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'request.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Request _$RequestFromJson(Map<String, dynamic> json) => Request(
url: json['url'] as String,
extra: json['extra'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$RequestToJson(Request instance) {
final val = <String, dynamic>{
'url': instance.url,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('extra', instance.extra);
return val;
}
+44
View File
@@ -0,0 +1,44 @@
import 'request.dart';
import 'package:json_annotation/json_annotation.dart';
part 'resource.g.dart';
@JsonSerializable(explicitToJson: true)
class Resource {
Request req;
String name;
int size;
bool range;
List<FileInfo> files;
Map<String, dynamic>? extra;
Resource({
required this.req,
required this.name,
required this.size,
required this.range,
required this.files,
this.extra,
});
factory Resource.fromJson(Map<String, dynamic> json) =>
_$ResourceFromJson(json);
Map<String, dynamic> toJson() => _$ResourceToJson(this);
}
@JsonSerializable()
class FileInfo {
String name;
String path;
int size;
FileInfo({
required this.name,
required this.path,
required this.size,
});
factory FileInfo.fromJson(Map<String, dynamic> json) =>
_$FileInfoFromJson(json);
Map<String, dynamic> toJson() => _$FileInfoToJson(this);
}
+49
View File
@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'resource.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
req: Request.fromJson(json['req'] as Map<String, dynamic>),
name: json['name'] as String,
size: json['size'] as int,
range: json['range'] as bool,
files: (json['files'] as List<dynamic>)
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
.toList(),
extra: json['extra'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$ResourceToJson(Resource instance) {
final val = <String, dynamic>{
'req': instance.req.toJson(),
'name': instance.name,
'size': instance.size,
'range': instance.range,
'files': instance.files.map((e) => e.toJson()).toList(),
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('extra', instance.extra);
return val;
}
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
name: json['name'] as String,
path: json['path'] as String,
size: json['size'] as int,
);
Map<String, dynamic> _$FileInfoToJson(FileInfo instance) => <String, dynamic>{
'name': instance.name,
'path': instance.path,
'size': instance.size,
};
+26
View File
@@ -0,0 +1,26 @@
import 'package:json_annotation/json_annotation.dart';
part 'result.g.dart';
@JsonSerializable(genericArgumentFactories: true)
class Result<T> {
String? msg;
T? data;
Result({
this.msg,
this.data,
});
factory Result.fromJson(
Map<String, dynamic> json,
T Function(dynamic json) fromJsonT,
) =>
_$ResultFromJson(json, fromJsonT);
Map<String, dynamic> toJson() => {
'msg': msg,
'data': data is List
? (data as dynamic)?.map((e) => e.toJson()).toList()
: (data as dynamic)?.toJson(),
};
}
+45
View File
@@ -0,0 +1,45 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'result.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Result<T> _$ResultFromJson<T>(
Map<String, dynamic> json,
T Function(Object? json) fromJsonT,
) =>
Result<T>(
msg: json['msg'] as String?,
data: _$nullableGenericFromJson(json['data'], fromJsonT),
);
Map<String, dynamic> _$ResultToJson<T>(
Result<T> instance,
Object? Function(T value) toJsonT,
) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('msg', instance.msg);
writeNotNull('data', _$nullableGenericToJson(instance.data, toJsonT));
return val;
}
T? _$nullableGenericFromJson<T>(
Object? input,
T Function(Object? json) fromJson,
) =>
input == null ? null : fromJson(input);
Object? _$nullableGenericToJson<T>(
T? input,
Object? Function(T value) toJson,
) =>
input == null ? null : toJson(input);
@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
part 'server_config.g.dart';
@JsonSerializable()
class ServerConfig {
late String host;
late int port;
late int connections;
late String downloadDir;
Map<String, dynamic>? extra;
ServerConfig({
required this.host,
required this.port,
required this.connections,
required this.downloadDir,
this.extra,
});
factory ServerConfig.fromJson(Map<String, dynamic> json) =>
_$ServerConfigFromJson(json);
Map<String, dynamic> toJson() => _$ServerConfigToJson(this);
}
@@ -0,0 +1,33 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_config.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ServerConfig _$ServerConfigFromJson(Map<String, dynamic> json) => ServerConfig(
host: json['host'] as String,
port: json['port'] as int,
connections: json['connections'] as int,
downloadDir: json['downloadDir'] as String,
extra: json['extra'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$ServerConfigToJson(ServerConfig instance) {
final val = <String, dynamic>{
'host': instance.host,
'port': instance.port,
'connections': instance.connections,
'downloadDir': instance.downloadDir,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('extra', instance.extra);
return val;
}
+50
View File
@@ -0,0 +1,50 @@
import 'options.dart';
import 'resource.dart';
import 'package:json_annotation/json_annotation.dart';
part 'task.g.dart';
enum Status { ready, running, pause, error, done }
@JsonSerializable()
class Task {
String id;
Resource res;
Options opts;
Status status;
Progress progress;
int size;
DateTime createdAt;
Task({
required this.id,
required this.res,
required this.opts,
required this.status,
required this.progress,
required this.size,
required this.createdAt,
});
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
Map<String, dynamic> toJson() => _$TaskToJson(this);
}
@JsonSerializable()
class Progress {
int used;
int speed;
int downloaded;
Progress({
required this.used,
required this.speed,
required this.downloaded,
});
factory Progress.fromJson(Map<String, dynamic> json) =>
_$ProgressFromJson(json);
Map<String, dynamic> toJson() => _$ProgressToJson(this);
}
+47
View File
@@ -0,0 +1,47 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'task.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Task _$TaskFromJson(Map<String, dynamic> json) => Task(
id: json['id'] as String,
res: Resource.fromJson(json['res'] as Map<String, dynamic>),
opts: Options.fromJson(json['opts'] as Map<String, dynamic>),
status: $enumDecode(_$StatusEnumMap, json['status']),
progress: Progress.fromJson(json['progress'] as Map<String, dynamic>),
size: json['size'] as int,
createdAt: DateTime.parse(json['createdAt'] as String),
);
Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
'id': instance.id,
'res': instance.res,
'opts': instance.opts,
'status': _$StatusEnumMap[instance.status]!,
'progress': instance.progress,
'size': instance.size,
'createdAt': instance.createdAt.toIso8601String(),
};
const _$StatusEnumMap = {
Status.ready: 'ready',
Status.running: 'running',
Status.pause: 'pause',
Status.error: 'error',
Status.done: 'done',
};
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
used: json['used'] as int,
speed: json['speed'] as int,
downloaded: json['downloaded'] as int,
);
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
'used': instance.used,
'speed': instance.speed,
'downloaded': instance.downloaded,
};
@@ -0,0 +1,22 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:gopeed/core/common/libgopeed_interface.dart';
import 'package:gopeed/core/common/start_config.dart';
class LibgopeedChannel implements LibgopeedInterface {
static const _channel = MethodChannel('gopeed.com/libgopeed');
@override
Future<int> start(StartConfig cfg) async {
final port = await _channel.invokeMethod('start', {
'cfg': jsonEncode(cfg),
});
return port as int;
}
@override
Future<void> stop() async {
return await _channel.invokeMethod('stop');
}
}
@@ -0,0 +1,37 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:gopeed/core/ffi/libgopeed_bind.dart';
import 'libgopeed_interface.dart';
import 'start_config.dart';
class LibgopeedFFi implements LibgopeedInterface {
late LibgopeedBind _libgopeed;
LibgopeedFFi(LibgopeedBind libgopeed) {
_libgopeed = libgopeed;
}
@override
Future<int> start(StartConfig cfg) {
var completer = Completer<int>();
var result = _libgopeed.Start(jsonEncode(cfg).toNativeUtf8().cast());
if (result.r1 != nullptr) {
completer.completeError(result.r1.cast<Utf8>().toDartString());
} else {
completer.complete(result.r0);
}
return completer.future;
}
@override
Future<void> stop() {
var completer = Completer<void>();
_libgopeed.Stop();
completer.complete();
return completer.future;
}
}
@@ -0,0 +1,7 @@
import 'package:gopeed/core/common/start_config.dart';
abstract class LibgopeedInterface {
Future<int> start(StartConfig cfg);
Future<void> stop();
}
@@ -0,0 +1,25 @@
import 'package:json_annotation/json_annotation.dart';
part 'start_config.g.dart';
@JsonSerializable()
class StartConfig {
String network;
String address;
String storage;
String storageDir;
int refreshInterval;
StartConfig({
required this.network,
required this.address,
required this.storage,
required this.storageDir,
required this.refreshInterval,
});
factory StartConfig.fromJson(Map<String, dynamic> json) =>
_$StartConfigFromJson(json);
Map<String, dynamic> toJson() => _$StartConfigToJson(this);
}
@@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'start_config.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig(
network: json['network'] as String,
address: json['address'] as String,
storage: json['storage'] as String,
storageDir: json['storageDir'] as String,
refreshInterval: json['refreshInterval'] as int,
);
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
<String, dynamic>{
'network': instance.network,
'address': instance.address,
'storage': instance.storage,
'storageDir': instance.storageDir,
'refreshInterval': instance.refreshInterval,
};
+241
View File
@@ -0,0 +1,241 @@
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Bindings to gopeed core.
class LibgopeedBind {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
LibgopeedBind(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
LibgopeedBind.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
Start_return Start(
ffi.Pointer<ffi.Char> cfg,
) {
return _Start(
cfg,
);
}
late final _StartPtr =
_lookup<ffi.NativeFunction<Start_return Function(ffi.Pointer<ffi.Char>)>>(
'Start');
late final _Start =
_StartPtr.asFunction<Start_return Function(ffi.Pointer<ffi.Char>)>();
void Stop() {
return _Stop();
}
late final _StopPtr =
_lookup<ffi.NativeFunction<ffi.Void Function()>>('Stop');
late final _Stop = _StopPtr.asFunction<void Function()>();
}
class __mbstate_t extends ffi.Union {
@ffi.Array.multi([128])
external ffi.Array<ffi.Char> __mbstate8;
@ffi.LongLong()
external int _mbstateL;
}
class __darwin_pthread_handler_rec extends ffi.Struct {
external ffi
.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
__routine;
external ffi.Pointer<ffi.Void> __arg;
external ffi.Pointer<__darwin_pthread_handler_rec> __next;
}
class _opaque_pthread_attr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([56])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_cond_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([40])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_condattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_mutex_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([56])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_mutexattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_once_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_rwlock_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([192])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_rwlockattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([16])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_t extends ffi.Struct {
@ffi.Long()
external int __sig;
external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack;
@ffi.Array.multi([8176])
external ffi.Array<ffi.Char> __opaque;
}
class _GoString_ extends ffi.Struct {
external ffi.Pointer<ffi.Char> p;
@ptrdiff_t()
external int n;
}
typedef ptrdiff_t = __darwin_ptrdiff_t;
typedef __darwin_ptrdiff_t = ffi.Long;
class GoInterface extends ffi.Struct {
external ffi.Pointer<ffi.Void> t;
external ffi.Pointer<ffi.Void> v;
}
class GoSlice extends ffi.Struct {
external ffi.Pointer<ffi.Void> data;
@GoInt()
external int len;
@GoInt()
external int cap;
}
typedef GoInt = GoInt64;
typedef GoInt64 = ffi.LongLong;
class Start_return extends ffi.Struct {
@GoInt()
external int r0;
external ffi.Pointer<ffi.Char> r1;
}
const int __DARWIN_ONLY_64_BIT_INO_T = 1;
const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1;
const int __DARWIN_ONLY_VERS_1050 = 1;
const int __DARWIN_UNIX03 = 1;
const int __DARWIN_64_BIT_INO_T = 1;
const int __DARWIN_VERS_1050 = 1;
const int __DARWIN_NON_CANCELABLE = 0;
const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN';
const int __DARWIN_C_ANSI = 4096;
const int __DARWIN_C_FULL = 900000;
const int __DARWIN_C_LEVEL = 900000;
const int __STDC_WANT_LIB_EXT1__ = 1;
const int __DARWIN_NO_LONG_LONG = 0;
const int _DARWIN_FEATURE_64_BIT_INODE = 1;
const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1;
const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1;
const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1;
const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3;
const int __has_ptrcheck = 0;
const int __DARWIN_NULL = 0;
const int __PTHREAD_SIZE__ = 8176;
const int __PTHREAD_ATTR_SIZE__ = 56;
const int __PTHREAD_MUTEXATTR_SIZE__ = 8;
const int __PTHREAD_MUTEX_SIZE__ = 56;
const int __PTHREAD_CONDATTR_SIZE__ = 8;
const int __PTHREAD_COND_SIZE__ = 40;
const int __PTHREAD_ONCE_SIZE__ = 8;
const int __PTHREAD_RWLOCK_SIZE__ = 192;
const int __PTHREAD_RWLOCKATTR_SIZE__ = 16;
const int __DARWIN_WCHAR_MAX = 2147483647;
const int __DARWIN_WCHAR_MIN = -2147483648;
const int __DARWIN_WEOF = -1;
const int _FORTIFY_SOURCE = 2;
const int NULL = 0;
const int USER_ADDR_NULL = 0;
+171
View File
@@ -0,0 +1,171 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'package:gopeed/core/common/libgopeed_channel.dart';
import 'package:gopeed/core/common/libgopeed_ffi.dart';
import 'package:gopeed/core/common/start_config.dart';
import 'package:path_provider/path_provider.dart';
import '../util/util.dart';
import 'common/libgopeed_interface.dart';
import 'ffi/libgopeed_bind.dart';
const unixSocketPath = 'gopeed.sock';
class LibgopeedBoot {
String network = "";
String address = "";
int port = 0;
int refreshInterval = 250;
late LibgopeedInterface _libgopeed;
late SendPort _childSendPort;
static LibgopeedBoot? _instance;
static LibgopeedBoot get instance {
if (_instance == null) {
_instance = LibgopeedBoot._internal();
if (!Util.isDesktop()) {
_instance!._libgopeed = LibgopeedChannel();
}
}
return _instance!;
}
LibgopeedBoot._internal();
Future<void> start() async {
var storageDir = "./";
if (!Util.isUnix()) {
// not support unix socket, use tcp
network = "tcp";
address = "127.0.0.1:0";
} else {
network = "unix";
if (Util.isDesktop()) {
address = unixSocketPath;
} else if (Platform.isAndroid) {
address = "${(await getTemporaryDirectory()).path}/$unixSocketPath";
storageDir = (await getExternalStorageDirectory())?.path ?? "";
}
}
final cfg = StartConfig(
network: network,
address: address,
storage: 'bolt',
storageDir: storageDir,
refreshInterval: refreshInterval);
if (Util.isDesktop()) {
port = await _ffiStart(cfg);
} else {
port = await _libgopeed.start(cfg);
}
}
Future<void> stop() async {
if (Util.isDesktop()) {
await _ffiStop();
} else {
await _libgopeed.stop();
}
}
_ffiInit() {
if (Platform.isMacOS) {
_libgopeed =
LibgopeedFFi(LibgopeedBind(DynamicLibrary.open('libgopeed.dylib')));
}
}
// FFI run in isolate
Future<int> _ffiStart(StartConfig cfg) async {
var startCompleter = Completer<int>();
var mainReceive = ReceivePort();
var isolate = await Isolate.spawn((SendPort sendPort) {
_ffiInit();
var childReceive = ReceivePort();
sendPort.send(childReceive.sendPort);
childReceive.listen((message) async {
late _Result result;
try {
if (message is _StartParam) {
result = _StartResult();
(result as _StartResult).port = await _libgopeed.start(message.cfg);
}
if (message is _StopParam) {
result = _StopResult();
await _libgopeed.stop();
childReceive.close();
Isolate.exit(sendPort);
}
} on Exception catch (e) {
result.exception = e;
}
sendPort.send(result);
});
}, mainReceive.sendPort);
var childCompleter = Completer<void>();
mainReceive.listen((message) {
if (message is SendPort) {
_childSendPort = message;
childCompleter.complete();
}
if (message is _StartResult) {
if (message.exception != null) {
startCompleter.completeError(message.exception!);
} else {
startCompleter.complete(message.port!);
}
}
if (message is _StopResult) {
isolate.kill();
if (message.exception != null) {
_stopCompleter.completeError(message.exception!);
} else {
_stopCompleter.complete();
}
}
});
await childCompleter.future;
_childSendPort.send(_StartParam(cfg: cfg));
return startCompleter.future;
}
late Completer<void> _stopCompleter;
Future<void> _ffiStop() async {
_stopCompleter = Completer<void>();
_childSendPort.send(_StopParam());
return _stopCompleter.future;
}
}
class _Result {
Exception? exception;
_Result();
}
class _StartParam {
final StartConfig cfg;
_StartParam({required this.cfg});
}
class _StartResult extends _Result {
int? port;
_StartResult();
}
class _StopParam {}
class _StopResult extends _Result {
_StopResult();
}
+24
View File
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'core/libgopeed_boot.dart';
import 'routes/router.dart';
import 'setting/setting.dart';
import 'theme/theme.dart';
import 'util/mac_secure_util.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LibgopeedBoot.instance.start();
await Setting.instance.load();
MacSecureUtil.loadBookmark();
runApp(GetMaterialApp.router(
debugShowCheckedModeBanner: false,
theme: GopeedTheme.light,
darkTheme: GopeedTheme.dark,
themeMode: Setting.instance.themeMode,
getPages: Routes.routes,
));
}
@@ -0,0 +1,5 @@
import 'package:get/get.dart';
class CreateController extends GetxController {
var currentIndex = 0.obs;
}
@@ -0,0 +1,198 @@
import 'dart:developer' as developer;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/api.dart';
import 'package:gopeed/api/model/create_task.dart';
import 'package:gopeed/api/model/options.dart';
import 'package:gopeed/api/model/request.dart';
import 'package:gopeed/api/model/resource.dart';
import 'package:gopeed/pages/create/create_controller.dart';
import 'package:gopeed/setting/setting.dart';
import 'package:gopeed/widget/file_list_view.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:rounded_loading_button/rounded_loading_button.dart';
import '../../routes/router.dart';
import '../../widget/directory_selector.dart';
class CreateView extends GetView<CreateController> {
CreateView({Key? key}) : super(key: key);
final _resolveFormKey = GlobalKey<FormState>();
final _urlController = TextEditingController(
text: "https://dldir1.qq.com/weixin/Windows/WeChatSetup.exe");
final _confirmController = RoundedLoadingButtonController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.rootDelegate.offNamed(Routes.task)),
actions: [Container()],
title: const Text("创建任务"),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
child: Form(
key: _resolveFormKey,
autovalidateMode: AutovalidateMode.always, //开启自动校验
child: Column(
children: [
TextFormField(
autofocus: true,
controller: _urlController,
minLines: 1,
maxLines: 15,
decoration: const InputDecoration(
hintText: "请输入下载链接,支持 HTTP/HTTPS/MAGNET",
labelText: "下载链接",
icon: Icon(Icons.link)),
validator: (v) {
return v!.trim().isNotEmpty ? null : "请输入下载链接";
}),
// 登录按钮
Center(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: ConstrainedBox(
constraints: const BoxConstraints.tightFor(
width: 150,
height: 40,
),
child: RoundedLoadingButton(
color: Get.theme.colorScheme.secondary,
onPressed: () async {
if (_resolveFormKey.currentState!.validate()) {
_confirmController.start();
try {
final res = await resolve(Request(
url: _urlController.text,
));
await _showResolveDialog(res);
} catch (e) {
Get.snackbar("错误", e.toString());
} finally {
_confirmController.reset();
}
}
},
controller: _confirmController,
child: const Text("确认"),
),
)),
),
],
),
),
),
);
}
Future<void> _showResolveDialog(Resource res) async {
final createFormKey = GlobalKey<FormState>();
final pathController =
TextEditingController(text: Setting.instance.downloadDir);
final downloadController = RoundedLoadingButtonController();
var fileValues = List.filled(res.files.length, true);
return await showDialog<void>(
context: Get.context!,
barrierDismissible: false,
builder: (_) => AlertDialog(
content: Builder(
builder: (context) {
// Get available height and width of the build area of this widget. Make a choice depending on the size.
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return SizedBox(
height: height * 0.5,
width: width,
child: Form(
key: createFormKey,
autovalidateMode: AutovalidateMode.always, //开启自动校验
child: Column(
children: [
Expanded(
child: FileListView(
files: res.files,
values: fileValues,
)),
DirectorySelector(
controller: pathController,
),
],
)),
);
},
),
actions: [
ConstrainedBox(
constraints: BoxConstraints.tightFor(
width: Get.theme.buttonTheme.minWidth,
height: Get.theme.buttonTheme.height,
),
child: ElevatedButton(
style:
ElevatedButton.styleFrom(shape: const StadiumBorder())
.copyWith(
backgroundColor: MaterialStateProperty.all(
Get.theme.backgroundColor)),
onPressed: () {
Get.back();
},
child: const Text("取消"),
),
),
ConstrainedBox(
constraints: BoxConstraints.tightFor(
width: Get.theme.buttonTheme.minWidth,
height: Get.theme.buttonTheme.height,
),
child: RoundedLoadingButton(
color: Get.theme.colorScheme.secondary,
onPressed: () async {
try {
if (createFormKey.currentState!.validate()) {
downloadController.start();
if (Platform.isAndroid) {
if (!await Permission.storage.request().isGranted) {
Get.snackbar("错误", "需要开启存储权限");
return;
}
}
await createTask(CreateTask(
res: res,
opts: Options(
name: '',
path: pathController.text,
connections: Setting.instance.connections,
selectFiles: fileValues
.asMap()
.entries
.where((e) => e.value)
.map((e) => e.key)
.toList(),
)));
Get.back();
Get.rootDelegate.offNamed(Routes.task);
}
} catch (e) {
Get.snackbar("错误", e.toString());
developer.log(e.toString(), name: 'flutter');
} finally {
downloadController.reset();
}
},
controller: downloadController,
child: const Text("下载"),
),
),
],
));
}
}
@@ -0,0 +1,5 @@
import 'package:get/get.dart';
class HomeController extends GetxController {
var currentIndex = 0.obs;
}
+85
View File
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../routes/router.dart';
import 'home_controller.dart';
class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetRouterOutlet.builder(
builder: (context, delegate, currentRoute) {
//This router outlet handles the appbar and the bottom navigation bar
final currentLocation = currentRoute?.location;
var currentIndex = 0;
if (currentLocation?.startsWith(Routes.setting) == true) {
currentIndex = 1;
}
return Scaffold(
extendBody: true, //to make floating action button notch transparent
//to avoid the floating action button overlapping behavior,
// when a soft keyboard is displayed
// resizeToAvoidBottomInset: false,
body: GetRouterOutlet(
initialRoute: Routes.task,
//delegate: Get.nestedKey(Routes.HOME),
// key: Get.nestedKey(Routes.HOME),
// key: Get.nestedKey(Routes.home),
),
bottomNavigationBar: StylishBottomBar(
items: [
AnimatedBarItems(
icon: const Icon(Icons.list),
selectedColor: Get.theme.primaryColor,
title: const Text('任务')),
AnimatedBarItems(
icon: const Icon(Icons.settings),
selectedColor: Get.theme.primaryColor,
title: const Text('设置')),
],
iconSize: 32,
barAnimation: BarAnimation.fade,
iconStyle: IconStyle.Default,
hasNotch: true,
opacity: 0.3,
currentIndex: currentIndex,
onTap: (index) {
switch (index) {
case 0:
delegate.toNamed(Routes.task);
break;
case 1:
delegate.toNamed(Routes.setting);
break;
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
/* Get.isDarkMode
? {
Get.changeTheme(GopeedTheme.light),
Get.changeThemeMode(ThemeMode.light)
}
: {
Get.changeTheme(GopeedTheme.dark),
Get.changeThemeMode(ThemeMode.dark)
}; */
Get.rootDelegate.toNamed(Routes.create);
},
child: const Icon(
Icons.add,
)),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
);
},
);
}
}
@@ -0,0 +1,20 @@
import 'package:get/get.dart';
class RootController extends GetxController {
//TODO: Implement RootController
final count = 0.obs;
@override
void onInit() {
super.onInit();
}
@override
void onReady() {
super.onReady();
}
@override
void onClose() {}
void increment() => count.value++;
}
+26
View File
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../routes/router.dart';
import 'root_controller.dart';
class RootView extends GetView<RootController> {
const RootView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetRouterOutlet.builder(
builder: (context, delegate, current) {
return Scaffold(
body: GetRouterOutlet(
initialRoute: Routes.home,
// anchorRoute: '/',
// filterPages: (afterAnchor) {
// return afterAnchor.take(1);
// },
),
);
},
);
}
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'package:gopeed/api/api.dart';
import 'package:gopeed/api/model/server_config.dart';
import '../../setting/setting.dart';
class SettingController extends GetxController {
final setting = Setting.instance.obs;
final tapStatues = <int, bool>{}.obs;
}
@@ -0,0 +1,176 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:gopeed/setting/setting.dart';
import 'package:gopeed/widget/directory_selector.dart';
import 'setting_controller.dart';
class SettingView extends GetView<SettingController> {
const SettingView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
Timer? timer;
debounceSave() {
timer?.cancel();
timer = Timer(const Duration(milliseconds: 500), Setting.instance.save);
}
return Scaffold(
appBar: AppBar(
title: const Text('设置'),
centerTitle: true,
),
body: Column(
// 左对齐
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("基本设置", style: Get.textTheme.titleLarge),
const SizedBox(height: 10),
Obx(() => Card(
child: Column(
children: _buildConfigItems([
_ConfigItem(
"主题",
() => _getThemeName(controller.setting.value.themeMode),
() => DropdownButton<ThemeMode>(
value: controller.setting.value.themeMode,
isDense: true,
onChanged: (value) {
controller.setting.value.themeMode = value!;
Get.changeThemeMode(value);
debounceSave();
},
items: ThemeMode.values
.map((e) => DropdownMenuItem<ThemeMode>(
value: e,
child: Text(_getThemeName(e)),
))
.toList(),
)),
_ConfigItem(
"下载目录", () => controller.setting.value.downloadDir, () {
final downloadDirController = TextEditingController(
text: controller.setting.value.downloadDir);
downloadDirController.addListener(() {
if (downloadDirController.text !=
controller.setting.value.downloadDir) {
controller.setting.value.downloadDir =
downloadDirController.text;
debounceSave();
}
});
return DirectorySelector(
controller: downloadDirController,
showLabel: false,
);
}),
_ConfigItem("连接数",
() => controller.setting.value.connections.toString(),
() {
final connectionsController = TextEditingController(
text: controller.setting.value.connections.toString());
connectionsController.addListener(() {
if (connectionsController.text.isNotEmpty &&
connectionsController.text !=
controller.setting.value.connections.toString()) {
controller.setting.value.connections =
int.parse(connectionsController.text);
debounceSave();
}
});
return TextField(
controller: connectionsController,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
NumericalRangeFormatter(min: 1, max: 256),
],
);
}),
]),
))),
],
).paddingAll(16),
);
}
List<Widget> _buildConfigItems(List<_ConfigItem> buildItems) {
final result = <Widget>[];
for (var i = 0; i < buildItems.length; i++) {
final buildItem = buildItems[i];
result.add(ListTile(
title: Text(buildItem.label),
subtitle: controller.tapStatues[i] ?? false
? buildItem.inputItem()
: Text(buildItem.text()),
onTap: () {
controller.tapStatues[i] = true;
// set other false
for (var j = 0; j < controller.tapStatues.length; j++) {
if (j != i) {
controller.tapStatues[j] = false;
}
}
}));
if (i != buildItems.length - 1) {
result.add(const Divider());
}
}
return result;
}
String _getThemeName(ThemeMode themeMode) {
switch (themeMode) {
case ThemeMode.system:
return "跟随系统";
case ThemeMode.light:
return "明亮主题";
case ThemeMode.dark:
return "暗黑主题";
}
}
}
class _ConfigItem {
late String label;
late String Function() text;
late Widget Function() inputItem;
_ConfigItem(this.label, this.text, this.inputItem);
}
class NumericalRangeFormatter extends TextInputFormatter {
final int min;
final int max;
NumericalRangeFormatter({required this.min, required this.max});
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
if (newValue.text.isEmpty) {
return newValue;
}
var intVal = int.tryParse(newValue.text);
if (intVal == null) {
return oldValue;
}
if (intVal < min) {
return newValue.copyWith(text: min.toString());
} else if (intVal > max) {
return oldValue.copyWith(text: max.toString());
} else {
return newValue;
}
}
}
@@ -0,0 +1,70 @@
import 'dart:async';
import 'package:get/get.dart';
import 'package:gopeed/api/model/task.dart';
import 'package:gopeed/core/libgopeed_boot.dart';
import '../../api/api.dart';
class TaskController extends GetxController {
final tabIndex = 0.obs;
final unDoneTasks = <Task>[].obs;
final doneTasks = <Task>[].obs;
var _lastUnDoneSize = 0;
var _lastDoneSize = 0;
late final Timer _timer;
_loadUnDone() async {
unDoneTasks.value = await getTasks(
[Status.ready, Status.running, Status.pause, Status.error]);
final lastLastSize = _lastUnDoneSize;
if (lastLastSize != unDoneTasks.length) {
_lastUnDoneSize = unDoneTasks.length;
}
// when unDoneTasks.length reduce, it means some task is done, refresh doneTasks
return lastLastSize > _lastUnDoneSize;
}
_loadDone() async {
doneTasks.value = await getTasks([Status.done]);
final lastLastSize = _lastDoneSize;
if (lastLastSize != doneTasks.length) {
_lastDoneSize = doneTasks.length;
}
// when doneTasks.length increase, it means some task is done, refresh unDoneTasks
return lastLastSize < _lastDoneSize;
}
loadTab() async {
if (tabIndex.value == 0) {
if (await _loadUnDone()) {
await _loadDone();
}
} else {
if (await _loadDone()) {
await _loadUnDone();
}
}
}
@override
void onInit() {
super.onInit();
loadTab();
_timer = Timer.periodic(
Duration(milliseconds: LibgopeedBoot.instance.refreshInterval),
(timer) async {
loadTab();
});
}
@override
void onClose() {
super.onClose();
_timer.cancel();
}
}
+199
View File
@@ -0,0 +1,199 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/api.dart';
import 'package:gopeed/api/model/task.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../util/util.dart';
import 'task_controller.dart';
class TaskView extends GetView<TaskController> {
const TaskView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(56),
child: AppBar(
bottom: TabBar(
tabs: const [
Tab(
icon: Icon(Icons.file_download),
),
Tab(
icon: Icon(Icons.done),
),
],
onTap: (index) {
controller.tabIndex.value = index;
if (index == 1) {
controller.loadTab();
}
},
),
)),
body: TabBarView(
children: [
Obx(() => buildTaskList(controller.unDoneTasks)),
Obx(() => buildTaskList(controller.doneTasks)),
],
),
),
);
}
Widget buildTaskList(List<Task> tasks) {
return ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
return Item(task: tasks[index]);
},
);
}
}
class Item extends StatelessWidget {
final Task task;
const Item({Key? key, required this.task}) : super(key: key);
@override
Widget build(BuildContext context) {
var controller = Get.find<TaskController>();
return Card(
elevation: 4.0,
child: InkWell(
onTap: () {},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text(task.res.name),
leading: const Icon(Icons.insert_drive_file),
),
Row(
children: [
Expanded(
flex: 1,
child: Text(
"${isDone() ? "" : "${Util.fmtByte(task.progress.downloaded)} / "}${Util.fmtByte(task.size)}",
style: Get.textTheme.bodyText1
?.copyWith(color: Get.theme.disabledColor),
).padding(left: 18)),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${Util.fmtByte(task.progress.speed)} / s",
style: Get.textTheme.subtitle2),
...buildActions()
],
)),
],
),
LinearProgressIndicator(
value: getProgress(),
),
],
),
)).padding(horizontal: 8, top: 8);
}
List<Widget> buildActions() {
final list = <Widget>[];
if (isDone()) {
if (Util.isDesktop()) {
list.add(IconButton(
icon: const Icon(Icons.folder_open),
onPressed: () async {
print(Util.buildAbsPath(task.opts.path, task.res.files[0].path,
task.res.files[0].name));
final file = File(Util.buildAbsPath(task.opts.path,
task.res.files[0].path, task.res.files[0].name));
await launchUrl(file.parent.uri);
},
));
}
} else {
if (isRunning()) {
list.add(IconButton(
icon: const Icon(Icons.pause),
onPressed: () async {
await pauseTask(task.id);
},
));
} else {
list.add(IconButton(
icon: const Icon(Icons.play_arrow),
onPressed: () async {
await continueTask(task.id);
},
));
}
}
list.add(IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_showDeleteDialog(task.id);
},
));
return list;
}
Future<void> _showDeleteDialog(String id) {
final keep = true.obs;
return showDialog<void>(
context: Get.context!,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: const Text("删除任务"),
content: Obx(() => CheckboxListTile(
value: keep.value,
title: Text("保留已下载的文件", style: Get.textTheme.bodyText1),
onChanged: (v) {
keep.value = v!;
})),
actions: [
TextButton(
child: const Text("取消"),
onPressed: () => Get.back(), //关闭对话框
),
TextButton(
child: const Text(
"删除",
style: TextStyle(color: Colors.redAccent),
),
onPressed: () async {
try {
await deleteTask(id, !keep.value);
Get.find<TaskController>().loadTab();
Get.back();
} catch (e) {
Get.snackbar("错误", e.toString());
}
},
),
],
));
}
double getProgress() {
return task.size <= 0 ? 1 : task.progress.downloaded / task.size;
}
bool isRunning() {
return task.status == Status.running;
}
bool isDone() {
return task.status == Status.done;
}
}
+79
View File
@@ -0,0 +1,79 @@
import 'package:get/get.dart';
import 'package:gopeed/pages/create/create_controller.dart';
import 'package:gopeed/pages/setting/setting_view.dart';
import '../pages/create/create_view.dart';
import '../pages/home/home_controller.dart';
import '../pages/home/home_view.dart';
import '../pages/root/root_controller.dart';
import '../pages/root/root_view.dart';
import '../pages/setting/setting_controller.dart';
import '../pages/task/task_controller.dart';
import '../pages/task/task_view.dart';
abstract class _Paths {
static const root = '/';
static const create = '/create';
static const home = '/home';
static const task = '/task';
static const setting = '/setting';
}
class Routes {
static const root = _Paths.root;
static const create = _Paths.create;
static const home = _Paths.home;
static const task = _Paths.home + _Paths.task;
static const setting = _Paths.home + _Paths.setting;
static final routes = [
GetPage(
name: _Paths.root,
participatesInRootNavigator: true,
// preventDuplicates: true,
page: () => const RootView(),
binding: BindingsBuilder(() {
Get.lazyPut<RootController>(() => RootController(), fenix: true);
}),
children: [
GetPage(
name: _Paths.create,
transition: Transition.downToUp,
// preventDuplicates: true,
page: () => CreateView(),
binding: BindingsBuilder(() {
Get.lazyPut<CreateController>(() => CreateController(),
fenix: true);
}),
),
GetPage(
name: _Paths.home,
// preventDuplicates: true,
page: () => const HomeView(),
binding: BindingsBuilder(() {
Get.lazyPut<HomeController>(() => HomeController(),
fenix: true);
}),
children: [
GetPage(
name: _Paths.task,
page: () => const TaskView(),
binding: BindingsBuilder(() {
Get.lazyPut<TaskController>(() => TaskController());
}),
),
GetPage(
name: _Paths.setting,
page: () => const SettingView(),
binding: BindingsBuilder(() {
Get.lazyPut<SettingController>(() => SettingController());
}),
)
]),
]),
];
}
+76
View File
@@ -0,0 +1,76 @@
import 'dart:convert';
import 'dart:io';
import 'package:external_path/external_path.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/api.dart';
import 'package:path_provider/path_provider.dart';
import '../api/model/server_config.dart';
import '../util/util.dart';
class Setting {
late String host;
late int port;
late int connections;
late String downloadDir;
late ThemeMode themeMode;
Setting({
required this.host,
required this.port,
required this.connections,
required this.downloadDir,
required this.themeMode,
});
// singleton pattern
static Setting? _instance;
static Setting get instance {
_instance ??= Setting._internal();
return _instance!;
}
Setting._internal();
Future<void> load() async {
final config = await getConfig();
host = config.host;
port = config.port;
connections = config.connections;
downloadDir = config.downloadDir;
final themeMode = config.extra?['themeMode'];
if (themeMode == null) {
this.themeMode = ThemeMode.system;
} else {
this.themeMode = ThemeMode.values.byName(themeMode);
}
if (Platform.isAndroid) {
downloadDir =
'${await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOWNLOADS)}/com.gopeed';
return;
}
if (downloadDir.isEmpty) {
if (Util.isDesktop()) {
downloadDir = (await getDownloadsDirectory())!.path;
}
}
}
Future<void> save() async {
final config = ServerConfig(
host: host,
port: port,
connections: connections,
downloadDir: downloadDir,
extra: {
'themeMode': themeMode.name,
},
);
await putConfig(config);
}
}
+37
View File
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
class GopeedTheme {
static const _gopeedreenPrimaryValue = 0xFF79C476;
static const _gopeedreen =
MaterialColor(_gopeedreenPrimaryValue, <int, Color>{
50: Color(0xFFEFF8EF),
100: Color(0xFFD7EDD6),
200: Color(0xFFBCE2BB),
300: Color(0xFFA1D69F),
400: Color(0xFF8DCD8B),
500: Color(_gopeedreenPrimaryValue),
600: Color(0xFF71BE6E),
700: Color(0xFF66B663),
800: Color(0xFF5CAF59),
900: Color(0xFF49A246),
});
static const _gopeedreenAccentValue = 0xFFC9FFC7;
static const _gopeedreenAccent =
MaterialColor(_gopeedreenAccentValue, <int, Color>{
100: Color(0xFFFAFFFA),
200: Color(_gopeedreenAccentValue),
400: Color(0xFF97FF94),
700: Color(0xFF7FFF7A),
});
static final _light =
ThemeData(brightness: Brightness.light, primarySwatch: _gopeedreen);
static final light = _light.copyWith(
colorScheme: _light.colorScheme.copyWith(secondary: _gopeedreenAccent));
static final _dark =
ThemeData(brightness: Brightness.dark, primarySwatch: _gopeedreen);
static final dark = _dark.copyWith(
colorScheme: _dark.colorScheme.copyWith(secondary: _gopeedreenAccent));
}
Loaded 100 of 170 files, more files were not shown because too many files have changed in this diff. Show more