├── .gitignore ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .classpath │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── deluge_client │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ ├── ic_launcher.png │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ ├── ic_launcher.png │ │ │ └── launch_background.xml │ │ │ ├── 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 │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── animation.gif ├── banner.png ├── fonts │ └── SFUIDisplay │ │ ├── sf-ui-display-heavy.otf │ │ ├── sf-ui-display-light.otf │ │ └── sf-ui-display-medium.otf ├── github.png ├── loader.gif ├── logo.png ├── no_data.png ├── poster.png ├── server_end_error.png └── slack.png ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── 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-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── api │ ├── apis.dart │ └── models │ │ ├── settings.dart │ │ └── torrent_prop.dart ├── components │ ├── accounts.dart │ ├── all_info.dart │ ├── bottom_sheet │ │ ├── add_magnet_uri.dart │ │ ├── add_torrent.dart │ │ ├── choose_account.dart │ │ ├── qr_magnet_reader.dart │ │ ├── sorter.dart │ │ └── ssh_config.dart │ ├── download_upload_pane.dart │ ├── error_on_dash.dart │ ├── licence.dart │ ├── loader.dart │ ├── no_data.dart │ ├── progress_bar.dart │ ├── sidebar.dart │ ├── storage_indicator.dart │ └── tile.dart ├── control_center │ ├── theme.dart │ ├── theme_changer.dart │ └── theme_controller.dart ├── core │ ├── all_acc.dart │ └── auth_valid.dart ├── database │ └── dbmanager.dart ├── main.dart ├── notification │ └── notification_controller.dart ├── screens │ ├── about.dart │ ├── auth.dart │ ├── auth_qr.dart │ ├── dashboard.dart │ ├── multi_dash.dart │ └── splash.dart ├── settings │ ├── client │ │ └── client_setting.dart │ └── deluge │ │ ├── core_settings.dart │ │ ├── deluge_setting.dart │ │ └── type │ │ ├── advance.dart │ │ ├── basic.dart │ │ ├── general.dart │ │ ├── sftp_setting_field.dart │ │ └── sftp_streaming_settings.dart ├── sftp_streaming │ ├── image_streamer.dart │ ├── sftp_explorer.dart │ ├── video_dir.dart │ └── web_spacer.dart ├── state_ware_house │ └── state_ware_house.dart └── string │ ├── controller.dart │ ├── magnet_detect.dart │ └── sorter.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart ├── user_guide.md └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.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 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Directory created by dartdoc 37 | # If you don't generate documentation locally you can remove this line. 38 | doc/api/ 39 | 40 | # Avoid committing generated Javascript files: 41 | *.dart.js 42 | *.info.json # Produced by the --dump-info flag. 43 | *.js # When generated by dart2js. Don't specify *.js if your 44 | # project includes source files written in JavaScript. 45 | *.js_ 46 | *.js.deps 47 | *.js.map 48 | 49 | # test coverage 50 | /coverage 51 | 52 | # Android related 53 | **/android/**/gradle-wrapper.jar 54 | **/android/.gradle 55 | **/android/captures/ 56 | **/android/gradlew 57 | **/android/gradlew.bat 58 | **/android/key.properties 59 | **/android/local.properties 60 | **/android/**/GeneratedPluginRegistrant.java 61 | *.jks 62 | key*.properties 63 | 64 | # iOS/XCode related 65 | **/ios/**/*.mode1v3 66 | **/ios/**/*.mode2v3 67 | **/ios/**/*.moved-aside 68 | **/ios/**/*.pbxuser 69 | **/ios/**/*.perspectivev3 70 | **/ios/**/*sync/ 71 | **/ios/**/.sconsign.dblite 72 | **/ios/**/.tags* 73 | **/ios/**/.vagrant/ 74 | **/ios/**/DerivedData/ 75 | **/ios/**/Icon? 76 | **/ios/**/Pods/ 77 | **/ios/**/.symlinks/ 78 | **/ios/**/profile 79 | **/ios/**/xcuserdata 80 | **/ios/.generated/ 81 | **/ios/Flutter/.last_build_id 82 | **/ios/Flutter/App.framework 83 | **/ios/Flutter/Flutter.framework 84 | **/ios/Flutter/Flutter.podspec 85 | **/ios/Flutter/Generated.xcconfig 86 | **/ios/Flutter/app.flx 87 | **/ios/Flutter/app.zip 88 | **/ios/Flutter/flutter_assets/ 89 | **/ios/Flutter/flutter_export_environment.sh 90 | **/ios/ServiceDefinitions.json 91 | **/ios/Runner/GeneratedPluginRegistrant.* 92 | 93 | # Exceptions to above rules. 94 | !**/ios/**/default.mode1v3 95 | !**/ios/**/default.mode2v3 96 | !**/ios/**/default.pbxuser 97 | !**/ios/**/default.perspectivev3 98 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CCExtractor Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://i.imgur.com/emdyzUf.png) 2 | 3 |

4 | 5 |
6 | 7 |
8 | 9 |
10 | 11 |

12 | 13 | 14 | 15 |

16 | Deluge Google Play Store Deluge Download Link 17 |

18 |
19 | 20 | Deluge mobile client - Manage deluge with ease | Product Hunt 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ## Demo Run 30 | 31 | https://user-images.githubusercontent.com/57806993/128637426-71d45add-9458-4175-a20f-89885f2cf1fd.mp4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ## About The Project 43 | Deluge is a lightweight, free, cross-platform BitTorrent Monitoring service. 44 | Deluge mobile client app interacts with deluge via WEB-JSONRPC and gives all the administrative abilites. The Web app, Desktop app and all other relevant documentation can be found at 45 | Deluge, and its repository can be found here. 46 | 47 | ![issues](https://img.shields.io/github/issues/CCExtractor/Deluge-mobile-remote-client) 48 | 49 | ### Features in Client Application 50 | | Features | Support | 51 | |------------------------------------------------|------------------| 52 | | Single Account | ✅ (tested) | 53 | | Multiple Account | ✅ (tested) | 54 | | operation with single account | ✅ (tested) | 55 | | operate with multiple account at the same time | ✅ (tested) | 56 | | dark mode | ✅ (tested) | 57 | | Deluge settings with all options | ✅ (tested) | 58 | | Live notification | ✅ (tested) | 59 | | automatic troubleshoot on cookie expire | ✅ (tested) | 60 | | Filtering and searching sorting .. etc | ✅ (tested) | 61 | | resume pause delete .. all basic functionality | ✅ (tested) | 62 | | long press to select and select all | ✅ (tested) | 63 | | Manual login | ✅ (tested) | 64 | | Rapid login by Q.R Code on the go | ✅ (tested) | 65 | | can handle reverse proxied | ✅ (tested) | 66 | | Live storage tile | ✅ (tested) | 67 | | Add new torrent as file | ✅ (tested) | 68 | | Add new torrent by magnet link | ✅ (tested) | 69 | | Add new torrent by Q.R code on the go | ✅ (tested) | 70 | | file exploration | ✅ (tested) | 71 | | streaming torrent content | ✅ (tested) | 72 | | Ability to download torrent content to mobile device | ✅ (tested)| 73 | 74 | ## Usage 75 | 76 | In order to use this flutter application you should have Deluge configured on your system, after which you can connect your mobile on the same network as your system and use the app by entering the configuration (IP address). 77 | 78 | If you find any difficulty in running Deluge on your system, you can use this [docker image](https://hub.docker.com/r/linuxserver/deluge). 79 | 80 | ***Note: The primary usage of this application is to control Deluge hosted on your seedbox account.*** 81 | 82 | ## Seedbox 83 | 84 | A seedbox is a dedicated BitTorrent server. Often they are rented out by companies called seedbox providers. 85 | 86 | Seedboxes usually have a high speed Internet connection. This allows users to download torrents quickly and seed the torrents for a long time. 87 | 88 | You can learn more about seedbox [here](https://en.wikipedia.org/wiki/Seedbox). 89 | 90 | 91 | 92 | 93 | ## Getting Started 94 | 95 | #### For setting up the development environment, follow the steps given below: 96 | 97 | 1. For making changes to the project, fork this repository using the green button on this page. 98 | 99 | 2. Clone your fork or this repository, as applicable- 100 | 101 | ```bash 102 | git clone https://github.com/CCExtractor/Deluge-mobile-remote-client.git 103 | cd Deluge-mobile-remote-client 104 | ``` 105 | 106 | 3. Check for flutter setup and connected devices. 107 | 108 | ```bash 109 | flutter doctor 110 | ``` 111 | 112 | 4. Run the app using 113 | ```bash 114 | flutter run 115 | ``` 116 | 117 | For help getting started with Flutter, view our 118 | [online documentation](https://flutter.dev/docs), which offers tutorials, 119 | samples, guidance on mobile development, and a full API reference. 120 | 121 | ## Community 122 | 123 | * CCExtractor Development on [Slack](https://rhccgsoc15.slack.com/). 124 | 125 | ## License 126 | 127 | The project is released under the [MIT License](http://www.opensource.org/licenses/mit-license.php). The license can be found [here](LICENSE). 128 | 129 | 130 | 131 | 132 | ***Note: some of the images are taken from internet, I am not claiming that those images belongs to me. All the credits and copyrights belong to their respective owners.*** 133 | 134 | ## Contact 135 | Mohammad Arshad 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1629546067328 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=C\:/Program Files/Java/jdk-15.0.1 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | 25 | 1629546067337 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | 4 | if (localPropertiesFile.exists()) { 5 | localPropertiesFile.withReader('UTF-8') { reader -> 6 | localProperties.load(reader) 7 | } 8 | } 9 | 10 | def flutterRoot = localProperties.getProperty('flutter.sdk') 11 | if (flutterRoot == null) { 12 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | apply plugin: 'com.android.application' 26 | apply plugin: 'kotlin-android' 27 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | 35 | android { 36 | compileSdkVersion 31 37 | 38 | sourceSets { 39 | main.java.srcDirs += 'src/main/kotlin' 40 | } 41 | 42 | defaultConfig { 43 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 44 | applicationId "com.cc_extractor.deluge_client" 45 | minSdkVersion 21 46 | targetSdkVersion 30 47 | versionCode flutterVersionCode.toInteger() 48 | versionName flutterVersionName 49 | } 50 | signingConfigs { 51 | release { 52 | keyAlias keystoreProperties['keyAlias'] 53 | keyPassword keystoreProperties['keyPassword'] 54 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 55 | storePassword keystoreProperties['storePassword'] 56 | } 57 | } 58 | buildTypes { 59 | release { 60 | signingConfig signingConfigs.release 61 | minifyEnabled true 62 | useProguard true 63 | 64 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 65 | } 66 | } 67 | 68 | // buildTypes { 69 | // release { 70 | // // TODO: Add your own signing config for the release build. 71 | // // Signing with the debug keys for now, so `flutter run --release` works. 72 | // signingConfig signingConfigs.debug 73 | // minifyEnabled true 74 | // useProguard true 75 | 76 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 77 | 78 | 79 | // } 80 | // } 81 | } 82 | 83 | flutter { 84 | source '../..' 85 | } 86 | 87 | dependencies { 88 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 89 | } 90 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #Flutter Wrapper 2 | -keep class io.flutter.app.** { *; } 3 | -keep class io.flutter.plugin.** { *; } 4 | -keep class io.flutter.util.** { *; } 5 | -keep class io.flutter.view.** { *; } 6 | -keep class io.flutter.** { *; } 7 | -keep class io.flutter.plugins.** { *; } 8 | -keep class com.jcraft.** { *; } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/deluge_client/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cc_extractor.deluge_client 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/drawable-v21/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | jcenter() 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 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/animation.gif -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/banner.png -------------------------------------------------------------------------------- /assets/fonts/SFUIDisplay/sf-ui-display-heavy.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/fonts/SFUIDisplay/sf-ui-display-heavy.otf -------------------------------------------------------------------------------- /assets/fonts/SFUIDisplay/sf-ui-display-light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/fonts/SFUIDisplay/sf-ui-display-light.otf -------------------------------------------------------------------------------- /assets/fonts/SFUIDisplay/sf-ui-display-medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/fonts/SFUIDisplay/sf-ui-display-medium.otf -------------------------------------------------------------------------------- /assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/github.png -------------------------------------------------------------------------------- /assets/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/loader.gif -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/logo.png -------------------------------------------------------------------------------- /assets/no_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/no_data.png -------------------------------------------------------------------------------- /assets/poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/poster.png -------------------------------------------------------------------------------- /assets/server_end_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/server_end_error.png -------------------------------------------------------------------------------- /assets/slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/assets/slack.png -------------------------------------------------------------------------------- /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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.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/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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/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/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | deluge_client 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 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/api/models/settings.dart: -------------------------------------------------------------------------------- 1 | class Settings { 2 | bool sendInfo; 3 | int infoSent; 4 | int daemonPort; 5 | bool allowRemote; 6 | bool preAllocateStorage; 7 | String downloadLocation; 8 | List listenPorts; 9 | String listenInterface; 10 | String outgoingInterface; 11 | String randomPort; 12 | int listenRandomPort; 13 | bool listenUseSysPort; 14 | bool listenReusePort; 15 | List outgoingPorts; 16 | String randomOutgoingPorts; 17 | bool copyTorrentFile; 18 | bool delCopyTorrentFile; 19 | String torrentfilesLocation; 20 | String pluginsLocation; 21 | bool prioritizeFirstLastPieces; 22 | bool sequentialDownload; 23 | bool dht; 24 | bool upnp; 25 | bool natpmp; 26 | bool utpex; 27 | bool lsd; 28 | int encInPolicy; 29 | int encOutPolicy; 30 | int encLevel; 31 | int maxConnectionsGlobal; 32 | int maxUploadSpeed; 33 | int maxDownloadSpeed; 34 | int maxUploadSlotsGlobal; 35 | int maxHalfOpenConnections; 36 | int maxConnectionsPerSecond; 37 | bool ignoreLimitsOnLocalNetwork; 38 | int maxConnectionsPerTorrent; 39 | int maxUploadSlotsPerTorrent; 40 | int maxUploadSpeedPerTorrent; 41 | int maxDownloadSpeedPerTorrent; 42 | List enabledPlugins; 43 | bool addPaused; 44 | int maxActiveSeeding; 45 | int maxActiveDownloading; 46 | int maxActiveLimit; 47 | bool dontCountSlowTorrents; 48 | bool queueNewToTop; 49 | bool stopSeedAtRatio; 50 | bool removeSeedAtRatio; 51 | int stopSeedRatio; 52 | int shareRatioLimit; 53 | int seedTimeRatioLimit; 54 | int seedTimeLimit; 55 | bool autoManaged; 56 | bool moveCompleted; 57 | String moveCompletedPath; 58 | List moveCompletedPathsList; 59 | List downloadLocationPathsList; 60 | bool pathChooserShowChooserButtonOnLocalhost; 61 | bool pathChooserAutoCompleteEnabled; 62 | String pathChooserAcceleratorString; 63 | int pathChooserMaxPopupRows; 64 | bool pathChooserShowHiddenFiles; 65 | bool newReleaseCheck; 66 | Map proxy; 67 | String peerTos; 68 | bool rateLimitIpOverhead; 69 | String geoipDbLocation; 70 | int cacheSize; 71 | int cacheExpiry; 72 | bool autoManagePreferSeeds; 73 | bool shared; 74 | bool superSeeding; 75 | String autoaddLocation; 76 | 77 | Settings(Map json) { 78 | this.sendInfo = json['send_info']; 79 | this.infoSent = json["info_sent"]; 80 | this.daemonPort = json["daemon_port"]; 81 | this.allowRemote = json["allow_remote"]; 82 | this.preAllocateStorage = json["pre_allocate_storage"]; 83 | this.downloadLocation = json["download_location"]; 84 | this.listenPorts = json["listen_ports"]; 85 | this.listenInterface = json["listen_interface"]; 86 | this.outgoingInterface = json["outgoing_interface"]; 87 | this.randomPort = json["random_port"]; 88 | this.listenRandomPort = json["listen_random_port"]; 89 | this.listenUseSysPort = json["listen_use_sys_port"]; 90 | this.listenReusePort = json["listen_reuse_port"]; 91 | this.outgoingPorts = json["outgoing_ports"]; 92 | this.randomOutgoingPorts = json["random_outgoing_ports"]; 93 | this.copyTorrentFile = json["copy_torrent_file"]; 94 | this.delCopyTorrentFile = json["del_copy_torrent_file"]; 95 | this.torrentfilesLocation = json["torrentfiles_location"]; 96 | this.pluginsLocation = json["plugins_location"]; 97 | this.prioritizeFirstLastPieces = json["prioritize_first_last_pieces"]; 98 | this.sequentialDownload = json["sequential_download"]; 99 | this.dht = json["dht"]; 100 | this.upnp = json["upnp"]; 101 | this.natpmp = json["natpmp"]; 102 | this.utpex = json["utpex"]; 103 | this.lsd = json["lsd"]; 104 | this.encInPolicy = json["enc_in_policy"]; 105 | this.encOutPolicy = json["enc_out_policy"]; 106 | this.encLevel = json["enc_level"]; 107 | this.maxConnectionsGlobal = json["max_connections_global"]; 108 | this.maxUploadSpeed = json["max_upload_speed"]; 109 | this.maxDownloadSpeed = json["max_download_speed"]; 110 | this.maxUploadSlotsGlobal = json["max_upload_slots_global"]; 111 | this.maxHalfOpenConnections = json["max_half_open_connections"]; 112 | this.maxConnectionsPerSecond = json["max_connections_per_second"]; 113 | this.ignoreLimitsOnLocalNetwork = json["ignore_limits_on_local_network"]; 114 | this.maxConnectionsPerTorrent = json["max_connections_per_torrent"]; 115 | this.maxUploadSlotsPerTorrent = json["max_upload_slots_per_torrent"]; 116 | this.maxUploadSpeedPerTorrent = json["max_upload_speed_per_torrent"]; 117 | this.maxDownloadSpeedPerTorrent = json["max_download_speed_per_torrent"]; 118 | this.enabledPlugins = json["enabled_plugins"]; 119 | this.addPaused = json["add_paused"]; 120 | this.maxActiveSeeding = json["max_active_seeding"]; 121 | this.maxActiveDownloading = json["max_active_downloading"]; 122 | this.maxActiveLimit = json["max_active_limi"]; 123 | this.dontCountSlowTorrents = json["dont_count_slow_torrents"]; 124 | this.queueNewToTop = json["queue_new_to_top"]; 125 | this.stopSeedAtRatio = json["stop_seed_at_ratio"]; 126 | this.removeSeedAtRatio = json["remove_seed_at_ratio"]; 127 | this.stopSeedRatio = json["stop_seed_ratio"]; 128 | this.shareRatioLimit = json["share_ratio_limit"]; 129 | this.seedTimeRatioLimit = json["seed_time_ratio_limit"]; 130 | this.seedTimeLimit = json["seed_time_limit"]; 131 | this.autoManaged = json["auto_managed"]; 132 | this.moveCompleted = json["move_completed"]; 133 | this.moveCompletedPath = json["move_completed_path"]; 134 | this.moveCompletedPathsList = json["move_completed_paths_list"]; 135 | this.downloadLocationPathsList = json["download_location_paths_list"]; 136 | this.pathChooserShowChooserButtonOnLocalhost = 137 | json["path_chooser_show_chooser_button_on_localhost"]; 138 | this.pathChooserAutoCompleteEnabled = 139 | json["path_chooser_auto_complete_enabled"]; 140 | this.pathChooserAcceleratorString = json["path_chooser_accelerator_string"]; 141 | this.pathChooserMaxPopupRows = json["path_chooser_max_popup_rows"]; 142 | this.pathChooserShowHiddenFiles = json["path_chooser_show_hidden_files"]; 143 | this.newReleaseCheck = json["new_release_check"]; 144 | this.proxy = json["proxy"]; 145 | this.peerTos = json["peer_tos"]; 146 | this.rateLimitIpOverhead = json["rate_limit_ip_overhead"]; 147 | this.geoipDbLocation = json["geoip_db_location"]; 148 | this.cacheSize = json["cache_size"]; 149 | 150 | this.cacheExpiry = json["cache_expiry"]; 151 | this.autoManagePreferSeeds = json["auto_manage_prefer_seeds"]; 152 | this.shared = json["shared"]; 153 | this.superSeeding = json["super_seeding"]; 154 | this.autoaddLocation = json["autoadd_location"]; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/components/accounts.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:deluge_client/components/loader.dart'; 3 | import 'package:deluge_client/database/dbmanager.dart'; 4 | import 'package:deluge_client/screens/auth.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:deluge_client/control_center/theme.dart'; 7 | import 'package:fluttertoast/fluttertoast.dart'; 8 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 9 | import 'package:deluge_client/control_center/theme_controller.dart'; 10 | 11 | class accounts extends StatefulWidget { 12 | final VoidCallback update_account_selection; 13 | final VoidCallback dashboard_state; 14 | 15 | accounts({Key key, this.update_account_selection, this.dashboard_state}) 16 | : super(key: key); 17 | @override 18 | accountsState createState() => accountsState( 19 | key: key, 20 | update_account_selection: update_account_selection, 21 | dashboard_state: dashboard_state); 22 | } 23 | 24 | class accountsState extends State { 25 | final VoidCallback update_account_selection; 26 | final VoidCallback dashboard_state; 27 | 28 | accountsState({Key key, this.update_account_selection, this.dashboard_state}); 29 | 30 | final DbbucketManager dbmanager = new DbbucketManager(); 31 | 32 | int selected_account; 33 | 34 | void update_account_state(int id) async { 35 | states.update_account_state(id); 36 | } 37 | 38 | void fetch_selected_account() async { 39 | int mid = await states.state_selected_account(); 40 | if (this.mounted) { 41 | setState(() { 42 | selected_account = mid; 43 | }); 44 | } 45 | } 46 | 47 | void initState() { 48 | fetch_selected_account(); 49 | 50 | super.initState(); 51 | } 52 | 53 | List avail_acc = new List(); 54 | 55 | prompt_to_delete( 56 | BuildContext context, int id, String account, int length_acc) { 57 | // set up the buttons 58 | Widget cancelButton = TextButton( 59 | child: Text( 60 | "Yes", 61 | style: TextStyle( 62 | color: Colors.white, 63 | fontSize: theme.alert_box_font_size, 64 | fontWeight: FontWeight.bold, 65 | fontFamily: theme.font_family), 66 | ), 67 | onPressed: () async { 68 | //some code 69 | if (length_acc > 1) { 70 | //if it is selected account and you wanted to delete it then 71 | avail_acc.remove(id); 72 | if (id == selected_account) { 73 | //----------------- 74 | if (this.mounted) { 75 | setState(() { 76 | selected_account = avail_acc[0]; 77 | 78 | update_account_state(selected_account); 79 | }); 80 | } 81 | update_account_selection(); 82 | dashboard_state(); 83 | //here i will add logic to reset sftp 84 | if (await states.get_sftP_reset_bool()) { 85 | states.reset_sftp_config(); 86 | } 87 | 88 | //-------------------- 89 | 90 | } 91 | //-------------- 92 | dbmanager.deletebucket(id); 93 | Navigator.of(context, rootNavigator: true) 94 | .pop(); // for shutting the alertbox 95 | Navigator.of(context).pop(); //for closing sidebar 96 | toastMessage(account + " deleted successfully"); 97 | } else if (length_acc == 1) { 98 | states.reset_auth(); // it will reset the auth state 99 | dbmanager.deletebucket(id); 100 | avail_acc.remove(id); 101 | states.make_it_first_time(); 102 | 103 | Navigator.of(context, rootNavigator: true) 104 | .pop(); // for shutting the alertbox 105 | //------------it should also reset config 106 | dbmanager.delete_table(); // it will delete whole table 107 | 108 | //-------------------------- 109 | 110 | Navigator.pushReplacement( 111 | context, 112 | MaterialPageRoute( 113 | builder: (context) => auth_view( 114 | tow_attachment: false, 115 | ))); 116 | } 117 | }, 118 | ); 119 | Widget continueButton = TextButton( 120 | child: Text( 121 | "No", 122 | style: TextStyle( 123 | color: Colors.blue, 124 | fontSize: theme.alert_box_font_size, 125 | fontWeight: FontWeight.bold, 126 | fontFamily: theme.font_family), 127 | ), 128 | onPressed: () { 129 | Navigator.of(context, rootNavigator: true).pop(); 130 | }, 131 | ); 132 | 133 | // set up the AlertDialog 134 | AlertDialog alert = AlertDialog( 135 | backgroundColor: theme.base_color, 136 | title: Text("Pay Attention", 137 | style: TextStyle(color: Colors.white, fontFamily: theme.font_family)), 138 | content: Text( 139 | "Do you wanted to remove " + account, 140 | style: TextStyle( 141 | fontSize: theme.alert_box_font_size, 142 | color: Colors.white, 143 | fontFamily: theme.font_family), 144 | ), 145 | actions: [ 146 | cancelButton, 147 | continueButton, 148 | ], 149 | ); 150 | 151 | // show the dialog 152 | showDialog( 153 | context: context, 154 | barrierDismissible: false, 155 | builder: (BuildContext context) { 156 | return alert; 157 | }, 158 | ); 159 | } 160 | 161 | void toastMessage(String message) { 162 | Fluttertoast.showToast( 163 | msg: message, 164 | toastLength: Toast.LENGTH_SHORT, 165 | gravity: ToastGravity.BOTTOM, 166 | fontSize: 16.0, 167 | backgroundColor: Colors.black, 168 | ); 169 | } 170 | 171 | @override 172 | Widget build(BuildContext context) { 173 | return Container( 174 | height: 140.0, 175 | child: FutureBuilder( 176 | future: dbmanager.getbucketitem(), 177 | builder: (BuildContext context, AsyncSnapshot snapshot) { 178 | if (snapshot.data == null) { 179 | return Container(child: Center(child: loader())); 180 | } else { 181 | List accounts = snapshot.data; 182 | 183 | return ListView.builder( 184 | itemCount: accounts == null ? 0 : accounts.length, 185 | itemBuilder: (BuildContext context, int index) { 186 | avail_acc.add(accounts[index].id); 187 | return ListTile( 188 | visualDensity: 189 | VisualDensity(horizontal: 0, vertical: -4), 190 | title: Text(accounts[index].deluge_url, 191 | // overflow: TextOverflow.ellipsis, 192 | style: TextStyle( 193 | fontSize: 12.0, 194 | fontFamily: theme.font_family, 195 | color: theme_controller.is_it_dark() 196 | ? Colors.white 197 | : Colors.black, 198 | )), 199 | leading: Icon(selected_account == accounts[index].id 200 | ? Icons.radio_button_checked 201 | : Icons.radio_button_unchecked), 202 | trailing: IconButton( 203 | icon: Icon( 204 | Icons.delete, 205 | color: Colors.red, 206 | size: 18.0, 207 | ), 208 | onPressed: () { 209 | prompt_to_delete(context, accounts[index].id, 210 | accounts[index].deluge_url, accounts.length); 211 | }, 212 | ), 213 | onTap: () async { 214 | if (this.mounted) { 215 | setState(() { 216 | selected_account = accounts[index].id; 217 | 218 | update_account_state(selected_account); 219 | }); 220 | } 221 | update_account_selection(); 222 | dashboard_state(); 223 | //here i will add logic to reset sftp 224 | if (await states.get_sftP_reset_bool()) { 225 | states.reset_sftp_config(); 226 | } 227 | Navigator.of(context).pop(); //closing side bar 228 | }, 229 | ); 230 | }); 231 | } 232 | })); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /lib/components/all_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:expandable/expandable.dart'; 3 | import 'package:deluge_client/control_center/theme.dart'; 4 | import 'package:intl/intl.dart'; 5 | 6 | import 'package:deluge_client/api/models/torrent_prop.dart'; 7 | 8 | class more_info extends StatelessWidget { 9 | final Properties inside_res; 10 | final bool paused; 11 | more_info({Key key, @required this.inside_res, this.paused}) 12 | : super(key: key); 13 | 14 | final format = new DateFormat('dd-MM-yyyy hh:mm a'); 15 | final eta = new DateFormat('hh:mm'); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | buildItem(String label) { 20 | return Padding( 21 | padding: const EdgeInsets.all(10.0), 22 | child: Text(label), 23 | ); 24 | } 25 | 26 | buildList() { 27 | return Column( 28 | children: [ 29 | ListTile( 30 | dense: true, 31 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 32 | title: Text( 33 | "ETA", 34 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 35 | ), 36 | subtitle: Text( 37 | !paused 38 | ? (inside_res.eta ~/ 60).toString() + 39 | ":" + 40 | (inside_res.eta % 60).toString() 41 | : "_:_", 42 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 43 | ), 44 | ), 45 | ListTile( 46 | dense: true, 47 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 48 | title: Text( 49 | "Save path", 50 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 51 | ), 52 | subtitle: Text( 53 | inside_res.savePath, 54 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 55 | ), 56 | ), 57 | ListTile( 58 | dense: true, 59 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 60 | title: Text( 61 | "Is shared?", 62 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 63 | ), 64 | subtitle: Text( 65 | inside_res.shared.toString(), 66 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 67 | ), 68 | ), 69 | ListTile( 70 | dense: true, 71 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 72 | title: Text( 73 | "Is Private?", 74 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 75 | ), 76 | subtitle: Text( 77 | inside_res.private.toString(), 78 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 79 | ), 80 | ), 81 | ListTile( 82 | dense: true, 83 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 84 | title: Text( 85 | "Total peers", 86 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 87 | ), 88 | subtitle: Text( 89 | inside_res.numPeers.toString(), 90 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 91 | ), 92 | ), 93 | ListTile( 94 | dense: true, 95 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 96 | title: Text( 97 | "Total seeds", 98 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 99 | ), 100 | subtitle: Text( 101 | inside_res.numSeeds.toString(), 102 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 103 | ), 104 | ), 105 | ListTile( 106 | dense: true, 107 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 108 | title: Text( 109 | "Date and time added", 110 | style: TextStyle(fontSize: 12.0, fontFamily: theme.font_family), 111 | ), 112 | subtitle: Text( 113 | format 114 | .format(DateTime.fromMillisecondsSinceEpoch( 115 | inside_res.timeAdded * 1000)) 116 | .toString(), 117 | style: TextStyle(fontSize: 14.0, fontFamily: theme.font_family), 118 | ), 119 | ), 120 | ], 121 | ); 122 | } 123 | 124 | return ExpandableNotifier( 125 | child: ScrollOnExpand( 126 | child: ExpandablePanel( 127 | theme: const ExpandableThemeData( 128 | tapBodyToExpand: true, 129 | tapBodyToCollapse: true, 130 | hasIcon: false, 131 | ), 132 | header: ExpandableIcon( 133 | theme: const ExpandableThemeData( 134 | expandIcon: Icons.arrow_drop_down, 135 | collapseIcon: Icons.arrow_drop_up, 136 | iconColor: Colors.black, 137 | iconSize: 20.0, 138 | iconPadding: EdgeInsets.only(right: 5), 139 | hasIcon: false, 140 | ), 141 | ), 142 | collapsed: Container(), 143 | expanded: buildList(), 144 | ), 145 | ), 146 | ); 147 | } 148 | } 149 | //------------------------------------ 150 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/add_magnet_uri.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:deluge_client/control_center/theme.dart'; 6 | import 'package:deluge_client/api/apis.dart'; 7 | import 'package:deluge_client/string/magnet_detect.dart'; 8 | import 'package:fluttertoast/fluttertoast.dart'; 9 | 10 | class add_magnet extends StatefulWidget { 11 | final List cookie; 12 | final VoidCallback refresh; 13 | //----------------------------------- 14 | final String url; 15 | final String is_reverse_proxied; 16 | final String seed_username; 17 | final String seed_pass; 18 | final String qr_auth; 19 | add_magnet( 20 | {Key key, 21 | @required this.refresh, 22 | @required this.cookie, 23 | @required this.url, 24 | @required this.is_reverse_proxied, 25 | @required this.seed_username, 26 | @required this.seed_pass, 27 | @required this.qr_auth}) 28 | : super(key: key); 29 | @override 30 | _add_magnetState createState() => _add_magnetState( 31 | refresh: refresh, 32 | cookie: cookie, 33 | url: url, 34 | is_reverse_proxied: is_reverse_proxied, 35 | seed_username: seed_username, 36 | seed_pass: seed_pass, 37 | qr_auth: qr_auth); 38 | } 39 | 40 | class _add_magnetState extends State { 41 | final VoidCallback refresh; 42 | final List cookie; 43 | //-------------------------------------- 44 | final String url; 45 | final String is_reverse_proxied; 46 | final String seed_username; 47 | final String seed_pass; 48 | final String qr_auth; 49 | 50 | _add_magnetState( 51 | {this.cookie, 52 | this.refresh, 53 | this.url, 54 | this.is_reverse_proxied, 55 | this.seed_username, 56 | this.seed_pass, 57 | this.qr_auth}); 58 | var myController = TextEditingController(); 59 | String input = ""; 60 | Future fetch_clipboard() async { 61 | ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); 62 | input = data.text; 63 | if (this.mounted) { 64 | setState(() { 65 | myController.text = input; 66 | }); 67 | } 68 | } 69 | 70 | void toastMessage(String message) { 71 | Fluttertoast.showToast( 72 | msg: message, 73 | toastLength: Toast.LENGTH_SHORT, 74 | gravity: ToastGravity.BOTTOM, 75 | fontSize: 16.0, 76 | backgroundColor: Colors.black, 77 | ); 78 | } 79 | 80 | void add_torrent_by_magnet_uri(String link) async { 81 | if (magnet_detect.parse(link)) { 82 | apis.add_magnet(link, cookie, url, is_reverse_proxied, seed_username, 83 | seed_pass, qr_auth, context); 84 | 85 | // after adding file then it should refresh it self; 86 | Navigator.of(context).pop(); //bottom sheet should get closed 87 | Future.delayed(Duration(seconds: 1), () { 88 | refresh(); 89 | }); 90 | } else { 91 | toastMessage("Please enter correct link"); 92 | } 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | return SingleChildScrollView( 98 | child: Material( 99 | shape: RoundedRectangleBorder( 100 | borderRadius: new BorderRadius.only( 101 | topRight: Radius.circular(15.0), topLeft: Radius.circular(15.0)), 102 | ), 103 | child: Container( 104 | padding: 105 | EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), 106 | child: Column( 107 | mainAxisSize: MainAxisSize.min, 108 | children: [ 109 | Divider( 110 | color: Color.fromRGBO(255, 79, 90, 1), 111 | thickness: 4.0, 112 | indent: 70.0, 113 | endIndent: 70.0, 114 | ), 115 | Padding( 116 | padding: EdgeInsets.all(10.0), 117 | child: TextField( 118 | onChanged: (String val) { 119 | setState(() { 120 | myController.text; 121 | }); 122 | }, 123 | style: TextStyle( 124 | color: Colors.black, 125 | fontSize: theme.alert_box_font_size, 126 | fontFamily: theme.font_family), 127 | decoration: new InputDecoration( 128 | border: new OutlineInputBorder( 129 | borderRadius: const BorderRadius.all( 130 | const Radius.circular(10.0), 131 | ), 132 | ), 133 | filled: true, 134 | hintStyle: new TextStyle(color: Colors.grey[800]), 135 | hintText: "Enter Magnet URL", 136 | suffixIcon: InkWell( 137 | child: Icon(Icons.paste), 138 | onTap: () { 139 | fetch_clipboard(); 140 | }, 141 | ), 142 | fillColor: Colors.white70, 143 | ), 144 | controller: myController, 145 | autofocus: true, 146 | )), 147 | ElevatedButton( 148 | onPressed: () { 149 | if (myController.text.length > 0) { 150 | add_torrent_by_magnet_uri(myController.text); 151 | } 152 | }, 153 | style: ButtonStyle( 154 | backgroundColor: MaterialStateProperty.all( 155 | myController.text.length > 0 ? theme.base_color : Colors.grey, 156 | )), 157 | child: Text( 158 | "Add", 159 | style: TextStyle( 160 | color: Colors.black, 161 | fontSize: theme.alert_box_font_size, 162 | fontFamily: theme.font_family), 163 | ), 164 | ) 165 | ], 166 | )), 167 | )); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/add_torrent.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:fluttertoast/fluttertoast.dart'; 6 | import 'package:deluge_client/components/bottom_sheet/add_magnet_uri.dart'; 7 | import 'package:deluge_client/components/bottom_sheet/qr_magnet_reader.dart'; 8 | import 'package:deluge_client/control_center/theme.dart'; 9 | import 'package:deluge_client/api/apis.dart'; 10 | import 'package:deluge_client/control_center/theme_controller.dart'; 11 | import 'package:file_picker/file_picker.dart'; 12 | import 'package:flutter/services.dart'; 13 | 14 | class add_new extends StatefulWidget { 15 | final VoidCallback refresh; 16 | final List cookie; 17 | //--------------- 18 | final String url; 19 | final String is_reverse_proxied; 20 | final String seed_username; 21 | final String seed_pass; 22 | final String qr_auth; 23 | add_new( 24 | {Key key, 25 | @required this.cookie, 26 | @required this.refresh, 27 | @required this.url, 28 | @required this.is_reverse_proxied, 29 | @required this.seed_username, 30 | @required this.seed_pass, 31 | @required this.qr_auth}) 32 | : super(key: key); 33 | 34 | @override 35 | _add_newState createState() => _add_newState( 36 | cookie: cookie, 37 | refresh: refresh, 38 | url: url, 39 | is_reverse_proxied: is_reverse_proxied, 40 | seed_username: seed_username, 41 | seed_pass: seed_pass, 42 | qr_auth: qr_auth); 43 | } 44 | 45 | class _add_newState extends State { 46 | final VoidCallback refresh; 47 | final List cookie; 48 | //---------------------------- 49 | final String url; 50 | final String is_reverse_proxied; 51 | final String seed_username; 52 | final String seed_pass; 53 | final String qr_auth; 54 | 55 | _add_newState( 56 | {this.cookie, 57 | this.refresh, 58 | this.url, 59 | this.is_reverse_proxied, 60 | this.seed_username, 61 | this.seed_pass, 62 | this.qr_auth}); 63 | void toastMessage(String message) { 64 | Fluttertoast.showToast( 65 | msg: message, 66 | toastLength: Toast.LENGTH_SHORT, 67 | gravity: ToastGravity.BOTTOM, 68 | fontSize: 16.0, 69 | backgroundColor: Colors.black, 70 | ); 71 | } 72 | 73 | void add_new_torrent_file() async { 74 | String _fileName; 75 | List _paths; 76 | 77 | try { 78 | _paths = (await FilePicker.platform.pickFiles( 79 | type: FileType.custom, 80 | allowMultiple: false, 81 | onFileLoading: (FilePickerStatus status) => print(status), 82 | allowedExtensions: ["torrent"])) 83 | .files; 84 | } on PlatformException catch (e) { 85 | print("Unsupported operation" + e.toString()); 86 | } catch (ex) { 87 | print(ex); 88 | } 89 | String file_path = ""; 90 | 91 | file_path = _paths != null ? _paths.map((e) => e.path).toString() : '...'; 92 | if (!mounted) { 93 | toastMessage("Please retry to add"); 94 | } 95 | print(file_path.substring(1, file_path.length - 1)); 96 | 97 | List torrent_byte = 98 | new File(file_path.substring(1, file_path.length - 1)) 99 | .readAsBytesSync(); 100 | 101 | if (torrent_byte.isNotEmpty) { 102 | String base_64_encoded_file = base64Encode(torrent_byte); 103 | apis.add_torrent_file(base_64_encoded_file, cookie, url, 104 | is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 105 | Future.delayed(Duration(seconds: 1), () { 106 | refresh(); 107 | }); 108 | } 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | return Material( 114 | child: SafeArea( 115 | top: false, 116 | child: Container( 117 | child: Column( 118 | mainAxisSize: MainAxisSize.min, 119 | children: [ 120 | Padding( 121 | padding: EdgeInsets.only(top: 3.0), 122 | child: Text("Add new torrent as", 123 | style: TextStyle( 124 | color: (!theme_controller.is_it_dark() 125 | ? Colors.black 126 | : Colors.white), 127 | fontSize: theme.bottom_sheet_heading_font_size, 128 | fontFamily: theme.font_family))), 129 | Divider( 130 | color: theme.base_color, 131 | thickness: 4.0, 132 | indent: 55.0, 133 | endIndent: 55.0, 134 | ), 135 | ListTile( 136 | title: Text( 137 | 'Add torrent file', 138 | style: TextStyle( 139 | color: (!theme_controller.is_it_dark() 140 | ? Colors.black 141 | : Colors.white), 142 | fontSize: theme.alert_box_font_size, 143 | fontFamily: theme.font_family), 144 | ), 145 | leading: 146 | Icon(Icons.folder_rounded, color: theme.base_color), 147 | onTap: () { 148 | Navigator.of(context).pop(); 149 | add_new_torrent_file(); 150 | }, 151 | ), 152 | ListTile( 153 | title: Text( 154 | 'Add magnet link', 155 | style: TextStyle( 156 | color: (!theme_controller.is_it_dark() 157 | ? Colors.black 158 | : Colors.white), 159 | fontSize: theme.alert_box_font_size, 160 | fontFamily: theme.font_family), 161 | ), 162 | leading: Icon(Icons.link_sharp, color: theme.base_color), 163 | onTap: () { 164 | Navigator.of(context).pop(); 165 | showModalBottomSheet( 166 | isScrollControlled: true, 167 | context: context, 168 | backgroundColor: Colors.transparent, 169 | builder: (context) => add_magnet( 170 | cookie: cookie, 171 | url: url, 172 | is_reverse_proxied: is_reverse_proxied, 173 | seed_username: seed_username, 174 | seed_pass: seed_pass, 175 | qr_auth: qr_auth, 176 | refresh: () => refresh(), 177 | )); 178 | }), 179 | ListTile( 180 | title: Text( 181 | 'Scan magnet QR', 182 | style: TextStyle( 183 | color: (!theme_controller.is_it_dark() 184 | ? Colors.black 185 | : Colors.white), 186 | fontSize: theme.alert_box_font_size, 187 | fontFamily: theme.font_family), 188 | ), 189 | leading: Icon(Icons.qr_code_scanner_outlined, 190 | color: theme.base_color), 191 | onTap: () { 192 | Navigator.of(context).pop(); 193 | showModalBottomSheet( 194 | isScrollControlled: false, 195 | context: context, 196 | backgroundColor: Colors.transparent, 197 | builder: (context) => magnet_qr_reader( 198 | cookie: cookie, 199 | url: url, 200 | is_reverse_proxied: is_reverse_proxied, 201 | seed_username: seed_username, 202 | seed_pass: seed_pass, 203 | qr_auth: qr_auth, 204 | refresh: () => refresh(), 205 | )); 206 | }), 207 | ], 208 | ), 209 | ))); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/choose_account.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:deluge_client/components/bottom_sheet/add_torrent.dart'; 4 | import 'package:deluge_client/database/dbmanager.dart'; 5 | import 'package:deluge_client/settings/deluge/deluge_setting.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:deluge_client/control_center/theme.dart'; 8 | import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; 9 | 10 | class multi_account_menu extends StatefulWidget { 11 | final int widget_id; 12 | final Map cookie_all_account; 13 | final VoidCallback refresh; 14 | multi_account_menu( 15 | {Key key, this.cookie_all_account, this.refresh, this.widget_id}) 16 | : super(key: key); 17 | @override 18 | _multi_account_menuState createState() => _multi_account_menuState( 19 | cookie_all_account: cookie_all_account, 20 | refresh: refresh, 21 | widget_id: widget_id); 22 | } 23 | 24 | class _multi_account_menuState extends State { 25 | final int widget_id; 26 | final Map cookie_all_account; 27 | final VoidCallback refresh; 28 | 29 | _multi_account_menuState( 30 | {this.cookie_all_account, this.refresh, this.widget_id}); 31 | final DbbucketManager dbmanager = new DbbucketManager(); 32 | void switch_to_next( 33 | List cookie, 34 | String url, 35 | String is_reverse_proxied, 36 | String seed_username, 37 | String seed_pass, 38 | String qr_auth) { 39 | Navigator.of(context).pop(); 40 | showCupertinoModalBottomSheet( 41 | expand: false, 42 | context: context, 43 | backgroundColor: Colors.transparent, 44 | builder: (context) => add_new( 45 | cookie: cookie, 46 | url: url, 47 | is_reverse_proxied: is_reverse_proxied, 48 | seed_username: seed_username, 49 | seed_pass: seed_pass, 50 | qr_auth: qr_auth, 51 | refresh: () => refresh())); 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Material( 57 | child: SafeArea( 58 | top: false, 59 | child: Container( 60 | height: 400.0, 61 | child: Column( 62 | children: [ 63 | Padding( 64 | padding: EdgeInsets.only(top: 3.0), 65 | child: Text("Please Choose Account", 66 | style: TextStyle( 67 | fontSize: theme.bottom_sheet_heading_font_size, 68 | fontFamily: theme.font_family))), 69 | Divider( 70 | color: theme.base_color, 71 | thickness: 4.0, 72 | indent: 55.0, 73 | endIndent: 55.0, 74 | ), 75 | Expanded( 76 | child: FutureBuilder( 77 | future: dbmanager.getbucketitem(), 78 | builder: 79 | (BuildContext context, AsyncSnapshot snapshot) { 80 | if (snapshot.data == null) { 81 | return Container( 82 | child: Center( 83 | child: 84 | Image.asset('assets/loader.gif'))); 85 | } else { 86 | List accounts = snapshot.data; 87 | 88 | return ListView.builder( 89 | itemCount: 90 | accounts == null ? 0 : accounts.length, 91 | itemBuilder: 92 | (BuildContext context, int index) { 93 | return ListTile( 94 | visualDensity: VisualDensity( 95 | horizontal: 0, vertical: -4), 96 | leading: Icon( 97 | Icons.connect_without_contact_sharp, 98 | color: theme.base_color, 99 | ), 100 | title: Text(accounts[index].deluge_url, 101 | style: TextStyle( 102 | fontFamily: theme.font_family, 103 | fontSize: theme.minimal_font_size, 104 | )), 105 | onTap: () { 106 | if (widget_id == 1) { 107 | if (cookie_all_account != null) { 108 | switch_to_next( 109 | cookie_all_account[ 110 | accounts[index] 111 | .deluge_url], 112 | accounts[index].deluge_url, 113 | accounts[index] 114 | .is_reverse_proxied, 115 | accounts[index].username, 116 | accounts[index].password, 117 | accounts[index].via_qr); 118 | } 119 | } else if (widget_id == -1) { 120 | Navigator.of(context) 121 | .pop(); //closing the bottom sheet 122 | Navigator.push( 123 | context, 124 | MaterialPageRoute( 125 | builder: (context) => 126 | deluge_settings( 127 | cookie: 128 | cookie_all_account[ 129 | accounts[ 130 | index] 131 | .deluge_url], 132 | selected_account: 133 | accounts[ 134 | index]))); 135 | } 136 | }, 137 | ); 138 | }); 139 | } 140 | })), 141 | ], 142 | )))); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/qr_magnet_reader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:qr_code_scanner/qr_code_scanner.dart'; 5 | import 'package:deluge_client/control_center/theme.dart'; 6 | import 'package:deluge_client/api/apis.dart'; 7 | import 'package:deluge_client/string/magnet_detect.dart'; 8 | import 'package:fluttertoast/fluttertoast.dart'; 9 | 10 | class magnet_qr_reader extends StatefulWidget { 11 | final VoidCallback refresh; 12 | final List cookie; 13 | //---------------------------------------- 14 | final String url; 15 | final String is_reverse_proxied; 16 | final String seed_username; 17 | final String seed_pass; 18 | final String qr_auth; 19 | magnet_qr_reader( 20 | {Key key, 21 | @required this.cookie, 22 | @required this.refresh, 23 | @required this.url, 24 | @required this.is_reverse_proxied, 25 | @required this.seed_username, 26 | @required this.seed_pass, 27 | @required this.qr_auth}) 28 | : super(key: key); 29 | 30 | @override 31 | _magnet_qr_readerState createState() => _magnet_qr_readerState( 32 | refresh: refresh, 33 | cookie: cookie, 34 | url: url, 35 | is_reverse_proxied: is_reverse_proxied, 36 | seed_username: seed_username, 37 | seed_pass: seed_pass, 38 | qr_auth: qr_auth); 39 | } 40 | 41 | class _magnet_qr_readerState extends State { 42 | final VoidCallback refresh; 43 | final List cookie; 44 | //------------------------------------- 45 | final String url; 46 | final String is_reverse_proxied; 47 | final String seed_username; 48 | final String seed_pass; 49 | final String qr_auth; 50 | 51 | _magnet_qr_readerState( 52 | {this.cookie, 53 | this.refresh, 54 | this.is_reverse_proxied, 55 | this.seed_username, 56 | this.seed_pass, 57 | this.url, 58 | this.qr_auth}); 59 | Barcode result; 60 | QRViewController controller; 61 | final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); 62 | // In order to get hot reload to work we need to pause the camera if the platform 63 | // is android, or resume the camera if the platform is iOS. 64 | // now no need to do this 65 | // @override 66 | // void reassemble() { 67 | // super.reassemble(); 68 | // if (Platform.isAndroid) { 69 | // controller.pauseCamera(); 70 | // } 71 | // controller.resumeCamera(); 72 | // } 73 | @override 74 | void dispose() { 75 | controller?.dispose(); 76 | super.dispose(); 77 | } 78 | 79 | void toastMessage(String message) { 80 | Fluttertoast.showToast( 81 | msg: message, 82 | toastLength: Toast.LENGTH_SHORT, 83 | gravity: ToastGravity.BOTTOM, 84 | fontSize: 16.0, 85 | backgroundColor: Colors.black, 86 | ); 87 | } 88 | 89 | void add_torrent_by_magnet_uri(String link) async { 90 | if (magnet_detect.parse(link)) { 91 | apis.add_magnet(link, cookie, url, is_reverse_proxied, seed_username, 92 | seed_pass, qr_auth, context); 93 | 94 | // after adding file then it should refresh it self; 95 | 96 | Future.delayed(Duration(seconds: 1), () { 97 | refresh(); 98 | Navigator.of(context).pop(); 99 | }); 100 | } else { 101 | toastMessage("Magnet Q.R is invalid"); 102 | } 103 | // // bottom sheet should get closed 104 | } 105 | 106 | @override 107 | Widget build(BuildContext context) { 108 | return Material( 109 | shape: RoundedRectangleBorder( 110 | borderRadius: new BorderRadius.only( 111 | topRight: Radius.circular(15.0), topLeft: Radius.circular(15.0)), 112 | ), 113 | child: SafeArea( 114 | top: false, 115 | child: Container( 116 | child: Column(mainAxisSize: MainAxisSize.min, children: [ 117 | Row( 118 | mainAxisAlignment: MainAxisAlignment.center, 119 | children: [ 120 | Flexible( 121 | fit: FlexFit.tight, 122 | child: Container( 123 | color: Colors.transparent, 124 | height: MediaQuery.of(context).orientation == 125 | Orientation.portrait 126 | ? 300.0 127 | : 150.0, 128 | child: _build_qr(context), 129 | )), 130 | ], 131 | ), 132 | Flexible( 133 | child: ElevatedButton( 134 | style: ButtonStyle( 135 | backgroundColor: 136 | MaterialStateProperty.all(theme.base_color)), 137 | onPressed: () { 138 | if (result != null) { 139 | // print(result.code); 140 | add_torrent_by_magnet_uri(result.code); 141 | controller.stopCamera(); 142 | Navigator.of(context).pop(); 143 | } else { 144 | //todo i need to add a message here 145 | controller.stopCamera(); 146 | Navigator.of(context).pop(); 147 | } 148 | }, 149 | child: Text( 150 | "read QR", 151 | style: TextStyle( 152 | color: Colors.white, 153 | fontSize: theme.alert_box_font_size, 154 | fontFamily: theme.font_family), 155 | ))), 156 | ]), 157 | ))); 158 | } 159 | 160 | //----------------- 161 | Widget _build_qr(BuildContext context) { 162 | // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. 163 | var scanArea = (MediaQuery.of(context).size.width < 400 || 164 | MediaQuery.of(context).size.height < 400) 165 | ? 150.0 166 | : 300.0; 167 | // To ensure the Scanner view is properly sizes after rotation 168 | // we need to listen for Flutter SizeChanged notification and update controller 169 | return QRView( 170 | key: qrKey, 171 | onQRViewCreated: _onQRViewCreated, 172 | overlay: QrScannerOverlayShape( 173 | borderColor: theme.base_color, 174 | borderRadius: 10, 175 | borderLength: 30, 176 | borderWidth: 10, 177 | cutOutSize: scanArea), 178 | ); 179 | } 180 | 181 | void _onQRViewCreated(QRViewController controller) { 182 | if (this.mounted) { 183 | setState(() { 184 | this.controller = controller; 185 | }); 186 | } 187 | controller.scannedDataStream.listen((scanData) { 188 | if (this.mounted) { 189 | setState(() { 190 | result = scanData; 191 | }); 192 | } 193 | }); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/sorter.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:deluge_client/control_center/theme.dart'; 4 | import 'package:deluge_client/string/sorter.dart'; 5 | import 'package:deluge_client/control_center/theme_controller.dart'; 6 | 7 | class sorter extends StatefulWidget { 8 | final VoidCallback rebuilt_list; 9 | sorter({Key key, this.rebuilt_list}) : super(key: key); 10 | @override 11 | _sorterState createState() => _sorterState( 12 | rebuilt_list: rebuilt_list, 13 | ); 14 | } 15 | 16 | class _sorterState extends State { 17 | final VoidCallback rebuilt_list; 18 | _sorterState({Key key, this.rebuilt_list}); 19 | @override 20 | Widget build(BuildContext context) { 21 | return Material( 22 | child: SafeArea( 23 | top: false, 24 | child: Container( 25 | child: Column( 26 | mainAxisSize: MainAxisSize.min, 27 | children: [ 28 | Padding( 29 | padding: EdgeInsets.only(top: 3.0), 30 | child: Text("Sort as", 31 | style: TextStyle( 32 | color: !theme_controller.is_it_dark() 33 | ? Colors.black 34 | : Colors.white, 35 | fontSize: theme.bottom_sheet_heading_font_size, 36 | fontFamily: theme.font_family))), 37 | Divider( 38 | color: theme.base_color, 39 | thickness: 4.0, 40 | indent: 55.0, 41 | endIndent: 55.0, 42 | ), 43 | ListTile( 44 | title: Text( 45 | 'Sort by non-reverse order', 46 | style: TextStyle( 47 | color: sort_helper.non_reverse_order 48 | ? theme.base_color 49 | : (!theme_controller.is_it_dark() 50 | ? Colors.black 51 | : Colors.white), 52 | fontSize: theme.alert_box_font_size, 53 | fontFamily: theme.font_family), 54 | ), 55 | leading: Icon(Icons.arrow_upward, 56 | color: sort_helper.non_reverse_order 57 | ? theme.base_color 58 | : (!theme_controller.is_it_dark() 59 | ? Colors.black 60 | : Colors.white)), 61 | onTap: () { 62 | if (this.mounted) { 63 | setState(() { 64 | sort_helper.reverse_order = false; 65 | sort_helper.non_reverse_order = true; 66 | sort_helper.by_size_order = false; 67 | sort_helper.by_date_time = false; 68 | }); 69 | } 70 | rebuilt_list(); 71 | Navigator.of(context).pop(); 72 | }, 73 | ), 74 | ListTile( 75 | title: Text( 76 | 'Sort by reverse order', 77 | style: TextStyle( 78 | color: sort_helper.reverse_order 79 | ? theme.base_color 80 | : (!theme_controller.is_it_dark() 81 | ? Colors.black 82 | : Colors.white), 83 | fontSize: theme.alert_box_font_size, 84 | fontFamily: theme.font_family), 85 | ), 86 | leading: Icon( 87 | Icons.arrow_downward_rounded, 88 | color: sort_helper.reverse_order 89 | ? theme.base_color 90 | : (!theme_controller.is_it_dark() 91 | ? Colors.black 92 | : Colors.white), 93 | ), 94 | onTap: () { 95 | if (this.mounted) { 96 | setState(() { 97 | sort_helper.reverse_order = true; 98 | sort_helper.non_reverse_order = false; 99 | sort_helper.by_size_order = false; 100 | sort_helper.by_date_time = false; 101 | }); 102 | } 103 | rebuilt_list(); 104 | 105 | Navigator.of(context).pop(); 106 | }), 107 | ListTile( 108 | title: Text( 109 | 'Sort by size', 110 | style: TextStyle( 111 | color: sort_helper.by_size_order 112 | ? theme.base_color 113 | : (!theme_controller.is_it_dark() 114 | ? Colors.black 115 | : Colors.white), 116 | fontSize: theme.alert_box_font_size, 117 | fontFamily: theme.font_family), 118 | ), 119 | leading: Icon( 120 | Icons.sd_storage_outlined, 121 | color: sort_helper.by_size_order 122 | ? theme.base_color 123 | : (!theme_controller.is_it_dark() 124 | ? Colors.black 125 | : Colors.white), 126 | ), 127 | onTap: () { 128 | if (this.mounted) { 129 | setState(() { 130 | sort_helper.reverse_order = false; 131 | sort_helper.non_reverse_order = false; 132 | sort_helper.by_size_order = true; 133 | sort_helper.by_date_time = false; 134 | }); 135 | } 136 | rebuilt_list(); 137 | 138 | Navigator.of(context).pop(); 139 | }), 140 | ListTile( 141 | title: Text( 142 | 'Sort by date and time added', 143 | style: TextStyle( 144 | color: sort_helper.by_date_time 145 | ? theme.base_color 146 | : (!theme_controller.is_it_dark() 147 | ? Colors.black 148 | : Colors.white), 149 | fontSize: theme.alert_box_font_size, 150 | fontFamily: theme.font_family), 151 | ), 152 | leading: Icon( 153 | Icons.watch, 154 | color: sort_helper.by_date_time 155 | ? theme.base_color 156 | : (!theme_controller.is_it_dark() 157 | ? Colors.black 158 | : Colors.white), 159 | ), 160 | onTap: () { 161 | if (this.mounted) { 162 | setState(() { 163 | sort_helper.reverse_order = false; 164 | sort_helper.non_reverse_order = false; 165 | sort_helper.by_size_order = false; 166 | sort_helper.by_date_time = true; 167 | }); 168 | } 169 | rebuilt_list(); 170 | 171 | Navigator.of(context).pop(); 172 | }), 173 | ], 174 | ), 175 | ))); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /lib/components/bottom_sheet/ssh_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:deluge_client/database/dbmanager.dart'; 2 | import 'package:deluge_client/sftp_streaming/sftp_explorer.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:deluge_client/control_center/theme.dart'; 5 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 6 | import 'package:deluge_client/settings/deluge/core_settings.dart'; 7 | import 'package:fluttertoast/fluttertoast.dart'; 8 | import 'package:deluge_client/control_center/theme_controller.dart'; 9 | import 'package:deluge_client/settings/deluge/type/sftp_setting_field.dart'; 10 | 11 | class ssh_config extends StatefulWidget { 12 | final String direx; 13 | 14 | final Bucket selected_account; 15 | ssh_config({Key key, @required this.direx, @required this.selected_account}) 16 | : super(key: key); 17 | 18 | @override 19 | _ssh_configState createState() => 20 | _ssh_configState(direx: direx, selected_account: selected_account); 21 | } 22 | 23 | class _ssh_configState extends State { 24 | final String direx; 25 | 26 | final Bucket selected_account; 27 | _ssh_configState( 28 | {Key key, @required this.direx, @required this.selected_account}); 29 | 30 | void update_sftp_settings() async { 31 | states.set_sftp_host(core_settings.sftp_host.text.toString()); 32 | states.set_sftp_pass(core_settings.sftp_pass.text.toString()); 33 | states.set_sftp_port(core_settings.sftpport.text.toString()); 34 | states.set_sftp_route(core_settings.sftp_route_url.text.toString()); 35 | states.set_sftp_username(core_settings.sftp_username.text.toString()); 36 | } 37 | 38 | void toastMessage(String message) { 39 | Fluttertoast.showToast( 40 | msg: message, 41 | toastLength: Toast.LENGTH_SHORT, 42 | gravity: ToastGravity.BOTTOM, 43 | fontSize: 16.0, 44 | backgroundColor: Colors.black, 45 | ); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return SingleChildScrollView( 51 | child: Material( 52 | shape: RoundedRectangleBorder( 53 | borderRadius: new BorderRadius.only( 54 | topRight: Radius.circular(15.0), 55 | topLeft: Radius.circular(15.0)), 56 | ), 57 | child: Container( 58 | padding: EdgeInsets.only( 59 | bottom: MediaQuery.of(context).viewInsets.bottom), 60 | child: Column( 61 | children: [ 62 | Padding( 63 | padding: EdgeInsets.only(top: 3.0), 64 | child: Text("Add SFTP Config", 65 | style: TextStyle( 66 | color: (!theme_controller.is_it_dark() 67 | ? Colors.black 68 | : Colors.white), 69 | fontSize: theme.bottom_sheet_heading_font_size, 70 | fontFamily: theme.font_family))), 71 | Divider( 72 | color: Color.fromRGBO(255, 79, 90, 1), 73 | thickness: 4.0, 74 | indent: 70.0, 75 | endIndent: 70.0, 76 | ), 77 | sftp_settings_fields( 78 | selected_account: selected_account, 79 | ), 80 | Row( 81 | children: [ 82 | Padding( 83 | padding: EdgeInsets.only(left: 5.0), 84 | child: Icon( 85 | Icons.warning, 86 | )), 87 | Flexible( 88 | child: Padding( 89 | padding: EdgeInsets.only(left: 3.0), 90 | child: Text( 91 | "These Settings can be change later, from SFTP settings option", 92 | style: TextStyle(fontFamily: theme.font_family), 93 | )), 94 | ) 95 | ], 96 | ), 97 | ElevatedButton( 98 | style: ButtonStyle( 99 | backgroundColor: 100 | MaterialStateProperty.all(theme.base_color)), 101 | onPressed: () { 102 | update_sftp_settings(); 103 | toastMessage("Added Config"); 104 | Navigator.of(context).pop(); //close bottom sheet 105 | Navigator.push( 106 | context, 107 | MaterialPageRoute( 108 | builder: (context) => files( 109 | direx: direx, 110 | host: core_settings.sftp_host.text 111 | .toString(), 112 | password: core_settings.sftp_pass.text 113 | .toString(), 114 | path: core_settings.sftp_route_url.text 115 | .toString(), 116 | port: core_settings.sftpport.text 117 | .toString(), 118 | username: core_settings 119 | .sftp_username.text 120 | .toString(), 121 | choosen_account: selected_account, 122 | ))); 123 | }, 124 | child: Text("Save Configuration", 125 | style: TextStyle( 126 | fontFamily: theme.font_family, 127 | fontSize: theme.minimal_font_size))) 128 | ], 129 | )))); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/components/download_upload_pane.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'dart:io'; 4 | import 'package:filesize/filesize.dart'; 5 | import "package:flutter/material.dart"; 6 | import 'package:deluge_client/control_center/theme.dart'; 7 | import 'package:deluge_client/api/apis.dart'; 8 | import 'package:deluge_client/control_center/theme_controller.dart'; 9 | import 'package:deluge_client/api/models/torrent_prop.dart'; 10 | 11 | class network_speed extends StatefulWidget { 12 | final String torrent_id; 13 | final List cookie; 14 | final String tor_name; 15 | final String url; 16 | final String is_reverse_proxied; 17 | final String seed_username; 18 | final String seed_pass; 19 | final String qr_auth; 20 | final bool paused; 21 | final bool completed; 22 | const network_speed( 23 | {Key key, 24 | @required this.torrent_id, 25 | this.cookie, 26 | this.tor_name, 27 | this.url, 28 | this.is_reverse_proxied, 29 | this.seed_pass, 30 | this.seed_username, 31 | this.qr_auth, 32 | this.paused, 33 | this.completed}) 34 | : super(key: key); 35 | 36 | @override 37 | network_speedState createState() => network_speedState( 38 | tor_id: torrent_id, 39 | tor_name: tor_name, 40 | cookie: cookie, 41 | url: url, 42 | is_reverse_proxied: is_reverse_proxied, 43 | seed_username: seed_username, 44 | seed_pass: seed_pass, 45 | qr_auth: qr_auth, 46 | paused: paused, 47 | completed: completed); 48 | } 49 | 50 | class network_speedState extends State { 51 | String tor_id; 52 | String tor_name; 53 | List cookie; 54 | final String url; 55 | final String is_reverse_proxied; 56 | final String seed_username; 57 | final String seed_pass; 58 | final String qr_auth; 59 | bool paused; 60 | bool completed; 61 | network_speedState( 62 | {this.tor_id, 63 | this.cookie, 64 | this.tor_name, 65 | this.url, 66 | this.is_reverse_proxied, 67 | this.seed_username, 68 | this.seed_pass, 69 | this.qr_auth, 70 | this.paused, 71 | this.completed}); 72 | int download_speed_in_byte = 0; 73 | int upload_speed_in_byte = 0; 74 | String download_speed = "0.0 KB"; 75 | String upload_speed = "0.0 KB"; 76 | bool stop_listening_speeds = false; 77 | bool have_to_stop = false; 78 | 79 | Future fetch_speed() async { 80 | try { 81 | Map api_output = await apis.get_torrent_list(cookie, 82 | url, is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 83 | if (api_output != null) { 84 | Properties properties = api_output[tor_id]; 85 | if (this.mounted) { 86 | setState(() { 87 | completed = properties.isFinished; 88 | }); 89 | } 90 | 91 | download_speed_in_byte = properties.downloadPayloadRate; 92 | 93 | upload_speed_in_byte = properties.uploadPayloadRate; 94 | print("Download size: " + filesize(download_speed_in_byte)); 95 | if (this.mounted) { 96 | setState(() { 97 | download_speed = filesize(download_speed_in_byte); 98 | upload_speed = filesize(upload_speed_in_byte); 99 | }); 100 | } 101 | 102 | if (completed || have_to_stop) { 103 | stop_listening_speeds = true; 104 | } 105 | } 106 | } 107 | // print(progress_percent); 108 | catch (e) { 109 | print(e); 110 | } 111 | } 112 | 113 | @override 114 | void initState() { 115 | // TODO: implement initState 116 | if (this.mounted) { 117 | setState(() { 118 | paused; 119 | }); 120 | } 121 | trace_down_up_speed(); 122 | 123 | super.initState(); 124 | } 125 | 126 | void trace_down_up_speed() { 127 | if (!completed) { 128 | if (!paused) { 129 | Timer.periodic(Duration(seconds: 1), (timer) { 130 | if (stop_listening_speeds) { 131 | timer.cancel(); 132 | } 133 | if (this.mounted) { 134 | setState(() { 135 | fetch_speed(); 136 | }); 137 | } 138 | }); 139 | } 140 | } 141 | } 142 | 143 | void stop_listening_network_speed() { 144 | have_to_stop = true; 145 | } 146 | 147 | void start_listening_network_speed() { 148 | have_to_stop = false; 149 | stop_listening_speeds = false; 150 | } 151 | 152 | void make_pause() { 153 | paused = true; 154 | } 155 | 156 | void make_resume() { 157 | paused = false; 158 | } 159 | 160 | @override 161 | Widget build(BuildContext context) { 162 | return Row( 163 | children: [ 164 | Icon(Icons.arrow_downward_sharp), 165 | Text( 166 | download_speed + "/S", 167 | style: TextStyle( 168 | color: (!theme_controller.is_it_dark() 169 | ? Colors.black 170 | : Colors.white), 171 | fontSize: 11.0, 172 | fontFamily: theme.font_family), 173 | ), 174 | Icon(Icons.arrow_upward), 175 | Text(upload_speed + "/S", 176 | style: TextStyle( 177 | color: (!theme_controller.is_it_dark() 178 | ? Colors.black 179 | : Colors.white), 180 | fontSize: 11.0, 181 | fontFamily: theme.font_family)), 182 | ], 183 | ); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /lib/components/error_on_dash.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:deluge_client/control_center/theme.dart'; 3 | 4 | class error extends StatefulWidget { 5 | final VoidCallback retry; 6 | final String account_name; 7 | error({Key key, @required this.retry, this.account_name}) : super(key: key); 8 | @override 9 | _errorState createState() => 10 | _errorState(retry: retry, account_name: account_name); 11 | } 12 | 13 | class _errorState extends State { 14 | final VoidCallback retry; 15 | final String account_name; 16 | _errorState({Key key, @required this.retry, this.account_name}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Expanded( 21 | child: SingleChildScrollView( 22 | child: Column( 23 | children: [ 24 | Row( 25 | children: [ 26 | Flexible( 27 | fit: FlexFit.tight, 28 | child: Image.asset( 29 | "assets/server_end_error.png", 30 | height: 300.0, 31 | width: 300.0, 32 | ), 33 | ) 34 | ], 35 | ), 36 | Padding( 37 | padding: EdgeInsets.all(13.0), 38 | child: Container( 39 | child: Column( 40 | children: [ 41 | Text( 42 | "Deluge is not responding.", 43 | style: TextStyle( 44 | fontFamily: theme.font_family, 45 | fontWeight: FontWeight.bold), 46 | ), 47 | account_name != null 48 | ? Text( 49 | account_name, 50 | style: TextStyle( 51 | fontFamily: theme.font_family, 52 | ), 53 | ) 54 | : new Container( 55 | height: 0.0, 56 | width: 0.0, 57 | ), 58 | Divider( 59 | color: theme.base_color, 60 | thickness: 4.0, 61 | indent: 55.0, 62 | endIndent: 55.0, 63 | ), 64 | ], 65 | ))), 66 | Row( 67 | children: [ 68 | Icon(Icons.insights_outlined), 69 | Flexible( 70 | fit: FlexFit.tight, 71 | child: Padding( 72 | padding: EdgeInsets.only(left: 20.0), 73 | child: Text( 74 | "If you have just installed deluge at your remote location, you may need to log in on the web ui once.", 75 | style: TextStyle(fontFamily: theme.font_family)))) 76 | ], 77 | ), 78 | Row( 79 | children: [ 80 | Icon(Icons.insights_outlined), 81 | Flexible( 82 | fit: FlexFit.tight, 83 | child: Padding( 84 | padding: EdgeInsets.only(left: 20.0), 85 | child: Text("Deluge may be down.", 86 | style: TextStyle(fontFamily: theme.font_family)))) 87 | ], 88 | ), 89 | Row( 90 | children: [ 91 | Icon(Icons.insights_outlined), 92 | Flexible( 93 | fit: FlexFit.tight, 94 | child: Padding( 95 | padding: EdgeInsets.only(left: 20.0), 96 | child: Text( 97 | "Deluge is taking time to respond, May be some internal problem.", 98 | style: TextStyle(fontFamily: theme.font_family)))) 99 | ], 100 | ), 101 | Row( 102 | children: [ 103 | Icon(Icons.insights_outlined), 104 | Flexible( 105 | fit: FlexFit.tight, 106 | child: Padding( 107 | padding: EdgeInsets.only(left: 20.0), 108 | child: Text( 109 | "It may also possible that Deluge is working but it is taking time to respond.", 110 | style: TextStyle(fontFamily: theme.font_family)))) 111 | ], 112 | ), 113 | ElevatedButton( 114 | style: ButtonStyle( 115 | elevation: MaterialStateProperty.all(5.0), 116 | backgroundColor: MaterialStateProperty.all(Colors.red)), 117 | onPressed: () { 118 | retry(); 119 | }, 120 | child: Text("Retry", 121 | style: TextStyle( 122 | fontFamily: theme.font_family, color: Colors.white)), 123 | ) 124 | ], 125 | ))); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/components/licence.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class license extends StatefulWidget { 4 | @override 5 | _licenseState createState() => _licenseState(); 6 | } 7 | 8 | class _licenseState extends State { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Text("""MIT License 12 | 13 | Copyright (c) 2021 CCExtractor Development 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | 33 | 34 | """); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/components/loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:deluge_client/control_center/theme.dart'; 3 | 4 | class loader extends StatefulWidget { 5 | @override 6 | _loaderState createState() => _loaderState(); 7 | } 8 | 9 | class _loaderState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return SizedBox( 13 | height: 200, 14 | width: 100, 15 | child: Center( 16 | child: CircularProgressIndicator( 17 | valueColor: new AlwaysStoppedAnimation(theme.base_color), 18 | ))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/no_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class no_data extends StatefulWidget { 4 | @override 5 | _no_dataState createState() => _no_dataState(); 6 | } 7 | 8 | class _no_dataState extends State { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Image.asset( 13 | 'assets/no_data.png', 14 | ), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/components/progress_bar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:deluge_client/control_center/theme.dart'; 4 | import "package:flutter/material.dart"; 5 | import 'package:percent_indicator/linear_percent_indicator.dart'; 6 | import 'package:deluge_client/api/apis.dart'; 7 | import 'package:deluge_client/notification/notification_controller.dart'; 8 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 9 | import 'package:deluge_client/api/models/torrent_prop.dart'; 10 | 11 | class download_progress extends StatefulWidget { 12 | final String torrent_id; 13 | final List cookie; 14 | final String tor_name; 15 | final double initial_progress; 16 | //-- 17 | final String url; 18 | final String is_reverse_proxied; 19 | final String seed_username; 20 | final String seed_pass; 21 | final String qr_auth; 22 | final bool paused; 23 | final bool completed; 24 | final Function(bool) update_completion_state; 25 | final Function(bool) update_seeding_state; 26 | //-------------asking refresh function so we can refresh if it get download 27 | final VoidCallback refresh_list; 28 | const download_progress( 29 | {Key key, 30 | @required this.torrent_id, 31 | @required this.cookie, 32 | @required this.tor_name, 33 | @required this.url, 34 | @required this.is_reverse_proxied, 35 | @required this.seed_username, 36 | @required this.seed_pass, 37 | @required this.qr_auth, 38 | @required this.paused, 39 | @required this.initial_progress, 40 | @required this.update_completion_state, 41 | @required this.completed, 42 | @required this.refresh_list, 43 | @required this.update_seeding_state}) 44 | : super(key: key); 45 | 46 | @override 47 | download_progressState createState() => download_progressState( 48 | tor_id: torrent_id, 49 | tor_name: tor_name, 50 | cookie: cookie, 51 | url: url, 52 | is_reverse_proxied: is_reverse_proxied, 53 | seed_username: seed_username, 54 | seed_pass: seed_pass, 55 | qr_auth: qr_auth, 56 | paused: paused, 57 | initial_progress: initial_progress, 58 | update_completion_state: update_completion_state, 59 | completed: completed, 60 | refresh: refresh_list, 61 | update_seeding_state: update_seeding_state); 62 | } 63 | 64 | class download_progressState extends State { 65 | String tor_id; 66 | String tor_name; 67 | List cookie; 68 | final double initial_progress; 69 | final Function(bool) update_completion_state; 70 | final Function(bool) update_seeding_state; 71 | //-- 72 | bool completed; 73 | 74 | final String url; 75 | final String is_reverse_proxied; 76 | final String seed_username; 77 | final String seed_pass; 78 | final String qr_auth; 79 | bool paused; 80 | final VoidCallback refresh; 81 | bool seeding = false; 82 | download_progressState( 83 | {this.tor_id, 84 | this.cookie, 85 | this.tor_name, 86 | this.url, 87 | this.is_reverse_proxied, 88 | this.seed_username, 89 | this.seed_pass, 90 | this.qr_auth, 91 | this.paused, 92 | this.initial_progress, 93 | this.update_completion_state, 94 | this.completed, 95 | this.refresh, 96 | this.update_seeding_state}); 97 | double progress_percent = 0; 98 | //----------------- 99 | bool stop_listening_progress = false; 100 | bool needs_to_stop_listening = false; 101 | void get_status() async { 102 | try { 103 | Map api_output = await apis.get_torrent_list(cookie, 104 | url, is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 105 | 106 | if (api_output != null) { 107 | Properties content = await api_output[tor_id]; 108 | double middletemp = content.progress; 109 | 110 | double temp = (middletemp / 100.0); 111 | print(temp); 112 | seeding = content.isSeed; 113 | 114 | if (this.mounted) { 115 | setState(() { 116 | progress_percent = temp; 117 | completed = content.isFinished; 118 | }); 119 | } 120 | 121 | update_completion_state(completed); 122 | update_seeding_state(seeding); 123 | 124 | //--------------------------notification 125 | if (notification_status == true) { 126 | notification.store_ids.add(tor_id); 127 | int idt = notification.fetch_noti_id(tor_id); 128 | notification.notification_on_progress( 129 | tor_id, 130 | idt > 0 ? idt : 0, 131 | "torrents", 132 | tor_name, 133 | (progress_percent * 100).roundToDouble().toString() + " %", 134 | progress_percent.toInt() * 100); 135 | } 136 | 137 | if (temp == 1.0 || needs_to_stop_listening) { 138 | stop_listening_progress = true; 139 | } 140 | } 141 | } catch (e) { 142 | print(e); 143 | } 144 | } 145 | 146 | int progress_fire = 0; 147 | bool notification_status; 148 | @override 149 | void initState() { 150 | // TODO: implement initState 151 | if (this.mounted) { 152 | setState(() { 153 | paused; 154 | }); 155 | } 156 | handle_first_progress(); 157 | trace_download_progress_bar(); 158 | 159 | //---------------------- 160 | fetch_notification_settings(); 161 | 162 | super.initState(); 163 | } 164 | 165 | void fetch_notification_settings() async { 166 | bool mid = await states.fetch_notification_settings(); 167 | setState(() { 168 | notification_status = mid; 169 | }); 170 | } 171 | 172 | void handle_first_progress() { 173 | double temp = initial_progress / 100.0; 174 | 175 | if (this.mounted) { 176 | setState(() { 177 | progress_percent = temp; 178 | }); 179 | } 180 | } 181 | 182 | void trace_download_progress_bar() { 183 | if (!completed) { 184 | if (!paused) { 185 | Timer.periodic(Duration(seconds: 1), (timer) { 186 | if (stop_listening_progress) { 187 | timer.cancel(); 188 | } 189 | 190 | if (this.mounted) { 191 | setState(() { 192 | get_status(); 193 | }); 194 | } 195 | }); 196 | } 197 | } 198 | } 199 | 200 | void stop_listening_progress_bar() { 201 | needs_to_stop_listening = true; 202 | } 203 | 204 | void start_listening_progress_bar() { 205 | needs_to_stop_listening = false; 206 | stop_listening_progress = false; 207 | } 208 | 209 | void make_pause() { 210 | paused = true; 211 | } 212 | 213 | void make_resume() { 214 | paused = false; 215 | } 216 | 217 | //Text(tor_id == null ? "" : tor_id); 218 | @override 219 | Widget build(BuildContext context) { 220 | return Expanded( 221 | child: Container( 222 | padding: EdgeInsets.all(15.0), 223 | child: new LinearPercentIndicator( 224 | width: MediaQuery.of(context).size.width - 120.0, 225 | animation: false, 226 | lineHeight: 15.0, 227 | animationDuration: 2000, 228 | percent: progress_percent, 229 | center: Text( 230 | (progress_percent * 100).toString().length >= 5 231 | ? (progress_percent * 100).toString().substring(0, 5) + " %" 232 | : (progress_percent * 100).roundToDouble().toString() + " %", 233 | style: TextStyle( 234 | color: Colors.white, 235 | fontFamily: theme.font_family, 236 | fontSize: 11.0), 237 | ), 238 | linearStrokeCap: LinearStrokeCap.roundAll, 239 | progressColor: (progress_percent * 100).roundToDouble() == 100.0 240 | ? theme.base_color 241 | : Colors.green, 242 | ), 243 | )); 244 | } 245 | } 246 | 247 | //---------------------------------------------------- 248 | -------------------------------------------------------------------------------- /lib/components/storage_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:deluge_client/database/dbmanager.dart'; 4 | import 'package:filesize/filesize.dart'; 5 | import "package:flutter/material.dart"; 6 | import 'package:deluge_client/control_center/theme.dart'; 7 | import 'package:deluge_client/api/apis.dart'; 8 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 9 | 10 | import '../string/controller.dart'; 11 | import 'package:deluge_client/control_center/theme_controller.dart'; 12 | 13 | class storage_indicator extends StatefulWidget { 14 | List cookie; 15 | final BuildContext context; 16 | storage_indicator({Key key, @required this.cookie, @required this.context}) 17 | : super(key: key); 18 | @override 19 | _storage_indicatorState createState() => 20 | _storage_indicatorState(cookie: cookie, context: context); 21 | } 22 | 23 | class _storage_indicatorState extends State { 24 | List cookie; 25 | final BuildContext context; 26 | _storage_indicatorState( 27 | {Key key, @required this.cookie, @required this.context}); 28 | 29 | DbbucketManager account_manager = DbbucketManager(); 30 | int selected_account = 0; 31 | Bucket selx_acc; 32 | 33 | void fetch_selectx_account() async { 34 | int mid = await states.state_selected_account(); 35 | 36 | selected_account = mid; 37 | if (selected_account > 0) { 38 | selx_acc = await account_manager.get_acc_by_id(selected_account); 39 | } 40 | } 41 | 42 | void get_config() async { 43 | Future.delayed(Duration(seconds: 1), () async { 44 | Map conf = await apis.fetch_config( 45 | cookie != null 46 | ? cookie 47 | : await cookie_substitue( 48 | selx_acc.deluge_url, 49 | selx_acc.deluge_pwrd, 50 | selx_acc.has_deluge_pwrd, 51 | selx_acc.is_reverse_proxied, 52 | selx_acc.username, 53 | selx_acc.password, 54 | selx_acc.via_qr), 55 | selx_acc.deluge_url, 56 | selx_acc.is_reverse_proxied, 57 | selx_acc.username, 58 | selx_acc.password, 59 | selx_acc.via_qr, 60 | context); 61 | if (this.mounted) { 62 | setState(() { 63 | controller.path_controller = conf['result']['download_location']; 64 | }); 65 | } 66 | }); 67 | } 68 | 69 | void get_path_size() async { 70 | Future.delayed(Duration(seconds: 1), () async { 71 | int free_space = await apis.fetch_free_space( 72 | cookie != null 73 | ? cookie 74 | : await cookie_substitue( 75 | selx_acc.deluge_url, 76 | selx_acc.deluge_pwrd, 77 | selx_acc.has_deluge_pwrd, 78 | selx_acc.is_reverse_proxied, 79 | selx_acc.username, 80 | selx_acc.password, 81 | selx_acc.via_qr), 82 | controller.path_controller, 83 | selx_acc.deluge_url, 84 | selx_acc.is_reverse_proxied, 85 | selx_acc.username, 86 | selx_acc.password, 87 | selx_acc.via_qr, 88 | context); 89 | if (this.mounted) { 90 | setState(() { 91 | controller.storage_controller = filesize(free_space).toString(); 92 | }); 93 | } 94 | }); 95 | } 96 | 97 | void initState() { 98 | fetch_selectx_account(); 99 | 100 | get_config(); 101 | get_path_size(); 102 | super.initState(); 103 | } 104 | 105 | Future> cookie_substitue( 106 | String url, 107 | String password, 108 | String has_deluge_pass, 109 | String is_reverse_proxied, 110 | String seed_username, 111 | String seed_pass, 112 | String qr_auth) async { 113 | cookie = await apis.authentication_to_deluge(url, password, has_deluge_pass, 114 | is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 115 | 116 | print("it is executed"); 117 | print(cookie); 118 | 119 | return cookie; 120 | } 121 | 122 | @override 123 | Widget build(BuildContext context) { 124 | return Row( 125 | children: [ 126 | Flexible( 127 | fit: FlexFit.tight, 128 | child: Container( 129 | child: ListTile( 130 | dense: true, 131 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 132 | title: Text(controller.path_controller, 133 | style: TextStyle( 134 | fontSize: 12.0, 135 | fontFamily: theme.font_family, 136 | color: theme_controller.is_it_dark() 137 | ? Colors.white 138 | : Colors.black)), 139 | subtitle: Text( 140 | controller.storage_controller + " " + "Available ", 141 | style: theme.sidebar_tile_style, 142 | ), 143 | leading: Icon(Icons.sd_storage_sharp), 144 | ))) 145 | ], 146 | ); 147 | } 148 | } 149 | 150 | //---------------------------------------------------- 151 | -------------------------------------------------------------------------------- /lib/control_center/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class theme { 4 | static String thread_title = "Deluge Mobile client"; 5 | static Color material_color = Colors.red; 6 | static Color base_color = Color.fromRGBO(241, 94, 90, 1.0); 7 | static String font_family = "SFUIDisplay"; 8 | static double heading_font_size = 20.0; 9 | static double minimal_font_size = 15.0; 10 | static double alert_box_font_size = 18.0; 11 | static double bottom_sheet_heading_font_size = 21.0; 12 | static TextStyle logo_text_style = TextStyle( 13 | fontFamily: font_family, 14 | fontSize: heading_font_size, 15 | fontWeight: FontWeight.bold, 16 | color: Colors.black); 17 | static TextStyle sidebar_tile_style = 18 | TextStyle(fontFamily: font_family, fontSize: 15.0, color: base_color); 19 | 20 | static double children_expension_tile_font_size = 12.0; 21 | static TextStyle app_bar_style = TextStyle( 22 | fontSize: 20.0, fontFamily: theme.font_family, color: Colors.white); 23 | static TextStyle warning_style = TextStyle( 24 | fontFamily: theme.font_family, fontSize: 15.0, color: Colors.white); 25 | } 26 | -------------------------------------------------------------------------------- /lib/control_center/theme_changer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:deluge_client/control_center/theme_controller.dart'; 3 | 4 | class ThemeBuilder extends StatefulWidget { 5 | final Widget Function(BuildContext context, Brightness brightness) builder; 6 | 7 | ThemeBuilder({this.builder}); 8 | 9 | @override 10 | _ThemeBuilderState createState() => _ThemeBuilderState(); 11 | 12 | static _ThemeBuilderState of(BuildContext context) { 13 | return context.findAncestorStateOfType(); 14 | } 15 | } 16 | 17 | class _ThemeBuilderState extends State { 18 | Brightness extracted_current_theme; 19 | @override 20 | void initState() { 21 | super.initState(); 22 | fetch_current_theme(); 23 | } 24 | 25 | void fetch_current_theme() async { 26 | extracted_current_theme = await theme_controller.get_set_theme(); 27 | if (mounted) { 28 | setState(() { 29 | theme_controller.brightness = extracted_current_theme; 30 | }); 31 | } 32 | } 33 | 34 | void changeTheme() { 35 | setState(() { 36 | theme_controller.brightness = 37 | theme_controller.brightness == Brightness.dark 38 | ? Brightness.light 39 | : Brightness.dark; 40 | }); 41 | } 42 | 43 | void set_theme(Brightness br) { 44 | theme_controller.brightness = br; 45 | } 46 | 47 | Brightness getCurrentTheme() { 48 | return theme_controller.brightness; 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return widget.builder(context, theme_controller.brightness); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/control_center/theme_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 3 | 4 | class theme_controller { 5 | static Brightness brightness; 6 | static bool is_it_dark() { 7 | return theme_controller.brightness == Brightness.dark; 8 | } 9 | 10 | static List brt_set = [ 11 | Brightness.light, 12 | Brightness.dark, 13 | ]; 14 | //-------------------------------------------------------------------------- 15 | static Future get_set_theme() async { 16 | int index = await states.get_theme_mode(); 17 | 18 | switch (index) { 19 | case 1: 20 | return brt_set[0]; 21 | case 2: 22 | return brt_set[1]; 23 | default: 24 | return brt_set[0]; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/core/all_acc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:deluge_client/database/dbmanager.dart'; 4 | import 'package:deluge_client/api/apis.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:deluge_client/api/models/torrent_prop.dart'; 7 | 8 | class all_account_core { 9 | static final DbbucketManager db = new DbbucketManager(); 10 | 11 | static Map all_account_cookie = Map(); 12 | static Future> config_cache(BuildContext context) async { 13 | List acc = await db.getbucketitem(); 14 | for (int i = 0; i < acc.length; i++) { 15 | apis 16 | .authentication_to_deluge( 17 | acc[i].deluge_url, 18 | acc[i].deluge_pwrd, 19 | acc[i].has_deluge_pwrd, 20 | acc[i].is_reverse_proxied, 21 | acc[i].username, 22 | acc[i].password, 23 | acc[i].via_qr, 24 | context) 25 | .then((List t) => { 26 | print(t), 27 | if (t != null) 28 | { 29 | // print(all_account_cookie.runtimeType), 30 | all_account_cookie.putIfAbsent(acc[i].deluge_url, () => t), 31 | } 32 | }); 33 | } 34 | 35 | return all_account_cookie; 36 | } 37 | 38 | static String error_account = ""; 39 | 40 | static Map output = Map(); 41 | static Future> config_torrent_list( 42 | Map cookies, BuildContext context) async { 43 | List acc = await db.getbucketitem(); 44 | // @todo i need to add that already logged in account can not logged in to make unique 45 | print("before it have :" + output.length.toString()); 46 | print("its content is:" + output.toString()); 47 | if (output.length > 0) { 48 | output.clear(); 49 | } 50 | for (int i = 0; i < acc.length; i++) { 51 | List cke = cookies[acc[i].deluge_url]; 52 | 53 | Map t = await apis.get_torrent_list( 54 | cke, 55 | acc[i].deluge_url, 56 | acc[i].is_reverse_proxied, 57 | acc[i].username, 58 | acc[i].password, 59 | acc[i].via_qr, 60 | context); 61 | 62 | print(t); 63 | if (t == null) { 64 | error_account = acc[i].deluge_url.toString(); 65 | } 66 | // todo i will add url with the hash of map 67 | for (int j = 0; j < t.length; j++) { 68 | String hash = t.keys.elementAt(j); 69 | Properties cont = t[hash]; 70 | output.putIfAbsent(multtorrent(hash: hash, need: acc[i]), () => cont); 71 | } 72 | } 73 | 74 | return output; 75 | } 76 | } 77 | 78 | class multtorrent { 79 | String hash; 80 | Bucket need; 81 | multtorrent({this.hash, this.need}); 82 | } 83 | -------------------------------------------------------------------------------- /lib/core/auth_valid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class auth_valid { 4 | int valid; 5 | List cookie; 6 | auth_valid({this.valid, this.cookie}); 7 | } 8 | -------------------------------------------------------------------------------- /lib/database/dbmanager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:path/path.dart'; 4 | import 'package:sqflite/sqflite.dart'; 5 | 6 | class DbbucketManager { 7 | Database _database; 8 | 9 | Future openDb() async { 10 | if (_database == null) { 11 | _database = await openDatabase(join(await getDatabasesPath(), "auth.db"), 12 | version: 1, onCreate: (Database db, int version) async { 13 | await db.execute( 14 | "CREATE TABLE bucket(id INTEGER PRIMARY KEY autoincrement, deluge_url TEXT ,has_deluge_pwrd TEXT,deluge_pwrd TEXT,is_reverse_proxied TEXT,via_qr Text, username TEXT,password TEXT)", 15 | ); 16 | }); 17 | } 18 | } 19 | 20 | Future insertbucket(Bucket bucket) async { 21 | await openDb(); 22 | return await _database.insert('bucket', bucket.toMap()); 23 | } 24 | 25 | Future> getbucketitem() async { 26 | await openDb(); 27 | final List> maps = await _database.query('bucket'); 28 | return List.generate(maps.length, (i) { 29 | return Bucket( 30 | id: maps[i]['id'], 31 | deluge_url: maps[i]['deluge_url'], 32 | has_deluge_pwrd: maps[i]['has_deluge_pwrd'], 33 | deluge_pwrd: maps[i]['deluge_pwrd'], 34 | is_reverse_proxied: maps[i]['is_reverse_proxied'], 35 | username: maps[i]['username'], 36 | password: maps[i]['password'], 37 | via_qr: maps[i]['via_qr'], 38 | ); 39 | }); 40 | } 41 | 42 | Future updatebucket(Bucket bucket) async { 43 | await openDb(); 44 | return await _database.update('bucket', bucket.toMap(), 45 | where: "id = ?", whereArgs: [bucket.id]); 46 | } 47 | 48 | Future deletebucket(int id) async { 49 | await openDb(); 50 | await _database.delete('bucket', // it is db name 51 | where: "id = ?", 52 | whereArgs: [id]); 53 | } 54 | 55 | //-- 56 | Future get_acc_by_id(int id) async { 57 | await openDb(); 58 | final List> maps = 59 | await _database.query('bucket', where: "id=?", whereArgs: [id]); 60 | 61 | return Bucket( 62 | id: maps[0]['id'], 63 | deluge_url: maps[0]['deluge_url'], 64 | has_deluge_pwrd: maps[0]['has_deluge_pwrd'], 65 | deluge_pwrd: maps[0]['deluge_pwrd'], 66 | is_reverse_proxied: maps[0]['is_reverse_proxied'], 67 | username: maps[0]['username'], 68 | password: maps[0]['password'], 69 | via_qr: maps[0]['via_qr'], 70 | ); 71 | } 72 | 73 | Future delete_table() async { 74 | await openDb(); 75 | await _database.execute("DROP TABLE bucket"); 76 | await _database.execute( 77 | "CREATE TABLE bucket(id INTEGER PRIMARY KEY autoincrement, deluge_url TEXT ,has_deluge_pwrd TEXT,deluge_pwrd TEXT,is_reverse_proxied TEXT,via_qr Text, username TEXT,password TEXT)"); 78 | } 79 | } //root class 80 | 81 | class Bucket { 82 | int id; 83 | String deluge_url; 84 | String deluge_pwrd; 85 | String has_deluge_pwrd; 86 | String is_reverse_proxied; 87 | String via_qr; 88 | String username; 89 | String password; 90 | 91 | Bucket( 92 | {@required this.deluge_url, 93 | @required this.deluge_pwrd, 94 | this.id, 95 | @required this.has_deluge_pwrd, 96 | @required this.is_reverse_proxied, 97 | @required this.username, 98 | @required this.password, 99 | @required this.via_qr}); 100 | Map toMap() { 101 | //return {'name': name, 'course': course}; 102 | return { 103 | 'id': id, 104 | 'deluge_url': deluge_url, 105 | 'has_deluge_pwrd': has_deluge_pwrd, 106 | 'deluge_pwrd': deluge_pwrd, 107 | 'is_reverse_proxied': is_reverse_proxied, 108 | 'via_qr': via_qr, 109 | 'username': username, 110 | 'password': password 111 | }; 112 | } 113 | } 114 | //------------- 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:deluge_client/control_center/theme_changer.dart'; 3 | 4 | import 'package:deluge_client/screens/splash.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:deluge_client/control_center/theme.dart'; 7 | import 'package:flutter_phoenix/flutter_phoenix.dart'; 8 | 9 | 10 | void main() { 11 | runApp( 12 | Phoenix(child: root()), 13 | ); 14 | } 15 | 16 | class MyHttpOverrides extends HttpOverrides { 17 | @override 18 | HttpClient createHttpClient(SecurityContext context) { 19 | return super.createHttpClient(context) 20 | ..badCertificateCallback = 21 | (X509Certificate cert, String host, int port) => true; 22 | } 23 | } 24 | 25 | class root extends StatefulWidget { 26 | @override 27 | _rootState createState() => _rootState(); 28 | } 29 | 30 | class _rootState extends State { 31 | @override 32 | Widget build(BuildContext context) { 33 | return ThemeBuilder(builder: (context, _brightness) { 34 | return MaterialApp( 35 | title: theme.thread_title, 36 | theme: ThemeData( 37 | primarySwatch: theme.material_color, brightness: _brightness), 38 | home: splash(), 39 | ); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/notification/notification_controller.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 3 | 4 | class notification { 5 | static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = 6 | FlutterLocalNotificationsPlugin(); 7 | static void init() { 8 | //------------------- 9 | var initializationSettingsAndroid = 10 | AndroidInitializationSettings('ic_launcher'); 11 | var initializationSettingsIOs = IOSInitializationSettings(); 12 | var initSetttings = InitializationSettings( 13 | android: initializationSettingsAndroid, iOS: initializationSettingsIOs); 14 | 15 | flutterLocalNotificationsPlugin.initialize(initSetttings); 16 | } 17 | 18 | static Future notification_on_progress( 19 | String id, 20 | int identity_container, 21 | String progress_channel, 22 | String title, 23 | String msg, 24 | int percent) async { 25 | await Future.delayed(const Duration(seconds: 1), () async { 26 | final AndroidNotificationDetails androidPlatformChannelSpecifics = 27 | AndroidNotificationDetails( 28 | id, progress_channel, 'progress channel description', 29 | channelShowBadge: false, 30 | importance: Importance.max, 31 | priority: Priority.high, 32 | onlyAlertOnce: true, 33 | showProgress: true, 34 | ongoing: true, 35 | maxProgress: 100, 36 | progress: percent); 37 | final NotificationDetails platformChannelSpecifics = 38 | NotificationDetails(android: androidPlatformChannelSpecifics); 39 | await flutterLocalNotificationsPlugin.show( 40 | identity_container, title, msg, platformChannelSpecifics); 41 | }); 42 | } 43 | 44 | //--------------------------------------------------------------- 45 | 46 | //---------------------------------------------------------- 47 | static List store_ids = List(); 48 | static int fetch_noti_id(String hash) { 49 | return store_ids.indexOf(hash); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/screens/auth_qr.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:deluge_client/database/dbmanager.dart'; 3 | import 'package:deluge_client/screens/dashboard.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:deluge_client/control_center/theme.dart'; 6 | import 'package:fluttertoast/fluttertoast.dart'; 7 | import 'package:qr_code_scanner/qr_code_scanner.dart'; 8 | 9 | import 'package:deluge_client/api/apis.dart'; 10 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 11 | import 'package:deluge_client/core/auth_valid.dart'; 12 | 13 | class auth_qr extends StatefulWidget { 14 | final bool tow_attachment; 15 | auth_qr({Key key, this.tow_attachment}) : super(key: key); 16 | @override 17 | _auth_qrState createState() => _auth_qrState(tow_attachment: tow_attachment); 18 | } 19 | 20 | class _auth_qrState extends State { 21 | final bool tow_attachment; 22 | _auth_qrState({this.tow_attachment}); 23 | //---------------------------------------------------------- 24 | final DbbucketManager dbmanager = new DbbucketManager(); 25 | 26 | bool has_deluge_pass = false; 27 | 28 | void add_in_db(String url, String qr_auth) { 29 | Bucket item = new Bucket( 30 | deluge_url: "https://" + url, 31 | has_deluge_pwrd: false.toString(), 32 | deluge_pwrd: "", 33 | is_reverse_proxied: true.toString(), 34 | username: "", 35 | password: "", 36 | via_qr: qr_auth, 37 | ); 38 | dbmanager.insertbucket(item).then((id) => { 39 | print('item Added to Db ${id}'), 40 | }); 41 | } 42 | 43 | void set_auth_state() async { 44 | states.set_auth(); 45 | } 46 | 47 | //--------- 48 | //- 49 | void toastMessage(String message) { 50 | Fluttertoast.showToast( 51 | msg: message, 52 | toastLength: Toast.LENGTH_SHORT, 53 | gravity: ToastGravity.BOTTOM, 54 | fontSize: 16.0, 55 | backgroundColor: Colors.black, 56 | ); 57 | } 58 | 59 | int count = 0; 60 | void check_validity(String url, String auth_qr) async { 61 | auth_valid validity = await apis.auth_validity("https://" + url, "", 62 | false.toString(), true.toString(), "", "", auth_qr); 63 | 64 | if (validity.valid == 1) { 65 | controller.stopCamera(); 66 | add_in_db(url, auth_qr); 67 | set_auth_state(); 68 | 69 | !tow_attachment 70 | ? toastMessage("Successfully Authorized") 71 | : toastMessage("Successfully added"); 72 | !tow_attachment 73 | ? Navigator.pushReplacement( 74 | context, MaterialPageRoute(builder: (context) => dashboard())) 75 | : Navigator.popUntil(context, (route) { 76 | return count++ == 1; 77 | }); 78 | } else if (validity.valid == 0) { 79 | toastMessage("Credentials are wrong"); 80 | } else if (validity.valid == -1) { 81 | toastMessage("Deluge is not responding, May be it is down"); 82 | } else if (validity.valid == -11) { 83 | toastMessage("Deluge is not reachable"); 84 | } else if (validity.valid == -2) { 85 | toastMessage("Seedbox authentification failed."); 86 | } else { 87 | toastMessage("Something goes wrong"); 88 | } 89 | } 90 | 91 | // @override 92 | // void initState() { 93 | // states.first_time_setup_selection(); 94 | // super.initState(); 95 | // } //it will be fired from the auth screen on have qr button click 96 | 97 | Barcode result; 98 | QRViewController controller; 99 | final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); 100 | // In order to get hot reload to work we need to pause the camera if the platform 101 | // is android, or resume the camera if the platform is iOS. 102 | // now no need to do this 103 | // @override 104 | // void reassemble() { 105 | // super.reassemble(); 106 | // if (Platform.isAndroid) { 107 | // controller.pauseCamera(); 108 | // } 109 | // controller.resumeCamera(); 110 | // } 111 | @override 112 | void dispose() { 113 | controller?.dispose(); 114 | super.dispose(); 115 | } 116 | 117 | List creds; 118 | 119 | @override 120 | Widget build(BuildContext context) { 121 | return Scaffold( 122 | appBar: AppBar( 123 | title: Text( 124 | "Scan to setup", 125 | style: theme.app_bar_style, 126 | ), 127 | elevation: 0.0, 128 | backgroundColor: Color.fromRGBO(241, 94, 90, 1.0), 129 | ), 130 | body: SingleChildScrollView( 131 | child: Column(children: [ 132 | Row( 133 | mainAxisAlignment: MainAxisAlignment.center, 134 | children: [ 135 | Flexible( 136 | fit: FlexFit.tight, 137 | child: Container( 138 | color: Colors.transparent, 139 | height: MediaQuery.of(context).orientation == 140 | Orientation.portrait 141 | ? MediaQuery.of(context).size.height - 250.0 142 | : MediaQuery.of(context).size.height - 200.0, 143 | child: _build_qr(context), 144 | )), 145 | ], 146 | ), 147 | //-------------------------------------------------------- 148 | //--- 149 | Padding( 150 | padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32), 151 | child: Container( 152 | width: double.infinity, 153 | child: ElevatedButton( 154 | style: ElevatedButton.styleFrom( 155 | shape: RoundedRectangleBorder( 156 | borderRadius: BorderRadius.circular(5.0), 157 | side: BorderSide(color: Color.fromRGBO(241, 94, 90, 1.0)), 158 | ), 159 | primary: Colors.white, 160 | ), 161 | child: Padding( 162 | padding: 163 | const EdgeInsets.symmetric(horizontal: 28, vertical: 16), 164 | child: Text( 165 | !tow_attachment 166 | ? 'Scan to get started' 167 | : 'Scan to Add account', 168 | style: TextStyle( 169 | color: theme.base_color, 170 | fontSize: 18, 171 | fontFamily: theme.font_family), 172 | ), 173 | ), 174 | onPressed: () { 175 | if (result != null) { 176 | creds = (result.code.toString().split('\nP')); 177 | check_validity(creds[0], creds[1].replaceFirst('\n', '')); 178 | } else { 179 | toastMessage("Qr code is invalid"); 180 | } 181 | // if (url.text.length > 0) { 182 | // // check_validity( 183 | // // url.text, 184 | // // pass.text, 185 | // // has_deluge_pass.toString(), 186 | // // isreverse_proxied.toString(), 187 | // // username.text, 188 | // // seed_pass.text); 189 | // } else { 190 | // toastMessage("Please enter location"); 191 | // } 192 | }, 193 | ), 194 | ), 195 | ) 196 | 197 | //--- 198 | 199 | // this column belongs to this container 200 | ]))); 201 | } 202 | 203 | //--------- 204 | Widget _build_qr(BuildContext context) { 205 | // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. 206 | var scanArea = (MediaQuery.of(context).size.width < 400 || 207 | MediaQuery.of(context).size.height < 400) 208 | ? 150.0 209 | : 300.0; 210 | // To ensure the Scanner view is properly sizes after rotation 211 | // we need to listen for Flutter SizeChanged notification and update controller 212 | return QRView( 213 | key: qrKey, 214 | onQRViewCreated: _onQRViewCreated, 215 | overlay: QrScannerOverlayShape( 216 | borderColor: theme.base_color, 217 | borderRadius: 10, 218 | borderLength: 30, 219 | borderWidth: 10, 220 | cutOutSize: scanArea), 221 | ); 222 | } 223 | 224 | void _onQRViewCreated(QRViewController controller) { 225 | if (this.mounted) { 226 | setState(() { 227 | this.controller = controller; 228 | }); 229 | } 230 | controller.scannedDataStream.listen((scanData) { 231 | if (this.mounted) { 232 | setState(() { 233 | result = scanData; 234 | }); 235 | } 236 | }); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /lib/screens/splash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:deluge_client/screens/auth.dart'; 4 | import 'package:deluge_client/screens/dashboard.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:deluge_client/control_center/theme.dart'; 7 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 8 | 9 | class splash extends StatefulWidget { 10 | @override 11 | _splashState createState() => _splashState(); 12 | } 13 | 14 | class _splashState extends State { 15 | bool is_auth = false; 16 | startTime() async { 17 | var _duration = new Duration(seconds: 4); 18 | return new Timer(_duration, navigationPage); 19 | } 20 | 21 | void navigationPage() { 22 | if (is_auth == false) { 23 | Navigator.pushReplacement( 24 | context, 25 | MaterialPageRoute( 26 | builder: (context) => auth_view( 27 | tow_attachment: false, 28 | ))); 29 | } else { 30 | Navigator.pushReplacement( 31 | context, MaterialPageRoute(builder: (context) => dashboard())); 32 | } 33 | } 34 | 35 | void fetch_is_auth() async { 36 | bool ctxt_state = await states.state_is_auth_fetch(); 37 | if (ctxt_state == null) { 38 | is_auth = false; 39 | } else if (ctxt_state == false) { 40 | is_auth = false; 41 | } else { 42 | is_auth = true; 43 | } 44 | } 45 | 46 | @override 47 | void initState() { 48 | // TODO: implement initState 49 | fetch_is_auth(); 50 | startTime(); 51 | 52 | super.initState(); 53 | } 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return Scaffold( 58 | backgroundColor: theme.base_color, 59 | body: Center( 60 | child: Column( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | Image.asset( 64 | "assets/animation.gif", 65 | height: 350.0, 66 | ), 67 | Row( 68 | mainAxisAlignment: MainAxisAlignment.center, 69 | children: [ 70 | Flexible( 71 | child: Container( 72 | decoration: BoxDecoration( 73 | color: Colors.white, 74 | borderRadius: 75 | BorderRadius.all(Radius.circular(25.0))), 76 | child: Text("\tDeluge mobile client\t", 77 | style: theme.logo_text_style))), 78 | ], 79 | ) 80 | ], 81 | )), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/settings/client/client_setting.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:deluge_client/control_center/theme.dart'; 3 | import 'package:deluge_client/control_center/theme_controller.dart'; 4 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 5 | 6 | import '../../state_ware_house/state_ware_house.dart'; 7 | 8 | class clt_st extends StatefulWidget { 9 | @override 10 | _clt_stState createState() => _clt_stState(); 11 | } 12 | 13 | class _clt_stState extends State { 14 | bool notification = false; 15 | bool sftp_reset = false; 16 | @override 17 | void initState() { 18 | super.initState(); 19 | 20 | fetch_client_settings(); 21 | } 22 | 23 | void fetch_client_settings() async { 24 | bool mid1 = await states.fetch_notification_settings(); 25 | if (this.mounted) { 26 | setState(() { 27 | notification = mid1; 28 | }); 29 | } 30 | 31 | bool mid2 = await states.get_sftP_reset_bool(); 32 | if (this.mounted) { 33 | setState(() { 34 | sftp_reset = mid2; 35 | }); 36 | } 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Material( 42 | child: SafeArea( 43 | top: false, 44 | child: Container( 45 | child: Column( 46 | mainAxisSize: MainAxisSize.min, 47 | children: [ 48 | Padding( 49 | padding: EdgeInsets.only(top: 3.0), 50 | child: Text("Client Settings", 51 | style: TextStyle( 52 | color: (!theme_controller.is_it_dark() 53 | ? Colors.black 54 | : Colors.white), 55 | fontSize: theme.bottom_sheet_heading_font_size, 56 | fontFamily: theme.font_family))), 57 | Divider( 58 | color: theme.base_color, 59 | thickness: 4.0, 60 | indent: 55.0, 61 | endIndent: 55.0, 62 | ), 63 | ListTile( 64 | title: Text("On-going notification "), 65 | trailing: Switch( 66 | value: notification, 67 | onChanged: (val) { 68 | setState(() { 69 | notification = val; 70 | }); 71 | states.set_notification_settings(val); 72 | }, 73 | ), 74 | ), 75 | ListTile( 76 | title: Text("sftp settings reset on account change"), 77 | trailing: Switch( 78 | value: sftp_reset, 79 | onChanged: (val) { 80 | setState(() { 81 | sftp_reset = val; 82 | }); 83 | states.set_sftP_reset_bool(val); 84 | }, 85 | ), 86 | ), 87 | ], 88 | ), 89 | ))); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/settings/deluge/core_settings.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:deluge_client/api/apis.dart'; 5 | import 'package:deluge_client/api/models/settings.dart'; 6 | 7 | class core_settings { 8 | static Settings settings; 9 | 10 | static var download_Directory = TextEditingController(); 11 | static var torrent_file_Directory = TextEditingController(); 12 | static var move_after_completion_path = TextEditingController(); 13 | static var max_connection_global = TextEditingController(); 14 | static var max_upload_speed = TextEditingController(); 15 | static var max_download_speed = TextEditingController(); 16 | static var max_upload_slots = TextEditingController(); 17 | static var max_half_open_connection = TextEditingController(); 18 | static var max_connection_per_second = TextEditingController(); 19 | static var max_connection_per_torrent = TextEditingController(); 20 | static var max_uploads_slots_per_torrent = TextEditingController(); 21 | static var max_download_speed_per_torrent = TextEditingController(); 22 | //--------------------------------------------------------------------------- 23 | static bool send_info = false; 24 | static bool allow_remote = false; 25 | static bool pre_allocate_storage = false; 26 | static bool random_port = false; 27 | static bool listen_use_sys_port = false; 28 | static bool listen_reuse_port = false; 29 | static bool random_outgoing_ports = false; 30 | static bool copy_torrent = false; 31 | static bool delete_copy_torrent_file = false; 32 | static bool prioritize_first_last_pieces = false; 33 | static bool sequential_download = false; 34 | static bool dht = false; 35 | static bool upnp = false; 36 | static bool natpmp = false; 37 | static bool utpex = false; 38 | static bool lsd = false; 39 | static bool rate_limit_ip_overhead = false; 40 | static bool auto_manage_prefer_seeds = false; 41 | static bool shared = false; 42 | static bool super_seeding = false; 43 | //-------------------------------------------------- 44 | //advance 45 | static var daemon_port = TextEditingController(); 46 | static var listen_ports = TextEditingController(); 47 | static var listen_interface = TextEditingController(); 48 | static var listen_random_port = TextEditingController(); 49 | static var outgoing_ports = TextEditingController(); 50 | static var port = TextEditingController(); 51 | static var hostname = TextEditingController(); 52 | static var user_name = TextEditingController(); 53 | static var password = TextEditingController(); 54 | static var cache_size = TextEditingController(); 55 | static var cache_expiry = TextEditingController(); 56 | //--------------------------------------------- 57 | static bool force_proxy = false; 58 | static bool anonymous_mode = false; 59 | static bool proxy_hostnames = false; 60 | static bool proxy_peer_connections = false; 61 | static bool proxy_tracker_connections = false; 62 | //---------------------------------------------------------------------------------- 63 | //editing controller for the sftp and streamer config 64 | static var sftp_host = TextEditingController(); 65 | static var sftpport = TextEditingController(); 66 | static var sftp_username = TextEditingController(); 67 | static var sftp_pass = TextEditingController(); 68 | static var sftp_route_url = TextEditingController(); 69 | //------------------------------------------------------------------------------------------ 70 | 71 | static void fetch( 72 | List cookie, 73 | String url, 74 | String is_reverse_proxied, 75 | String seed_username, 76 | String seed_pass, 77 | String qr_auth, 78 | BuildContext context) async { 79 | Map json = await apis.fetch_settings(cookie, url, 80 | is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 81 | 82 | settings = new Settings(json); 83 | 84 | Future.delayed(Duration(seconds: 1), () { 85 | initiate_workspace(); 86 | }); 87 | } 88 | 89 | static void initiate_workspace() { 90 | download_Directory.text = settings.downloadLocation; 91 | torrent_file_Directory.text = settings.torrentfilesLocation; 92 | move_after_completion_path.text = settings.moveCompletedPath; 93 | max_connection_global.text = settings.maxConnectionsGlobal.toString(); 94 | max_upload_slots.text = settings.maxUploadSlotsGlobal.toString(); 95 | max_half_open_connection.text = settings.maxHalfOpenConnections.toString(); 96 | max_connection_per_second.text = 97 | settings.maxConnectionsPerSecond.toString(); 98 | max_connection_per_torrent.text = 99 | settings.maxConnectionsPerTorrent.toString(); 100 | max_uploads_slots_per_torrent.text = 101 | settings.maxUploadSlotsPerTorrent.toString(); 102 | max_download_speed_per_torrent.text = 103 | settings.maxDownloadSpeedPerTorrent.toString(); 104 | max_download_speed.text = settings.maxDownloadSpeed.toString(); 105 | max_upload_speed.text = settings.maxUploadSpeed.toString(); 106 | //----------------------- 107 | //advance section 108 | daemon_port.text = settings.daemonPort.toString(); 109 | listen_ports.text = settings.listenPorts 110 | .toString() 111 | .replaceFirst('[', "") 112 | .replaceFirst("]", ""); 113 | listen_interface.text = settings.listenInterface; 114 | listen_random_port.text = settings.listenRandomPort.toString(); 115 | outgoing_ports.text = settings.outgoingPorts 116 | .toString() 117 | .replaceFirst('[', "") 118 | .replaceFirst("]", ""); 119 | port.text = settings.proxy["port"].toString(); 120 | hostname.text = settings.proxy["hostname"].toString(); 121 | password.text = settings.proxy["password"].toString(); 122 | user_name.text = settings.proxy["username"].toString(); 123 | cache_size.text = settings.cacheSize.toString(); 124 | cache_expiry.text = settings.cacheExpiry.toString(); 125 | //-------------------------------------------- 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/settings/deluge/deluge_setting.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:deluge_client/database/dbmanager.dart'; 4 | import 'package:deluge_client/settings/deluge/type/advance.dart'; 5 | import 'package:deluge_client/settings/deluge/type/basic.dart'; 6 | import 'package:deluge_client/settings/deluge/type/general.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:deluge_client/control_center/theme.dart'; 9 | import 'package:deluge_client/api/apis.dart'; 10 | import 'package:deluge_client/settings/deluge/core_settings.dart'; 11 | import 'package:fluttertoast/fluttertoast.dart'; 12 | import 'package:keyboard_dismisser/keyboard_dismisser.dart'; 13 | 14 | class deluge_settings extends StatefulWidget { 15 | List cookie; 16 | final Bucket selected_account; 17 | deluge_settings( 18 | {Key key, @required this.cookie, @required this.selected_account}) 19 | : super(key: key); 20 | 21 | @override 22 | _deluge_settingsState createState() => 23 | _deluge_settingsState(cookie: cookie, selected_account: selected_account); 24 | } 25 | 26 | class _deluge_settingsState extends State { 27 | List cookie; 28 | final Bucket selected_account; 29 | _deluge_settingsState( 30 | {Key key, @required this.cookie, @required this.selected_account}); 31 | 32 | @override 33 | void initState() { 34 | core_settings_fetcher(); 35 | // TODO: implement initState 36 | super.initState(); 37 | } 38 | 39 | void core_settings_fetcher() async { 40 | core_settings.fetch( 41 | cookie != null 42 | ? cookie 43 | : await cookie_substitue( 44 | selected_account.deluge_url, 45 | selected_account.deluge_pwrd, 46 | selected_account.has_deluge_pwrd, 47 | selected_account.is_reverse_proxied, 48 | selected_account.username, 49 | selected_account.password, 50 | selected_account.via_qr), 51 | selected_account.deluge_url, 52 | selected_account.is_reverse_proxied, 53 | selected_account.username, 54 | selected_account.password, 55 | selected_account.via_qr, 56 | context); 57 | } 58 | 59 | Future> cookie_substitue( 60 | String url, 61 | String password, 62 | String has_deluge_pass, 63 | String is_reverse_proxied, 64 | String seed_username, 65 | String seed_pass, 66 | String qr_auth) async { 67 | cookie = await apis.authentication_to_deluge(url, password, has_deluge_pass, 68 | is_reverse_proxied, seed_username, seed_pass, qr_auth, context); 69 | 70 | print("it is executed"); 71 | print(cookie); 72 | 73 | return cookie; 74 | } 75 | 76 | void toastMessage(String message) { 77 | Fluttertoast.showToast( 78 | msg: message, 79 | toastLength: Toast.LENGTH_SHORT, 80 | gravity: ToastGravity.BOTTOM, 81 | fontSize: 16.0, 82 | backgroundColor: Colors.black, 83 | ); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return KeyboardDismisser( 89 | gestures: [ 90 | GestureType.onTap, 91 | GestureType.onPanUpdateDownDirection, 92 | ], 93 | child: Scaffold( 94 | appBar: AppBar( 95 | backgroundColor: theme.base_color, 96 | title: Text("Deluge's Settings", style: theme.app_bar_style), 97 | elevation: 0.0, 98 | actions: [ 99 | InkWell( 100 | child: Center( 101 | child: Padding( 102 | padding: EdgeInsets.only(right: 3.0, top: 1.0), 103 | child: Text( 104 | "Save Changes", 105 | style: TextStyle( 106 | fontSize: theme.minimal_font_size, 107 | fontFamily: theme.font_family, 108 | fontWeight: FontWeight.bold), 109 | )), 110 | ), 111 | onTap: () async { 112 | apis.update_config_settings( 113 | cookie, 114 | selected_account.deluge_url, 115 | selected_account.is_reverse_proxied, 116 | selected_account.username, 117 | selected_account.password, 118 | selected_account.via_qr, 119 | context); 120 | 121 | toastMessage("Setting updated"); 122 | }, 123 | ) 124 | ], 125 | ), 126 | body: SingleChildScrollView( 127 | child: Column( 128 | children: [ 129 | ExpansionTile( 130 | initiallyExpanded: true, 131 | title: Text( 132 | "General", 133 | style: TextStyle( 134 | fontSize: 16.0, 135 | fontWeight: FontWeight.w500, 136 | fontFamily: theme.font_family), 137 | ), 138 | children: [ 139 | general(), 140 | ], 141 | ), 142 | ExpansionTile( 143 | title: Text( 144 | "Basic", 145 | style: TextStyle( 146 | fontSize: 16.0, 147 | fontWeight: FontWeight.w500, 148 | fontFamily: theme.font_family), 149 | ), 150 | children: [ 151 | basic(), 152 | ], 153 | ), 154 | ExpansionTile( 155 | title: Text( 156 | "Advance", 157 | style: TextStyle( 158 | fontSize: 16.0, 159 | fontWeight: FontWeight.w500, 160 | fontFamily: theme.font_family), 161 | ), 162 | children: [ 163 | advance(), 164 | ], 165 | ), 166 | ], 167 | )), 168 | )); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /lib/settings/deluge/type/sftp_streaming_settings.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:deluge_client/database/dbmanager.dart'; 3 | import 'package:deluge_client/state_ware_house/state_ware_house.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:deluge_client/control_center/theme.dart'; 6 | import 'package:deluge_client/settings/deluge/core_settings.dart'; 7 | import 'package:fluttertoast/fluttertoast.dart'; 8 | import 'package:deluge_client/settings/deluge/type/sftp_setting_field.dart'; 9 | 10 | class ssh extends StatefulWidget { 11 | final Bucket selected_account; 12 | ssh({Key key, @required this.selected_account}) : super(key: key); 13 | @override 14 | _sshState createState() => _sshState(selected_account: selected_account); 15 | } 16 | 17 | class _sshState extends State { 18 | final Bucket selected_account; 19 | _sshState({Key key, @required this.selected_account}); 20 | void initiate_setup() async { 21 | core_settings.sftp_host.text = await states.get_sftp_host(); 22 | core_settings.sftp_pass.text = await states.get_sftp_password(); 23 | core_settings.sftpport.text = await states.get_sftp_port(); 24 | core_settings.sftp_username.text = await states.get_sftp_username(); 25 | core_settings.sftp_route_url.text = await states.get_sftp_route(); 26 | } 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | initiate_setup(); 32 | } 33 | 34 | // update sftp and streaming config 35 | void update_sftp_settings() async { 36 | states.set_sftp_host(core_settings.sftp_host.text.toString()); 37 | states.set_sftp_pass(core_settings.sftp_pass.text.toString()); 38 | states.set_sftp_port(core_settings.sftpport.text.toString()); 39 | states.set_sftp_route(core_settings.sftp_route_url.text.toString()); 40 | states.set_sftp_username(core_settings.sftp_username.text.toString()); 41 | } 42 | 43 | void toastMessage(String message) { 44 | Fluttertoast.showToast( 45 | msg: message, 46 | toastLength: Toast.LENGTH_SHORT, 47 | gravity: ToastGravity.BOTTOM, 48 | fontSize: 16.0, 49 | backgroundColor: Colors.black, 50 | ); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Scaffold( 56 | appBar: AppBar( 57 | backgroundColor: theme.base_color, 58 | title: Text("SFTP settings", style: theme.app_bar_style), 59 | elevation: 0.0, 60 | actions: [ 61 | InkWell( 62 | child: Center( 63 | child: Padding( 64 | padding: EdgeInsets.only(right: 3.0, top: 1.0), 65 | child: Text( 66 | "Save Changes", 67 | style: TextStyle( 68 | fontSize: theme.minimal_font_size, 69 | fontFamily: theme.font_family, 70 | fontWeight: FontWeight.bold), 71 | )), 72 | ), 73 | onTap: () async { 74 | update_sftp_settings(); 75 | toastMessage("Setting updated"); 76 | }, 77 | ) 78 | ], 79 | ), 80 | body: sftp_settings_fields( 81 | selected_account: selected_account, 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/sftp_streaming/image_streamer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:photo_view/photo_view.dart'; 3 | 4 | class img extends StatefulWidget { 5 | final String path; 6 | final Map headers; 7 | img({Key key, @required this.path, @required this.headers}) : super(key: key); 8 | 9 | @override 10 | _imgState createState() => _imgState(path: path, headers: headers); 11 | } 12 | 13 | class _imgState extends State { 14 | final String path; 15 | final Map headers; 16 | _imgState({@required this.path, @required this.headers}); 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | child: PhotoView(imageProvider: NetworkImage(path, headers: headers))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/sftp_streaming/video_dir.dart: -------------------------------------------------------------------------------- 1 | import 'package:better_player/better_player.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:deluge_client/control_center/theme.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class media_stream extends StatefulWidget { 7 | final String file_name; 8 | final String selected_file; 9 | final Map headers; 10 | media_stream( 11 | {Key key, 12 | @required this.selected_file, 13 | @required this.file_name, 14 | @required this.headers}) 15 | : super(key: key); 16 | @override 17 | _media_streamState createState() => _media_streamState( 18 | selected_file: selected_file, file_name: file_name, headers: headers); 19 | } 20 | 21 | class _media_streamState extends State { 22 | final String file_name; 23 | final String selected_file; 24 | final Map headers; 25 | _media_streamState({this.selected_file, this.file_name, this.headers}); 26 | BetterPlayerListVideoPlayerController controller; 27 | List orientations = [DeviceOrientation.portraitUp]; 28 | void initState() { 29 | super.initState(); 30 | SystemChrome.setPreferredOrientations([ 31 | DeviceOrientation.portraitUp, 32 | ]); 33 | controller = BetterPlayerListVideoPlayerController(); 34 | Future.delayed(Duration(seconds: 1), () { 35 | controller.play(); 36 | }); 37 | } 38 | 39 | @override 40 | dispose() { 41 | SystemChrome.setPreferredOrientations([ 42 | DeviceOrientation.landscapeRight, 43 | DeviceOrientation.landscapeLeft, 44 | DeviceOrientation.portraitUp, 45 | DeviceOrientation.portraitDown, 46 | ]); 47 | super.dispose(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return Scaffold( 53 | backgroundColor: Colors.black, 54 | appBar: AppBar( 55 | title: Text(file_name), 56 | backgroundColor: theme.base_color, 57 | ), 58 | body: Column( 59 | children: [ 60 | Expanded( 61 | flex: 10, 62 | child: BetterPlayerListVideoPlayer( 63 | BetterPlayerDataSource( 64 | BetterPlayerDataSourceType.network, 65 | // videoListData!.videoUrl, 66 | selected_file, 67 | notificationConfiguration: 68 | BetterPlayerNotificationConfiguration( 69 | showNotification: true, 70 | title: "Streaming on", 71 | author: file_name.toString()), 72 | headers: headers), 73 | 74 | configuration: BetterPlayerConfiguration( 75 | autoPlay: false, 76 | deviceOrientationsAfterFullScreen: orientations), 77 | //key: Key(videoListData.hashCode.toString()), 78 | playFraction: 0.8, 79 | betterPlayerListVideoPlayerController: controller, 80 | )), 81 | ], 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/sftp_streaming/web_spacer.dart: -------------------------------------------------------------------------------- 1 | class remove_space { 2 | static String remove(String path) { 3 | return path.replaceAll(" ", "%20"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/state_ware_house/state_ware_house.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class states { 4 | static void set_auth() async { 5 | SharedPreferences pref = await SharedPreferences.getInstance(); 6 | pref.setBool("is_auth", true); 7 | } 8 | 9 | static void reset_auth() async { 10 | SharedPreferences pref = await SharedPreferences.getInstance(); 11 | pref.setBool("is_auth", false); 12 | } 13 | 14 | static Future isFirstTime() async { 15 | SharedPreferences pf = await SharedPreferences.getInstance(); 16 | var isFirstTime = pf.getBool('first_time'); 17 | if (isFirstTime != null && !isFirstTime) { 18 | pf.setBool('first_time', false); 19 | return false; 20 | } else { 21 | pf.setBool('first_time', false); 22 | return true; 23 | } 24 | } 25 | 26 | static void first_time_setup_selection() async { 27 | SharedPreferences pref = await SharedPreferences.getInstance(); 28 | 29 | isFirstTime().then((isFirstTime) { 30 | if (isFirstTime) { 31 | pref.setInt("selected_account", 1); 32 | pref.setInt("selected_theme", 1); 33 | pref.setBool("notification_settings", true); 34 | pref.setBool("sftp_reset", false); 35 | pref.setString("sftp_host", ""); 36 | pref.setString("sftp_port", ""); 37 | pref.setString("sftp_username", ""); 38 | pref.setString("sftp_pass", ""); 39 | pref.setString("sftp_dir_route", ""); 40 | } else { 41 | print("it is not first"); 42 | } 43 | }); 44 | } 45 | 46 | static void make_it_first_time() async { 47 | SharedPreferences pref = await SharedPreferences.getInstance(); 48 | pref.setBool("first_time", true); 49 | } 50 | 51 | //-- 52 | static Future state_selected_account() async { 53 | SharedPreferences pref = await SharedPreferences.getInstance(); 54 | int t = pref.getInt("selected_account"); 55 | return t; 56 | } 57 | 58 | static Future state_all_selected_account() async { 59 | SharedPreferences pref = await SharedPreferences.getInstance(); 60 | int t = pref.getInt("all_selected_account_idx"); 61 | return t; 62 | } 63 | 64 | static Future state_is_auth_fetch() async { 65 | SharedPreferences pref = await SharedPreferences.getInstance(); 66 | bool ctxt_state = pref.getBool('is_auth'); 67 | return ctxt_state; 68 | } 69 | 70 | static void update_account_state(int id) async { 71 | SharedPreferences pf = await SharedPreferences.getInstance(); 72 | pf.setInt("selected_account", id); 73 | } 74 | 75 | //------------------------------------------------------------------------------- 76 | static void set_theme_mode(int mode) async { 77 | SharedPreferences pf = await SharedPreferences.getInstance(); 78 | pf.setInt("selected_theme", mode); 79 | } 80 | 81 | static Future get_theme_mode() async { 82 | SharedPreferences pf = await SharedPreferences.getInstance(); 83 | int t = pf.getInt("selected_theme"); 84 | return t; 85 | } 86 | 87 | //---------------------------------------------------------------------- 88 | static void set_notification_settings(bool val) async { 89 | SharedPreferences pf = await SharedPreferences.getInstance(); 90 | pf.setBool("notification_settings", val); 91 | } 92 | 93 | static Future fetch_notification_settings() async { 94 | SharedPreferences pf = await SharedPreferences.getInstance(); 95 | return pf.get("notification_settings"); 96 | } 97 | 98 | //-------------------------------------------------sftp area 99 | static Future set_sftp_host(String host) async { 100 | SharedPreferences pf = await SharedPreferences.getInstance(); 101 | pf.setString("sftp_host", host); 102 | } 103 | 104 | static Future set_sftp_port(String port) async { 105 | SharedPreferences pf = await SharedPreferences.getInstance(); 106 | pf.setString("sftp_port", port); 107 | } 108 | 109 | static Future set_sftp_username(String user) async { 110 | SharedPreferences pf = await SharedPreferences.getInstance(); 111 | pf.setString("sftp_username", user); 112 | } 113 | 114 | static Future set_sftp_pass(String password) async { 115 | SharedPreferences pf = await SharedPreferences.getInstance(); 116 | pf.setString("sftp_pass", password); 117 | } 118 | 119 | static Future set_sftp_route(String route) async { 120 | SharedPreferences pf = await SharedPreferences.getInstance(); 121 | pf.setString("sftp_dir_route", route); 122 | } 123 | 124 | //------------- 125 | static Future get_sftp_route() async { 126 | SharedPreferences pf = await SharedPreferences.getInstance(); 127 | return pf.getString("sftp_dir_route"); 128 | } 129 | 130 | static Future get_sftp_host() async { 131 | SharedPreferences pf = await SharedPreferences.getInstance(); 132 | return pf.getString("sftp_host"); 133 | } 134 | 135 | static Future get_sftp_port() async { 136 | SharedPreferences pf = await SharedPreferences.getInstance(); 137 | return pf.getString("sftp_port"); 138 | } 139 | 140 | static Future get_sftp_username() async { 141 | SharedPreferences pf = await SharedPreferences.getInstance(); 142 | return pf.getString("sftp_username"); 143 | } 144 | 145 | static Future get_sftp_password() async { 146 | SharedPreferences pf = await SharedPreferences.getInstance(); 147 | return pf.getString("sftp_pass"); 148 | } 149 | 150 | //------------------------------------------------------------------------- 151 | // it should return sftp true false 152 | static Future get_sftP_reset_bool() async { 153 | SharedPreferences pf = await SharedPreferences.getInstance(); 154 | return pf.get("sftp_reset"); 155 | } 156 | 157 | static Future set_sftP_reset_bool(bool val) async { 158 | SharedPreferences pf = await SharedPreferences.getInstance(); 159 | pf.setBool("sftp_reset", val); 160 | } 161 | 162 | static Future reset_sftp_config() async { 163 | SharedPreferences pf = await SharedPreferences.getInstance(); 164 | pf.setString("sftp_host", ""); 165 | pf.setString("sftp_port", ""); 166 | pf.setString("sftp_username", ""); 167 | pf.setString("sftp_pass", ""); 168 | pf.setString("sftp_dir_route", ""); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /lib/string/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class controller { 4 | static var search_field_controller = TextEditingController(); 5 | static String storage_controller = ""; 6 | static String path_controller = ""; 7 | } 8 | -------------------------------------------------------------------------------- /lib/string/magnet_detect.dart: -------------------------------------------------------------------------------- 1 | class magnet_detect { 2 | static bool parse(String url) { 3 | if (url.contains("magnet:?")) { 4 | return true; 5 | } else { 6 | return false; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/string/sorter.dart: -------------------------------------------------------------------------------- 1 | import 'package:deluge_client/core/all_acc.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'package:deluge_client/api/models/torrent_prop.dart'; 4 | 5 | class sort_helper { 6 | static bool reverse_order = false; 7 | static bool non_reverse_order = true; 8 | static bool by_size_order = false; 9 | static bool by_date_time = false; 10 | static Map sort(Map map) { 11 | return Map.fromEntries(map.entries.toList().reversed); 12 | } 13 | 14 | static Map sort_for_multi( 15 | Map map) { 16 | return Map.fromEntries(map.entries.toList().reversed); 17 | } 18 | 19 | static Map sort_by_size(Map map) { 20 | List mid_list = new List(); 21 | map.entries.forEach((e) => 22 | mid_list.add(map_as_list_view(hash: e.key, properties: e.value))); 23 | mid_list.sort((a, b) => (a.properties.totalSize ~/ 1000000) 24 | .compareTo(b.properties.totalSize ~/ 1000000)); 25 | Map output_map = new Map(); 26 | mid_list.forEach((val) => output_map[val.hash] = val.properties); 27 | 28 | return output_map; 29 | } 30 | 31 | static Map sort_by_size_for_multi( 32 | Map map) { 33 | List mid_list = 34 | new List(); 35 | map.entries.forEach((e) => mid_list 36 | .add(map_as_list_view_for_multi(identity: e.key, properties: e.value))); 37 | mid_list.sort((a, b) => (a.properties.totalSize ~/ 1000000) 38 | .compareTo(b.properties.totalSize ~/ 1000000)); 39 | Map output_map = 40 | new Map(); 41 | mid_list.forEach((val) => output_map[val.identity] = val.properties); 42 | 43 | return output_map; 44 | } 45 | 46 | //-----------date and time comparision 47 | static Map sort_by_date_time( 48 | Map map) { 49 | final format = new DateFormat('dd-MM-yyyy hh:mm a'); 50 | final eta = new DateFormat('hh:mm'); 51 | List mid_list = new List(); 52 | map.entries.forEach((e) => 53 | mid_list.add(map_as_list_view(hash: e.key, properties: e.value))); 54 | mid_list.sort((a, b) => (format.format( 55 | DateTime.fromMillisecondsSinceEpoch(a.properties.timeAdded * 1000))) 56 | .compareTo(format.format(DateTime.fromMillisecondsSinceEpoch( 57 | b.properties.timeAdded * 1000)))); 58 | Map output_map = new Map(); 59 | mid_list.forEach((val) => output_map[val.hash] = val.properties); 60 | 61 | return output_map; 62 | } 63 | 64 | //-for multi 65 | static Map sort_by_date_time_for_multi( 66 | Map map) { 67 | final format = new DateFormat('dd-MM-yyyy hh:mm a'); 68 | final eta = new DateFormat('hh:mm'); 69 | 70 | List mid_list = 71 | new List(); 72 | map.entries.forEach((e) => mid_list 73 | .add(map_as_list_view_for_multi(identity: e.key, properties: e.value))); 74 | mid_list.sort((a, b) => (format.format( 75 | DateTime.fromMillisecondsSinceEpoch(a.properties.timeAdded * 1000))) 76 | .compareTo(format.format(DateTime.fromMillisecondsSinceEpoch( 77 | b.properties.timeAdded * 1000)))); 78 | Map output_map = 79 | new Map(); 80 | mid_list.forEach((val) => output_map[val.identity] = val.properties); 81 | 82 | return output_map; 83 | } 84 | } 85 | 86 | class map_as_list_view { 87 | String hash; 88 | Properties properties; 89 | map_as_list_view({this.hash, this.properties}); 90 | } 91 | 92 | class map_as_list_view_for_multi { 93 | multtorrent identity; 94 | Properties properties; 95 | map_as_list_view_for_multi({this.identity, this.properties}); 96 | } 97 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: deluge_client 2 | description: A mobile client for Deluge bittorrent monitoring service. 3 | 4 | 5 | # The following line prevents the package from being accidentally published to 6 | # pub.dev using `pub publish`. This is preferred for private packages. 7 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 8 | 9 | # The following defines the version and build number for your application. 10 | # A version number is three numbers separated by dots, like 1.2.43 11 | # followed by an optional build number separated by a +. 12 | # Both the version and the builder number may be overridden in flutter 13 | # build by specifying --build-name and --build-number, respectively. 14 | # In Android, build-name is used as versionName while build-number used as versionCode. 15 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 16 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 17 | # Read more about iOS versioning at 18 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 19 | version: 0.1.4+15 20 | #versioning example 21 | #You can update both the version name and the version code number in the same place in pubspec.yaml. Just separate them with a + sign. For example: 22 | 23 | # version: 2.0.0+8 24 | # This means 25 | 26 | # The version name is 2.0.0 27 | # The version code is 8 28 | 29 | environment: 30 | sdk: ">=2.7.0 <3.0.0" 31 | 32 | dependencies: 33 | flutter: 34 | sdk: flutter 35 | 36 | 37 | # The following adds the Cupertino Icons font to your application. 38 | # Use with the CupertinoIcons class for iOS style icons. 39 | cupertino_icons: ^1.0.2 40 | http: ^0.12.1 41 | percent_indicator: ^3.0.1 42 | qr_code_scanner: ^0.4.0 43 | modal_bottom_sheet: ^2.0.0 44 | sqflite: ^2.0.0+3 45 | shared_preferences: ^2.0.5 46 | fluttertoast: ^8.0.8 47 | expandable: ^5.0.1 48 | flutter_slidable: ^0.6.0 49 | intl: ^0.17.0 50 | filesize: ^2.0.1 51 | flutter_local_notifications: ^5.0.0 52 | flutter_phoenix: ^1.0.0 53 | ssh: ^0.0.7 54 | better_player: 55 | photo_view: ^0.13.0 56 | url_launcher: ^6.0.6 57 | keyboard_dismisser: ^2.0.0 58 | file_picker: ^4.0.0 59 | flutter_lints: ^1.0.4 60 | 61 | 62 | 63 | 64 | 65 | dev_dependencies: 66 | flutter_test: 67 | sdk: flutter 68 | 69 | # For information on the generic Dart part of this file, see the 70 | # following page: https://dart.dev/tools/pub/pubspec 71 | 72 | # The following section is specific to Flutter. 73 | flutter: 74 | 75 | # The following line ensures that the Material Icons font is 76 | # included with your application, so that you can use the icons in 77 | # the material Icons class. 78 | uses-material-design: true 79 | 80 | # To add assets to your application, add an assets section, like this: 81 | assets: 82 | - assets/logo.png 83 | - assets/loader.gif 84 | - assets/no_data.png 85 | - assets/animation.gif 86 | - assets/server_end_error.png 87 | - assets/poster.png 88 | - assets/slack.png 89 | - assets/github.png 90 | 91 | 92 | # - images/a_dot_ham.jpeg 93 | 94 | # An image asset can refer to one or more resolution-specific "variants", see 95 | # https://flutter.dev/assets-and-images/#resolution-aware. 96 | 97 | # For details regarding adding assets from package dependencies, see 98 | # https://flutter.dev/assets-and-images/#from-packages 99 | 100 | # To add custom fonts to your application, add a fonts section here, 101 | # in this "flutter" section. Each entry in this list should have a 102 | # "family" key with the font family name, and a "fonts" key with a 103 | # list giving the asset and other descriptors for the font. For 104 | # example: 105 | fonts: 106 | - family: SFUIDisplay 107 | fonts: 108 | - asset: assets/fonts/SFUIDisplay/sf-ui-display-heavy.otf 109 | - asset: assets/fonts/SFUIDisplay/sf-ui-display-light.otf 110 | - asset: assets/fonts/SFUIDisplay/sf-ui-display-medium.otf 111 | # fonts: 112 | # - family: Schyler 113 | # fonts: 114 | # - asset: fonts/Schyler-Regular.ttf 115 | # - asset: fonts/Schyler-Italic.ttf 116 | # style: italic 117 | # - family: Trajan Pro 118 | # fonts: 119 | # - asset: fonts/TrajanPro.ttf 120 | # - asset: fonts/TrajanPro_Bold.ttf 121 | # weight: 700 122 | # 123 | # For details regarding fonts from package dependencies, 124 | # see https://flutter.dev/custom-fonts/#from-packages 125 | -------------------------------------------------------------------------------- /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:deluge_client/main.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 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(root()); 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 | -------------------------------------------------------------------------------- /user_guide.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://user-images.githubusercontent.com/57806993/128637426-71d45add-9458-4175-a20f-89885f2cf1fd.mp4 6 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/Deluge-mobile-remote-client/02fff48736f066b9d9c7d0ff1314f6ac1f64a8e4/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | deluge_client 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deluge_client", 3 | "short_name": "deluge_client", 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 | } 24 | --------------------------------------------------------------------------------