├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── assets ├── icon │ └── icon.png └── readme_payloads │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── lib ├── fonts │ ├── Coda-Regular.ttf │ ├── Coda-ExtraBold.ttf │ └── OFL.txt ├── utils │ ├── tools.dart │ └── aria2_api.dart ├── components │ ├── custom_snack_bar.dart │ ├── speed_shower.dart │ ├── task_list.dart │ └── custom_drawer.dart ├── views │ ├── global_setting.dart │ ├── app_setting.dart │ ├── aria2_global_option.dart │ ├── edit_aria2_server_config.dart │ └── task_detail.dart ├── l10n │ ├── messages_all.dart │ ├── messages_zh_Hans_CN.dart │ ├── messages_messages.dart │ └── localization_intl.dart └── states │ ├── aria2.dart │ └── app.dart ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── aria_z │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── intl.sh ├── .metadata ├── .gitignore ├── test └── widget_test.dart ├── analysis_options.yaml ├── README-CN.md ├── README.md ├── pubspec.yaml └── pubspec.lock /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/web/favicon.png -------------------------------------------------------------------------------- /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/assets/icon/icon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /lib/fonts/Coda-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/lib/fonts/Coda-Regular.ttf -------------------------------------------------------------------------------- /assets/readme_payloads/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/assets/readme_payloads/1.png -------------------------------------------------------------------------------- /assets/readme_payloads/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/assets/readme_payloads/2.png -------------------------------------------------------------------------------- /assets/readme_payloads/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/assets/readme_payloads/3.png -------------------------------------------------------------------------------- /assets/readme_payloads/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/assets/readme_payloads/4.png -------------------------------------------------------------------------------- /lib/fonts/Coda-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/lib/fonts/Coda-ExtraBold.ttf -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengqiaozhu/aria_z/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/aria_z/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.aria_z 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /intl.sh: -------------------------------------------------------------------------------- 1 | flutter pub pub run intl_generator:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart 2 | flutter pub pub run intl_generator:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aria_z", 3 | "short_name": "aria_z", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | // import 'package:flutter/material.dart'; 9 | // import 'package:flutter_test/flutter_test.dart'; 10 | 11 | // import 'package:aria_z/main.dart'; 12 | 13 | // void main() { 14 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // // Build our app and trigger a frame. 16 | // await tester.pumpWidget(const MyApp()); 17 | 18 | // // Verify that our counter starts at 0. 19 | // expect(find.text('0'), findsOneWidget); 20 | // expect(find.text('1'), findsNothing); 21 | 22 | // // Tap the '+' icon and trigger a frame. 23 | // await tester.tap(find.byIcon(Icons.add)); 24 | // await tester.pump(); 25 | 26 | // // Verify that our counter has incremented. 27 | // expect(find.text('0'), findsNothing); 28 | // expect(find.text('1'), findsOneWidget); 29 | // }); 30 | // } 31 | -------------------------------------------------------------------------------- /lib/utils/tools.dart: -------------------------------------------------------------------------------- 1 | class BitUnit { 2 | String bit; 3 | 4 | String unit; 5 | 6 | BitUnit(this.bit, this.unit); 7 | } 8 | 9 | BitUnit bitToUnit(int bitNum) { 10 | late BitUnit result; 11 | if (bitNum < 1024) { 12 | result = BitUnit(bitNum.toString(), ''); 13 | } else if (bitNum < 1024 * 1024) { 14 | result = BitUnit((bitNum / 1024).toStringAsFixed(1), "K"); 15 | } else if (bitNum < 1024 * 1024 * 1024) { 16 | result = BitUnit((bitNum / (1024 * 1024)).toStringAsFixed(1), 'M'); 17 | } else if (bitNum < 1024 * 1024 * 1024 * 1024) { 18 | result = BitUnit((bitNum / (1024 * 1024 * 1024)).toStringAsFixed(1), 'G'); 19 | } else { 20 | result = 21 | BitUnit((bitNum / (1024 * 1024 * 1024 * 1024)).toStringAsFixed(1), 'T'); 22 | } 23 | return result; 24 | } 25 | 26 | double unitToBit(BitUnit ub) { 27 | int _u = 1; 28 | switch (ub.unit) { 29 | case 'K': 30 | _u = 1024; 31 | break; 32 | case 'M': 33 | _u = 1024 * 1024; 34 | break; 35 | case 'G': 36 | _u = 1024 * 1024 * 1024; 37 | break; 38 | case 'T': 39 | _u = 1024 * 1024 * 1024 * 1024; 40 | break; 41 | } 42 | return double.parse(ub.bit) * _u; 43 | } 44 | 45 | String formatFileSize(bitSpeed) { 46 | BitUnit bitUnit = bitToUnit(bitSpeed); 47 | return "${bitUnit.bit}${bitUnit.unit}B"; 48 | } 49 | 50 | String formatSpeed(bitSpeed) => "${formatFileSize(bitSpeed)}/s"; 51 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | aria_z 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # AriaZ 2 | 3 | 4 | App Icon 5 | 6 | 7 | ## 跨平台Aria2客户端,支持安卓,ios平台,使用 Flutter Logo框架编写; 8 | 9 | ![Support Platforms](https://img.shields.io/badge/platform-android%7Cios-%23007acc?style=for-the-badge) 10 | [![GitHub Release](https://img.shields.io/github/v/release/fengqiaozhu/aria_z?display_name=tag&include_prereleases&style=for-the-badge)](https://github.com/fengqiaozhu/aria_z/releases) 11 | ![Last commit](https://img.shields.io/github/last-commit/fengqiaozhu/aria_z/master?style=for-the-badge) 12 | ![Support Language](https://img.shields.io/badge/Support%20Language-English%7C%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87-green?style=for-the-badge) 13 | [![GitHub License](https://img.shields.io/github/license/fengqiaozhu/aria_z?style=for-the-badge)](https://github.com/fengqiaozhu/aria_z/blob/master/LICENSE) 14 | 15 | 简体中文 | [English](./README.md) 16 | 17 | ## 功能截图 18 | 19 |
20 | Flutter Logo 21 |   22 | Flutter Logo 23 |   24 | Flutter Logo 25 |   26 | Flutter Logo 27 |
28 | 29 | ## 更新日志 30 | ### v0.0.1 31 | - 添加aria2服务器连接; 32 | - 添加下载任务,支持种子下载,磁力链下载,metalink下载,url(http,https,ftp,sftp等); 33 | - 自定义任务配置,包括下载目录,限速,覆盖下载; 34 | - 任务暂停,启动,删除等功能; 35 | - aria2全局配置,包括全局限速设置,默认下载地址,最多同时下载数,断点续传等; 36 | - app多语言支持 37 | - app主体色配置 38 | - aria2刷新频率配置 -------------------------------------------------------------------------------- /lib/components/custom_snack_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria_z/states/app.dart'; 2 | import 'package:aria_z/utils/aria2_api.dart' 3 | show Aria2Client, Aria2Response, Aria2ResponseStatus; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | /// 底部提醒弹框 8 | /// level = 1:成功,2:失败,3:警告 9 | void showCustomSnackBar(BuildContext context, int level, Widget content, 10 | {int durationSecond = 2}) { 11 | Color bg = Colors.grey; 12 | 13 | switch (level) { 14 | case 1: 15 | bg = Colors.green; 16 | break; 17 | case 2: 18 | bg = Colors.red; 19 | break; 20 | case 3: 21 | bg = Colors.orange; 22 | break; 23 | } 24 | 25 | ScaffoldMessenger.of(context).showSnackBar( 26 | SnackBar( 27 | duration: Duration(seconds: durationSecond), 28 | content: content, 29 | backgroundColor: bg, 30 | ), 31 | ); 32 | } 33 | 34 | checkAndUseConfig(BuildContext context, Aria2ConnectConfig config) async { 35 | AppState _appState = Provider.of(context, listen: false); 36 | _appState.clearCurrentServerAllState(); 37 | handleAria2ApiResponse( 38 | context, await _appState.checkAria2ConnectConfig(config), (_client) { 39 | _appState.connectToAria2Server(_client); 40 | }); 41 | } 42 | 43 | /// 处理 aria2 请求结果 44 | handleAria2ApiResponse(BuildContext context, Aria2Response requestResult, 45 | Function(T)? handler) { 46 | if (requestResult.status == Aria2ResponseStatus.error) { 47 | // 判断返回错误是否指定错误类型,如果未指定,责不予处理 48 | if(requestResult.error!=null){ 49 | showCustomSnackBar(context, 2, Text(requestResult.message)); 50 | } 51 | return; 52 | } 53 | if (handler != null) { 54 | handler(requestResult.data as T); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AriaZ 2 | 3 | 4 | App Icon 5 | 6 | 7 | ## A Cross-platform aria2 client on android or ios write with Flutter Logo; 8 | 9 | ![Support Platforms](https://img.shields.io/badge/platform-android%7Cios-%23007acc?style=for-the-badge) 10 | [![GitHub Release](https://img.shields.io/github/v/release/fengqiaozhu/aria_z?display_name=tag&include_prereleases&style=for-the-badge)](https://github.com/fengqiaozhu/aria_z/releases) 11 | ![Last commit](https://img.shields.io/github/last-commit/fengqiaozhu/aria_z/master?style=for-the-badge) 12 | ![Support Language](https://img.shields.io/badge/Support%20Language-English%7C%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87-green?style=for-the-badge) 13 | [![GitHub License](https://img.shields.io/github/license/fengqiaozhu/aria_z?style=for-the-badge)](https://github.com/fengqiaozhu/aria_z/blob/master/LICENSE) 14 | 15 | English | [简体中文](./README-CN.md) 16 | 17 | ## Feature screen shot 18 | 19 |
20 | Flutter Logo 21 |   22 | Flutter Logo 23 |   24 | Flutter Logo 25 |   26 | Flutter Logo 27 |
28 | 29 | ## ChangeLog 30 | ### v0.0.1 31 | - Add new aria2 connection; 32 | - Add new download task, include torrent,magnet,metalink,url(http,https,ftp,sftp...); 33 | - Custom task option, include download path,download speed limit,overwrite; 34 | - Task pause, unparse, delete and so on; 35 | - Aria2 global option, include global speed limit, default download path, max current download number, broke-continue download ...; 36 | - App multiple language support; 37 | - App theme color; 38 | - Aria2 request delay setting; 39 | - Support android platform; -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.aria_z" 47 | minSdkVersion 16 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /lib/views/global_setting.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: must_be_immutable 2 | 3 | import 'package:aria2/aria2.dart' show Aria2Option; 4 | import 'package:aria_z/l10n/localization_intl.dart'; 5 | import 'package:aria_z/states/aria2.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | import 'package:provider/provider.dart'; 9 | import 'app_setting.dart'; 10 | import 'aria2_global_option.dart'; 11 | import '../components/custom_snack_bar.dart'; 12 | 13 | class GlobalSetting extends StatelessWidget { 14 | const GlobalSetting({Key? key}) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | AriazLocalizations _l10n = AriazLocalizations.of(context); 19 | final List tabs = [ 20 | Tab(text: _l10n.appSetting), 21 | Tab(text: _l10n.aria2Setting), 22 | ]; 23 | Aria2Option? _globalOption = Provider.of(context).globalOption; 24 | return DefaultTabController( 25 | length: tabs.length, 26 | child: Builder( 27 | builder: (BuildContext context) { 28 | final TabController tabController = 29 | DefaultTabController.of(context)!; 30 | tabController.addListener(() { 31 | if (tabController.indexIsChanging && 32 | tabController.index == 1 && 33 | _globalOption == null) { 34 | tabController.index = 0; 35 | showCustomSnackBar( 36 | context, 3, Text(_l10n.aria2SettingWarning)); 37 | } 38 | }); 39 | return Scaffold( 40 | appBar: AppBar( 41 | title: Text(_l10n.setting), 42 | bottom: TabBar(tabs: tabs), 43 | ), 44 | body: TabBarView( 45 | physics: _globalOption == null 46 | ? const NeverScrollableScrollPhysics() 47 | : const AlwaysScrollableScrollPhysics(), 48 | children: const [ 49 | AppSettingsWidgets(), 50 | Aria2GlobalOptionsWidgets() 51 | ]), 52 | ); 53 | }, 54 | )); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/components/speed_shower.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_typing_uninitialized_variables 2 | 3 | import 'package:aria_z/utils/tools.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class SpeedShower extends StatelessWidget { 7 | final int? downloadSpeed; 8 | 9 | final int? uploadSpeed; 10 | 11 | const SpeedShower( 12 | {Key? key, required this.downloadSpeed, required this.uploadSpeed}) 13 | : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Text.rich( 18 | TextSpan(children: [ 19 | downloadSpeed == null 20 | ? const WidgetSpan(child: SizedBox()) 21 | : TextSpan( 22 | children: [ 23 | const WidgetSpan( 24 | child: Icon( 25 | Icons.file_download_outlined, 26 | size: 18, 27 | color: Colors.green, 28 | )), 29 | TextSpan(text: formatSpeed(downloadSpeed ?? 0)), 30 | ], 31 | style: const TextStyle( 32 | fontFamily: 'Coda', 33 | fontSize: 14, 34 | )), 35 | downloadSpeed == null || uploadSpeed == null 36 | ? const WidgetSpan(child: SizedBox()) 37 | : const WidgetSpan( 38 | child: SizedBox( 39 | height: 16, 40 | child: VerticalDivider( 41 | indent: 1, 42 | endIndent: 5, 43 | color: Colors.grey, 44 | thickness: 2, 45 | width: 20, 46 | ), 47 | )), 48 | uploadSpeed == null 49 | ? const WidgetSpan(child: SizedBox()) 50 | : TextSpan( 51 | children: [ 52 | const WidgetSpan( 53 | child: Icon( 54 | Icons.file_upload_outlined, 55 | size: 18, 56 | color: Colors.red, 57 | )), 58 | TextSpan(text: formatSpeed(uploadSpeed ?? 0)), 59 | ], 60 | style: const TextStyle( 61 | fontFamily: 'Coda', 62 | fontSize: 14, 63 | )) 64 | ])); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 8 | 15 | 19 | 23 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/l10n/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | // ignore_for_file:avoid_catches_without_on_clauses 12 | 13 | import 'dart:async'; 14 | 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_messages.dart' as messages_messages; 20 | import 'messages_zh_Hans_CN.dart' as messages_zh_hans_cn; 21 | 22 | typedef Future LibraryLoader(); 23 | Map _deferredLibraries = { 24 | 'messages': () => Future.value(null), 25 | 'zh_Hans_CN': () => Future.value(null), 26 | }; 27 | 28 | MessageLookupByLibrary? _findExact(String localeName) { 29 | switch (localeName) { 30 | case 'messages': 31 | return messages_messages.messages; 32 | case 'zh_Hans_CN': 33 | return messages_zh_hans_cn.messages; 34 | default: 35 | return null; 36 | } 37 | } 38 | 39 | /// User programs should call this before using [localeName] for messages. 40 | Future initializeMessages(String localeName) async { 41 | final availableLocale = Intl.verifiedLocale( 42 | localeName, 43 | (locale) => _deferredLibraries[locale] != null, 44 | onFailure: (_) => null); 45 | if (availableLocale == null) { 46 | return Future.value(false); 47 | } 48 | final lib = _deferredLibraries[availableLocale]; 49 | await (lib == null ? Future.value(false) : lib()); 50 | initializeInternalMessageLookup(() => CompositeMessageLookup()); 51 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 52 | return Future.value(true); 53 | } 54 | 55 | bool _messagesExistFor(String locale) { 56 | try { 57 | return _findExact(locale) != null; 58 | } catch (e) { 59 | return false; 60 | } 61 | } 62 | 63 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 64 | final actualLocale = Intl.verifiedLocale(locale, _messagesExistFor, 65 | onFailure: (_) => null); 66 | if (actualLocale == null) return null; 67 | return _findExact(actualLocale); 68 | } 69 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DKImagePickerController/Core (4.3.2): 3 | - DKImagePickerController/ImageDataManager 4 | - DKImagePickerController/Resource 5 | - DKImagePickerController/ImageDataManager (4.3.2) 6 | - DKImagePickerController/PhotoGallery (4.3.2): 7 | - DKImagePickerController/Core 8 | - DKPhotoGallery 9 | - DKImagePickerController/Resource (4.3.2) 10 | - DKPhotoGallery (0.0.17): 11 | - DKPhotoGallery/Core (= 0.0.17) 12 | - DKPhotoGallery/Model (= 0.0.17) 13 | - DKPhotoGallery/Preview (= 0.0.17) 14 | - DKPhotoGallery/Resource (= 0.0.17) 15 | - SDWebImage 16 | - SwiftyGif 17 | - DKPhotoGallery/Core (0.0.17): 18 | - DKPhotoGallery/Model 19 | - DKPhotoGallery/Preview 20 | - SDWebImage 21 | - SwiftyGif 22 | - DKPhotoGallery/Model (0.0.17): 23 | - SDWebImage 24 | - SwiftyGif 25 | - DKPhotoGallery/Preview (0.0.17): 26 | - DKPhotoGallery/Model 27 | - DKPhotoGallery/Resource 28 | - SDWebImage 29 | - SwiftyGif 30 | - DKPhotoGallery/Resource (0.0.17): 31 | - SDWebImage 32 | - SwiftyGif 33 | - file_picker (0.0.1): 34 | - DKImagePickerController/PhotoGallery 35 | - Flutter 36 | - Flutter (1.0.0) 37 | - path_provider_ios (0.0.1): 38 | - Flutter 39 | - SDWebImage (5.12.1): 40 | - SDWebImage/Core (= 5.12.1) 41 | - SDWebImage/Core (5.12.1) 42 | - shared_preferences_ios (0.0.1): 43 | - Flutter 44 | - SwiftyGif (5.4.0) 45 | 46 | DEPENDENCIES: 47 | - file_picker (from `.symlinks/plugins/file_picker/ios`) 48 | - Flutter (from `Flutter`) 49 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) 50 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) 51 | 52 | SPEC REPOS: 53 | trunk: 54 | - DKImagePickerController 55 | - DKPhotoGallery 56 | - SDWebImage 57 | - SwiftyGif 58 | 59 | EXTERNAL SOURCES: 60 | file_picker: 61 | :path: ".symlinks/plugins/file_picker/ios" 62 | Flutter: 63 | :path: Flutter 64 | path_provider_ios: 65 | :path: ".symlinks/plugins/path_provider_ios/ios" 66 | shared_preferences_ios: 67 | :path: ".symlinks/plugins/shared_preferences_ios/ios" 68 | 69 | SPEC CHECKSUMS: 70 | DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d 71 | DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 72 | file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 73 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 74 | path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 75 | SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b 76 | shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32 77 | SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 78 | 79 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 80 | 81 | COCOAPODS: 1.11.2 82 | -------------------------------------------------------------------------------- /lib/states/aria2.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria2/aria2.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | enum TaskType { torrent, magnet, url, metaLink } 5 | 6 | class NewTaskOption { 7 | List params; 8 | 9 | TaskType taskType; 10 | 11 | Aria2Option option; 12 | 13 | NewTaskOption(this.taskType, this.params, this.option); 14 | } 15 | 16 | class Aria2States extends ChangeNotifier { 17 | List completedTasks = []; 18 | 19 | List downloadingTasks = []; 20 | 21 | List waittingTasks = []; 22 | 23 | Aria2GlobalStat? globalStatus; 24 | 25 | Aria2Option? globalOption; 26 | 27 | Aria2Version? versionInfo; 28 | 29 | final List _opratingGids = []; 30 | 31 | Aria2States(); 32 | 33 | List get taskListOfNotComplete { 34 | List list = [...downloadingTasks, ...waittingTasks]; 35 | list = list.map((t) { 36 | if (_opratingGids.contains(t.gid)) { 37 | switch (t.status) { 38 | case 'active': 39 | t.status = 'pausing'; 40 | break; 41 | case 'paused': 42 | t.status = 'unparsing'; 43 | break; 44 | } 45 | } 46 | return t; 47 | }).toList(); 48 | return list; 49 | } 50 | 51 | List get opratingGids => _opratingGids; 52 | 53 | updateDownloadingTasks(List taskList) { 54 | downloadingTasks = taskList; 55 | notifyListeners(); 56 | } 57 | 58 | updateWaittingTasks(List taskList) { 59 | waittingTasks = taskList; 60 | notifyListeners(); 61 | } 62 | 63 | updateCompletedTasks(List taskList) { 64 | completedTasks = taskList; 65 | notifyListeners(); 66 | } 67 | 68 | removeCompletedTaskFromLocalList(String gid) { 69 | completedTasks.removeWhere((element) => element.gid == gid); 70 | } 71 | 72 | updateGlobalStatus(Aria2GlobalStat stat) { 73 | globalStatus = stat; 74 | notifyListeners(); 75 | } 76 | 77 | addOpratingGids(List gids) { 78 | for (var gid in gids) { 79 | if (!(_opratingGids.contains(gid))) { 80 | _opratingGids.add(gid); 81 | } 82 | } 83 | notifyListeners(); 84 | } 85 | 86 | removeOpratingGids(List gids) { 87 | for (var gid in gids) { 88 | if (_opratingGids.contains(gid)) { 89 | _opratingGids.remove(gid); 90 | } 91 | } 92 | notifyListeners(); 93 | } 94 | 95 | updateGlobalOption(Aria2Option options) { 96 | globalOption = options; 97 | notifyListeners(); 98 | } 99 | 100 | updateVersion(version) { 101 | versionInfo = version; 102 | notifyListeners(); 103 | } 104 | 105 | clearStates() { 106 | completedTasks = []; 107 | downloadingTasks = []; 108 | waittingTasks = []; 109 | globalStatus = null; 110 | globalOption = null; 111 | versionInfo = null; 112 | _opratingGids.removeRange(0, _opratingGids.length); 113 | notifyListeners(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | aria_z 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: aria_z 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 0.0.1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | flutter_localizations: 33 | sdk: flutter 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^1.0.2 38 | provider: ^6.0.1 39 | hive: ^2.0.4 40 | hive_flutter: ^1.1.0 41 | tuple: ^2.0.0 42 | toggle_switch: ^1.3.0 43 | file_picker: ^4.2.4 44 | shared_preferences: ^2.0.9 45 | intl: ^0.17.0 46 | flutter_slidable: ^1.1.0 47 | flutter_spinkit: ^5.1.0 48 | aria2: ^0.1.3 49 | 50 | dev_dependencies: 51 | flutter_test: 52 | sdk: flutter 53 | 54 | # The "flutter_lints" package below contains a set of recommended lints to 55 | # encourage good coding practices. The lint set provided by the package is 56 | # activated in the `analysis_options.yaml` file located at the root of your 57 | # package. See that file for information about deactivating specific lint 58 | # rules and activating additional ones. 59 | flutter_lints: ^1.0.0 60 | intl_generator: ^0.2.1 61 | flutter_launcher_icons: "^0.9.2" 62 | # For information on the generic Dart part of this file, see the 63 | # following page: https://dart.dev/tools/pub/pubspec 64 | 65 | # The following section is specific to Flutter. 66 | flutter: 67 | 68 | # The following line ensures that the Material Icons font is 69 | # included with your application, so that you can use the icons in 70 | # the material Icons class. 71 | uses-material-design: true 72 | 73 | # To add assets to your application, add an assets section, like this: 74 | # assets: 75 | # - images/a_dot_burr.jpeg 76 | # - images/a_dot_ham.jpeg 77 | 78 | # An image asset can refer to one or more resolution-specific "variants", see 79 | # https://flutter.dev/assets-and-images/#resolution-aware. 80 | 81 | # For details regarding adding assets from package dependencies, see 82 | # https://flutter.dev/assets-and-images/#from-packages 83 | 84 | # To add custom fonts to your application, add a fonts section here, 85 | # in this "flutter" section. Each entry in this list should have a 86 | # "family" key with the font family name, and a "fonts" key with a 87 | # list giving the asset and other descriptors for the font. For 88 | # example: 89 | fonts: 90 | - family: Coda 91 | fonts: 92 | - asset: lib/fonts/Coda-Regular.ttf 93 | - asset: lib/fonts/Coda-ExtraBold.ttf 94 | weight: 800 95 | # - family: Trajan Pro 96 | # fonts: 97 | # - asset: fonts/TrajanPro.ttf 98 | # - asset: fonts/TrajanPro_Bold.ttf 99 | # weight: 700 100 | # 101 | # For details regarding fonts from package dependencies, 102 | # see https://flutter.dev/custom-fonts/#from-packages 103 | 104 | 105 | flutter_icons: 106 | android: true 107 | ios: true 108 | image_path: "assets/icon/icon.png" 109 | remove_alpha_ios: true -------------------------------------------------------------------------------- /lib/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, 2011 Vernon Adams (vern@newtypography.co.uk), 2 | with Reserved Font Name "Coda" and "Coda Caption". 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /lib/components/task_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria2/models/aria2Task.dart'; 2 | import 'package:aria_z/components/custom_snack_bar.dart'; 3 | import 'package:aria_z/components/speed_shower.dart'; 4 | import 'package:aria_z/l10n/localization_intl.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_slidable/flutter_slidable.dart'; 7 | 8 | import '../states/app.dart' show AppState; 9 | import '../states/aria2.dart' show Aria2States, TaskType; 10 | import '../utils/tools.dart'; 11 | 12 | List taskListTileWidget(BuildContext context, AppState app, 13 | Aria2States aria2States, List taskList) { 14 | AriazLocalizations _l10n = AriazLocalizations.of(context); 15 | Widget trailingOption(String gid, String status) { 16 | late Widget w = Text(status); 17 | switch (status) { 18 | case 'active': 19 | w = IconButton( 20 | color: Colors.redAccent, 21 | icon: const Icon(Icons.pause), 22 | onPressed: () async { 23 | if (gid != "") { 24 | await app.aria2?.pauseTask(gid); 25 | } 26 | }); 27 | break; 28 | case 'paused': 29 | w = IconButton( 30 | color: Colors.lightGreen, 31 | icon: const Icon(Icons.play_arrow), 32 | onPressed: () async { 33 | if (gid != "") { 34 | await app.aria2?.unPauseTask(gid); 35 | } 36 | }, 37 | ); 38 | break; 39 | case 'waiting': 40 | w = Text(_l10n.waiting); 41 | break; 42 | case 'pausing': 43 | w = Text(_l10n.pausing); 44 | break; 45 | case 'unparsing': 46 | w = Text(_l10n.unparsing); 47 | break; 48 | case 'complete': 49 | w = Text(_l10n.complete); 50 | break; 51 | } 52 | return w; 53 | } 54 | 55 | return taskList 56 | // .where((element) => element.bittorrent?["info"] != null) 57 | .map((task) { 58 | late TaskType _taskType; 59 | late String _taskName; 60 | 61 | if (task.bittorrent == null) { 62 | String uri = task.files?[0]['uris'][0]['uri'] as String; 63 | _taskType = TaskType.url; 64 | _taskName = uri.split('/').last; 65 | } else if (task.bittorrent != null) { 66 | if (task.bittorrent!.containsKey('info')) { 67 | _taskType = TaskType.torrent; 68 | _taskName = task.bittorrent?["info"]["name"] ?? ''; 69 | } else { 70 | _taskType = TaskType.magnet; 71 | _taskName = '[METADATA]${task.infoHash}'; 72 | } 73 | } else { 74 | _taskType = TaskType.metaLink; 75 | _taskName = '==========metaLink============'; 76 | } 77 | 78 | return Padding( 79 | padding: const EdgeInsets.all(10.0), 80 | child: Card( 81 | child: Column( 82 | mainAxisSize: MainAxisSize.min, 83 | children: [ 84 | Slidable( 85 | startActionPane: ActionPane( 86 | motion: const ScrollMotion(), 87 | children: [ 88 | SlidableAction( 89 | onPressed: (BuildContext context) async { 90 | handleAria2ApiResponse( 91 | context, 92 | await app.aria2! 93 | .removeTask(task.gid!, task.status!), (res) { 94 | if (res == 'OK') { 95 | aria2States 96 | .removeCompletedTaskFromLocalList(task.gid!); 97 | } 98 | // showCustomSnackBar(context, 1, Text(res)); 99 | }); 100 | }, 101 | backgroundColor: const Color(0xFFFE4A49), 102 | foregroundColor: Colors.white, 103 | icon: Icons.delete, 104 | label: _l10n.deleteText, 105 | ), 106 | ], 107 | ), 108 | child: ListTile( 109 | onTap: () async { 110 | var gid = task.gid!; 111 | Navigator.of(context).pushNamed('/task_detail', 112 | arguments: [gid, _taskName, _taskType]); 113 | }, 114 | leading: Icon(app.aria2TaskTypes 115 | .where((att) => att.taskType == _taskType) 116 | .first 117 | .icon), 118 | title: Text( 119 | _taskName, 120 | overflow: TextOverflow.ellipsis, 121 | maxLines: 2, 122 | // softWrap: false, 123 | ), 124 | trailing: 125 | trailingOption(task.gid ?? '', task.status ?? ''))), 126 | Padding( 127 | padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), 128 | child: Flex( 129 | direction: Axis.horizontal, 130 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 131 | children: [ 132 | Expanded( 133 | child: Text( 134 | "${formatFileSize(task.completedLength ?? 0)} / ${formatFileSize(task.totalLength ?? 0)}", 135 | style: TextStyle( 136 | color: Theme.of(context).colorScheme.onSurface, 137 | fontSize: 14, 138 | fontFamily: 'Coda'), 139 | )), 140 | Expanded( 141 | flex: 1, 142 | child: Container( 143 | alignment: Alignment.centerRight, 144 | child: task.status == 'paused' 145 | ? Text(_l10n.paused) 146 | : DefaultTextStyle( 147 | textAlign: TextAlign.end, 148 | style: TextStyle( 149 | color: Theme.of(context) 150 | .colorScheme 151 | .onSurface, 152 | fontSize: 14, 153 | fontFamily: 'Coda'), 154 | child: SpeedShower( 155 | downloadSpeed: task.downloadSpeed, 156 | uploadSpeed: _taskType != TaskType.torrent 157 | ? null 158 | : task.uploadSpeed)), 159 | )) 160 | ], 161 | )), 162 | LinearProgressIndicator( 163 | value: (task.completedLength ?? 0) / 164 | (task.totalLength == 0 ? 1 : task.totalLength ?? 1)), 165 | ], 166 | ), 167 | ), 168 | ); 169 | }).toList(); 170 | } 171 | -------------------------------------------------------------------------------- /lib/views/app_setting.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria_z/l10n/localization_intl.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../states/app.dart'; 5 | 6 | GlobalKey _globalOptionKey = GlobalKey(); 7 | 8 | class AppSettingsWidgets extends StatefulWidget { 9 | const AppSettingsWidgets({Key? key}) : super(key: key); 10 | 11 | @override 12 | State createState() => _AppSettingsStates(); 13 | } 14 | 15 | class _AppSettingsStates extends State 16 | with AutomaticKeepAliveClientMixin { 17 | late String language; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | language = '简体中文'; 23 | } 24 | 25 | Widget _settingGroupWidget(List innerWidgets) { 26 | return Container( 27 | width: double.infinity, 28 | decoration: BoxDecoration( 29 | color: Colors.grey.withOpacity(0.2), 30 | borderRadius: const BorderRadius.all(Radius.circular(12)), 31 | ), 32 | child: Padding( 33 | padding: const EdgeInsets.fromLTRB(10, 12, 10, 12), 34 | child: Column(children: innerWidgets), 35 | )); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | super.build(context); 41 | AriazLocalizations _l10n = AriazLocalizations.of(context); 42 | TextStyle _labelTextStyle = 43 | const TextStyle(fontSize: 17, fontWeight: FontWeight.bold); 44 | AppState appState = Provider.of(context); 45 | return Form( 46 | key: _globalOptionKey, 47 | autovalidateMode: AutovalidateMode.onUserInteraction, 48 | child: Padding( 49 | padding: const EdgeInsets.fromLTRB(10, 20, 10, 20), 50 | child: Column( 51 | children: [ 52 | _settingGroupWidget([ 53 | Flex( 54 | direction: Axis.horizontal, 55 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 56 | children: [ 57 | Expanded( 58 | flex: 1, 59 | child: Text( 60 | _l10n.language, 61 | style: _labelTextStyle, 62 | ), 63 | ), 64 | Expanded( 65 | flex: 3, 66 | child: DropdownButtonHideUnderline( 67 | child: DropdownButton( 68 | isExpanded: true, 69 | value: appState.selectedLocale?.toLanguageTag(), 70 | borderRadius: 71 | const BorderRadius.all(Radius.circular(8)), 72 | onChanged: (String? newValue) { 73 | appState.changeLocale(newValue ?? ''); 74 | }, 75 | items: appState.localeItems 76 | .map>((LocaleItem lc) { 77 | return DropdownMenuItem( 78 | value: lc.locale?.toLanguageTag(), 79 | child: Text(lc.label), 80 | ); 81 | }).toList(), 82 | ), 83 | ), 84 | ) 85 | ]), 86 | Flex( 87 | direction: Axis.horizontal, 88 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 89 | children: [ 90 | Expanded( 91 | flex: 1, 92 | child: Text(_l10n.themeColor, style: _labelTextStyle), 93 | ), 94 | Expanded( 95 | flex: 3, 96 | child: DropdownButtonHideUnderline( 97 | child: DropdownButton( 98 | isExpanded: true, 99 | value: appState.appUsingColorName, 100 | borderRadius: 101 | const BorderRadius.all(Radius.circular(8)), 102 | onChanged: (String? newValue) { 103 | setState(() { 104 | appState.changeTheme(appState.appThemeColors 105 | .where((_th) => _th.name == newValue) 106 | .first 107 | .name); 108 | }); 109 | }, 110 | items: appState.appThemeColors 111 | .map>( 112 | (CustomMateriaColor _theme) { 113 | return DropdownMenuItem( 114 | value: _theme.name, 115 | child: Row( 116 | children: [ 117 | Text(_theme.desc), 118 | const SizedBox(width: 8), 119 | Container( 120 | width: 80, 121 | height: 16, 122 | decoration: BoxDecoration( 123 | color: _theme.color, 124 | borderRadius: const BorderRadius.all( 125 | Radius.circular(5))), 126 | ) 127 | ], 128 | ), 129 | ); 130 | }).toList(), 131 | ), 132 | ), 133 | ) 134 | ]) 135 | ]), 136 | const SizedBox( 137 | height: 10, 138 | ), 139 | _settingGroupWidget([ 140 | Flex( 141 | direction: Axis.horizontal, 142 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 143 | children: [ 144 | Expanded( 145 | flex: 1, 146 | child: Text(_l10n.refreshDelay, style: _labelTextStyle), 147 | ), 148 | Expanded( 149 | flex: 3, 150 | child: DropdownButtonHideUnderline( 151 | child: DropdownButton( 152 | borderRadius: 153 | const BorderRadius.all(Radius.circular(8)), 154 | isExpanded: true, 155 | value: appState.intervalSecond.toString(), 156 | onChanged: (String? newValue) { 157 | if (newValue != null) { 158 | appState.updateIntervalSecond(newValue); 159 | } 160 | }, 161 | items: ['1', '3', '5', '10', '15'] 162 | .map>((String value) { 163 | return DropdownMenuItem( 164 | value: value, 165 | child: Text('$value ${_l10n.second}'), 166 | ); 167 | }).toList(), 168 | ), 169 | ), 170 | ) 171 | ], 172 | ) 173 | ]) 174 | ], 175 | ), 176 | )); 177 | } 178 | 179 | @override 180 | bool get wantKeepAlive => true; 181 | } 182 | -------------------------------------------------------------------------------- /lib/views/aria2_global_option.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria2/models/index.dart'; 2 | import 'package:aria_z/components/custom_snack_bar.dart'; 3 | import 'package:aria_z/l10n/localization_intl.dart'; 4 | import 'package:aria_z/states/app.dart'; 5 | import 'package:aria_z/states/aria2.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | class Aria2GlobalOptionsWidgets extends StatefulWidget { 10 | const Aria2GlobalOptionsWidgets({Key? key}) : super(key: key); 11 | 12 | @override 13 | State createState() => _Aria2GlobalOptionsStates(); 14 | } 15 | 16 | class _Aria2GlobalOptionsStates extends State 17 | with AutomaticKeepAliveClientMixin { 18 | static Aria2Option _newOption = Aria2Option(); 19 | 20 | late bool _optionChanged; 21 | late AriazLocalizations _l10n; 22 | 23 | _checkOptionChange(Map _ooMap) { 24 | Map _noMap = _newOption.toJson(); 25 | List _toRemoveKeys = []; 26 | for (var _k in _noMap.keys) { 27 | if (_ooMap.containsKey(_k) && _noMap[_k] != _ooMap[_k]) { 28 | setState(() { 29 | _optionChanged = true; 30 | }); 31 | } else { 32 | _toRemoveKeys.add(_k); 33 | } 34 | } 35 | for (String _k in _toRemoveKeys) { 36 | _noMap.remove(_k); 37 | } 38 | if (_noMap.keys.isEmpty) { 39 | _optionChanged = false; 40 | } 41 | _newOption = Aria2Option.fromJson(_noMap); 42 | } 43 | 44 | _submitOptionChange() async { 45 | handleAria2ApiResponse( 46 | context, 47 | await Provider.of(context, listen: false) 48 | .aria2! 49 | .updateTheGlobalOption(_newOption), (res) async { 50 | handleAria2ApiResponse( 51 | context, 52 | await Provider.of(context, listen: false) 53 | .aria2! 54 | .getAria2GlobalOption(), 55 | null); 56 | showCustomSnackBar(context, 1, Text(_l10n.changeGlobalOptionSuccessTip)); 57 | }); 58 | 59 | _optionChanged = false; 60 | _newOption = Aria2Option(); 61 | } 62 | 63 | @override 64 | void initState() { 65 | super.initState(); 66 | _optionChanged = false; 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | super.build(context); 72 | 73 | _l10n = AriazLocalizations.of(context); 74 | Aria2Option _oldOption = Provider.of(context).globalOption!; 75 | 76 | return Stack( 77 | children: [ 78 | !_optionChanged 79 | ? const SizedBox() 80 | : Card( 81 | child: Padding( 82 | padding: const EdgeInsets.fromLTRB(8, 0, 6, 0), 83 | child: Row( 84 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 85 | children: [ 86 | Text(_l10n.optionChangedTip), 87 | TextButton.icon( 88 | onPressed: _submitOptionChange, 89 | icon: const Icon(Icons.send), 90 | label: Text(_l10n.submit)), 91 | ], 92 | ), 93 | )), 94 | Form( 95 | autovalidateMode: AutovalidateMode.onUserInteraction, 96 | child: Padding( 97 | padding: 98 | EdgeInsets.fromLTRB(10, _optionChanged ? 70 : 20, 10, 20), 99 | child: ListView( 100 | children: [ 101 | TextFormField( 102 | initialValue: _oldOption.dir, 103 | onChanged: (v) { 104 | _newOption.dir = v.trim(); 105 | _checkOptionChange(_oldOption.toJson()); 106 | }, 107 | validator: (v) => v == null || v.isEmpty 108 | ? _l10n.dirInputValidatorText 109 | : null, 110 | decoration: InputDecoration( 111 | border: const OutlineInputBorder(), 112 | labelText: _l10n.dirInputLabel, 113 | contentPadding: const EdgeInsets.all(8)), 114 | ), 115 | const SizedBox(height: 10), 116 | TextFormField( 117 | initialValue: _oldOption.maxConcurrentDownloads?.toString(), 118 | onChanged: (v) { 119 | _newOption.maxConcurrentDownloads = int.parse(v.trim()); 120 | _checkOptionChange(_oldOption.toJson()); 121 | }, 122 | validator: (v) { 123 | v = v ?? ''; 124 | if (v.isEmpty) { 125 | return _l10n.maxCurrentDownloadValidator_1; 126 | } 127 | if (v.length > 1 && v.startsWith('0')) { 128 | return _l10n.maxCurrentDownloadValidator_2; 129 | } 130 | 131 | if (int.tryParse(v) == null) { 132 | return _l10n.maxCurrentDownloadValidator_3; 133 | } 134 | 135 | if (int.parse(v) < 0) { 136 | return _l10n.maxCurrentDownloadValidator_4; 137 | } 138 | }, 139 | decoration: InputDecoration( 140 | border: const OutlineInputBorder(), 141 | labelText: _l10n.maxCurrentDownload4, 142 | contentPadding: const EdgeInsets.all(8)), 143 | ), 144 | const SizedBox(height: 10), 145 | SwitchListTile( 146 | contentPadding: EdgeInsets.zero, 147 | title: Text(_l10n.ariaContinue), 148 | value: _newOption.continue_ == null 149 | ? _oldOption.continue_! 150 | : _newOption.continue_!, 151 | onChanged: (newVal) { 152 | setState(() { 153 | _newOption.continue_ = newVal; 154 | _checkOptionChange(_oldOption.toJson()); 155 | }); 156 | }), 157 | const SizedBox(height: 10), 158 | SwitchListTile( 159 | contentPadding: EdgeInsets.zero, 160 | title: Text(_l10n.checkIntegrity), 161 | value: _newOption.checkIntegrity == null 162 | ? _oldOption.checkIntegrity! 163 | : _newOption.checkIntegrity!, 164 | onChanged: (newVal) { 165 | setState(() { 166 | _newOption.checkIntegrity = newVal; 167 | _checkOptionChange(_oldOption.toJson()); 168 | }); 169 | }), 170 | const SizedBox(height: 10), 171 | SwitchListTile( 172 | contentPadding: EdgeInsets.zero, 173 | title: Text(_l10n.optimizeConcurrentDownloads), 174 | value: _newOption.optimizeConcurrentDownloads == null 175 | ? _oldOption.optimizeConcurrentDownloads! 176 | : _newOption.optimizeConcurrentDownloads!, 177 | onChanged: (newVal) { 178 | setState(() { 179 | _newOption.optimizeConcurrentDownloads = newVal; 180 | _checkOptionChange(_oldOption.toJson()); 181 | }); 182 | }), 183 | ], 184 | ), 185 | )) 186 | ], 187 | ); 188 | } 189 | 190 | @override 191 | bool get wantKeepAlive => true; 192 | } 193 | 194 | 195 | // dir 默认下载地址 196 | 197 | // max-concurrent-downloads 最大同时下载任务数量 198 | 199 | // max-overall-download-limit 下载速度上限 200 | 201 | // max-overall-upload-limit 上传速度上限 202 | 203 | // continue 断点续传 204 | 205 | // check-integrity 检查完整性 206 | 207 | // optimize-concurrent-downloads 优化并行下载 208 | 209 | // log 日志文件下载地址 210 | 211 | // log-level 日志等级 212 | -------------------------------------------------------------------------------- /lib/states/app.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_init_to_null 2 | import 'package:aria_z/l10n/localization_intl.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:hive_flutter/adapters.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | import '../utils/aria2_api.dart'; 9 | import 'aria2.dart'; 10 | 11 | class Aria2ConnectConfig { 12 | String host; 13 | 14 | String port; 15 | 16 | String secret; 17 | 18 | String protocol; 19 | 20 | String type; 21 | 22 | String path; 23 | 24 | String configName; 25 | 26 | Aria2ConnectConfig({ 27 | required this.protocol, 28 | required this.host, 29 | required this.port, 30 | required this.path, 31 | required this.type, 32 | required this.configName, 33 | this.secret = '', 34 | }); 35 | 36 | factory Aria2ConnectConfig.fromJson(Map parsedJson) { 37 | return Aria2ConnectConfig( 38 | protocol: parsedJson['protocol'], 39 | host: parsedJson['host'], 40 | port: parsedJson['port'], 41 | path: parsedJson['path'], 42 | type: parsedJson['type'], 43 | configName: parsedJson['configName'], 44 | secret: parsedJson['secret'], 45 | ); 46 | } 47 | 48 | Map toJson() { 49 | return { 50 | 'protocol': protocol, 51 | 'host': host, 52 | 'port': port, 53 | 'path': path, 54 | 'type': type, 55 | 'secret': secret, 56 | 'configName': configName 57 | }; 58 | } 59 | } 60 | 61 | class Aria2TaskType { 62 | String desc; 63 | 64 | IconData icon; 65 | 66 | String name; 67 | 68 | TaskType taskType; 69 | 70 | Aria2TaskType(this.name, this.desc, this.icon, this.taskType); 71 | } 72 | 73 | class CustomMateriaColor { 74 | MaterialColor color; 75 | 76 | String desc; 77 | 78 | String name; 79 | 80 | CustomMateriaColor(this.name, this.color, this.desc); 81 | } 82 | 83 | class LocaleItem { 84 | String label; 85 | 86 | Locale? locale; 87 | 88 | LocaleItem(this.locale, this.label); 89 | } 90 | 91 | class AppState extends ChangeNotifier { 92 | AriazLocalizations _l10n = AriazLocalizations(); 93 | 94 | AppState(this.aria2ConnectConfigBox, this.prefs); 95 | 96 | /// hiveBox容器 97 | Box aria2ConnectConfigBox; 98 | 99 | /// shared_preferences容器 100 | SharedPreferences prefs; 101 | 102 | /// 选择连接的服务器配置 103 | Aria2ConnectConfig? _selectedAria2ConnectConfig = null; 104 | 105 | /// aria2连接实例化 106 | Aria2Client? _client = null; 107 | 108 | /// aria2 请求结构缓存状态 109 | late final Aria2States states; 110 | 111 | /// 是否正在检查服务器配置 112 | bool checkingConfig = false; 113 | 114 | /// 是否正在连接服务器 115 | bool connectingServer = false; 116 | 117 | //getters 118 | /// 本地化 119 | Locale? get selectedLocale { 120 | String? _l = prefs.getString('userSelectedLocaleLanguageTag'); 121 | if (_l != null) { 122 | List languageTag = _l.split('-'); 123 | return Locale.fromSubtags( 124 | languageCode: languageTag.first, 125 | scriptCode: languageTag.length == 3 ? languageTag[1] : null, 126 | countryCode: languageTag.last); 127 | } 128 | } 129 | 130 | String get appUsingColorName => prefs.getString('primaryColor') ?? 'green'; 131 | 132 | int get intervalSecond => prefs.getInt('intervalSecond') ?? 3; 133 | 134 | ThemeData get brightTheme => ThemeData( 135 | primarySwatch: appThemeColors 136 | .where((_color) => _color.name == appUsingColorName) 137 | .first 138 | .color, 139 | brightness: Brightness.light); 140 | ThemeData get darkTheme => ThemeData( 141 | primarySwatch: appThemeColors 142 | .where((_color) => _color.name == appUsingColorName) 143 | .first 144 | .color, 145 | brightness: Brightness.dark); 146 | 147 | Aria2Client? get aria2 => _client; 148 | 149 | Aria2ConnectConfig? get selectedAria2ConnectConfig { 150 | Map? _config = 151 | prefs.getString('lastConnectedAria2ConfigName')?.isNotEmpty == true 152 | ? Map.from(aria2ConnectConfigBox 153 | .toMap() 154 | .values 155 | .firstWhere((element) => 156 | element['configName'] == 157 | prefs.getString('lastConnectedAria2ConfigName'))) 158 | : null; 159 | 160 | return _selectedAria2ConnectConfig ?? 161 | (_config?.isNotEmpty == true 162 | ? Aria2ConnectConfig.fromJson(_config!) 163 | : null); 164 | } 165 | 166 | List get aria2ConnectConfigs { 167 | // aria2ConnectConfigBox.deleteAt(0); 168 | List tmpList = []; 169 | aria2ConnectConfigBox.toMap().forEach((key, value) { 170 | tmpList.add(Aria2ConnectConfig.fromJson(value)); 171 | }); 172 | return tmpList; 173 | } 174 | 175 | List get aria2TaskTypes => [ 176 | Aria2TaskType(_l10n.taskTypeNameTorrent, _l10n.taskTypeDescTorrent, 177 | Icons.file_present, TaskType.torrent), 178 | Aria2TaskType(_l10n.taskTypeNameMagnet, _l10n.taskTypeDescMagnet, 179 | Icons.link, TaskType.magnet), 180 | Aria2TaskType(_l10n.taskTypeNameUrl, _l10n.taskTypeDescUrl, Icons.web, 181 | TaskType.url), 182 | Aria2TaskType(_l10n.taskTypeNameMetalink, _l10n.taskTypeDescMetalink, 183 | Icons.all_inclusive, TaskType.metaLink), 184 | ]; 185 | 186 | List get appThemeColors => [ 187 | CustomMateriaColor('blue', Colors.blue, _l10n.colorBlue), 188 | CustomMateriaColor('red', Colors.red, _l10n.colorRed), 189 | CustomMateriaColor('green', Colors.green, _l10n.colorGreen), 190 | CustomMateriaColor('purple', Colors.purple, _l10n.colorPurple), 191 | ]; 192 | 193 | List get localeItems => [ 194 | LocaleItem(null, _l10n.systemLanguage), 195 | LocaleItem(const Locale('en', 'US'), 'English'), 196 | LocaleItem( 197 | const Locale.fromSubtags( 198 | languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), 199 | '简体中文') 200 | ]; 201 | //*********************************************************************************************// 202 | 203 | void bindAria2States(Aria2States state) { 204 | states = state; 205 | } 206 | 207 | void bindL10n(AriazLocalizations l10n) { 208 | _l10n = l10n; 209 | } 210 | 211 | ///连接到aria2服务器 212 | connectToAria2Server(Aria2Client? newClient) async { 213 | updateConnectingStatus(true); 214 | _client = newClient; 215 | if (_client != null) { 216 | _client?.getAria2GlobalOption(); 217 | _client?.getVersionInfo(); 218 | _client?.getInfosInterval(intervalSecond, this); 219 | } 220 | notifyListeners(); 221 | } 222 | 223 | updateConnectingStatus(bool status) { 224 | connectingServer = status; 225 | notifyListeners(); 226 | } 227 | 228 | // 清理当前服务器所有的状态 229 | clearCurrentServerAllState() { 230 | _client?.clearGIInterval(); 231 | states.clearStates(); 232 | _client = null; 233 | } 234 | 235 | /// 添加连接配置 236 | bool addAria2ConnectConfig(Aria2ConnectConfig config) { 237 | bool isExist = false; 238 | aria2ConnectConfigBox.toMap().forEach((key, value) { 239 | if (value['configName'] == config.configName) { 240 | isExist = true; 241 | } 242 | }); 243 | if (isExist) { 244 | return false; 245 | } 246 | aria2ConnectConfigBox.add(config.toJson()); 247 | return true; 248 | } 249 | 250 | Future> checkAria2ConnectConfig( 251 | Aria2ConnectConfig config) async { 252 | _selectedAria2ConnectConfig = config; 253 | prefs.setString('lastConnectedAria2ConfigName', config.configName); 254 | checkingConfig = true; 255 | notifyListeners(); 256 | Aria2Client __client = Aria2Client( 257 | '${config.protocol}://${config.host}:${config.port}${config.path}', 258 | config.type, 259 | config.secret, 260 | states, 261 | _l10n); 262 | Aria2Response checkResult = 263 | await __client.checkServerConnection(); 264 | checkingConfig = false; 265 | notifyListeners(); 266 | return checkResult; 267 | } 268 | 269 | /// 删除连接配置 270 | removeAria2ConnectConfig(Aria2ConnectConfig config) { 271 | aria2ConnectConfigBox.toMap().forEach((key, value) { 272 | if (value['configName'] == config.configName) { 273 | if (config.configName == selectedAria2ConnectConfig?.configName) { 274 | clearCurrentServerAllState(); 275 | // _client = null; 276 | } 277 | aria2ConnectConfigBox.delete(key); 278 | } 279 | }); 280 | notifyListeners(); 281 | } 282 | 283 | /// 更新连接配置 284 | updateAria2ConnectConfig(Aria2ConnectConfig config, int idx) { 285 | aria2ConnectConfigBox.putAt(idx, config.toJson()); 286 | notifyListeners(); 287 | } 288 | 289 | changeTheme(String _colorName) { 290 | prefs.setString('primaryColor', _colorName); 291 | notifyListeners(); 292 | } 293 | 294 | /// 更新刷新aria2服务访问频率 295 | void updateIntervalSecond(String second) { 296 | int _intervalSecond = int.parse(second); 297 | _client?.clearGIInterval(); 298 | _client?.getInfosInterval(_intervalSecond, this); 299 | prefs.setInt('intervalSecond', _intervalSecond); 300 | notifyListeners(); 301 | } 302 | 303 | void changeLocale(String languageCode) { 304 | if (languageCode.isEmpty) { 305 | prefs.remove('userSelectedLocaleLanguageTag'); 306 | } else { 307 | prefs.setString('userSelectedLocaleLanguageTag', languageCode); 308 | } 309 | notifyListeners(); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /lib/components/custom_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:aria2/aria2.dart'; 2 | import 'package:aria2/models/aria2GlobalStat.dart'; 3 | import 'package:aria_z/components/custom_snack_bar.dart'; 4 | import 'package:aria_z/components/speed_shower.dart'; 5 | import 'package:aria_z/l10n/localization_intl.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:flutter/widgets.dart'; 9 | import 'package:provider/provider.dart'; 10 | import 'package:tuple/tuple.dart'; 11 | 12 | import '../states/aria2.dart'; 13 | import '../states/app.dart'; 14 | import '../views/edit_aria2_server_config.dart' 15 | show Aria2ConnectConfigArguments; 16 | 17 | Widget customDrawer(BuildContext _parentContext) { 18 | AriazLocalizations _l10n = AriazLocalizations.of(_parentContext); 19 | _deleteServerConfig(BuildContext context, Aria2ConnectConfig config) { 20 | showDialog( 21 | context: context, 22 | builder: (BuildContext context) { 23 | return AlertDialog( 24 | title: Text(_l10n.deleteDialogTitle), 25 | content: Text(_l10n.deleteDialogContent), 26 | actions: [ 27 | TextButton( 28 | child: Text(_l10n.cancelBtnText), 29 | onPressed: () { 30 | Navigator.of(context).pop(); 31 | }, 32 | ), 33 | TextButton( 34 | child: Text(_l10n.confirmBtnText), 35 | onPressed: () { 36 | Provider.of(context, listen: false) 37 | .removeAria2ConnectConfig(config); 38 | Navigator.of(context).pop(); 39 | }, 40 | ), 41 | ], 42 | ); 43 | }, 44 | ); 45 | } 46 | 47 | return Selector, Aria2ConnectConfig?>>( 49 | selector: (BuildContext context, AppState app) => 50 | Tuple2(app.aria2ConnectConfigs, app.selectedAria2ConnectConfig), 51 | builder: (context, app, _) { 52 | return Drawer( 53 | child: Column( 54 | children: [ 55 | Builder(builder: (context) { 56 | return Selector>( 58 | selector: (BuildContext context, Aria2States aria2State) => 59 | Tuple2(aria2State.globalStatus, aria2State.versionInfo), 60 | builder: (context, serverInfo, _) { 61 | return DrawerHeader( 62 | decoration: 63 | BoxDecoration(color: Theme.of(context).primaryColor), 64 | child: DefaultTextStyle( 65 | style: TextStyle( 66 | color: Theme.of(context).colorScheme.onPrimary, 67 | fontSize: 12), 68 | child: Container( 69 | alignment: Alignment.topLeft, 70 | child: Column( 71 | crossAxisAlignment: CrossAxisAlignment.start, 72 | children: [ 73 | const Text("AriaZ", 74 | style: TextStyle( 75 | fontFamily: 'Coda', fontSize: 32)), 76 | const SizedBox(height: 4), 77 | Text( 78 | app.item2 != null 79 | ? "${_l10n.connectedText} ${app.item2?.host}:${app.item2?.port}" 80 | : _l10n.notConnectTip, 81 | style: const TextStyle()), 82 | serverInfo.item2 != null 83 | ? Text( 84 | "${_l10n.aria2VersionLabel} ${serverInfo.item2?.version ?? ''}", 85 | style: const TextStyle()) 86 | : const SizedBox(), 87 | serverInfo.item1 == null 88 | ? const SizedBox() 89 | : Chip( 90 | backgroundColor: 91 | Theme.of(context).backgroundColor, 92 | avatar: const Icon(Icons.speed), 93 | label: DefaultTextStyle( 94 | textAlign: TextAlign.center, 95 | style: TextStyle( 96 | color: Theme.of(context) 97 | .colorScheme 98 | .onBackground, 99 | fontSize: 12), 100 | child: SpeedShower( 101 | downloadSpeed: serverInfo 102 | .item1!.downloadSpeed, 103 | uploadSpeed: serverInfo 104 | .item1!.uploadSpeed))) 105 | ], 106 | ), 107 | ))); 108 | }); 109 | }), 110 | Container( 111 | constraints: const BoxConstraints(maxHeight: 400), 112 | // 使用 scrollbar 在滚动时显示滚动条 113 | child: Scrollbar( 114 | child: ListView.builder( 115 | // listview 和父组件 scrollBar 都有 scrollController,此处需要指定 listview 独立的scrollController,否则在滑动时会触发多个scrollController同时触发的错误 116 | controller: ScrollController(), 117 | // listView 高度在列表中内容减少时自动收缩,默认为false,也就是列表高度在渲染完毕之后不会因为内容的减少而收缩 118 | shrinkWrap: true, 119 | itemCount: app.item1.length, 120 | itemBuilder: (context, idx) { 121 | Aria2ConnectConfig acc = app.item1[idx]; 122 | return ListTile( 123 | minLeadingWidth: 10, 124 | leading: acc.toJson().toString() == 125 | app.item2?.toJson().toString() 126 | ? const Icon(Icons.check) 127 | : const Icon(Icons.link), 128 | title: Text( 129 | acc.configName, 130 | overflow: TextOverflow.fade, 131 | maxLines: 1, 132 | softWrap: false, 133 | style: const TextStyle( 134 | fontSize: 16, fontWeight: FontWeight.w600), 135 | ), 136 | subtitle: Text( 137 | '${acc.host}:${acc.port}', 138 | overflow: TextOverflow.fade, 139 | maxLines: 1, 140 | softWrap: false, 141 | style: const TextStyle(fontSize: 12), 142 | ), 143 | trailing: PopupMenuButton( 144 | child: const Icon(Icons.more_vert), 145 | itemBuilder: (context) => [ 146 | PopupMenuItem( 147 | value: 'edit', 148 | child: Text(_l10n.editText), 149 | ), 150 | PopupMenuItem( 151 | value: 'delete', 152 | child: Text(_l10n.deleteText), 153 | ), 154 | ], 155 | onSelected: (value) { 156 | if (value == 'edit') { 157 | Navigator.of(context).popAndPushNamed( 158 | "/update_aria2_server", 159 | arguments: [ 160 | Aria2ConnectConfigArguments(acc, idx), 161 | _parentContext 162 | ]); 163 | // Navigator.of(context).pushNamed('/editServer',arguments: acc); 164 | } else if (value == 'delete') { 165 | _deleteServerConfig(context, acc); 166 | } 167 | }, 168 | ), 169 | selected: acc.toJson().toString() == 170 | app.item2?.toJson().toString(), 171 | onTap: () { 172 | Navigator.pop(context); 173 | if (acc.configName != app.item2?.configName) { 174 | checkAndUseConfig(_parentContext, acc); 175 | } 176 | }, 177 | ); 178 | })), 179 | ), 180 | Expanded( 181 | child: ListView( 182 | physics: const NeverScrollableScrollPhysics(), 183 | padding: EdgeInsets.zero, 184 | children: [ 185 | Builder(builder: (context) { 186 | return ListTile( 187 | minLeadingWidth: 10, 188 | leading: const Icon(Icons.add), 189 | title: Text(_l10n.addNewServerConfig), 190 | onTap: () => Navigator.of(context).popAndPushNamed( 191 | "/add_new_aria2_server", 192 | arguments: [null, _parentContext])); 193 | }), 194 | Builder(builder: (context) { 195 | return ListTile( 196 | minLeadingWidth: 10, 197 | leading: const Icon(Icons.settings), 198 | title: Text(_l10n.setting), 199 | onTap: () => 200 | Navigator.of(context).popAndPushNamed("/global_setting"), 201 | ); 202 | }) 203 | ], 204 | )) 205 | ], 206 | )); 207 | }, 208 | ); 209 | } 210 | -------------------------------------------------------------------------------- /lib/l10n/messages_zh_Hans_CN.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a zh_Hans_CN locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, always_declare_return_types 11 | 12 | import 'package:intl/intl.dart'; 13 | import 'package:intl/message_lookup_by_library.dart'; 14 | 15 | final messages = MessageLookup(); 16 | 17 | typedef String MessageIfAbsent(String? messageStr, List? args); 18 | 19 | class MessageLookup extends MessageLookupByLibrary { 20 | String get localeName => 'zh_Hans_CN'; 21 | 22 | final messages = _notInlinedMessages(_notInlinedMessages); 23 | static Map _notInlinedMessages(_) => { 24 | "addAndDownload" : MessageLookupByLibrary.simpleMessage("添加并开始下载"), 25 | "addConfigSuccessTip" : MessageLookupByLibrary.simpleMessage("添加服务配置成功!"), 26 | "addNewServerConfig" : MessageLookupByLibrary.simpleMessage("添加新的服务器配置"), 27 | "addNewTaskToolTip" : MessageLookupByLibrary.simpleMessage("添加新任务"), 28 | "addSuccessTip" : MessageLookupByLibrary.simpleMessage("添加任务成功!"), 29 | "addText" : MessageLookupByLibrary.simpleMessage("添加"), 30 | "address" : MessageLookupByLibrary.simpleMessage("地址"), 31 | "allowOverwrite" : MessageLookupByLibrary.simpleMessage("允许覆盖历史下载同名文件"), 32 | "appSetting" : MessageLookupByLibrary.simpleMessage("APP设置"), 33 | "applyBtnText" : MessageLookupByLibrary.simpleMessage("应用"), 34 | "aria2Setting" : MessageLookupByLibrary.simpleMessage("Aria2全局配置"), 35 | "aria2SettingWarning" : MessageLookupByLibrary.simpleMessage("请连接到 Aria2 服务器再行配置..."), 36 | "aria2VersionLabel" : MessageLookupByLibrary.simpleMessage("aria2版本: "), 37 | "ariaContinue" : MessageLookupByLibrary.simpleMessage("断点续传"), 38 | "authFailed" : MessageLookupByLibrary.simpleMessage("Aria2身份认证失败!"), 39 | "bit" : MessageLookupByLibrary.simpleMessage("字节"), 40 | "bitField" : MessageLookupByLibrary.simpleMessage("个区块"), 41 | "bitFieldInfo" : MessageLookupByLibrary.simpleMessage("区块信息: "), 42 | "cancelBtnText" : MessageLookupByLibrary.simpleMessage("取消"), 43 | "changeGlobalOptionSuccessTip" : MessageLookupByLibrary.simpleMessage("修改全局配置成功"), 44 | "checkIntegrity" : MessageLookupByLibrary.simpleMessage("检查完整性"), 45 | "checkOptionWarningTip" : MessageLookupByLibrary.simpleMessage("请检查任务选项输入!"), 46 | "checkSourceTip" : MessageLookupByLibrary.simpleMessage("请检查下载源是否添加正确!"), 47 | "choose" : MessageLookupByLibrary.simpleMessage("选择"), 48 | "colorBlue" : MessageLookupByLibrary.simpleMessage("蓝色"), 49 | "colorGreen" : MessageLookupByLibrary.simpleMessage("绿色"), 50 | "colorPurple" : MessageLookupByLibrary.simpleMessage("紫色"), 51 | "colorRed" : MessageLookupByLibrary.simpleMessage("红色"), 52 | "complete" : MessageLookupByLibrary.simpleMessage("已完成"), 53 | "completeTipText" : MessageLookupByLibrary.simpleMessage("已完成"), 54 | "completedBtnText" : MessageLookupByLibrary.simpleMessage("已完成"), 55 | "configName" : MessageLookupByLibrary.simpleMessage("配置名称"), 56 | "configNameValidatorText_1" : MessageLookupByLibrary.simpleMessage("配置名称不能为空!"), 57 | "confirmBtnText" : MessageLookupByLibrary.simpleMessage("确定"), 58 | "confitExists" : MessageLookupByLibrary.simpleMessage("配置名称已存在!"), 59 | "connectFailedTip" : MessageLookupByLibrary.simpleMessage("连接到服务器失败..."), 60 | "connectedText" : MessageLookupByLibrary.simpleMessage("已连接到: "), 61 | "connecttingTip" : MessageLookupByLibrary.simpleMessage("正在尝试连接到Aria2服务器..."), 62 | "deleteDialogContent" : MessageLookupByLibrary.simpleMessage("确定删除当前服务器配置吗?"), 63 | "deleteDialogTitle" : MessageLookupByLibrary.simpleMessage("删除服务器配置"), 64 | "deleteText" : MessageLookupByLibrary.simpleMessage("删除"), 65 | "dirInputLabel" : MessageLookupByLibrary.simpleMessage("默认下载地址"), 66 | "dirInputValidatorText" : MessageLookupByLibrary.simpleMessage("请输入下载目录"), 67 | "dirValidatorText_1" : MessageLookupByLibrary.simpleMessage("请输入下载目录!"), 68 | "dirValidatorText_2" : MessageLookupByLibrary.simpleMessage("文件路径必须在包含在全局下载目录"), 69 | "downloadPath" : MessageLookupByLibrary.simpleMessage("下载路径"), 70 | "downloadingBtnText" : MessageLookupByLibrary.simpleMessage("下载中"), 71 | "downloadingTipText" : MessageLookupByLibrary.simpleMessage("下载中 "), 72 | "editText" : MessageLookupByLibrary.simpleMessage("编辑"), 73 | "enableHttps" : MessageLookupByLibrary.simpleMessage("启用HTTPS"), 74 | "error" : MessageLookupByLibrary.simpleMessage("错误"), 75 | "file" : MessageLookupByLibrary.simpleMessage("文件"), 76 | "fileList" : MessageLookupByLibrary.simpleMessage("文件列表"), 77 | "host" : MessageLookupByLibrary.simpleMessage("服务器地址"), 78 | "hostValidatorText_1" : MessageLookupByLibrary.simpleMessage("服务器地址不能为空!"), 79 | "language" : MessageLookupByLibrary.simpleMessage("语言"), 80 | "linkInputDialogTitle" : MessageLookupByLibrary.simpleMessage("输入链接地址"), 81 | "linkInputLabel" : MessageLookupByLibrary.simpleMessage("链接地址"), 82 | "magnetText" : MessageLookupByLibrary.simpleMessage("磁力链"), 83 | "maxCurrentDownload4" : MessageLookupByLibrary.simpleMessage("最大同时下载任务数量"), 84 | "maxCurrentDownloadValidator_1" : MessageLookupByLibrary.simpleMessage("请输入数量!"), 85 | "maxCurrentDownloadValidator_2" : MessageLookupByLibrary.simpleMessage("下载数不能以0开头!"), 86 | "maxCurrentDownloadValidator_3" : MessageLookupByLibrary.simpleMessage("请输入合法整数!"), 87 | "maxCurrentDownloadValidator_4" : MessageLookupByLibrary.simpleMessage("最小同时下载数量需大于等于1!"), 88 | "maxDonwloadSpeedInputLabel" : MessageLookupByLibrary.simpleMessage("最大下载速度"), 89 | "maxSpeedInputHelper" : MessageLookupByLibrary.simpleMessage("设置为0时表示不限速"), 90 | "maxUploadSpeedInputLabel" : MessageLookupByLibrary.simpleMessage("最大上传速度"), 91 | "more" : MessageLookupByLibrary.simpleMessage("添加更多"), 92 | "noText" : MessageLookupByLibrary.simpleMessage("无"), 93 | "notConnectTip" : MessageLookupByLibrary.simpleMessage("未连接到Aria2服务器"), 94 | "optimizeConcurrentDownloads" : MessageLookupByLibrary.simpleMessage("优化并行下载"), 95 | "optionChangedTip" : MessageLookupByLibrary.simpleMessage("配置已发生改变,点击右侧按钮提交"), 96 | "optionText" : MessageLookupByLibrary.simpleMessage("选项"), 97 | "path" : MessageLookupByLibrary.simpleMessage("路径"), 98 | "paused" : MessageLookupByLibrary.simpleMessage("已暂停"), 99 | "pausing" : MessageLookupByLibrary.simpleMessage("正在暂停"), 100 | "peers" : MessageLookupByLibrary.simpleMessage("连接节点"), 101 | "port" : MessageLookupByLibrary.simpleMessage("端口"), 102 | "portValidatorText_1" : MessageLookupByLibrary.simpleMessage("端口不能为空!"), 103 | "progress" : MessageLookupByLibrary.simpleMessage("已下载: "), 104 | "protocol" : MessageLookupByLibrary.simpleMessage("协议类型"), 105 | "reChoose" : MessageLookupByLibrary.simpleMessage("重新选择"), 106 | "reConnectBtnText" : MessageLookupByLibrary.simpleMessage("点击重试"), 107 | "refreshDelay" : MessageLookupByLibrary.simpleMessage("刷新频率"), 108 | "second" : MessageLookupByLibrary.simpleMessage("秒"), 109 | "secret" : MessageLookupByLibrary.simpleMessage("验证令牌"), 110 | "serverRefusedError" : MessageLookupByLibrary.simpleMessage("连接服务器被拒绝,请检查服务器配置..."), 111 | "serverUnknownError" : MessageLookupByLibrary.simpleMessage("Aria2服务器未知错误!"), 112 | "setSpeedLimitTitle" : MessageLookupByLibrary.simpleMessage("设置限速"), 113 | "setting" : MessageLookupByLibrary.simpleMessage("设置"), 114 | "speedLimit" : MessageLookupByLibrary.simpleMessage("限速"), 115 | "speedLimitInputValidatorText_1" : MessageLookupByLibrary.simpleMessage("请输入速度限制"), 116 | "speedLimitInputValidatorText_2" : MessageLookupByLibrary.simpleMessage("速度限制不能以0开头"), 117 | "speedLimitInputValidatorText_3" : MessageLookupByLibrary.simpleMessage("请输入合法数字"), 118 | "speedLimitSetSuccess" : MessageLookupByLibrary.simpleMessage("下载限速设置成功"), 119 | "status" : MessageLookupByLibrary.simpleMessage("任务状态: "), 120 | "submit" : MessageLookupByLibrary.simpleMessage("提交"), 121 | "systemLanguage" : MessageLookupByLibrary.simpleMessage("跟随系统"), 122 | "taskInfo" : MessageLookupByLibrary.simpleMessage("任务信息"), 123 | "taskName" : MessageLookupByLibrary.simpleMessage("任务名称: "), 124 | "taskStatusOfComplete" : MessageLookupByLibrary.simpleMessage("已完成"), 125 | "taskStatusOfDonloading" : MessageLookupByLibrary.simpleMessage("下载中"), 126 | "taskStatusOfError" : MessageLookupByLibrary.simpleMessage("下载错误"), 127 | "taskStatusOfPaused" : MessageLookupByLibrary.simpleMessage("已暂停"), 128 | "taskStatusOfWaitting" : MessageLookupByLibrary.simpleMessage("队列中"), 129 | "taskText" : MessageLookupByLibrary.simpleMessage("任务"), 130 | "taskTypeDescMagnet" : MessageLookupByLibrary.simpleMessage("输入磁力链接下载..."), 131 | "taskTypeDescMetalink" : MessageLookupByLibrary.simpleMessage("读取Metalink文件下载..."), 132 | "taskTypeDescTorrent" : MessageLookupByLibrary.simpleMessage("读取种子文件下载..."), 133 | "taskTypeDescUrl" : MessageLookupByLibrary.simpleMessage("输入Http,Ftp等链接下载..."), 134 | "taskTypeNameMagnet" : MessageLookupByLibrary.simpleMessage("磁力链下载"), 135 | "taskTypeNameMetalink" : MessageLookupByLibrary.simpleMessage("元数据下载"), 136 | "taskTypeNameTorrent" : MessageLookupByLibrary.simpleMessage("种子下载"), 137 | "taskTypeNameUrl" : MessageLookupByLibrary.simpleMessage("网址下载"), 138 | "themeColor" : MessageLookupByLibrary.simpleMessage("主题颜色"), 139 | "timeOutError" : MessageLookupByLibrary.simpleMessage("连接服务器超时!"), 140 | "tipOfMagnet" : MessageLookupByLibrary.simpleMessage("支持Metalink文件格式 \".metalink, .meta4\""), 141 | "tipOfMetalink" : MessageLookupByLibrary.simpleMessage("支持\"magnet:?\"磁力链"), 142 | "tipOfTorrent" : MessageLookupByLibrary.simpleMessage("支持种子文件格式 \".torrent\""), 143 | "tipOfUrl" : MessageLookupByLibrary.simpleMessage("支持协议类型包括 HTTP/FTP/SFTP/BitTorrent"), 144 | "total" : MessageLookupByLibrary.simpleMessage("共"), 145 | "unparsing" : MessageLookupByLibrary.simpleMessage("正在启动"), 146 | "updateConfigSuccessTip" : MessageLookupByLibrary.simpleMessage("修改服务配置成功!"), 147 | "waiting" : MessageLookupByLibrary.simpleMessage("队列中") 148 | }; 149 | } 150 | -------------------------------------------------------------------------------- /lib/utils/aria2_api.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_typing_uninitialized_variables, avoid_init_to_null 2 | 3 | import 'dart:async'; 4 | import 'package:aria2/aria2.dart'; 5 | import 'package:aria_z/l10n/localization_intl.dart'; 6 | import 'package:aria_z/states/app.dart' show AppState; 7 | import '../states/aria2.dart'; 8 | import 'package:dio/dio.dart'; 9 | import 'dart:convert'; 10 | 11 | enum Aria2ResponseErrorType { connectionRefused, unauthorized, timeout, other } 12 | 13 | enum Aria2ResponseStatus { success, error } 14 | 15 | class Aria2Response { 16 | T? data; 17 | 18 | Aria2ResponseErrorType? error; 19 | 20 | Aria2ResponseStatus status; 21 | 22 | String message; 23 | 24 | Aria2Response( 25 | {required this.status, 26 | required this.data, 27 | required this.error, 28 | required this.message}); 29 | } 30 | 31 | class Aria2Client extends Aria2c { 32 | Aria2States state; 33 | 34 | List _completedTasks = []; 35 | late List _downloadingGids; 36 | late Timer? periodicTimer = null; 37 | late Timer? periodicTimerInner = null; 38 | 39 | final AriazLocalizations _l10n; 40 | 41 | Aria2Client(rpcUrl, protocol, secret, this.state, this._l10n) 42 | : super(rpcUrl, protocol, secret); 43 | 44 | getInfosInterval(int timeIntervalSecend, AppState app) async { 45 | await getInfos(); 46 | app.updateConnectingStatus(false); 47 | Duration timeout = Duration(seconds: timeIntervalSecend); 48 | periodicTimer = Timer.periodic(timeout, (timer) async { 49 | periodicTimerInner = timer; 50 | getInfos(); 51 | }); 52 | } 53 | 54 | clearGIInterval() { 55 | periodicTimerInner?.cancel(); 56 | periodicTimer?.cancel(); 57 | } 58 | 59 | Future getInfos() async { 60 | return _try2Request(() async { 61 | await _getGlobalStats(); 62 | state.updateDownloadingTasks(await _getDownloadingTasks()); 63 | state.updateWaittingTasks(await _getWaittingTasks()); 64 | }); 65 | } 66 | 67 | Future _getGlobalStats() async { 68 | return _try2Request(() async { 69 | Aria2GlobalStat _globalStatus = await getGlobalStat(); 70 | state.updateGlobalStatus(_globalStatus); 71 | }); 72 | } 73 | 74 | Future getCompletedTasks(int offset, int limmit) async { 75 | return _try2Request(() async { 76 | _completedTasks = await tellStopped(offset, limmit); 77 | state.updateCompletedTasks( 78 | _completedTasks.where((ct) => ct.status != 'removed').toList()); 79 | }); 80 | } 81 | 82 | Future> _getDownloadingTasks() async { 83 | List _downloadingTasks = await tellActive(); 84 | // 暂停任务指令不会立即生效,所以手动改变任务状态 85 | for (var i = 0; i < _downloadingTasks.length; i++) { 86 | if (state.opratingGids.contains(_downloadingTasks[i].gid)) { 87 | if (_downloadingTasks[i].status == 'active') { 88 | _downloadingTasks.add(_downloadingTasks.removeAt(i)); 89 | } else { 90 | state.removeOpratingGids([_downloadingTasks[i].gid ?? ""]); 91 | } 92 | } 93 | } 94 | _downloadingGids = _downloadingTasks 95 | .map((dl) { 96 | return dl.gid!; 97 | }) 98 | .where((gid) => !state.opratingGids.contains(gid)) 99 | .toList(); 100 | return _downloadingTasks; 101 | } 102 | 103 | Future> _getWaittingTasks() async { 104 | int size = 1; 105 | Future> _getWT( 106 | int offset, int _num, List li) async { 107 | List tmp = await tellWaiting(offset, _num); 108 | li.addAll(tmp); 109 | if (tmp.length == _num) { 110 | await _getWT(offset + _num, _num, li); 111 | } 112 | return li; 113 | } 114 | 115 | List wt = await _getWT(0, size, []); 116 | for (var i = 0; i < wt.length; i++) { 117 | if (wt[i].status == 'waiting' && 118 | !(_downloadingGids.contains(wt[i].gid))) { 119 | _downloadingGids.add(wt[i].gid!); 120 | } 121 | if (state.opratingGids.contains(wt[i].gid) && 122 | ['paused', 'waiting'].contains(wt[i].status)) { 123 | state.removeOpratingGids([wt[i].gid ?? ""]); 124 | } 125 | } 126 | return wt; 127 | } 128 | 129 | Future pauseTask(String gid) async { 130 | return _try2Request(() async { 131 | state.addOpratingGids([gid]); 132 | _downloadingGids.remove(gid); 133 | await pause(gid); 134 | }); 135 | } 136 | 137 | Future unPauseTask(String gid) async { 138 | return _try2Request(() async { 139 | state.removeOpratingGids([gid]); 140 | await unpause(gid); 141 | await getInfos(); 142 | }); 143 | } 144 | 145 | Future pauseAllTask() async { 146 | return _try2Request(() async { 147 | state.addOpratingGids(_downloadingGids); 148 | await pauseAll(); 149 | }); 150 | } 151 | 152 | Future unPauseAllTask() async { 153 | return _try2Request(() async { 154 | await unpauseAll(); 155 | await getInfos(); 156 | }); 157 | } 158 | 159 | Future startWaitingTask(gid) async { 160 | return _try2Request(() async { 161 | await changePosition(gid, 0, 'POS_SET'); 162 | }); 163 | } 164 | 165 | Future addNewTask(NewTaskOption taskOption) async { 166 | return _try2Request(() async { 167 | Map option = taskOption.option.toJson(); 168 | switch (taskOption.taskType) { 169 | case TaskType.torrent: 170 | return await multicall(taskOption.params.map((p) { 171 | return Method('aria2.addTorrent', [p, [], option]); 172 | }).toList()); 173 | case TaskType.metaLink: 174 | await multicall(taskOption.params.map((p) { 175 | return Method('aria2.addMetalink', [p, option]); 176 | }).toList()); 177 | break; 178 | case TaskType.magnet: 179 | await multicall(taskOption.params.map((p) { 180 | return Method('aria2.addUri', [ 181 | [p], 182 | option 183 | ]); 184 | }).toList()); 185 | break; 186 | case TaskType.url: 187 | await multicall(taskOption.params.map((p) { 188 | return Method('aria2.addUri', [ 189 | [p], 190 | option 191 | ]); 192 | }).toList()); 193 | break; 194 | } 195 | }); 196 | } 197 | 198 | /// 获取全局配置 199 | Future getAria2GlobalOption() async { 200 | return _try2Request(() async { 201 | Aria2Option option = await getGlobalOption(); 202 | state.updateGlobalOption(option); 203 | }); 204 | } 205 | 206 | Future> updateTheGlobalOption( 207 | Aria2Option option) async { 208 | return _try2Request(() async { 209 | return await changeGlobalOption(option); 210 | }); 211 | } 212 | 213 | Future getVersionInfo() async { 214 | return _try2Request(() async { 215 | Aria2Version version = await getVersion(); 216 | state.updateVersion(version); 217 | }); 218 | } 219 | 220 | /// 删除任务 221 | /// [gid] 任务gid 222 | 223 | Future> removeTask( 224 | String gid, String taskStatus) async { 225 | return _try2Request(() async { 226 | switch (taskStatus) { 227 | case 'complete': 228 | case 'error': 229 | case 'removed': 230 | return await removeDownloadResult(gid); 231 | default: 232 | return await forceRemove(gid); 233 | } 234 | }); 235 | } 236 | 237 | Future> checkServerConnection() async { 238 | return _try2Request(() async { 239 | await getVersion(); 240 | return this; 241 | }); 242 | } 243 | 244 | Future>> getAria2Peers(String gid) async { 245 | return _try2Request(() async { 246 | return await getPeers(gid); 247 | }); 248 | } 249 | 250 | Future> _try2Request(Function request) async { 251 | try { 252 | T? data = await request(); 253 | return Aria2Response( 254 | status: Aria2ResponseStatus.success, 255 | data: data, 256 | error: null, 257 | message: 'OK'); 258 | } on DioError catch (e) { 259 | String msg; 260 | Aria2ResponseErrorType? errorType; 261 | 262 | switch (e.type) { 263 | case DioErrorType.response: 264 | String? dt = e.response?.data; 265 | if (dt != null && dt.isNotEmpty) { 266 | Map aria2Error = jsonDecode(dt); 267 | String __errormessage = aria2Error['error']['message']; 268 | if (__errormessage.contains('No peer data is available for GID')) { 269 | errorType = null; 270 | msg = ''; 271 | break; 272 | } else if (__errormessage.contains('Unauthorized')) { 273 | errorType = Aria2ResponseErrorType.unauthorized; 274 | msg = _l10n.authFailed; 275 | } else { 276 | errorType = Aria2ResponseErrorType.other; 277 | msg = aria2Error['error']['message']; 278 | } 279 | } else { 280 | errorType = Aria2ResponseErrorType.other; 281 | msg = _l10n.serverUnknownError; 282 | } 283 | break; 284 | case DioErrorType.connectTimeout: 285 | case DioErrorType.sendTimeout: 286 | errorType = Aria2ResponseErrorType.timeout; 287 | msg = _l10n.timeOutError; 288 | break; 289 | case DioErrorType.other: 290 | var __e = e.error; 291 | if (__e.runtimeType.toString() == 'SocketException' && 292 | __e.osError.runtimeType.toString() == 'OSError') { 293 | switch (__e.osError.errorCode) { 294 | case 61: 295 | errorType = Aria2ResponseErrorType.connectionRefused; 296 | msg = _l10n.serverRefusedError; 297 | break; 298 | default: 299 | errorType = Aria2ResponseErrorType.other; 300 | msg = __e.osError.message; 301 | break; 302 | } 303 | } else { 304 | errorType = Aria2ResponseErrorType.other; 305 | msg = e.message; 306 | } 307 | break; 308 | default: 309 | errorType = Aria2ResponseErrorType.other; 310 | msg = e.message; 311 | break; 312 | } 313 | return Aria2Response( 314 | status: Aria2ResponseStatus.error, 315 | data: null, 316 | error: errorType, 317 | message: msg); 318 | } on Exception catch (e) { 319 | return Aria2Response( 320 | status: Aria2ResponseStatus.error, 321 | data: null, 322 | error: Aria2ResponseErrorType.other, 323 | message: e.toString()); 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /lib/views/edit_aria2_server_config.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: must_be_immutable 2 | 3 | import 'package:aria_z/l10n/localization_intl.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:provider/provider.dart'; 7 | import 'package:toggle_switch/toggle_switch.dart'; 8 | import '../components/custom_snack_bar.dart'; 9 | import '../states/app.dart'; 10 | 11 | late AriazLocalizations _l10n; 12 | 13 | class Aria2ConnectConfigArguments { 14 | final Aria2ConnectConfig config; 15 | final int index; 16 | 17 | Aria2ConnectConfigArguments(this.config, this.index); 18 | } 19 | 20 | class Aria2ServerEditor extends StatelessWidget { 21 | const Aria2ServerEditor({Key? key}) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | Aria2ConnectConfigArguments? oldConfig; 26 | BuildContext? homePageContext; 27 | _l10n = AriazLocalizations.of(context); 28 | var args = ModalRoute.of(context)?.settings.arguments; 29 | if (args != null) { 30 | args = args as List; 31 | oldConfig = args[0] as Aria2ConnectConfigArguments?; 32 | homePageContext = args[1] as BuildContext; 33 | } 34 | 35 | void _submitNewServerConfig(context) { 36 | bodyKey.currentState?.submitServerConfig(); 37 | } 38 | 39 | return Scaffold( 40 | appBar: AppBar( 41 | title: Text(_l10n.addNewServerConfig), 42 | actions: [ 43 | IconButton( 44 | icon: const Icon(Icons.done_all), 45 | tooltip: _l10n.addNewServerConfig, 46 | onPressed: () => _submitNewServerConfig(context)) 47 | ], 48 | ), 49 | body: SingleChildScrollView( 50 | child: BodyWidget( 51 | key: bodyKey, 52 | oldConfig: oldConfig, 53 | homePageContext: homePageContext, 54 | )), 55 | ); 56 | } 57 | } 58 | 59 | class BodyWidget extends StatefulWidget { 60 | final Aria2ConnectConfigArguments? oldConfig; 61 | 62 | final BuildContext? homePageContext; 63 | 64 | const BodyWidget({Key? key, this.oldConfig, this.homePageContext}) 65 | : super(key: key); 66 | 67 | @override 68 | State createState() => BodyWidgetState(); 69 | } 70 | 71 | GlobalKey bodyKey = GlobalKey(); 72 | 73 | class BodyWidgetState extends State { 74 | String host = ''; 75 | String port = '6800'; 76 | String path = '/jsonrpc'; 77 | String protocol = 'http'; 78 | String secret = ''; 79 | String configName = ''; 80 | bool enableHttps = false; 81 | int? oldIndex; 82 | 83 | late bool _secretVisible; 84 | 85 | @override 86 | void initState() { 87 | super.initState(); 88 | _secretVisible = false; 89 | Aria2ConnectConfigArguments? oldConfig = widget.oldConfig; 90 | if (oldConfig != null) { 91 | Aria2ConnectConfig oc = oldConfig.config; 92 | host = oc.host; 93 | port = oc.port; 94 | path = oc.path; 95 | protocol = oc.type; 96 | secret = oc.secret; 97 | configName = oc.configName; 98 | enableHttps = oc.protocol == 'https' || oc.protocol == 'wss'; 99 | oldIndex = oldConfig.index; 100 | } 101 | } 102 | 103 | final GlobalKey _formKey = GlobalKey(); 104 | 105 | submitServerConfig() { 106 | String tmpProtocol = ''; 107 | if (enableHttps) { 108 | tmpProtocol = protocol == 'http' ? 'https' : 'wss'; 109 | } else { 110 | tmpProtocol = protocol == 'http' ? 'http' : 'ws'; 111 | } 112 | Map serverConfig = { 113 | 'host': host, 114 | 'port': port, 115 | 'path': path, 116 | 'protocol': tmpProtocol, 117 | 'type': protocol, 118 | 'secret': secret, 119 | 'configName': configName, 120 | }; 121 | if ((_formKey.currentState as FormState).validate()) { 122 | AppState app = Provider.of(context, listen: false); 123 | String msg = ''; 124 | Aria2ConnectConfig newConfig = Aria2ConnectConfig.fromJson(serverConfig); 125 | if (oldIndex == null) { 126 | bool isNotExist = app.addAria2ConnectConfig(newConfig); 127 | if (isNotExist) { 128 | checkAndUseConfig(widget.homePageContext!, newConfig); 129 | } else { 130 | showCustomSnackBar(context, 2, Text(_l10n.confitExists)); 131 | return; 132 | } 133 | msg = _l10n.addConfigSuccessTip; 134 | } else { 135 | app.updateAria2ConnectConfig(newConfig, oldIndex!); 136 | checkAndUseConfig(widget.homePageContext!, newConfig); 137 | msg = _l10n.updateConfigSuccessTip; 138 | } 139 | showCustomSnackBar(context, 1, Text(msg), durationSecond: 1); 140 | Navigator.pop(context, serverConfig); 141 | } 142 | } 143 | 144 | @override 145 | Widget build(BuildContext context) { 146 | return Form( 147 | key: _formKey, 148 | // autovalidateMode: AutovalidateMode.onUserInteraction, 149 | child: Padding( 150 | padding: const EdgeInsets.all(10.0), 151 | child: Column( 152 | children: [ 153 | const SizedBox(height: 20), 154 | TextFormField( 155 | autofocus: true, 156 | controller: TextEditingController(text: configName), 157 | decoration: InputDecoration( 158 | border: const OutlineInputBorder(), 159 | labelText: _l10n.configName, 160 | contentPadding: const EdgeInsets.all(8)), 161 | validator: (v) { 162 | return v!.trim().isNotEmpty 163 | ? null 164 | : _l10n.configNameValidatorText_1; 165 | }, 166 | onChanged: (v) { 167 | configName = v.trim(); 168 | }, 169 | ), 170 | const SizedBox(height: 18), 171 | Row( 172 | children: [ 173 | Text(_l10n.protocol), 174 | const SizedBox(width: 18), 175 | ToggleSwitch( 176 | activeBgColor: [ 177 | Theme.of(context).colorScheme.primary, 178 | Theme.of(context).colorScheme.secondary 179 | ], 180 | minWidth: 100, 181 | initialLabelIndex: 0, 182 | totalSwitches: 2, 183 | labels: const ["Http", "Websocket"], 184 | onToggle: (index) { 185 | protocol = index == 0 ? 'http' : 'websocket'; 186 | }, 187 | ), 188 | ], 189 | ), 190 | const SizedBox(height: 18), 191 | TextFormField( 192 | controller: TextEditingController(text: host), 193 | decoration: InputDecoration( 194 | border: const OutlineInputBorder(), 195 | labelText: _l10n.host, 196 | contentPadding: const EdgeInsets.all(8)), 197 | validator: (v) { 198 | return v!.trim().isNotEmpty 199 | ? null 200 | : _l10n.hostValidatorText_1; 201 | }, 202 | onChanged: (v) { 203 | host = v.trim(); 204 | }, 205 | ), 206 | const SizedBox(height: 18), 207 | Flex( 208 | direction: Axis.horizontal, 209 | children: [ 210 | Expanded( 211 | flex: 1, 212 | child: TextFormField( 213 | controller: TextEditingController(text: port), 214 | decoration: InputDecoration( 215 | border: const OutlineInputBorder(), 216 | labelText: _l10n.port, 217 | contentPadding: const EdgeInsets.all(8)), 218 | validator: (v) { 219 | return v!.trim().isNotEmpty 220 | ? null 221 | : _l10n.portValidatorText_1; 222 | }, 223 | onChanged: (v) { 224 | port = v.trim(); 225 | }, 226 | )), 227 | const SizedBox(width: 8), 228 | Expanded( 229 | flex: 3, 230 | child: TextFormField( 231 | controller: TextEditingController(text: path), 232 | decoration: InputDecoration( 233 | border: const OutlineInputBorder(), 234 | labelText: _l10n.path, 235 | contentPadding: const EdgeInsets.all(8), 236 | ), 237 | onChanged: (v) { 238 | path = v.trim(); 239 | }, 240 | )) 241 | ], 242 | ), 243 | const SizedBox(height: 18), 244 | TextFormField( 245 | controller: TextEditingController(text: secret), 246 | obscureText: !_secretVisible, 247 | decoration: InputDecoration( 248 | border: const OutlineInputBorder(), 249 | labelText: _l10n.secret, 250 | contentPadding: const EdgeInsets.all(8), 251 | suffixIcon: IconButton( 252 | icon: Icon( 253 | _secretVisible 254 | ? Icons.visibility_off 255 | : Icons.visibility, 256 | color: Theme.of(context).colorScheme.secondary, 257 | ), 258 | onPressed: () { 259 | setState(() { 260 | _secretVisible = !_secretVisible; 261 | }); 262 | }, 263 | )), 264 | onChanged: (v) { 265 | secret = v.trim(); 266 | }), 267 | const SizedBox(height: 18), 268 | Row( 269 | children: [ 270 | Checkbox( 271 | value: enableHttps, 272 | onChanged: (val) { 273 | setState(() { 274 | enableHttps = val ?? false; 275 | }); 276 | }), 277 | Text(_l10n.enableHttps), 278 | ], 279 | ) 280 | ], 281 | ))); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /lib/l10n/messages_messages.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a messages locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, always_declare_return_types 11 | 12 | import 'package:intl/intl.dart'; 13 | import 'package:intl/message_lookup_by_library.dart'; 14 | 15 | final messages = MessageLookup(); 16 | 17 | typedef String MessageIfAbsent(String? messageStr, List? args); 18 | 19 | class MessageLookup extends MessageLookupByLibrary { 20 | String get localeName => 'messages'; 21 | 22 | final messages = _notInlinedMessages(_notInlinedMessages); 23 | static Map _notInlinedMessages(_) => { 24 | "addAndDownload" : MessageLookupByLibrary.simpleMessage("Add & Download"), 25 | "addConfigSuccessTip" : MessageLookupByLibrary.simpleMessage("Success to add server config!"), 26 | "addNewServerConfig" : MessageLookupByLibrary.simpleMessage("Add new server config"), 27 | "addNewTaskToolTip" : MessageLookupByLibrary.simpleMessage("Add new task"), 28 | "addSuccessTip" : MessageLookupByLibrary.simpleMessage("Success to add new Task!"), 29 | "addText" : MessageLookupByLibrary.simpleMessage("Add"), 30 | "address" : MessageLookupByLibrary.simpleMessage(" address"), 31 | "allowOverwrite" : MessageLookupByLibrary.simpleMessage("Allow overwrite download result"), 32 | "appSetting" : MessageLookupByLibrary.simpleMessage("App setting"), 33 | "applyBtnText" : MessageLookupByLibrary.simpleMessage("Apply"), 34 | "aria2Setting" : MessageLookupByLibrary.simpleMessage("Aria2 setting"), 35 | "aria2SettingWarning" : MessageLookupByLibrary.simpleMessage("Please connect to Aria2 server before setting"), 36 | "aria2VersionLabel" : MessageLookupByLibrary.simpleMessage("Aria2 version: "), 37 | "ariaContinue" : MessageLookupByLibrary.simpleMessage("Continue the break"), 38 | "authFailed" : MessageLookupByLibrary.simpleMessage("Aria2 authentication failed!"), 39 | "bit" : MessageLookupByLibrary.simpleMessage("bit "), 40 | "bitField" : MessageLookupByLibrary.simpleMessage("bit fields "), 41 | "bitFieldInfo" : MessageLookupByLibrary.simpleMessage("BitField: "), 42 | "cancelBtnText" : MessageLookupByLibrary.simpleMessage("Cancel"), 43 | "changeGlobalOptionSuccessTip" : MessageLookupByLibrary.simpleMessage("Success to change global option"), 44 | "checkIntegrity" : MessageLookupByLibrary.simpleMessage("Check integrity"), 45 | "checkOptionWarningTip" : MessageLookupByLibrary.simpleMessage("Please check task option input!"), 46 | "checkSourceTip" : MessageLookupByLibrary.simpleMessage("Please check task source!"), 47 | "choose" : MessageLookupByLibrary.simpleMessage("Choose "), 48 | "colorBlue" : MessageLookupByLibrary.simpleMessage("Blue"), 49 | "colorGreen" : MessageLookupByLibrary.simpleMessage("Green"), 50 | "colorPurple" : MessageLookupByLibrary.simpleMessage("Purple"), 51 | "colorRed" : MessageLookupByLibrary.simpleMessage("Red"), 52 | "complete" : MessageLookupByLibrary.simpleMessage("Complete"), 53 | "completeTipText" : MessageLookupByLibrary.simpleMessage(" completed "), 54 | "completedBtnText" : MessageLookupByLibrary.simpleMessage("Complete"), 55 | "configName" : MessageLookupByLibrary.simpleMessage("Name"), 56 | "configNameValidatorText_1" : MessageLookupByLibrary.simpleMessage("Config name can not be empty!"), 57 | "confirmBtnText" : MessageLookupByLibrary.simpleMessage("Confirm"), 58 | "confitExists" : MessageLookupByLibrary.simpleMessage("Config name exsits!"), 59 | "connectFailedTip" : MessageLookupByLibrary.simpleMessage("Failed to connect to Aria2 server..."), 60 | "connectedText" : MessageLookupByLibrary.simpleMessage("Connected to: "), 61 | "connecttingTip" : MessageLookupByLibrary.simpleMessage("Trying to connect Aria2 server"), 62 | "deleteDialogContent" : MessageLookupByLibrary.simpleMessage("Sure to delete current server config ?"), 63 | "deleteDialogTitle" : MessageLookupByLibrary.simpleMessage("Delete server config"), 64 | "deleteText" : MessageLookupByLibrary.simpleMessage("Delete"), 65 | "dirInputLabel" : MessageLookupByLibrary.simpleMessage("Default download path"), 66 | "dirInputValidatorText" : MessageLookupByLibrary.simpleMessage("Please input the download path!"), 67 | "dirValidatorText_1" : MessageLookupByLibrary.simpleMessage("Please input download path!"), 68 | "dirValidatorText_2" : MessageLookupByLibrary.simpleMessage("Download result must under global path "), 69 | "downloadPath" : MessageLookupByLibrary.simpleMessage("Download path"), 70 | "downloadingBtnText" : MessageLookupByLibrary.simpleMessage("Download"), 71 | "downloadingTipText" : MessageLookupByLibrary.simpleMessage(" downloading "), 72 | "editText" : MessageLookupByLibrary.simpleMessage("Edit"), 73 | "enableHttps" : MessageLookupByLibrary.simpleMessage("Enable HTTPS"), 74 | "error" : MessageLookupByLibrary.simpleMessage("Error"), 75 | "file" : MessageLookupByLibrary.simpleMessage(" file"), 76 | "fileList" : MessageLookupByLibrary.simpleMessage("Files"), 77 | "host" : MessageLookupByLibrary.simpleMessage("HOST"), 78 | "hostValidatorText_1" : MessageLookupByLibrary.simpleMessage("Host can not be empty!"), 79 | "language" : MessageLookupByLibrary.simpleMessage("Language"), 80 | "linkInputDialogTitle" : MessageLookupByLibrary.simpleMessage("Please input link address"), 81 | "linkInputLabel" : MessageLookupByLibrary.simpleMessage("Link address"), 82 | "magnetText" : MessageLookupByLibrary.simpleMessage("Magnet"), 83 | "maxCurrentDownload4" : MessageLookupByLibrary.simpleMessage("Max current downloads"), 84 | "maxCurrentDownloadValidator_1" : MessageLookupByLibrary.simpleMessage("Please input the number!"), 85 | "maxCurrentDownloadValidator_2" : MessageLookupByLibrary.simpleMessage("Number cannot start with 0!"), 86 | "maxCurrentDownloadValidator_3" : MessageLookupByLibrary.simpleMessage("Please input legal number!"), 87 | "maxCurrentDownloadValidator_4" : MessageLookupByLibrary.simpleMessage("Max current download must >1!"), 88 | "maxDonwloadSpeedInputLabel" : MessageLookupByLibrary.simpleMessage("Max over all download speed limit"), 89 | "maxSpeedInputHelper" : MessageLookupByLibrary.simpleMessage("Set to 0 means no limit"), 90 | "maxUploadSpeedInputLabel" : MessageLookupByLibrary.simpleMessage("Max over all upload speed limit"), 91 | "more" : MessageLookupByLibrary.simpleMessage("More"), 92 | "noText" : MessageLookupByLibrary.simpleMessage("No"), 93 | "notConnectTip" : MessageLookupByLibrary.simpleMessage("Not connect to a Aria2 server now"), 94 | "optimizeConcurrentDownloads" : MessageLookupByLibrary.simpleMessage("Optimize current downloads"), 95 | "optionChangedTip" : MessageLookupByLibrary.simpleMessage("Option changed,please click to commit."), 96 | "optionText" : MessageLookupByLibrary.simpleMessage("Option"), 97 | "path" : MessageLookupByLibrary.simpleMessage("PATH"), 98 | "paused" : MessageLookupByLibrary.simpleMessage("Paused"), 99 | "pausing" : MessageLookupByLibrary.simpleMessage("Pausing"), 100 | "peers" : MessageLookupByLibrary.simpleMessage("Peers"), 101 | "port" : MessageLookupByLibrary.simpleMessage("PORT"), 102 | "portValidatorText_1" : MessageLookupByLibrary.simpleMessage("Port can not be empty!"), 103 | "progress" : MessageLookupByLibrary.simpleMessage("Progress: "), 104 | "protocol" : MessageLookupByLibrary.simpleMessage("Protocol"), 105 | "reChoose" : MessageLookupByLibrary.simpleMessage("Re-choose "), 106 | "reConnectBtnText" : MessageLookupByLibrary.simpleMessage("Re-connect"), 107 | "refreshDelay" : MessageLookupByLibrary.simpleMessage("Refresh"), 108 | "second" : MessageLookupByLibrary.simpleMessage("second"), 109 | "secret" : MessageLookupByLibrary.simpleMessage("SECRET"), 110 | "serverRefusedError" : MessageLookupByLibrary.simpleMessage("Connection refused by server! Please check the server config..."), 111 | "serverUnknownError" : MessageLookupByLibrary.simpleMessage("Some aria2 unknown error occured!"), 112 | "setSpeedLimitTitle" : MessageLookupByLibrary.simpleMessage("Set speed limit"), 113 | "setting" : MessageLookupByLibrary.simpleMessage("Setting"), 114 | "speedLimit" : MessageLookupByLibrary.simpleMessage("Speed limit"), 115 | "speedLimitInputValidatorText_1" : MessageLookupByLibrary.simpleMessage("Please input speed limit"), 116 | "speedLimitInputValidatorText_2" : MessageLookupByLibrary.simpleMessage("Speed limit cannot start with 0"), 117 | "speedLimitInputValidatorText_3" : MessageLookupByLibrary.simpleMessage("Speed limit must be a number"), 118 | "speedLimitSetSuccess" : MessageLookupByLibrary.simpleMessage("Success to set speed limit"), 119 | "status" : MessageLookupByLibrary.simpleMessage("Status: "), 120 | "submit" : MessageLookupByLibrary.simpleMessage("Submit"), 121 | "systemLanguage" : MessageLookupByLibrary.simpleMessage("Follow System"), 122 | "taskInfo" : MessageLookupByLibrary.simpleMessage("Info"), 123 | "taskName" : MessageLookupByLibrary.simpleMessage("Task Name: "), 124 | "taskStatusOfComplete" : MessageLookupByLibrary.simpleMessage("Complete"), 125 | "taskStatusOfDonloading" : MessageLookupByLibrary.simpleMessage("Downloading"), 126 | "taskStatusOfError" : MessageLookupByLibrary.simpleMessage("Error"), 127 | "taskStatusOfPaused" : MessageLookupByLibrary.simpleMessage("Paused"), 128 | "taskStatusOfWaitting" : MessageLookupByLibrary.simpleMessage("Waitting"), 129 | "taskText" : MessageLookupByLibrary.simpleMessage("task"), 130 | "taskTypeDescMagnet" : MessageLookupByLibrary.simpleMessage("Input magnet link to dwnload..."), 131 | "taskTypeDescMetalink" : MessageLookupByLibrary.simpleMessage("Read metalink file to download..."), 132 | "taskTypeDescTorrent" : MessageLookupByLibrary.simpleMessage("Read torrent file to download..."), 133 | "taskTypeDescUrl" : MessageLookupByLibrary.simpleMessage("Input http,ftp or some other protocol url to download..."), 134 | "taskTypeNameMagnet" : MessageLookupByLibrary.simpleMessage("Magnet"), 135 | "taskTypeNameMetalink" : MessageLookupByLibrary.simpleMessage("Metalink"), 136 | "taskTypeNameTorrent" : MessageLookupByLibrary.simpleMessage("Torrent"), 137 | "taskTypeNameUrl" : MessageLookupByLibrary.simpleMessage("Url-downlod"), 138 | "themeColor" : MessageLookupByLibrary.simpleMessage("Theme"), 139 | "timeOutError" : MessageLookupByLibrary.simpleMessage("Connection timeout!"), 140 | "tipOfMagnet" : MessageLookupByLibrary.simpleMessage("Support Metalink file format:\".metalink, .meta4\""), 141 | "tipOfMetalink" : MessageLookupByLibrary.simpleMessage("Magnet link start with \"magnet:?\""), 142 | "tipOfTorrent" : MessageLookupByLibrary.simpleMessage("Supprt torren file format: \".torrent\""), 143 | "tipOfUrl" : MessageLookupByLibrary.simpleMessage("Support protocol: HTTP/FTP/SFTP/BitTorrent"), 144 | "total" : MessageLookupByLibrary.simpleMessage("Total "), 145 | "unparsing" : MessageLookupByLibrary.simpleMessage("Unparsing"), 146 | "updateConfigSuccessTip" : MessageLookupByLibrary.simpleMessage("Success to update server config!"), 147 | "waiting" : MessageLookupByLibrary.simpleMessage("Waiting") 148 | }; 149 | } 150 | -------------------------------------------------------------------------------- /lib/views/task_detail.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:aria2/models/index.dart'; 4 | import 'package:aria_z/components/custom_snack_bar.dart'; 5 | import 'package:aria_z/components/speed_shower.dart'; 6 | import 'package:aria_z/l10n/localization_intl.dart'; 7 | import 'package:aria_z/states/app.dart'; 8 | import 'package:aria_z/states/aria2.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:provider/provider.dart'; 11 | import '../utils/tools.dart'; 12 | // import 'package:aria2/aria2.dart'; 13 | // import 'dart:async'; 14 | 15 | GlobalKey<_TabViewsBodyWidgtState> _tabViewBodyKey = GlobalKey(); 16 | 17 | late AriazLocalizations _l10n; 18 | 19 | // ignore: must_be_immutable 20 | class TaskDetail extends StatelessWidget { 21 | TaskDetail({Key? key}) : super(key: key); 22 | Aria2Task taskInfo = Aria2Task(); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | _l10n = AriazLocalizations.of(context); 27 | final List tabs = [ 28 | Tab(text: _l10n.taskInfo), 29 | Tab(text: _l10n.fileList), 30 | Tab(text: _l10n.peers), 31 | ]; 32 | List args = ModalRoute.of(context)?.settings.arguments as List; 33 | String _gid = args[0]; 34 | String _taskName = args[1]; 35 | TaskType _taskType = args[2]; 36 | return DefaultTabController( 37 | length: tabs.length, 38 | child: Builder(builder: (BuildContext context) { 39 | final TabController tabController = DefaultTabController.of(context)!; 40 | tabController.addListener(() { 41 | if (!tabController.indexIsChanging) { 42 | // Your code goes here. 43 | // To get index of current tab use tabController.index 44 | } 45 | }); 46 | return Scaffold( 47 | appBar: AppBar( 48 | title: Text(_taskName), 49 | bottom: TabBar(tabs: tabs), 50 | ), 51 | body: TabViewsBodyWidgt( 52 | gid: _gid, 53 | taskName: _taskName, 54 | taskType: _taskType, 55 | key: _tabViewBodyKey, 56 | )); 57 | }), 58 | ); 59 | } 60 | } 61 | 62 | class TabViewsBodyWidgt extends StatefulWidget { 63 | final String gid; 64 | 65 | final String taskName; 66 | 67 | final TaskType taskType; 68 | 69 | const TabViewsBodyWidgt( 70 | {Key? key, 71 | required this.gid, 72 | required this.taskName, 73 | required this.taskType}) 74 | : super(key: key); 75 | 76 | @override 77 | State createState() => _TabViewsBodyWidgtState(); 78 | } 79 | 80 | class _TabViewsBodyWidgtState extends State { 81 | String get taskName => widget.taskName; 82 | TaskType get taskType => widget.taskType; 83 | 84 | @override 85 | Widget build(BuildContext context) { 86 | Aria2States _at = Provider.of(context); 87 | Aria2Task taskInfo = [ 88 | ..._at.downloadingTasks, 89 | ..._at.waittingTasks, 90 | ..._at.completedTasks 91 | ].where((t) => t.gid == widget.gid).first; 92 | return TabBarView( 93 | children: [ 94 | TaskInfo(info: taskInfo), 95 | FileList(info: taskInfo), 96 | PeerListWidgt(gid: taskInfo.gid!), 97 | ], 98 | ); 99 | } 100 | } 101 | 102 | class TaskInfo extends StatefulWidget { 103 | final Aria2Task info; 104 | 105 | const TaskInfo({Key? key, required this.info}) : super(key: key); 106 | @override 107 | State createState() => _TaskInfoState(); 108 | } 109 | 110 | class _TaskInfoState extends State { 111 | Widget itemTitle(String title) { 112 | return Text( 113 | title, 114 | style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), 115 | ); 116 | } 117 | 118 | Widget itemText(String text) { 119 | return Expanded( 120 | child: Text( 121 | text, 122 | maxLines: 100, 123 | style: const TextStyle(fontSize: 15), 124 | ), 125 | ); 126 | } 127 | 128 | Widget _taskStatusWidgt(String stat) { 129 | String statLabel = stat; 130 | Color statColor = Theme.of(context).colorScheme.primary; 131 | switch (stat) { 132 | case 'active': 133 | statLabel = _l10n.taskStatusOfDonloading; 134 | break; 135 | case 'watting': 136 | statLabel = _l10n.taskStatusOfWaitting; 137 | statColor = Colors.grey; 138 | break; 139 | case 'complete': 140 | statLabel = _l10n.taskStatusOfComplete; 141 | statColor = Colors.green; 142 | break; 143 | case 'error': 144 | statLabel = _l10n.taskStatusOfError; 145 | statColor = Colors.red; 146 | break; 147 | case 'paused': 148 | statLabel = _l10n.taskStatusOfPaused; 149 | statColor = Colors.orange; 150 | break; 151 | case 'pausing': 152 | statLabel = _l10n.pausing; 153 | statColor = Colors.lightBlue; 154 | break; 155 | } 156 | return Container( 157 | padding: const EdgeInsets.fromLTRB(5, 2, 5, 2), 158 | decoration: BoxDecoration( 159 | borderRadius: BorderRadius.circular(5), 160 | color: statColor, 161 | ), 162 | child: Text( 163 | statLabel, 164 | style: TextStyle( 165 | fontSize: 12, color: Theme.of(context).colorScheme.onBackground), 166 | ), 167 | ); 168 | } 169 | 170 | List get _bitField { 171 | String bf = widget.info.bitfield ?? ''; 172 | int c = widget.info.numPieces ?? 0; 173 | String _bf2 = ''; 174 | for (var i = 0; i < bf.length; i++) { 175 | String _b2 = '0000' + int.parse(bf[i], radix: 16).toRadixString(2); 176 | _bf2 += _b2.substring(_b2.length - 4); 177 | } 178 | _bf2 = _bf2.substring(0, c); 179 | return _bf2.split('').map((e) => int.parse(e)).toList(); 180 | } 181 | 182 | Widget _bitFieldWidgt() { 183 | List blocks = _bitField 184 | .map((e) => Container( 185 | decoration: BoxDecoration( 186 | border: Border.all(color: Theme.of(context).backgroundColor), 187 | color: e == 1 188 | ? Theme.of(context).colorScheme.primary 189 | : Theme.of(context).cardColor, 190 | ), 191 | width: 12, 192 | height: 12, 193 | )) 194 | .toList(); 195 | 196 | return Wrap( 197 | direction: Axis.horizontal, 198 | children: blocks, 199 | spacing: 3, 200 | runSpacing: 3, 201 | ); 202 | } 203 | 204 | // const EdgeInsets.fromLTRB(10, 15, 10, 20), 205 | @override 206 | Widget build(BuildContext context) { 207 | return SingleChildScrollView( 208 | child: Padding( 209 | padding: const EdgeInsets.fromLTRB(10, 15, 10, 20), 210 | child: Column(children: [ 211 | Row( 212 | crossAxisAlignment: CrossAxisAlignment.start, 213 | children: [ 214 | itemTitle(_l10n.taskName), 215 | itemText(_tabViewBodyKey.currentState?.taskName ?? '') 216 | ], 217 | ), 218 | const SizedBox(height: 8), 219 | Row( 220 | crossAxisAlignment: CrossAxisAlignment.center, 221 | children: [ 222 | itemTitle(_l10n.progress), 223 | itemText( 224 | "${formatFileSize(widget.info.completedLength ?? 0)}/${formatFileSize(widget.info.totalLength ?? 0)}") 225 | ], 226 | ), 227 | const SizedBox(height: 8), 228 | Row( 229 | crossAxisAlignment: CrossAxisAlignment.start, 230 | children: [ 231 | itemTitle(_l10n.status), 232 | _taskStatusWidgt(widget.info.status ?? '') 233 | ], 234 | ), 235 | const SizedBox(height: 8), 236 | Row( 237 | crossAxisAlignment: CrossAxisAlignment.start, 238 | children: [ 239 | itemTitle("${_l10n.downloadPath}: "), 240 | itemText(widget.info.dir ?? '') 241 | ], 242 | ), 243 | const SizedBox(height: 8), 244 | Column(children: [ 245 | Row( 246 | crossAxisAlignment: CrossAxisAlignment.start, 247 | children: [ 248 | itemTitle(_l10n.bitFieldInfo), 249 | itemText( 250 | '${_l10n.total} ${widget.info.numPieces ?? 0} ${_l10n.bitField},${widget.info.pieceLength ?? 0} ${_l10n.bit}'), 251 | ], 252 | ), 253 | const SizedBox(height: 8), 254 | Container( 255 | child: _bitFieldWidgt(), 256 | ) 257 | ]) 258 | ]), 259 | )); 260 | } 261 | } 262 | 263 | class PeerListWidgt extends StatefulWidget { 264 | const PeerListWidgt({Key? key, required this.gid}) : super(key: key); 265 | final String gid; 266 | @override 267 | State createState() => _PeerListWidgtState(); 268 | } 269 | 270 | class _PeerListWidgtState extends State { 271 | List _peers = []; 272 | late Timer _periodicTimer; 273 | 274 | getPeers(AppState _app) async { 275 | handleAria2ApiResponse>( 276 | context, await _app.aria2!.getAria2Peers(widget.gid), (peers) { 277 | setState(() { 278 | _peers = peers; 279 | }); 280 | }); 281 | } 282 | 283 | Widget _peersWidget() { 284 | return ListView.builder( 285 | padding: const EdgeInsets.fromLTRB(10, 15, 10, 20), 286 | shrinkWrap: false, 287 | itemCount: _peers.length, 288 | itemBuilder: (BuildContext context, int index) { 289 | Aria2Peer _p = _peers[index]; 290 | return Padding( 291 | padding: const EdgeInsets.fromLTRB(0, 4, 0, 4), 292 | child: Container( 293 | color: Theme.of(context).cardColor, 294 | child: ListTile( 295 | title: Row( 296 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 297 | children: [ 298 | Text(_p.ip ?? ''), 299 | SpeedShower( 300 | downloadSpeed: _p.downloadSpeed, 301 | uploadSpeed: _p.uploadSpeed) 302 | ], 303 | ), 304 | ), 305 | ), 306 | ); 307 | }); 308 | } 309 | 310 | @override 311 | void initState() { 312 | super.initState(); 313 | AppState _app = Provider.of(context, listen: false); 314 | int _time = _app.intervalSecond; 315 | getPeers(_app); 316 | _periodicTimer = Timer.periodic(Duration(seconds: _time), (t) async { 317 | await getPeers(_app); 318 | }); 319 | } 320 | 321 | @override 322 | void dispose() { 323 | super.dispose(); 324 | _periodicTimer.cancel(); 325 | } 326 | 327 | @override 328 | Widget build(BuildContext context) { 329 | return SizedBox( 330 | height: MediaQuery.of(context).size.height * 0.5, 331 | child: _peers.isEmpty?Center(child: Text('${_l10n.noText} ${_l10n.peers}'),):_peersWidget()); 332 | } 333 | } 334 | 335 | class FileList extends StatefulWidget { 336 | const FileList({Key? key, required this.info}) : super(key: key); 337 | final Aria2Task info; 338 | 339 | @override 340 | State createState() => _FileListState(); 341 | } 342 | 343 | class _FileListState extends State { 344 | Widget _fileTreeView(List files) { 345 | return ListView.builder( 346 | padding: const EdgeInsets.fromLTRB(10, 15, 10, 20), 347 | shrinkWrap: false, 348 | itemCount: files.length, 349 | itemBuilder: (BuildContext context, int index) { 350 | Aria2File file = files[index]; 351 | String fileName = file.path!.split('/').last; 352 | return Padding( 353 | padding: const EdgeInsets.fromLTRB(0, 4, 0, 4), 354 | child: Container( 355 | color: Theme.of(context).cardColor, 356 | child: ListTile( 357 | title: Text(fileName, style: const TextStyle(fontSize: 15)), 358 | subtitle: Column( 359 | crossAxisAlignment: CrossAxisAlignment.start, 360 | children: [ 361 | Text( 362 | '${formatFileSize(file.completedLength)} / ${formatFileSize(file.length)}', 363 | style: const TextStyle(fontFamily: 'Coda'), 364 | ), 365 | const SizedBox(height: 8), 366 | LinearProgressIndicator( 367 | minHeight: 4, 368 | value: (file.completedLength ?? 0) / (file.length ?? 1), 369 | ) 370 | ]), 371 | trailing: Icon(Icons.check_circle_rounded, 372 | color: file.completedLength == file.length 373 | ? Theme.of(context).colorScheme.primary 374 | : Theme.of(context).secondaryHeaderColor), 375 | ), 376 | ), 377 | ); 378 | }); 379 | } 380 | 381 | @override 382 | Widget build(BuildContext context) { 383 | // String dPath = widget.info.dir ?? ""; 384 | List files = 385 | (widget.info.files ?? []).map((f) => Aria2File.fromJson(f)).toList(); 386 | 387 | return SizedBox( 388 | height: MediaQuery.of(context).size.height * 0.5, 389 | child: _fileTreeView(files), 390 | ); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "31.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.8.0" 18 | archive: 19 | dependency: transitive 20 | description: 21 | name: archive 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "3.1.6" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.3.0" 32 | aria2: 33 | dependency: "direct main" 34 | description: 35 | name: aria2 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.3" 39 | async: 40 | dependency: transitive 41 | description: 42 | name: async 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.8.1" 46 | boolean_selector: 47 | dependency: transitive 48 | description: 49 | name: boolean_selector 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.1.0" 53 | characters: 54 | dependency: transitive 55 | description: 56 | name: characters 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | charcode: 61 | dependency: transitive 62 | description: 63 | name: charcode 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.3.1" 67 | cli_util: 68 | dependency: transitive 69 | description: 70 | name: cli_util 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.3.5" 74 | clock: 75 | dependency: transitive 76 | description: 77 | name: clock 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.1.0" 81 | collection: 82 | dependency: transitive 83 | description: 84 | name: collection 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.15.0" 88 | convert: 89 | dependency: transitive 90 | description: 91 | name: convert 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "3.0.1" 95 | crypto: 96 | dependency: transitive 97 | description: 98 | name: crypto 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "3.0.1" 102 | cupertino_icons: 103 | dependency: "direct main" 104 | description: 105 | name: cupertino_icons 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.0.4" 109 | dart_style: 110 | dependency: transitive 111 | description: 112 | name: dart_style 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "2.2.0" 116 | dio: 117 | dependency: transitive 118 | description: 119 | name: dio 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "4.0.4" 123 | fake_async: 124 | dependency: transitive 125 | description: 126 | name: fake_async 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.2.0" 130 | ffi: 131 | dependency: transitive 132 | description: 133 | name: ffi 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "1.1.2" 137 | file: 138 | dependency: transitive 139 | description: 140 | name: file 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "6.1.2" 144 | file_picker: 145 | dependency: "direct main" 146 | description: 147 | name: file_picker 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "4.2.7" 151 | flutter: 152 | dependency: "direct main" 153 | description: flutter 154 | source: sdk 155 | version: "0.0.0" 156 | flutter_launcher_icons: 157 | dependency: "direct dev" 158 | description: 159 | name: flutter_launcher_icons 160 | url: "https://pub.dartlang.org" 161 | source: hosted 162 | version: "0.9.2" 163 | flutter_lints: 164 | dependency: "direct dev" 165 | description: 166 | name: flutter_lints 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "1.0.4" 170 | flutter_localizations: 171 | dependency: "direct main" 172 | description: flutter 173 | source: sdk 174 | version: "0.0.0" 175 | flutter_plugin_android_lifecycle: 176 | dependency: transitive 177 | description: 178 | name: flutter_plugin_android_lifecycle 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "2.0.5" 182 | flutter_slidable: 183 | dependency: "direct main" 184 | description: 185 | name: flutter_slidable 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "1.1.0" 189 | flutter_spinkit: 190 | dependency: "direct main" 191 | description: 192 | name: flutter_spinkit 193 | url: "https://pub.dartlang.org" 194 | source: hosted 195 | version: "5.1.0" 196 | flutter_test: 197 | dependency: "direct dev" 198 | description: flutter 199 | source: sdk 200 | version: "0.0.0" 201 | flutter_web_plugins: 202 | dependency: transitive 203 | description: flutter 204 | source: sdk 205 | version: "0.0.0" 206 | glob: 207 | dependency: transitive 208 | description: 209 | name: glob 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "2.0.2" 213 | hive: 214 | dependency: "direct main" 215 | description: 216 | name: hive 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "2.0.4" 220 | hive_flutter: 221 | dependency: "direct main" 222 | description: 223 | name: hive_flutter 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.1.0" 227 | http_parser: 228 | dependency: transitive 229 | description: 230 | name: http_parser 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "4.0.0" 234 | image: 235 | dependency: transitive 236 | description: 237 | name: image 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "3.1.0" 241 | intl: 242 | dependency: "direct main" 243 | description: 244 | name: intl 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "0.17.0" 248 | intl_generator: 249 | dependency: "direct dev" 250 | description: 251 | name: intl_generator 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "0.2.1" 255 | js: 256 | dependency: transitive 257 | description: 258 | name: js 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "0.6.3" 262 | json_annotation: 263 | dependency: transitive 264 | description: 265 | name: json_annotation 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "4.4.0" 269 | json_rpc_2: 270 | dependency: transitive 271 | description: 272 | name: json_rpc_2 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "3.0.1" 276 | lints: 277 | dependency: transitive 278 | description: 279 | name: lints 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "1.0.1" 283 | matcher: 284 | dependency: transitive 285 | description: 286 | name: matcher 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "0.12.10" 290 | meta: 291 | dependency: transitive 292 | description: 293 | name: meta 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "1.7.0" 297 | nested: 298 | dependency: transitive 299 | description: 300 | name: nested 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "1.0.0" 304 | package_config: 305 | dependency: transitive 306 | description: 307 | name: package_config 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "2.0.2" 311 | path: 312 | dependency: transitive 313 | description: 314 | name: path 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "1.8.0" 318 | path_provider: 319 | dependency: transitive 320 | description: 321 | name: path_provider 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "2.0.7" 325 | path_provider_android: 326 | dependency: transitive 327 | description: 328 | name: path_provider_android 329 | url: "https://pub.dartlang.org" 330 | source: hosted 331 | version: "2.0.9" 332 | path_provider_ios: 333 | dependency: transitive 334 | description: 335 | name: path_provider_ios 336 | url: "https://pub.dartlang.org" 337 | source: hosted 338 | version: "2.0.7" 339 | path_provider_linux: 340 | dependency: transitive 341 | description: 342 | name: path_provider_linux 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "2.1.2" 346 | path_provider_macos: 347 | dependency: transitive 348 | description: 349 | name: path_provider_macos 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "2.0.4" 353 | path_provider_platform_interface: 354 | dependency: transitive 355 | description: 356 | name: path_provider_platform_interface 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "2.0.1" 360 | path_provider_windows: 361 | dependency: transitive 362 | description: 363 | name: path_provider_windows 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "2.0.4" 367 | petitparser: 368 | dependency: transitive 369 | description: 370 | name: petitparser 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "4.4.0" 374 | platform: 375 | dependency: transitive 376 | description: 377 | name: platform 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "3.1.0" 381 | plugin_platform_interface: 382 | dependency: transitive 383 | description: 384 | name: plugin_platform_interface 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "2.0.2" 388 | process: 389 | dependency: transitive 390 | description: 391 | name: process 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "4.2.4" 395 | provider: 396 | dependency: "direct main" 397 | description: 398 | name: provider 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "6.0.1" 402 | pub_semver: 403 | dependency: transitive 404 | description: 405 | name: pub_semver 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "2.1.0" 409 | quiver: 410 | dependency: transitive 411 | description: 412 | name: quiver 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "3.0.1+1" 416 | shared_preferences: 417 | dependency: "direct main" 418 | description: 419 | name: shared_preferences 420 | url: "https://pub.dartlang.org" 421 | source: hosted 422 | version: "2.0.9" 423 | shared_preferences_android: 424 | dependency: transitive 425 | description: 426 | name: shared_preferences_android 427 | url: "https://pub.dartlang.org" 428 | source: hosted 429 | version: "2.0.9" 430 | shared_preferences_ios: 431 | dependency: transitive 432 | description: 433 | name: shared_preferences_ios 434 | url: "https://pub.dartlang.org" 435 | source: hosted 436 | version: "2.0.8" 437 | shared_preferences_linux: 438 | dependency: transitive 439 | description: 440 | name: shared_preferences_linux 441 | url: "https://pub.dartlang.org" 442 | source: hosted 443 | version: "2.0.3" 444 | shared_preferences_macos: 445 | dependency: transitive 446 | description: 447 | name: shared_preferences_macos 448 | url: "https://pub.dartlang.org" 449 | source: hosted 450 | version: "2.0.2" 451 | shared_preferences_platform_interface: 452 | dependency: transitive 453 | description: 454 | name: shared_preferences_platform_interface 455 | url: "https://pub.dartlang.org" 456 | source: hosted 457 | version: "2.0.0" 458 | shared_preferences_web: 459 | dependency: transitive 460 | description: 461 | name: shared_preferences_web 462 | url: "https://pub.dartlang.org" 463 | source: hosted 464 | version: "2.0.2" 465 | shared_preferences_windows: 466 | dependency: transitive 467 | description: 468 | name: shared_preferences_windows 469 | url: "https://pub.dartlang.org" 470 | source: hosted 471 | version: "2.0.3" 472 | sky_engine: 473 | dependency: transitive 474 | description: flutter 475 | source: sdk 476 | version: "0.0.99" 477 | source_span: 478 | dependency: transitive 479 | description: 480 | name: source_span 481 | url: "https://pub.dartlang.org" 482 | source: hosted 483 | version: "1.8.1" 484 | stack_trace: 485 | dependency: transitive 486 | description: 487 | name: stack_trace 488 | url: "https://pub.dartlang.org" 489 | source: hosted 490 | version: "1.10.0" 491 | stream_channel: 492 | dependency: transitive 493 | description: 494 | name: stream_channel 495 | url: "https://pub.dartlang.org" 496 | source: hosted 497 | version: "2.1.0" 498 | string_scanner: 499 | dependency: transitive 500 | description: 501 | name: string_scanner 502 | url: "https://pub.dartlang.org" 503 | source: hosted 504 | version: "1.1.0" 505 | term_glyph: 506 | dependency: transitive 507 | description: 508 | name: term_glyph 509 | url: "https://pub.dartlang.org" 510 | source: hosted 511 | version: "1.2.0" 512 | test_api: 513 | dependency: transitive 514 | description: 515 | name: test_api 516 | url: "https://pub.dartlang.org" 517 | source: hosted 518 | version: "0.4.2" 519 | toggle_switch: 520 | dependency: "direct main" 521 | description: 522 | name: toggle_switch 523 | url: "https://pub.dartlang.org" 524 | source: hosted 525 | version: "1.3.0" 526 | tuple: 527 | dependency: "direct main" 528 | description: 529 | name: tuple 530 | url: "https://pub.dartlang.org" 531 | source: hosted 532 | version: "2.0.0" 533 | typed_data: 534 | dependency: transitive 535 | description: 536 | name: typed_data 537 | url: "https://pub.dartlang.org" 538 | source: hosted 539 | version: "1.3.0" 540 | vector_math: 541 | dependency: transitive 542 | description: 543 | name: vector_math 544 | url: "https://pub.dartlang.org" 545 | source: hosted 546 | version: "2.1.0" 547 | watcher: 548 | dependency: transitive 549 | description: 550 | name: watcher 551 | url: "https://pub.dartlang.org" 552 | source: hosted 553 | version: "1.0.1" 554 | web_socket_channel: 555 | dependency: transitive 556 | description: 557 | name: web_socket_channel 558 | url: "https://pub.dartlang.org" 559 | source: hosted 560 | version: "2.1.0" 561 | win32: 562 | dependency: transitive 563 | description: 564 | name: win32 565 | url: "https://pub.dartlang.org" 566 | source: hosted 567 | version: "2.3.1" 568 | xdg_directories: 569 | dependency: transitive 570 | description: 571 | name: xdg_directories 572 | url: "https://pub.dartlang.org" 573 | source: hosted 574 | version: "0.2.0" 575 | xml: 576 | dependency: transitive 577 | description: 578 | name: xml 579 | url: "https://pub.dartlang.org" 580 | source: hosted 581 | version: "5.3.1" 582 | yaml: 583 | dependency: transitive 584 | description: 585 | name: yaml 586 | url: "https://pub.dartlang.org" 587 | source: hosted 588 | version: "3.1.0" 589 | sdks: 590 | dart: ">=2.14.0 <3.0.0" 591 | flutter: ">=2.5.0" 592 | -------------------------------------------------------------------------------- /lib/l10n/localization_intl.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'messages_all.dart'; //1 4 | 5 | class AriazLocalizations { 6 | static Future load(Locale locale) { 7 | final String name = 8 | locale.countryCode!.isEmpty ? locale.languageCode : locale.toString(); 9 | final String localeName = Intl.canonicalizedLocale(name); 10 | //2 11 | return initializeMessages(localeName).then((b) { 12 | Intl.defaultLocale = localeName; 13 | return AriazLocalizations(); 14 | }); 15 | } 16 | 17 | static AriazLocalizations of(BuildContext context) { 18 | return Localizations.of(context, AriazLocalizations) ?? 19 | AriazLocalizations(); 20 | } 21 | 22 | String get downloadingBtnText => Intl.message('Download', 23 | name: 'downloadingBtnText', desc: 'Downloading button text'); 24 | 25 | String get completedBtnText => Intl.message('Complete', 26 | name: 'completedBtnText', desc: 'Completed button text'); 27 | 28 | String get setSpeedLimitTitle => Intl.message('Set speed limit', 29 | name: 'setSpeedLimitTitle', desc: 'Set Speed Limit dialog Title'); 30 | 31 | String get speedLimitSetSuccess => Intl.message('Success to set speed limit', 32 | name: 'speedLimitSetSuccess', desc: 'Speed limit set success tip'); 33 | 34 | String get addNewTaskToolTip => Intl.message('Add new task', 35 | name: 'addNewTaskToolTip', desc: 'Add new task icon button toolTip'); 36 | 37 | String get speedLimitInputValidatorText_1 => 38 | Intl.message('Please input speed limit', 39 | name: 'speedLimitInputValidatorText_1', 40 | desc: 'Speed limit input validatator text'); 41 | 42 | String get speedLimitInputValidatorText_2 => 43 | Intl.message('Speed limit cannot start with 0', 44 | name: 'speedLimitInputValidatorText_2', 45 | desc: 'Speed limit input validatator text'); 46 | 47 | String get speedLimitInputValidatorText_3 => 48 | Intl.message('Speed limit must be a number', 49 | name: 'speedLimitInputValidatorText_3', 50 | desc: 'Speed limit input validatator text'); 51 | 52 | String get maxDonwloadSpeedInputLabel => 53 | Intl.message('Max over all download speed limit', 54 | name: 'maxDonwloadSpeedInputLabel', 55 | desc: 'Max donwload speed input label text'); 56 | 57 | String get maxUploadSpeedInputLabel => Intl.message('Max over all upload speed limit', 58 | name: 'maxUploadSpeedInputLabel', 59 | desc: 'Max upload speed input label text'); 60 | 61 | String get maxSpeedInputHelper => Intl.message('Set to 0 means no limit', 62 | name: 'maxSpeedInputHelper', desc: 'Max speed input helper'); 63 | 64 | String get connecttingTip => Intl.message('Trying to connect Aria2 server', 65 | name: 'connecttingTip', desc: 'Connectting aria2 server Tip'); 66 | 67 | String get connectFailedTip => 68 | Intl.message('Failed to connect to Aria2 server...', 69 | name: 'connectFailedTip', desc: 'Connect Failed Tip'); 70 | 71 | String get notConnectTip => Intl.message('Not connect to a Aria2 server now', 72 | name: 'notConnectTip', desc: 'Not connect tip'); 73 | 74 | String get reConnectBtnText => Intl.message('Re-connect', 75 | name: 'reConnectBtnText', desc: 're-connect button text'); 76 | 77 | String get noText => Intl.message('No', name: 'noText', desc: '"No" text'); 78 | 79 | String get downloadingTipText => Intl.message(' downloading ', 80 | name: 'downloadingTipText', desc: '"downloading" tip text'); 81 | 82 | String get completeTipText => Intl.message(' completed ', 83 | name: 'completeTipText', desc: '"complete" text'); 84 | 85 | String get taskText => 86 | Intl.message('task', name: 'taskText', desc: '"task" Text'); 87 | 88 | String get deleteDialogTitle => Intl.message('Delete server config', 89 | name: 'deleteDialogTitle', desc: 'delete dialog title'); 90 | 91 | String get deleteDialogContent => 92 | Intl.message('Sure to delete current server config ?', 93 | name: 'deleteDialogContent', desc: 'delete dialog content'); 94 | 95 | String get applyBtnText => 96 | Intl.message('Apply', name: 'applyBtnText', desc: 'Apply button text'); 97 | 98 | String get confirmBtnText => Intl.message('Confirm', 99 | name: 'confirmBtnText', desc: 'Confirm button text'); 100 | 101 | String get cancelBtnText => 102 | Intl.message('Cancel', name: 'cancelBtnText', desc: 'Cancel button text'); 103 | 104 | String get connectedText => Intl.message('Connected to: ', 105 | name: 'connectedText', desc: 'connected text'); 106 | String get aria2VersionLabel => Intl.message('Aria2 version: ', 107 | name: 'aria2VersionLabel', desc: 'Aria2 version label text'); 108 | String get editText => 109 | Intl.message('Edit', name: 'editText', desc: '"edit" Text'); 110 | String get deleteText => 111 | Intl.message('Delete', name: 'deleteText', desc: '"delete" Text'); 112 | 113 | String get addNewServerConfig => Intl.message('Add new server config', 114 | name: 'addNewServerConfig', desc: 'Add new server config text'); 115 | String get setting => 116 | Intl.message('Setting', name: 'setting', desc: '"setting" Text'); 117 | String get waiting => 118 | Intl.message('Waiting', name: 'waiting', desc: '"waiting" Text'); 119 | String get pausing => 120 | Intl.message('Pausing', name: 'pausing', desc: '"pausing" Text'); 121 | String get unparsing => 122 | Intl.message('Unparsing', name: 'unparsing', desc: '"unparsing" Text'); 123 | String get complete => 124 | Intl.message('Complete', name: 'complete', desc: '"complete" Text'); 125 | String get error => 126 | Intl.message('Error', name: 'error', desc: '"error" Text'); 127 | String get paused => 128 | Intl.message('Paused', name: 'paused', desc: '"paused" Text'); 129 | 130 | String get submit => 131 | Intl.message('Submit', name: 'submit', desc: '"submit" Text'); 132 | 133 | String get choose => 134 | Intl.message('Choose ', name: 'choose', desc: '"choose" Text'); 135 | 136 | String get reChoose => 137 | Intl.message('Re-choose ', name: 'reChoose', desc: '"reChoose" Text'); 138 | 139 | String get file => Intl.message(' file', name: 'file', desc: '"file" Text'); 140 | 141 | String get more => Intl.message('More', name: 'more', desc: '"more" Text'); 142 | 143 | String get address => 144 | Intl.message(' address', name: 'address', desc: '"address" Text'); 145 | 146 | String get taskTypeNameTorrent => Intl.message('Torrent', 147 | name: 'taskTypeNameTorrent', desc: 'task type name of torrent'); 148 | String get taskTypeNameMagnet => Intl.message('Magnet', 149 | name: 'taskTypeNameMagnet', desc: 'task type name of magnet'); 150 | String get taskTypeNameUrl => Intl.message('Url-downlod', 151 | name: 'taskTypeNameUrl', desc: 'task type name of url download'); 152 | String get taskTypeNameMetalink => Intl.message('Metalink', 153 | name: 'taskTypeNameMetalink', desc: 'task type name of metalink'); 154 | 155 | String get taskTypeDescTorrent => 156 | Intl.message('Read torrent file to download...', 157 | name: 'taskTypeDescTorrent', desc: 'task type decription of torrent'); 158 | String get taskTypeDescMagnet => 159 | Intl.message('Input magnet link to dwnload...', 160 | name: 'taskTypeDescMagnet', desc: 'task type decription of magnet'); 161 | String get taskTypeDescUrl => 162 | Intl.message('Input http,ftp or some other protocol url to download...', 163 | name: 'taskTypeDescUrl', 164 | desc: 'task type decription of url download'); 165 | String get taskTypeDescMetalink => 166 | Intl.message('Read metalink file to download...', 167 | name: 'taskTypeDescMetalink', 168 | desc: 'task type decription of metalink'); 169 | 170 | String get tipOfTorrent => 171 | Intl.message('Supprt torren file format: ".torrent"', 172 | name: 'tipOfTorrent', desc: 'tip of torrent'); 173 | String get tipOfMagnet => 174 | Intl.message('Support Metalink file format:".metalink, .meta4"', 175 | name: 'tipOfMagnet', desc: 'tip of magnet'); 176 | String get tipOfUrl => 177 | Intl.message('Support protocol: HTTP/FTP/SFTP/BitTorrent', 178 | name: 'tipOfUrl', desc: 'tip of url download'); 179 | String get tipOfMetalink => Intl.message('Magnet link start with "magnet:?"', 180 | name: 'tipOfMetalink', desc: 'tip of metalink'); 181 | 182 | String get magnetText => 183 | Intl.message('Magnet', name: 'magnetText', desc: 'magnet text'); 184 | 185 | String get colorBlue => 186 | Intl.message('Blue', name: 'colorBlue', desc: 'color blue'); 187 | String get colorRed => 188 | Intl.message('Red', name: 'colorRed', desc: 'color red'); 189 | String get colorGreen => 190 | Intl.message('Green', name: 'colorGreen', desc: 'color green'); 191 | String get colorPurple => 192 | Intl.message('Purple', name: 'colorPurple', desc: 'color purple'); 193 | 194 | String get systemLanguage => Intl.message('Follow System', 195 | name: 'systemLanguage', desc: 'system language'); 196 | 197 | String get authFailed => Intl.message('Aria2 authentication failed!', 198 | name: 'authFailed', desc: 'authentication failed tip'); 199 | 200 | String get serverUnknownError => 201 | Intl.message('Some aria2 unknown error occured!', 202 | name: 'serverUnknownError', desc: 'server unknown error tip'); 203 | 204 | String get timeOutError => Intl.message('Connection timeout!', 205 | name: 'timeOutError', desc: 'timeOut error tip'); 206 | 207 | String get serverRefusedError => Intl.message( 208 | 'Connection refused by server! Please check the server config...', 209 | name: 'serverRefusedError', 210 | desc: 'server refused error tip'); 211 | String get addText => 212 | Intl.message('Add', name: 'addText', desc: '"Add" text'); 213 | String get optionText => 214 | Intl.message('Option', name: 'optionText', desc: '"Option" text'); 215 | String get addSuccessTip => Intl.message('Success to add new Task!', 216 | name: 'addSuccessTip', desc: 'Add task success tip'); 217 | 218 | String get checkOptionWarningTip => 219 | Intl.message('Please check task option input!', 220 | name: 'checkOptionWarningTip', desc: 'Check option warning tip'); 221 | String get checkSourceTip => Intl.message('Please check task source!', 222 | name: 'checkSourceTip', desc: 'check task source tip'); 223 | String get linkInputDialogTitle => Intl.message('Please input link address', 224 | name: 'linkInputDialogTitle', desc: 'link input dialog title'); 225 | String get linkInputLabel => Intl.message('Link address', 226 | name: 'linkInputLabel', desc: 'link input label'); 227 | 228 | String get addAndDownload => Intl.message('Add & Download', 229 | name: 'addAndDownload', desc: 'add and download text'); 230 | 231 | String get dirValidatorText_1 => Intl.message('Please input download path!', 232 | name: 'dirValidatorText_1', desc: 'dirValidatorText_1'); 233 | 234 | String get dirValidatorText_2 => 235 | Intl.message('Download result must under global path ', 236 | name: 'dirValidatorText_2', desc: 'dirValidatorText_2'); 237 | 238 | String get downloadPath => Intl.message('Download path', 239 | name: 'downloadPath', desc: 'download path'); 240 | 241 | String get speedLimit => 242 | Intl.message('Speed limit', name: 'speedLimit', desc: 'speed limit'); 243 | 244 | String get allowOverwrite => Intl.message('Allow overwrite download result', 245 | name: 'allowOverwrite', desc: 'allowOverwrite'); 246 | 247 | String get language => 248 | Intl.message('Language', name: 'language', desc: 'language'); 249 | 250 | String get themeColor => 251 | Intl.message('Theme', name: 'themeColor', desc: 'theme color'); 252 | 253 | String get refreshDelay => Intl.message('Refresh', 254 | name: 'refreshDelay', desc: 'refresh delay'); 255 | 256 | String get second => Intl.message('second', name: 'second', desc: 'second'); 257 | 258 | String get changeGlobalOptionSuccessTip => 259 | Intl.message('Success to change global option', 260 | name: 'changeGlobalOptionSuccessTip', 261 | desc: 'changeGlobalOptionSuccessTip'); 262 | 263 | String get optionChangedTip => 264 | Intl.message('Option changed,please click to commit.', 265 | name: 'optionChangedTip', desc: 'optionChangedTip'); 266 | 267 | String get dirInputValidatorText => 268 | Intl.message('Please input the download path!', 269 | name: 'dirInputValidatorText', desc: 'dirInputValidatorText'); 270 | 271 | String get dirInputLabel => Intl.message('Default download path', 272 | name: 'dirInputLabel', desc: 'dirInputLabel'); 273 | 274 | String get maxCurrentDownloadValidator_1 => 275 | Intl.message('Please input the number!', 276 | name: 'maxCurrentDownloadValidator_1', 277 | desc: 'maxCurrentDownloadValidator_1'); 278 | 279 | String get maxCurrentDownloadValidator_2 => 280 | Intl.message('Number cannot start with 0!', 281 | name: 'maxCurrentDownloadValidator_2', 282 | desc: 'maxCurrentDownloadValidator_2'); 283 | 284 | String get maxCurrentDownloadValidator_3 => 285 | Intl.message('Please input legal number!', 286 | name: 'maxCurrentDownloadValidator_3', 287 | desc: 'maxCurrentDownloadValidator_3'); 288 | 289 | String get maxCurrentDownloadValidator_4 => 290 | Intl.message('Max current download must >1!', 291 | name: 'maxCurrentDownloadValidator_4', 292 | desc: 'maxCurrentDownloadValidator_4'); 293 | 294 | String get maxCurrentDownload4 => Intl.message('Max current downloads', 295 | name: 'maxCurrentDownload4', desc: 'maxCurrentDownload4'); 296 | 297 | String get ariaContinue => Intl.message('Continue the break', 298 | name: 'ariaContinue', desc: 'ariaContinue'); 299 | 300 | String get checkIntegrity => Intl.message('Check integrity', 301 | name: 'checkIntegrity', desc: 'checkIntegrity'); 302 | 303 | String get optimizeConcurrentDownloads => 304 | Intl.message('Optimize current downloads', 305 | name: 'optimizeConcurrentDownloads', 306 | desc: 'optimizeConcurrentDownloads'); 307 | 308 | String get confitExists => Intl.message('Config name exsits!', 309 | name: 'confitExists', desc: 'confitExists'); 310 | 311 | String get addConfigSuccessTip => 312 | Intl.message('Success to add server config!', 313 | name: 'addConfigSuccessTip', desc: 'addConfigSuccessTip'); 314 | 315 | String get updateConfigSuccessTip => 316 | Intl.message('Success to update server config!', 317 | name: 'updateConfigSuccessTip', desc: 'updateConfigSuccessTip'); 318 | 319 | String get configName => 320 | Intl.message('Name', name: 'configName', desc: 'configName'); 321 | 322 | String get configNameValidatorText_1 => 323 | Intl.message('Config name can not be empty!', 324 | name: 'configNameValidatorText_1', desc: 'configNameValidatorText_1'); 325 | 326 | String get protocol => 327 | Intl.message('Protocol', name: 'protocol', desc: 'protocol'); 328 | 329 | String get host => Intl.message('HOST', name: 'host', desc: 'host'); 330 | 331 | String get hostValidatorText_1 => Intl.message('Host can not be empty!', 332 | name: 'hostValidatorText_1', desc: 'hostValidatorText_1'); 333 | 334 | String get port => Intl.message('PORT', name: 'port', desc: 'port'); 335 | 336 | String get portValidatorText_1 => Intl.message('Port can not be empty!', 337 | name: 'portValidatorText_1', desc: 'portValidatorText_1'); 338 | 339 | String get path => Intl.message('PATH', name: 'path', desc: 'path'); 340 | 341 | String get secret => Intl.message('SECRET', name: 'secret', desc: 'secret'); 342 | 343 | String get enableHttps => 344 | Intl.message('Enable HTTPS', name: 'enableHttps', desc: 'enableHttps'); 345 | 346 | String get appSetting => 347 | Intl.message('App setting', name: 'appSetting', desc: 'appSetting'); 348 | 349 | String get aria2Setting => 350 | Intl.message('Aria2 setting', name: 'aria2Setting', desc: 'aria2Setting'); 351 | 352 | String get aria2SettingWarning => 353 | Intl.message('Please connect to Aria2 server before setting', 354 | name: 'aria2SettingWarning', desc: 'aria2SettingWarning'); 355 | 356 | String get taskInfo => 357 | Intl.message('Info', name: 'taskInfo', desc: 'taskInfo'); 358 | 359 | String get fileList => 360 | Intl.message('Files', name: 'fileList', desc: 'fileList'); 361 | 362 | String get peers => Intl.message('Peers', name: 'peers', desc: 'peers'); 363 | 364 | String get taskStatusOfDonloading => Intl.message('Downloading', 365 | name: 'taskStatusOfDonloading', desc: 'taskStatusOfDonloading'); 366 | 367 | String get taskStatusOfComplete => Intl.message('Complete', 368 | name: 'taskStatusOfComplete', desc: 'taskStatusOfComplete'); 369 | 370 | String get taskStatusOfError => Intl.message('Error', 371 | name: 'taskStatusOfError', desc: 'taskStatusOfError'); 372 | 373 | String get taskStatusOfPaused => Intl.message('Paused', 374 | name: 'taskStatusOfPaused', desc: 'taskStatusOfPaused'); 375 | 376 | String get taskStatusOfWaitting => Intl.message('Waitting', 377 | name: 'taskStatusOfWaitting', desc: 'taskStatusOfWaitting'); 378 | 379 | String get taskName => 380 | Intl.message('Task Name: ', name: 'taskName', desc: 'taskName'); 381 | 382 | String get progress => 383 | Intl.message('Progress: ', name: 'progress', desc: 'progress'); 384 | 385 | String get status => Intl.message('Status: ', name: 'status', desc: 'status'); 386 | 387 | String get bitFieldInfo => 388 | Intl.message('BitField: ', name: 'bitFieldInfo', desc: 'bitFieldInfo'); 389 | 390 | String get bitField => 391 | Intl.message('bit fields ', name: 'bitField', desc: 'bitField'); 392 | 393 | String get total => Intl.message('Total ', name: 'total', desc: 'total'); 394 | 395 | String get bit => Intl.message('bit ', name: 'bit', desc: 'bit'); 396 | } 397 | 398 | //Locale代理类 399 | class AriazLocalizationsDelegate 400 | extends LocalizationsDelegate { 401 | const AriazLocalizationsDelegate(); 402 | 403 | // 因为将app locale设置提前到material app中,所以这里可以直接置为true 404 | @override 405 | bool isSupported(Locale locale) => true; 406 | 407 | // Flutter会调用此类加载相应的Locale资源类 408 | @override 409 | Future load(Locale locale) { 410 | //3 411 | return AriazLocalizations.load(locale); 412 | } 413 | 414 | // 当Localizations Widget重新build时,是否调用load重新加载Locale资源. 415 | @override 416 | bool shouldReload(AriazLocalizationsDelegate old) => false; 417 | } 418 | --------------------------------------------------------------------------------