├── .gitignore ├── .metadata ├── CHANGELOG.md ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ir_net │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── 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 ├── app_icon.png ├── globe.ico ├── globe_leaked.ico ├── iran.ico ├── kerio.png ├── loading.ico ├── network_error.ico └── offline.ico ├── inno ├── Inno installer script.iss └── installer license.txt ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── 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 ├── app.dart ├── bloc.dart ├── data │ ├── leak_item.dart │ └── shared_preferences.dart ├── home.dart ├── main.dart ├── utils │ ├── cmd.dart │ ├── http.dart │ ├── kerio.dart │ └── system_tray.dart └── views │ ├── connection.dart │ ├── ip_stat.dart │ ├── kerio_login.dart │ ├── leak.dart │ └── options.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon_1024.png │ │ ├── app_icon_128.png │ │ ├── app_icon_16.png │ │ ├── app_icon_256.png │ │ ├── app_icon_32.png │ │ ├── app_icon_512.png │ │ └── app_icon_64.png │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── patches └── Speed_test_in_web_view.patch ├── pubspec.lock ├── pubspec.yaml ├── screenshot.png ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | /signing 35 | 36 | # Web related 37 | lib/generated_plugin_registrant.dart 38 | 39 | # Symbolication related 40 | app.*.symbols 41 | 42 | # Obfuscation related 43 | app.*.map.json 44 | 45 | # Android Studio will place build artifacts here 46 | /android/app/debug 47 | /android/app/profile 48 | /android/app/release 49 | 50 | # Inno 51 | /inno/Output -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 17 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 18 | - platform: android 19 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 20 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 21 | - platform: ios 22 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 23 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 24 | - platform: linux 25 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 26 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 27 | - platform: macos 28 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 29 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 30 | - platform: web 31 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 32 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 33 | - platform: windows 34 | create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 35 | base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.2.2 2 | - Add Kerio balance views 3 | - Show notification when Kerio balance is less than 1 GB 4 | 5 | # 1.2.1 6 | - Open kerio control page in the browser by clicking on end icon 7 | 8 | # 1.2.0 9 | - Integrate Kerio-Control login page 10 | - Show leaks on system tray by default 11 | 12 | # 1.1.0 13 | - Add speed test box 14 | - Set a fixed height and width when starting the app 15 | - Update window title 16 | 17 | # 1.0 18 | First official release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ir_net 2 | 3 | Windows tool to show VPN connection details 4 | 5 | 6 | 7 | ## Features 8 | - Show location of connection in map 9 | - Leak detection on your urls 10 | - SysTray icon without the app being open 11 | - Start by windows startup 12 | - Ability to minimize and hide from taskbar 13 | - Show details of your ISP 14 | - Update status when there is a network change 15 | - Connect through proxy vpn types (eg. ShadowSocks, VMess, ...) 16 | - Auto Connect to Kerio Network 17 | - Show usage and statistics of Kerio network 18 | 19 | ## How to build windows installer 20 | 1. Build .exe file: 21 | ```bat 22 | flutter build windows 23 | ``` 24 | 25 | 2. Open `Inno setup compiler` app and select .iss file from inno/Inno installer scrip.iss 26 | 3. Press on run button 27 | 4. Output .exe file will be in the `inno/Output` directory 28 | 29 | ## TODO 30 | - Add alternative api service 31 | - Choose default country and show flag based on that 32 | - Option to show all countries flag 33 | - Add statistics eg. (number of times and minutes there was a network error, reasons of network failure) 34 | - Add app launcher icon 35 | - Show error reason in leak detection list 36 | - Show which app occupied a port 37 | - Speed test 38 | - Shortcuts to important screens of windows (eg. Network adapters) 39 | - Retry one more time if there was a leak found during switch to new network 40 | - Reset ip lookup result when there is no network 41 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | ndkVersion flutter.ndkVersion 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.example.ir_net" 48 | // You can update the following values to match your application needs. 49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 50 | minSdkVersion flutter.minSdkVersion 51 | targetSdkVersion flutter.targetSdkVersion 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | } 55 | 56 | buildTypes { 57 | release { 58 | // TODO: Add your own signing config for the release build. 59 | // Signing with the debug keys for now, so `flutter run --release` works. 60 | signingConfig signingConfigs.debug 61 | } 62 | } 63 | } 64 | 65 | flutter { 66 | source '../..' 67 | } 68 | 69 | dependencies { 70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/ir_net/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.ir_net 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 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 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 6 | -------------------------------------------------------------------------------- /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/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/app_icon.png -------------------------------------------------------------------------------- /assets/globe.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/globe.ico -------------------------------------------------------------------------------- /assets/globe_leaked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/globe_leaked.ico -------------------------------------------------------------------------------- /assets/iran.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/iran.ico -------------------------------------------------------------------------------- /assets/kerio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/kerio.png -------------------------------------------------------------------------------- /assets/loading.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/loading.ico -------------------------------------------------------------------------------- /assets/network_error.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/network_error.ico -------------------------------------------------------------------------------- /assets/offline.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/assets/offline.ico -------------------------------------------------------------------------------- /inno/Inno installer script.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "IRNet" 5 | #define MyAppVersion "1.2.2" 6 | #define MyAppPublisher "BuildToApp, Inc." 7 | #define MyAppURL "https://www.buildtoapp.com/" 8 | #define MyAppExeName "ir_net.exe" 9 | 10 | [Setup] 11 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 12 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 13 | AppId={{C4962B60-F5A8-4D6B-8BDD-8A83922270B5} 14 | AppName={#MyAppName} 15 | AppVersion={#MyAppVersion} 16 | ;AppVerName={#MyAppName} {#MyAppVersion} 17 | AppPublisher={#MyAppPublisher} 18 | AppPublisherURL={#MyAppURL} 19 | AppSupportURL={#MyAppURL} 20 | AppUpdatesURL={#MyAppURL} 21 | DefaultDirName={autopf}\{#MyAppName} 22 | ; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run 23 | ; on anything but x64 and Windows 11 on Arm. 24 | ArchitecturesAllowed=x64compatible 25 | ; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the 26 | ; install be done in "64-bit mode" on x64 or Windows 11 on Arm, 27 | ; meaning it should use the native 64-bit Program Files directory and 28 | ; the 64-bit view of the registry. 29 | ArchitecturesInstallIn64BitMode=x64compatible 30 | DisableProgramGroupPage=yes 31 | LicenseFile=C:\Workspace\Flutter\ir_net\inno\installer license.txt 32 | ; Uncomment the following line to run in non administrative install mode (install for current user only.) 33 | ;PrivilegesRequired=lowest 34 | OutputBaseFilename=IRNet_windows_setup 35 | Compression=lzma 36 | SolidCompression=yes 37 | WizardStyle=modern 38 | 39 | [Languages] 40 | Name: "english"; MessagesFile: "compiler:Default.isl" 41 | 42 | [Tasks] 43 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 44 | 45 | [Files] 46 | Source: "C:\Workspace\Flutter\ir_net\build\windows\x64\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion 47 | Source: "C:\Workspace\Flutter\ir_net\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 48 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 49 | 50 | [Icons] 51 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 52 | Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 53 | 54 | [Run] 55 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent 56 | 57 | -------------------------------------------------------------------------------- /inno/installer license.txt: -------------------------------------------------------------------------------- 1 | End-User License Agreement (EULA) of IRNet 2 | 3 | This End-User License Agreement ("EULA") is a legal agreement between you and BuildToApp. 4 | 5 | This EULA agreement governs your acquisition and use of our IRNet software ("Software") directly from BuildToApp or indirectly through a BuildToApp authorized reseller or distributor (a "Reseller"). 6 | 7 | Please read this EULA agreement carefully before completing the installation process and using the IRNet software. It provides a license to use the IRNet software and contains warranty information and liability disclaimers. 8 | 9 | By installing and/or using the IRNet software, you are confirming your acceptance of the Software and agreeing to become bound by the terms of this EULA agreement. 10 | 11 | If you are entering into this EULA agreement on behalf of a company or other legal entity, you represent that you have the authority to bind such entity to these terms. If you do not have such authority, or if you do not agree with the terms of this EULA agreement, do not install or use the Software, and you must not accept this EULA agreement. 12 | 13 | This EULA agreement shall apply only to the Software supplied by BuildToApp herewith regardless of whether other software is referred to or described herein. The terms also apply to any BuildToApp updates, supplements, Internet-based services, and support services for the Software unless other terms accompany those items on delivery. If so, those terms apply. 14 | 15 | License Grant 16 | BuildToApp hereby grants you a personal, non-transferable, non-exclusive license to use the IRNet software on your devices in accordance with the terms of this EULA agreement. 17 | 18 | You are permitted to load the IRNet software on a single computer or multiple devices (as agreed upon in your license purchase), under your control. You are responsible for ensuring your device meets the minimum requirements of the IRNet software. 19 | 20 | You are not permitted to: 21 | 22 | Edit, alter, modify, adapt, translate, or otherwise change the whole or any part of the Software, nor permit the whole or any part of the Software to be combined with or become incorporated into any other software, nor decompile, disassemble or reverse engineer the Software or attempt to do any such things. 23 | Reproduce, copy, distribute, resell, or otherwise use the Software for any commercial purpose. 24 | Allow any third party to use the Software on behalf of or for the benefit of any third party. 25 | Use the Software in any way that breaches any applicable local, national, or international law. 26 | Use the Software for any purpose that BuildToApp considers is a breach of this EULA agreement. 27 | Intellectual Property and Ownership 28 | BuildToApp shall at all times retain ownership of the Software as downloaded by you and all subsequent downloads of the Software. The Software (and the copyright, and other intellectual property rights of whatever nature in the Software, including any modifications made thereto) are and shall remain the property of BuildToApp. 29 | 30 | BuildToApp reserves the right to grant licenses to use the Software to third parties. 31 | 32 | Termination 33 | This EULA agreement is effective from the date you first use the Software and shall continue until terminated. You may terminate it at any time upon written notice to BuildToApp. 34 | 35 | It will also terminate immediately if you fail to comply with any term of this EULA agreement. Upon such termination, the licenses granted by this EULA agreement will immediately terminate, and you agree to stop all access and use of the Software. The provisions that by their nature continue and survive will survive any termination of this EULA agreement. 36 | 37 | Limitation of Liability 38 | To the maximum extent permitted by applicable law, in no event shall BuildToApp, its affiliates, licensors, or suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever arising out of the use of or inability to use the Software (including, without limitation, damages for loss of profits, loss of data, business interruption, or any other pecuniary loss). 39 | 40 | Governing Law 41 | This EULA agreement, and any dispute arising out of or in connection with this EULA, shall be governed by and construed in accordance with the laws of the country, state, or jurisdiction in which BuildToApp is registered, without regard to its conflict of law provisions. 42 | 43 | Open Source Components 44 | Some components of the Software may be licensed under an open-source license, and the terms of the open-source licenses may override the terms of this EULA. You can find a list of these components and their licenses on the project’s official repository at: GitHub Repository of IRNet. 45 | 46 | Contact Information 47 | If you have any questions about this EULA, please contact BuildToApp at: 48 | 49 | Email: matthew.s@buildtoapp.com 50 | Address: Los Angles, U.S -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1300; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = ( 294 | "$(inherited)", 295 | "@executable_path/Frameworks", 296 | ); 297 | PRODUCT_BUNDLE_IDENTIFIER = com.example.irNet; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 300 | SWIFT_VERSION = 5.0; 301 | VERSIONING_SYSTEM = "apple-generic"; 302 | }; 303 | name = Profile; 304 | }; 305 | 97C147031CF9000F007C117D /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = dwarf; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | ENABLE_TESTABILITY = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 353 | MTL_ENABLE_DEBUG_INFO = YES; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | }; 358 | name = Debug; 359 | }; 360 | 97C147041CF9000F007C117D /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_ANALYZER_NONNULL = YES; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INFINITE_RECURSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 384 | CLANG_WARN_STRICT_PROTOTYPES = YES; 385 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 386 | CLANG_WARN_UNREACHABLE_CODE = YES; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 389 | COPY_PHASE_STRIP = NO; 390 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 391 | ENABLE_NS_ASSERTIONS = NO; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | GCC_C_LANGUAGE_STANDARD = gnu99; 394 | GCC_NO_COMMON_BLOCKS = YES; 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 402 | MTL_ENABLE_DEBUG_INFO = NO; 403 | SDKROOT = iphoneos; 404 | SUPPORTED_PLATFORMS = iphoneos; 405 | SWIFT_COMPILATION_MODE = wholemodule; 406 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | VALIDATE_PRODUCT = YES; 409 | }; 410 | name = Release; 411 | }; 412 | 97C147061CF9000F007C117D /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 415 | buildSettings = { 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | CLANG_ENABLE_MODULES = YES; 418 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 419 | ENABLE_BITCODE = NO; 420 | INFOPLIST_FILE = Runner/Info.plist; 421 | LD_RUNPATH_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "@executable_path/Frameworks", 424 | ); 425 | PRODUCT_BUNDLE_IDENTIFIER = com.example.irNet; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 428 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 429 | SWIFT_VERSION = 5.0; 430 | VERSIONING_SYSTEM = "apple-generic"; 431 | }; 432 | name = Debug; 433 | }; 434 | 97C147071CF9000F007C117D /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CLANG_ENABLE_MODULES = YES; 440 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 441 | ENABLE_BITCODE = NO; 442 | INFOPLIST_FILE = Runner/Info.plist; 443 | LD_RUNPATH_SEARCH_PATHS = ( 444 | "$(inherited)", 445 | "@executable_path/Frameworks", 446 | ); 447 | PRODUCT_BUNDLE_IDENTIFIER = com.example.irNet; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 450 | SWIFT_VERSION = 5.0; 451 | VERSIONING_SYSTEM = "apple-generic"; 452 | }; 453 | name = Release; 454 | }; 455 | /* End XCBuildConfiguration section */ 456 | 457 | /* Begin XCConfigurationList section */ 458 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147031CF9000F007C117D /* Debug */, 462 | 97C147041CF9000F007C117D /* Release */, 463 | 249021D3217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | 97C147061CF9000F007C117D /* Debug */, 472 | 97C147071CF9000F007C117D /* Release */, 473 | 249021D4217E4FDB00AE95B9 /* Profile */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | /* End XCConfigurationList section */ 479 | }; 480 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 481 | } 482 | -------------------------------------------------------------------------------- /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 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/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 | CFBundleDisplayName 8 | Ir Net 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ir_net 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'home.dart'; 4 | 5 | class App extends StatelessWidget { 6 | const App({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return MaterialApp( 11 | title: 'IRNet', 12 | debugShowCheckedModeBanner: false, 13 | theme: ThemeData( 14 | primarySwatch: Colors.blue, 15 | ), 16 | home: const HomePage(), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:connectivity_plus/connectivity_plus.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_speedtest/flutter_speedtest.dart'; 8 | import 'package:http/http.dart' as http; 9 | import 'package:http/io_client.dart'; 10 | import 'package:ir_net/data/leak_item.dart'; 11 | import 'package:ir_net/data/shared_preferences.dart'; 12 | import 'package:ir_net/utils/cmd.dart'; 13 | import 'package:ir_net/utils/http.dart'; 14 | import 'package:ir_net/utils/system_tray.dart'; 15 | import 'package:latlng/latlng.dart'; 16 | import 'package:live_event/live_event.dart'; 17 | import 'package:rxdart/rxdart.dart'; 18 | import 'package:win_toast/win_toast.dart'; 19 | 20 | import 'utils/kerio.dart'; 21 | 22 | class AppBloc with AppSystemTray { 23 | final _latLng = StreamController(); 24 | final _ipLookupResult = BehaviorSubject(); 25 | final _clearLeakInput = LiveEvent(); 26 | final _leakChecklist = BehaviorSubject>(); 27 | final _localNetwork = BehaviorSubject(); 28 | final _ping = BehaviorSubject(); 29 | final _downloadSpeed = BehaviorSubject(); 30 | final _uploadSpeed = BehaviorSubject(); 31 | final _speedTestStatus = BehaviorSubject(); 32 | 33 | bool _isPingingGoogle = false; 34 | bool _foundALeakedSite = false; 35 | String? _proxyServer; 36 | String? _leakInput; 37 | 38 | Stream get latLng => _latLng.stream; 39 | Stream get ipLookupResult => _ipLookupResult.stream; 40 | Stream get clearLeakInput => _clearLeakInput.stream; 41 | Stream> get leakChecklist => _leakChecklist.stream; 42 | Stream get localNetwork => _localNetwork.stream; 43 | Stream get ping => _ping.stream; 44 | Stream get downloadSpeed => _downloadSpeed.stream; 45 | Stream get uploadSpeed => _uploadSpeed.stream; 46 | Stream get speedTestStatus => _speedTestStatus.stream; 47 | 48 | final speedtest = FlutterSpeedtest( 49 | baseUrl: 'http://speedtest.jaosing.com:8080', 50 | pathDownload: '/download', 51 | pathUpload: '/upload', 52 | pathResponseTime: '/ping', 53 | ); 54 | 55 | void onLeakInputChanged(String value) { 56 | _leakInput = value; 57 | } 58 | 59 | void initialize() { 60 | initSystemTray(); 61 | _pingGoogle(); 62 | _runIpCheckInfinitely(); 63 | _runKerioCheckInfinitely(); 64 | _subscribeConnectivityChange(); 65 | _initializeLeakChecklist(); 66 | } 67 | 68 | void _initializeLeakChecklist() async { 69 | await _updateLeakChecklist(); 70 | _verifyLeakedSites(); 71 | } 72 | 73 | void onDeleteLeakItemClick(LeakItem item) async { 74 | await AppSharedPreferences.removeFromLeakChecklist(item.url); 75 | _updateLeakChecklist(); 76 | } 77 | 78 | void onAddLeakItemClick() async { 79 | if (_leakInput == null || _leakInput?.trim().isEmpty == true) { 80 | WinToast.instance().showToast(type: ToastType.text01, title: 'No input entered!'); 81 | return; 82 | } 83 | if ((await AppSharedPreferences.leakChecklist).contains(_leakInput)) { 84 | WinToast.instance().showToast(type: ToastType.text01, title: 'Repetitive input not allowed!'); 85 | return; 86 | } 87 | await AppSharedPreferences.addToLeakChecklist(_leakInput!); 88 | _updateLeakChecklist(); 89 | _clearLeakInput.fire(); 90 | _leakInput = null; 91 | } 92 | 93 | Future _updateLeakChecklist() async { 94 | _leakChecklist.value = 95 | (await AppSharedPreferences.leakChecklist).map((e) => LeakItem(e)).toList(); 96 | } 97 | 98 | void _verifyLeakedSites() async { 99 | _foundALeakedSite = false; 100 | _updateCountryTrayIcon(); 101 | final checklist = _leakChecklist.valueOrNull; 102 | if (checklist != null) { 103 | for (var item in checklist) { 104 | _checkLeakedSite(item); 105 | } 106 | } 107 | } 108 | 109 | IOClient get _client { 110 | final httpClient = HttpClient(); 111 | if (_proxyServer != null) { 112 | httpClient.findProxy = (uri) { 113 | return 'PROXY $_proxyServer'; 114 | }; 115 | } else { 116 | httpClient.findProxy = null; 117 | } 118 | return IOClient(httpClient); 119 | } 120 | 121 | Future _checkLeakedSite(LeakItem item) async { 122 | item.status = LeakStatus.loading; 123 | _replaceLeakItemInChecklist(item); 124 | try { 125 | final url = Uri.parse(item.url); 126 | final response = await _client.get(url).timeout(const Duration(seconds: 10)); 127 | if (response.statusCode == 200) { 128 | item.status = LeakStatus.passed; 129 | } else { 130 | item.status = LeakStatus.failed; 131 | } 132 | debugPrint('leak detection for $url => ${response.bodyBytes.length ~/ 1024} Kilobytes'); 133 | } on Exception catch (ex) { 134 | item.status = LeakStatus.failed; 135 | _checkNetworkRefuseException(ex); 136 | } 137 | if (item.status == LeakStatus.failed) { 138 | _foundALeakedSite = true; 139 | _updateCountryTrayIcon(); 140 | } 141 | _replaceLeakItemInChecklist(item); 142 | } 143 | 144 | void _replaceLeakItemInChecklist(LeakItem item) { 145 | _leakChecklist.value = _leakChecklist.value.map((e) => e.url == item.url ? item : e).toList(); 146 | } 147 | 148 | void _subscribeConnectivityChange() { 149 | Connectivity().onConnectivityChanged.listen((ConnectivityResult result) async { 150 | if (result != ConnectivityResult.none) { 151 | await _checkProxySettings(); 152 | _checkIpLocation(); 153 | _verifyLeakedSites(); 154 | _checkPing(); 155 | } else { 156 | setSystemTrayStatusToOffline(); 157 | } 158 | }); 159 | } 160 | 161 | void _checkPing() async { 162 | _ping.value = await HttpUtils.measureHttpPing(); 163 | } 164 | 165 | void _runIpCheckInfinitely() async { 166 | while (true) { 167 | await _checkProxySettings(); 168 | _checkIpLocation(); 169 | await Future.delayed(const Duration(seconds: 20)); 170 | } 171 | } 172 | 173 | void _runKerioCheckInfinitely() async { 174 | while (true) { 175 | _checkKerioBalance(); 176 | await Future.delayed(const Duration(seconds: 60)); 177 | } 178 | } 179 | 180 | void _pingGoogle() async { 181 | try { 182 | _isPingingGoogle = true; 183 | final url = Uri.parse('https://google.com'); 184 | await _client.get(url).timeout(const Duration(seconds: 5)); 185 | } on TimeoutException { 186 | _isPingingGoogle = false; 187 | _checkNetworkConnectivity(); 188 | return; 189 | } on SocketException catch (ex) { 190 | _checkNetworkRefuseException(ex); 191 | } 192 | _isPingingGoogle = false; 193 | } 194 | 195 | void _checkNetworkRefuseException(Exception ex) { 196 | if (ex is SocketException && 197 | ex.message.startsWith('The remote computer refused the network connection.')) { 198 | _proxyServer = null; 199 | } 200 | } 201 | 202 | Future _checkIpLocation() async { 203 | http.Response response; 204 | try { 205 | final ipv4 = (await _client.get(Uri.parse("https://api.ipify.org"))).body; 206 | final uri = Uri.parse('http://ip-api.com/json/$ipv4?fields=1057497'); 207 | response = await _client.get(uri).timeout(const Duration(seconds: 10)); 208 | } on TimeoutException { 209 | _checkNetworkConnectivity(); 210 | return; 211 | } on SocketException catch (ex) { 212 | _checkNetworkRefuseException(ex); 213 | return; 214 | } on Exception { 215 | _checkNetworkConnectivity(); 216 | return; 217 | } 218 | final json = jsonDecode(response.body); 219 | if (json['lat'] != null && json['lon'] != null) { 220 | _latLng.sink.add(LatLng(json['lat'], json['lon'])); 221 | } 222 | _ipLookupResult.value = json; 223 | _updateCountryTrayIcon(); 224 | } 225 | 226 | Future _checkProxySettings() async { 227 | final localNetwork = await AppCmd.getLocalNetworkInfo(); 228 | _localNetwork.value = localNetwork; 229 | final proxyResult = await AppCmd.getProxySettings(); 230 | var shouldRefreshLeakedSites = false; 231 | if ((proxyResult.proxyEnabled && _proxyServer != proxyResult.proxyServer) || 232 | (!proxyResult.proxyEnabled && _proxyServer != null)) { 233 | // there was a change in proxy settings 234 | shouldRefreshLeakedSites = true; 235 | } 236 | if (proxyResult.proxyEnabled) { 237 | _proxyServer = proxyResult.proxyServer; 238 | } else { 239 | _proxyServer = null; 240 | } 241 | if (shouldRefreshLeakedSites) { 242 | _verifyLeakedSites(); 243 | } 244 | } 245 | 246 | void _checkKerioBalance() async { 247 | final (total, remaining) = await KerioUtils.getAccountBalance(); 248 | var lowBalanceToastCount = await AppSharedPreferences.kerioLowBalanceToastCount; 249 | var lastToastDate = await AppSharedPreferences.kerioLowBalanceToastDate; 250 | 251 | final now = DateTime.now(); 252 | final today = DateTime(now.year, now.month, now.day); 253 | 254 | if (lastToastDate == null || DateTime.parse(lastToastDate).isBefore(today)) { 255 | // New day, reset count 256 | lowBalanceToastCount = 0; 257 | await AppSharedPreferences.setKerioLowBalanceToastCount(0); 258 | await AppSharedPreferences.setKerioLowBalanceToastDate(today.toIso8601String()); 259 | } 260 | 261 | if (remaining < 1073741824 && lowBalanceToastCount < 2) { 262 | WinToast.instance().showToast(type: ToastType.text01, title: 'Less than 1 GB is left in your kerio account!'); 263 | await AppSharedPreferences.setKerioLowBalanceToastCount(lowBalanceToastCount + 1); 264 | await AppSharedPreferences.setKerioLowBalanceToastDate(today.toIso8601String()); 265 | } 266 | } 267 | 268 | void _checkNetworkConnectivity() async { 269 | final result = await (Connectivity().checkConnectivity()); 270 | if (result == ConnectivityResult.none) { 271 | setSystemTrayStatusToOffline(); 272 | } else { 273 | setSystemTrayStatusToNetworkError(); 274 | if (!_isPingingGoogle) { 275 | _pingGoogle(); 276 | } 277 | } 278 | } 279 | 280 | void onConnectionTestClick() async { 281 | _downloadSpeed.value = 0; 282 | _uploadSpeed.value = 0; 283 | _speedTestStatus.value = 'Running'; 284 | speedtest.getDataspeedtest( 285 | downloadOnProgress: ((percent, transferRate) { 286 | _downloadSpeed.value = transferRate; 287 | }), 288 | uploadOnProgress: ((percent, transferRate) { 289 | _uploadSpeed.value = transferRate; 290 | }), 291 | progressResponse: ((responseTime, jitter) { 292 | // nothing to do 293 | }), 294 | onError: ((errorMessage) { 295 | _downloadSpeed.value = 0; 296 | _uploadSpeed.value = 0; 297 | _speedTestStatus.value = 'Error'; 298 | }), 299 | onDone: () { 300 | _speedTestStatus.value = 'Done'; 301 | }, 302 | ); 303 | } 304 | 305 | void onExitClick() { 306 | destroySystemTray(); 307 | exit(0); 308 | } 309 | 310 | void onRefreshButtonClick() async { 311 | _handleRefresh(); 312 | } 313 | 314 | @override 315 | void onSystemTrayRefreshButtonClick() { 316 | _handleRefresh(); 317 | } 318 | 319 | void _handleRefresh() async { 320 | await _checkProxySettings(); 321 | _checkPing(); 322 | _pingGoogle(); 323 | _checkIpLocation(); 324 | _verifyLeakedSites(); 325 | } 326 | 327 | void _updateCountryTrayIcon() async { 328 | final json = _ipLookupResult.valueOrNull; 329 | if (json == null) { 330 | return; 331 | } 332 | final country = json['country']; 333 | bool isIran = country == 'Iran'; 334 | var tooltip = ''; 335 | if (_foundALeakedSite) { 336 | tooltip = 'IRNet: $country (Leaked!)'; 337 | } else { 338 | tooltip = 'IRNet: $country'; 339 | } 340 | var globIcon = 'assets/globe.ico'; 341 | if (_foundALeakedSite && (await AppSharedPreferences.showLeakInSysTray)) { 342 | globIcon = 'assets/globe_leaked.ico'; 343 | } 344 | final iconPath = isIran ? 'assets/iran.ico' : globIcon; 345 | updateSysTrayIcon(tooltip, iconPath); 346 | debugPrint('Country => $country'); 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /lib/data/leak_item.dart: -------------------------------------------------------------------------------- 1 | class LeakItem { 2 | LeakItem(this.url, {this.status}); 3 | 4 | final String url; 5 | LeakStatus? status; 6 | 7 | static List prePopulatedUrls() { 8 | return [ 9 | 'https://developer.android.com', 10 | 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/4.1.3/gradle-4.1.3.pom', 11 | 'https://storage.googleapis.com/dartlang-pub-public-packages/packages/live_event-0.0.1.tar.gz' 12 | ]; 13 | } 14 | } 15 | 16 | enum LeakStatus { failed, passed, loading } 17 | -------------------------------------------------------------------------------- /lib/data/shared_preferences.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class AppSharedPreferences { 4 | static Future get kerioIP async { 5 | return (await _preference).getString(_keyKerioIP); 6 | } 7 | 8 | static Future setKerioIP(String value) async { 9 | (await _preference).setString(_keyKerioIP, value); 10 | } 11 | 12 | static Future get kerioUsername async { 13 | return (await _preference).getString(_keyKerioUsername); 14 | } 15 | 16 | static Future setKerioUsername(String value) async { 17 | (await _preference).setString(_keyKerioUsername, value); 18 | } 19 | 20 | static Future get kerioPassword async { 21 | return (await _preference).getString(_keyKerioPassword); 22 | } 23 | 24 | static Future setKerioPassword(String value) async { 25 | (await _preference).setString(_keyKerioPassword, value); 26 | } 27 | 28 | static Future get kerioAutoLogin async { 29 | return (await _preference).getBool(_keyKerioAutoLogin) ?? true; 30 | } 31 | 32 | static Future setKerioAutoLogin(bool value) async { 33 | (await _preference).setBool(_keyKerioAutoLogin, value); 34 | } 35 | 36 | static Future get kerioLowBalanceToastCount async { 37 | return (await _preference).getInt(_keyKerioLowBalanceToastCount) ?? 0; 38 | } 39 | 40 | static Future setKerioLowBalanceToastCount(int value) async { 41 | (await _preference).setInt(_keyKerioLowBalanceToastCount, value); 42 | } 43 | 44 | static Future get kerioLowBalanceToastDate async { 45 | return (await _preference).getString(_keyKerioLowBalanceToastDate); 46 | } 47 | 48 | static Future setKerioLowBalanceToastDate(String value) async { 49 | (await _preference).setString(_keyKerioLowBalanceToastDate, value); 50 | } 51 | 52 | static Future get showLeakInSysTray async { 53 | return (await _preference).getBool(_keyShowLeakInSysTray) ?? true; 54 | } 55 | 56 | static Future setShowLeakInSysTray(bool value) async { 57 | (await _preference).setBool(_keyShowLeakInSysTray, value); 58 | } 59 | 60 | static Future get isLeakPrePopulated async { 61 | return (await _preference).getBool(_keyIsLeakPrePopulated) ?? false; 62 | } 63 | 64 | static Future setIsLeakPrePopulated(bool value) async { 65 | (await _preference).setBool(_keyIsLeakPrePopulated, value); 66 | } 67 | 68 | static Future> get leakChecklist async { 69 | return ((await _preference).getString(_keyLeakCheckList) ?? '').split(';'); 70 | } 71 | 72 | static Future addToLeakChecklist(String value) async { 73 | var checklist = ((await _preference).getString(_keyLeakCheckList) ?? ''); 74 | if (checklist == '') { 75 | checklist += value; 76 | } else { 77 | checklist += ';$value'; 78 | } 79 | (await _preference).setString(_keyLeakCheckList, checklist); 80 | } 81 | 82 | static Future removeFromLeakChecklist(String value) async { 83 | final previousChecklist = await leakChecklist; 84 | previousChecklist.removeWhere((element) => element == value); 85 | (await _preference).setString(_keyLeakCheckList, previousChecklist.join(';')); 86 | } 87 | 88 | static SharedPreferences? __instance; 89 | static Future get _preference async { 90 | __instance ??= await SharedPreferences.getInstance(); 91 | return __instance!; 92 | } 93 | 94 | static const _keyIsLeakPrePopulated = 'isLeakPrePopulated'; 95 | static const _keyShowLeakInSysTray = 'showLeakInSysTray'; 96 | static const _keyLeakCheckList = 'leakChecklist'; 97 | static const _keyKerioIP = 'kerioIP'; 98 | static const _keyKerioUsername = 'kerioUsername'; 99 | static const _keyKerioPassword = 'kerioPassword'; 100 | static const _keyKerioAutoLogin = 'kerioAutoLogin'; 101 | static const _keyKerioLowBalanceToastCount = 'kerioLowBalanceToastCount'; 102 | static const _keyKerioLowBalanceToastDate = 'kerioLowBalanceToastDate'; 103 | } -------------------------------------------------------------------------------- /lib/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ir_net/views/connection.dart'; 3 | import 'package:ir_net/views/ip_stat.dart'; 4 | import 'package:ir_net/views/leak.dart'; 5 | import 'package:ir_net/views/options.dart'; 6 | 7 | import 'main.dart'; 8 | 9 | class HomePage extends StatefulWidget { 10 | const HomePage({super.key}); 11 | 12 | @override 13 | State createState() => _HomePageState(); 14 | } 15 | 16 | class _HomePageState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | body: Center( 21 | child: SingleChildScrollView( 22 | child: Column( 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | const Row( 27 | mainAxisAlignment: MainAxisAlignment.center, 28 | crossAxisAlignment: CrossAxisAlignment.start, 29 | children: [ 30 | LeakView(), 31 | SizedBox(width: 64), 32 | IpStatView(), 33 | ], 34 | ), 35 | const SizedBox(height: 16), 36 | const Row( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | AppOptions(), 40 | SizedBox(width: 64), 41 | Connection() 42 | ], 43 | ), 44 | const SizedBox(height: 24), 45 | Row( 46 | mainAxisAlignment: MainAxisAlignment.center, 47 | children: [ 48 | exitButton(), 49 | const SizedBox(width: 16), 50 | refreshButton(), 51 | ], 52 | ), 53 | ], 54 | ), 55 | ), 56 | ), 57 | ); 58 | } 59 | 60 | Widget exitButton() { 61 | return ElevatedButton( 62 | style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(80, 56))), 63 | onPressed: bloc.onExitClick, 64 | child: const Text('Exit'), 65 | ); 66 | } 67 | 68 | Widget refreshButton() { 69 | return ElevatedButton( 70 | style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(80, 56))), 71 | onPressed: bloc.onRefreshButtonClick, 72 | child: const Text('Refresh'), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:ir_net/data/leak_item.dart'; 5 | import 'package:ir_net/data/shared_preferences.dart'; 6 | import 'package:launch_at_startup/launch_at_startup.dart'; 7 | import 'package:package_info_plus/package_info_plus.dart'; 8 | import 'package:win_toast/win_toast.dart'; 9 | import 'package:window_manager/window_manager.dart'; 10 | import 'package:windows_single_instance/windows_single_instance.dart'; 11 | 12 | import 'app.dart'; 13 | import 'bloc.dart'; 14 | 15 | final bloc = AppBloc(); 16 | 17 | void main() async { 18 | WidgetsFlutterBinding.ensureInitialized(); 19 | await initWindowManager(); 20 | await initSingleInstance(); 21 | await initWinToast(); 22 | await initLaunchAtStartup(); 23 | await initSharedPreferences(); 24 | bloc.initialize(); 25 | runApp(const App()); 26 | } 27 | 28 | Future initWindowManager() async { 29 | await windowManager.ensureInitialized(); 30 | WindowOptions windowOptions = const WindowOptions( 31 | size: Size(1000, 780), 32 | ); 33 | windowManager.waitUntilReadyToShow(windowOptions, () { 34 | windowManager.setTitle("IRNet: freedom does not have a price"); 35 | },); 36 | } 37 | 38 | Future initSingleInstance() async { 39 | await WindowsSingleInstance.ensureSingleInstance([], "pipeMain"); 40 | } 41 | 42 | Future initWinToast() async { 43 | await WinToast.instance().initialize( 44 | appName: 'IRNet', 45 | productName: 'IRNet', 46 | companyName: 'BuildToApp', 47 | ); 48 | } 49 | 50 | Future initSharedPreferences() async { 51 | if (!(await AppSharedPreferences.isLeakPrePopulated)) { 52 | for (var url in LeakItem.prePopulatedUrls()) { 53 | await AppSharedPreferences.addToLeakChecklist(url); 54 | } 55 | await AppSharedPreferences.setIsLeakPrePopulated(true); 56 | } 57 | if ((await AppSharedPreferences.kerioIP) == null) { 58 | await AppSharedPreferences.setKerioIP('172.18.18.1:4080'); 59 | } 60 | } 61 | 62 | Future initLaunchAtStartup() async { 63 | PackageInfo packageInfo = await PackageInfo.fromPlatform(); 64 | LaunchAtStartup.instance.setup( 65 | appName: packageInfo.appName, 66 | appPath: Platform.resolvedExecutable, 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /lib/utils/cmd.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AppCmd { 4 | static Future getProxySettings() async { 5 | var executable = r'reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings"'; 6 | var result = await Process.run(executable, []); 7 | final stdout = result.stdout.toString(); 8 | String? proxyServer; 9 | var proxyIsEnabled = false; 10 | if (stdout.isNotEmpty) { 11 | final lines = stdout.split('\r\n'); 12 | for (var element in lines) { 13 | if (element.contains('ProxyEnable') && element.endsWith('0x1')) { 14 | proxyIsEnabled = true; 15 | } else if (element.contains('ProxyEnable') && element.endsWith('0x0')) { 16 | proxyIsEnabled = false; 17 | } 18 | if (element.contains('ProxyServer')) { 19 | proxyServer = element.split(' ').last; 20 | break; 21 | } 22 | } 23 | } 24 | return WinRegProxyResult(proxyIsEnabled, proxyServer); 25 | } 26 | 27 | static Future getLocalNetworkInfo() async { 28 | final arpAddresses = await _getArpAddresses(); 29 | var command = await Process.run(r'ipconfig', [r'/all']); 30 | final stdout = command.stdout.toString(); 31 | if (stdout.isEmpty) { 32 | throw r'Cannot resolve command ipconfig /all'; 33 | } 34 | final lines = stdout.split('\r\n'); 35 | final interfaces = []; 36 | var dns1 = ''; 37 | var dns2 = ''; 38 | var emptyLinesCount = 0; 39 | var interfaceName = ''; 40 | for (var line in lines) { 41 | if (emptyLinesCount == 2) { 42 | emptyLinesCount = 0; 43 | } 44 | if (line.isEmpty) { 45 | emptyLinesCount++; 46 | } 47 | if (emptyLinesCount == 1 && line.contains(' adapter ')) { 48 | interfaceName = line.split(' adapter ')[1].replaceAll(':', ''); 49 | } 50 | if (emptyLinesCount > 0) { 51 | continue; 52 | } 53 | if (line.contains(':') && line.contains('IPv4 Address')) { 54 | for (var ip in arpAddresses) { 55 | final ipPartOfLine = line.split(':')[1].trimLeft(); 56 | if (interfaceName.isNotEmpty && ipPartOfLine.startsWith(ip)) { 57 | interfaces.add(NetworkInterface(interfaceName, ip)); 58 | break; 59 | } 60 | } 61 | } 62 | if (dns1.isNotEmpty && dns2.isEmpty && !line.contains(':')) { 63 | dns2 = line.trimLeft(); 64 | } 65 | if (dns2.isEmpty && line.contains('DNS Servers')) { 66 | dns1 = line.split(':')[1].trimLeft(); 67 | } 68 | } 69 | return LocalNetworksResult(interfaces, [dns1, dns2]); 70 | } 71 | 72 | static Future> _getArpAddresses() async { 73 | final result = []; 74 | var command = await Process.run(r'arp', [r'-a']); 75 | final stdout = command.stdout.toString(); 76 | if (stdout.isNotEmpty) { 77 | final lines = stdout.split('\r\n'); 78 | for (var element in lines) { 79 | if (element.startsWith('Interface: ')) { 80 | result.add(element.split(' ')[1]); 81 | } 82 | } 83 | } 84 | return result; 85 | } 86 | } 87 | 88 | class LocalNetworksResult { 89 | LocalNetworksResult(this.interfaces, this.dns); 90 | 91 | final List interfaces; 92 | final List dns; 93 | } 94 | 95 | class NetworkInterface { 96 | NetworkInterface(this.interfaceName, this.ipv4); 97 | 98 | final String interfaceName; 99 | final String ipv4; 100 | } 101 | 102 | class WinRegProxyResult { 103 | WinRegProxyResult(this.proxyEnabled, this.proxyServer); 104 | 105 | final bool proxyEnabled; 106 | final String? proxyServer; 107 | } 108 | -------------------------------------------------------------------------------- /lib/utils/http.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | 5 | class HttpUtils { 6 | static final Dio _dio = Dio(); 7 | 8 | static Future measureHttpPing({ 9 | String url = "https://www.gstatic.com/generate_204", 10 | Duration timeout = const Duration(seconds: 5), 11 | }) async { 12 | _dio.options = BaseOptions(headers: { 13 | HttpHeaders.connectionHeader: 'keep-alive', // Ensure keep-alive headers 14 | }); 15 | try { 16 | await _dio.get(url).timeout(timeout); // just a warm up connection to get a reliable result 17 | final firstTime = DateTime.now().millisecondsSinceEpoch; 18 | final response = await _dio.get(url).timeout(timeout); 19 | final secondTime = DateTime.now().millisecondsSinceEpoch; 20 | if (response.statusCode == 200 || response.statusCode == 204) { 21 | return (secondTime - firstTime).toDouble(); 22 | } else { 23 | return 0; // server responded but with an error 24 | } 25 | } on Exception catch (_) { 26 | return 0; // timeout or unreachable 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/utils/kerio.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | 5 | import '../data/shared_preferences.dart'; 6 | 7 | class KerioUtils { 8 | static Future<(int, int)> getAccountBalance() async { 9 | final ip = await AppSharedPreferences.kerioIP; 10 | final username = await AppSharedPreferences.kerioUsername; 11 | final password = await AppSharedPreferences.kerioPassword; 12 | 13 | // Step 1: Login and extract cookie 14 | final loginUrl = 'http://$ip/internal/dologin.php'; 15 | final loginResponse = await http.post( 16 | Uri.parse(loginUrl), 17 | body: { 18 | 'kerio_username': username, 19 | 'kerio_password': password, 20 | }, 21 | ); 22 | 23 | final cookieHeader = loginResponse.headers['set-cookie']; 24 | if (cookieHeader == null || !cookieHeader.contains('TOKEN_CONTROL_WEBIFACE=')) { 25 | throw Exception('Login failed or TOKEN_CONTROL_WEBIFACE not found.'); 26 | } 27 | 28 | final cookies = cookieHeader.split(','); 29 | final tokenCookie = cookies 30 | .map((c) => c.trim()) 31 | .firstWhere( 32 | (c) => c.startsWith('TOKEN_CONTROL_WEBIFACE='), 33 | orElse: () => throw Exception('TOKEN_CONTROL_WEBIFACE not found in cookies'), 34 | ); 35 | 36 | final token = tokenCookie.split('=')[1].split(';')[0]; 37 | 38 | final rawCookies = cookieHeader.split(','); 39 | final cookieMap = {}; 40 | for (var cookie in rawCookies) { 41 | final parts = cookie.split(';')[0].trim(); // Take only key=value part 42 | final kv = parts.split('='); 43 | if (kv.length == 2) { 44 | cookieMap[kv[0]] = kv[1]; 45 | } 46 | } 47 | final cookieHeaderValue = cookieMap.entries.map((e) => '${e.key}=${e.value}').join('; '); 48 | 49 | // Step 2: Prepare headers 50 | final balanceUrl = 'http://$ip/lib/api/jsonrpc/'; 51 | final headers = { 52 | 'Accept': '*/*', 53 | 'Accept-Language': 'en-US,en;q=0.9,fa;q=0.8', 54 | 'Connection': 'keep-alive', 55 | 'Content-Type': 'application/json', 56 | 'Origin': 'http://$ip', 57 | 'Referer': 'http://$ip/', 58 | 'User-Agent': 59 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 60 | 'X-Requested-With': 'XMLHttpRequest', 61 | 'X-Token': token, 62 | 'Cookie': cookieHeaderValue 63 | }; 64 | 65 | // Step 3: Body 66 | final body = jsonEncode({ 67 | "jsonrpc": "2.0", 68 | "id": 1, 69 | "method": "Batch.run", 70 | "params": { 71 | "commandList": [ 72 | {"method": "MyAccount.get"}, 73 | {"method": "MyAccount.getRasIntefaces"} 74 | ] 75 | } 76 | }); 77 | 78 | // Step 4: Send request 79 | final balanceResponse = await http.post( 80 | Uri.parse(balanceUrl), 81 | headers: headers, 82 | body: body, 83 | ); 84 | 85 | if (balanceResponse.statusCode != 200) { 86 | throw Exception('Failed to fetch balance data'); 87 | } 88 | 89 | final data = jsonDecode(balanceResponse.body); 90 | final quota = data['result'][0]['result']['quota']['month']; 91 | 92 | final total = int.parse(quota['value']); 93 | final down = int.parse(quota['down']); 94 | final up = int.parse(quota['up']); 95 | final remaining = total - (down + up); 96 | 97 | return (total, remaining); 98 | } 99 | 100 | static String formatBytes(int bytes) { 101 | const kb = 1024; 102 | const mb = kb * 1024; 103 | const gb = mb * 1024; 104 | 105 | if (bytes >= gb) { 106 | return '${(bytes / gb).toStringAsFixed(2)} GB'; 107 | } else if (bytes >= mb) { 108 | return '${(bytes / mb).toStringAsFixed(2)} MB'; 109 | } else if (bytes >= kb) { 110 | return '${(bytes / kb).toStringAsFixed(2)} KB'; 111 | } else { 112 | return '$bytes B'; 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /lib/utils/system_tray.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:system_tray/system_tray.dart'; 5 | 6 | mixin AppSystemTray { 7 | final SystemTray _systemTray = SystemTray(); 8 | final AppWindow _appWindow = AppWindow(); 9 | 10 | void onSystemTrayRefreshButtonClick(); 11 | 12 | void updateSysTrayIcon(String tooltip, String iconPath) { 13 | _systemTray.setToolTip(tooltip); 14 | _systemTray.setImage(iconPath); 15 | } 16 | 17 | void setSystemTrayStatusToOffline() { 18 | _systemTray.setImage('assets/offline.ico'); 19 | _systemTray.setToolTip('IRNet: OFFLINE'); 20 | } 21 | 22 | void setSystemTrayStatusToNetworkError() { 23 | _systemTray.setImage('assets/network_error.ico'); 24 | _systemTray.setToolTip('IRNet: Network error'); 25 | } 26 | 27 | void destroySystemTray() { 28 | _systemTray.destroy(); 29 | } 30 | 31 | Future initSystemTray() async { 32 | await _systemTray.initSystemTray( 33 | title: "system tray", 34 | iconPath: 'assets/loading.ico', 35 | ); 36 | final Menu menu = Menu(); 37 | await menu.buildFrom([ 38 | MenuItemLabel(label: 'Show', onClicked: (menuItem) => _appWindow.show()), 39 | MenuItemLabel(label: 'Hide', onClicked: (menuItem) => _appWindow.hide()), 40 | MenuItemLabel( 41 | label: 'Refresh', 42 | onClicked: (menuItem) { 43 | onSystemTrayRefreshButtonClick(); 44 | }, 45 | ), 46 | MenuItemLabel( 47 | label: 'Exit', 48 | onClicked: (menuItem) { 49 | _systemTray.destroy(); 50 | exit(0); 51 | }, 52 | ), 53 | ]); 54 | 55 | await _systemTray.setContextMenu(menu); 56 | _systemTray.registerSystemTrayEventHandler((eventName) { 57 | debugPrint("eventName: $eventName"); 58 | if (eventName == kSystemTrayEventClick) { 59 | _appWindow.show(); 60 | } else if (eventName == kSystemTrayEventRightClick) { 61 | _systemTray.popUpContextMenu(); 62 | } 63 | }); 64 | 65 | _appWindow.hide(); 66 | _systemTray.setToolTip('IRNet: NOT READY'); 67 | } 68 | } -------------------------------------------------------------------------------- /lib/views/connection.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../main.dart'; 4 | 5 | class Connection extends StatefulWidget { 6 | const Connection({super.key}); 7 | 8 | @override 9 | State createState() => _ConnectionState(); 10 | } 11 | 12 | class _ConnectionState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | width: 400, 17 | height: 100, 18 | decoration: BoxDecoration( 19 | border: Border.all(color: Colors.blueAccent), 20 | borderRadius: BorderRadius.circular(8), 21 | ), 22 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), 23 | child: Row( 24 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 25 | crossAxisAlignment: CrossAxisAlignment.center, 26 | children: [ 27 | results(), 28 | const SizedBox(width: 24), 29 | testButton(), 30 | ], 31 | ), 32 | ); 33 | } 34 | 35 | Widget results() { 36 | return Column( 37 | crossAxisAlignment: CrossAxisAlignment.start, 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | children: [ 40 | pingRow(), 41 | downloadRow(), 42 | uploadRow(), 43 | ], 44 | ); 45 | } 46 | 47 | Widget pingRow() { 48 | return StreamBuilder( 49 | stream: bloc.ping, 50 | builder: (context, snapshot) { 51 | final value = snapshot.data ?? 0.0; 52 | return Row( 53 | crossAxisAlignment: CrossAxisAlignment.center, 54 | children: [ 55 | const Icon(Icons.timer, color: Colors.teal, size: 18), 56 | const SizedBox(width: 4), 57 | Text( 58 | 'Ping: ${value.toInt()} ms', 59 | textAlign: TextAlign.center, 60 | style: const TextStyle(fontSize: 16, color: Colors.teal, fontWeight: FontWeight.bold), 61 | ) 62 | ], 63 | ); 64 | }, 65 | ); 66 | } 67 | 68 | Widget downloadRow() { 69 | return StreamBuilder( 70 | stream: bloc.downloadSpeed, 71 | builder: (context, snapshot) { 72 | final value = (snapshot.data ?? 0.0).toInt(); 73 | final formattedValue = value == 0 ? '--' : '${value.toInt()} Mb/s'; 74 | return Row( 75 | crossAxisAlignment: CrossAxisAlignment.end, 76 | children: [ 77 | const Icon(Icons.download, color: Colors.deepOrange, size: 18), 78 | const SizedBox(width: 4), 79 | Text( 80 | 'Download speed: $formattedValue', 81 | textAlign: TextAlign.center, 82 | style: const TextStyle( 83 | fontSize: 16, color: Colors.deepOrange, fontWeight: FontWeight.bold), 84 | ), 85 | ], 86 | ); 87 | }, 88 | ); 89 | } 90 | 91 | Widget uploadRow() { 92 | return StreamBuilder( 93 | stream: bloc.uploadSpeed, 94 | builder: (context, snapshot) { 95 | var value = (snapshot.data ?? 0.0).toInt(); 96 | if (value > 500) { 97 | value = 0; 98 | } 99 | final formattedValue = value == 0 ? '--' : '${value.toInt()} Mb/s'; 100 | return Row( 101 | crossAxisAlignment: CrossAxisAlignment.end, 102 | children: [ 103 | const Icon(Icons.upload, color: Colors.deepPurple, size: 18), 104 | const SizedBox(width: 4), 105 | Text( 106 | 'Upload speed: $formattedValue', 107 | textAlign: TextAlign.center, 108 | style: const TextStyle( 109 | fontSize: 16, color: Colors.deepPurple, fontWeight: FontWeight.bold), 110 | ), 111 | ], 112 | ); 113 | }, 114 | ); 115 | } 116 | 117 | Widget testButton() { 118 | return StreamBuilder(stream: bloc.speedTestStatus, builder: (context, snapshot) { 119 | final value = snapshot.data ?? 'Not started'; 120 | 121 | return Column( 122 | mainAxisAlignment: MainAxisAlignment.center, 123 | children: [ 124 | ElevatedButton( 125 | onPressed: () { 126 | if (value != 'Running') { 127 | bloc.onConnectionTestClick(); 128 | } 129 | }, 130 | style: ElevatedButton.styleFrom( 131 | shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8))), 132 | padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 133 | backgroundColor: value == 'Running' ? Colors.grey : Colors.blue, 134 | ), 135 | child: const Text('Test', style: TextStyle(color: Colors.white, fontSize: 18)), 136 | ), 137 | const SizedBox(height: 8), 138 | if (value == 'Running') 139 | const SizedBox(width: 20, height: 20, child: CircularProgressIndicator()) 140 | else 141 | Text(value) 142 | ], 143 | ); 144 | }); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /lib/views/ip_stat.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:math'; 3 | 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:ir_net/main.dart'; 7 | import 'package:ir_net/utils/cmd.dart'; 8 | import 'package:latlng/latlng.dart'; 9 | import 'package:map/map.dart'; 10 | 11 | class IpStatView extends StatefulWidget { 12 | const IpStatView({super.key}); 13 | 14 | @override 15 | State createState() => _IpStatViewState(); 16 | } 17 | 18 | class _IpStatViewState extends State { 19 | late MapController controller; 20 | 21 | @override 22 | void initState() { 23 | controller = MapController( 24 | location: const LatLng(35.69439, 51.42151), 25 | zoom: 8, 26 | ); 27 | bloc.latLng.listen((latLng) { 28 | setState(() { 29 | controller.center = latLng; 30 | }); 31 | }); 32 | super.initState(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Column( 38 | crossAxisAlignment: CrossAxisAlignment.center, 39 | mainAxisAlignment: MainAxisAlignment.center, 40 | children: [ 41 | locationTag(), 42 | const SizedBox(height: 16), 43 | map(), 44 | const SizedBox(height: 16), 45 | networkInfo(), 46 | const SizedBox(height: 16), 47 | lookupResult(), 48 | ], 49 | ); 50 | } 51 | 52 | Widget networkInfo() { 53 | return StreamBuilder( 54 | stream: bloc.localNetwork, 55 | builder: (context, snapshot) { 56 | final data = snapshot.data; 57 | if (data == null) { 58 | return const SizedBox.shrink(); 59 | } 60 | return SizedBox( 61 | width: 400, 62 | child: Column( 63 | children: [ 64 | ipAddress(), 65 | coloredText('DNS records: ${data.dns[0]}, ${data.dns[1]}'), 66 | coloredText('Local IP Address: ${_localIpText(data.interfaces)}') 67 | ], 68 | ), 69 | ); 70 | }, 71 | ); 72 | } 73 | 74 | String _localIpText(List interfaces) { 75 | var result = ''; 76 | for (var i = 0; i < interfaces.length; i++) { 77 | final inf = interfaces[i]; 78 | if (interfaces.length > 1 && i > 0 && i < interfaces.length) { 79 | result += '\n '; 80 | } 81 | var interfaceName = inf.interfaceName; 82 | if (interfaceName.length > 10) { 83 | interfaceName = interfaceName.substring(0, 5) + "..." + interfaceName.substring(interfaceName.length - 5); 84 | } 85 | result += '${inf.ipv4} ($interfaceName)'; 86 | } 87 | return result; 88 | } 89 | 90 | Widget ipAddress() { 91 | return StreamBuilder( 92 | stream: bloc.ipLookupResult, 93 | builder: (context, snapshot) { 94 | final data = snapshot.data; 95 | if (data == null) { 96 | return const SizedBox.shrink(); 97 | } 98 | return coloredText('Public IP address: ${data['query']}'); 99 | }, 100 | ); 101 | } 102 | 103 | Widget coloredText(String value) { 104 | return Container( 105 | color: Colors.black12, 106 | width: double.infinity, 107 | padding: const EdgeInsets.all(8), 108 | child: Text(value), 109 | ); 110 | } 111 | 112 | Widget lookupResult() { 113 | return StreamBuilder( 114 | stream: bloc.ipLookupResult, 115 | builder: (context, snapshot) { 116 | final data = snapshot.data; 117 | if (data == null) { 118 | return const SizedBox.shrink(); 119 | } 120 | var result = ''; 121 | var longestLength = 1; 122 | for (MapEntry e in (data as LinkedHashMap).entries) { 123 | final len = e.key.toString().length; 124 | if (len > longestLength) { 125 | longestLength = len; 126 | } 127 | } 128 | for (MapEntry e in (data).entries) { 129 | if (e.key == 'lat' || e.key == 'lon' || e.key == 'query' || e.key == 'continent') { 130 | continue; 131 | } 132 | final charCode = '.'.codeUnitAt(0); 133 | final dots = String.fromCharCodes( 134 | List.generate(longestLength - e.key.toString().length, (index) => charCode), 135 | ); 136 | result += '${e.key} $dots................. ${e.value}\n'; 137 | } 138 | return Text(result); 139 | }, 140 | ); 141 | } 142 | 143 | Widget locationTag() { 144 | return StreamBuilder( 145 | stream: bloc.ipLookupResult, 146 | builder: (context, snapshot) { 147 | final data = snapshot.data; 148 | if (data == null) { 149 | return const Text('No data'); 150 | } 151 | return Text('${data['country']}, ${data['city']}'); 152 | }, 153 | ); 154 | } 155 | 156 | Widget map() { 157 | return Stack( 158 | alignment: Alignment.center, 159 | children: [ 160 | SizedBox( 161 | width: 400, 162 | height: 150, 163 | child: mapLayout(), 164 | ), 165 | ], 166 | ); 167 | } 168 | 169 | Widget mapLayout() { 170 | return MapLayout( 171 | controller: controller, 172 | builder: (context, transformer) { 173 | return TileLayer( 174 | builder: (context, x, y, z) { 175 | final tilesInZoom = pow(2.0, z).floor(); 176 | while (x < 0) { 177 | x += tilesInZoom; 178 | } 179 | while (y < 0) { 180 | y += tilesInZoom; 181 | } 182 | x %= tilesInZoom; 183 | y %= tilesInZoom; 184 | final url = 'https://a.tile.openstreetmap.fr/hot/$z/$x/$y.png'; 185 | 186 | return CachedNetworkImage( 187 | imageUrl: url, 188 | fit: BoxFit.cover, 189 | ); 190 | }, 191 | ); 192 | }, 193 | ); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /lib/views/kerio_login.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:ir_net/data/shared_preferences.dart'; 4 | import 'package:ir_net/utils/kerio.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | 7 | class KerioLoginView extends StatefulWidget { 8 | const KerioLoginView({super.key}); 9 | 10 | @override 11 | State createState() => _KerioLoginViewState(); 12 | } 13 | 14 | class _KerioLoginViewState extends State { 15 | final TextEditingController _ipController = TextEditingController(); 16 | final TextEditingController _usernameController = TextEditingController(); 17 | final TextEditingController _passwordController = TextEditingController(); 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | _attemptAutoLogin(); 23 | } 24 | 25 | Future _attemptAutoLogin() async { 26 | final ip = await AppSharedPreferences.kerioIP; 27 | final username = await AppSharedPreferences.kerioUsername; 28 | final password = await AppSharedPreferences.kerioPassword; 29 | final enabled = await AppSharedPreferences.kerioAutoLogin; 30 | 31 | _ipController.text = ip ?? ''; 32 | if (ip != null && username != null && password != null && enabled == true) { 33 | _ipController.text = ip; 34 | _usernameController.text = username; 35 | _passwordController.text = password; 36 | _login(true); 37 | } 38 | } 39 | 40 | void _login(bool auto) async { 41 | final ip = _ipController.text; 42 | final username = _usernameController.text; 43 | final password = _passwordController.text; 44 | 45 | if (ip.isEmpty || username.isEmpty || password.isEmpty) { 46 | _showMessage('Please fill in all fields'); 47 | return; 48 | } 49 | 50 | // Save credentials for auto-login 51 | await AppSharedPreferences.setKerioIP(ip); 52 | await AppSharedPreferences.setKerioUsername(username); 53 | await AppSharedPreferences.setKerioPassword(password); 54 | 55 | final url = 'http://$ip/internal/dologin.php'; 56 | final response = await http.post( 57 | Uri.parse(url), 58 | body: { 59 | 'kerio_username': username, 60 | 'kerio_password': password, 61 | }, 62 | ); 63 | 64 | if (auto) { 65 | return; 66 | } 67 | 68 | _showMessage('Login request sent'); // todo: handle failure case 69 | } 70 | 71 | void _showMessage(String message) { 72 | showDialog( 73 | context: context, 74 | builder: (context) => AlertDialog( 75 | content: Text(message), 76 | actions: [ 77 | TextButton( 78 | onPressed: () => Navigator.of(context).pop(), 79 | child: const Text('OK'), 80 | ), 81 | ], 82 | ), 83 | ); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return SizedBox( 89 | width: 400, 90 | child: Column( 91 | crossAxisAlignment: CrossAxisAlignment.center, 92 | children: [ 93 | ipAndBalanceRow(), 94 | const SizedBox(height: 4), 95 | credentialsRow(), 96 | const SizedBox(height: 8), 97 | loginRow() 98 | ], 99 | ), 100 | ); 101 | } 102 | 103 | Widget ipAndBalanceRow() { 104 | return Row( 105 | children: [ 106 | Expanded( 107 | child: ipInput(), 108 | ), 109 | const SizedBox(width: 4), 110 | Expanded( 111 | child: balance(), 112 | ), 113 | ], 114 | ); 115 | } 116 | 117 | Widget balance() { 118 | return FutureBuilder( 119 | future: KerioUtils.getAccountBalance(), 120 | builder: (context, snapshot) { 121 | var (total, remaining) = snapshot.data ?? (0, 0); 122 | var totalFormatted = total == 0 ? '--' : KerioUtils.formatBytes(total); 123 | var remainingFormatted = remaining == 0 ? '--' : KerioUtils.formatBytes(remaining); 124 | return Container( 125 | padding: const EdgeInsets.only(left: 16), 126 | child: Column( 127 | crossAxisAlignment: CrossAxisAlignment.start, 128 | children: [ 129 | Text( 130 | 'Remaining = $remainingFormatted', 131 | style: TextStyle(color: remaining < 1073741824 ? Colors.red : Colors.black), 132 | ), 133 | Text('Total = $totalFormatted'), 134 | ], 135 | ), 136 | ); 137 | }, 138 | ); 139 | } 140 | 141 | Widget ipInput() { 142 | return TextField( 143 | controller: _ipController, 144 | decoration: InputDecoration( 145 | focusedBorder: const OutlineInputBorder( 146 | borderSide: BorderSide(color: Colors.green), 147 | ), 148 | enabledBorder: const OutlineInputBorder( 149 | borderSide: BorderSide(color: Colors.blue), 150 | ), 151 | hintText: 'Kerio login page IP', 152 | hintStyle: const TextStyle(color: Colors.black38), 153 | suffixIcon: IconButton( 154 | onPressed: () { 155 | final url = _ipController.text.trim(); 156 | if (url.isNotEmpty) { 157 | final uri = Uri.tryParse(url.startsWith('http') ? url : 'http://$url'); 158 | if (uri != null) { 159 | launchUrl(uri); 160 | } 161 | } 162 | }, 163 | icon: Image.asset('assets/kerio.png', width: 24, height: 24), 164 | ), 165 | ), 166 | keyboardType: TextInputType.url, 167 | ); 168 | } 169 | 170 | Widget credentialsRow() { 171 | return Row( 172 | children: [ 173 | Expanded( 174 | child: username(), 175 | ), 176 | const SizedBox(width: 4), 177 | Expanded( 178 | child: password(), 179 | ), 180 | ], 181 | ); 182 | } 183 | 184 | Widget username() { 185 | return TextField( 186 | controller: _usernameController, 187 | decoration: const InputDecoration( 188 | focusedBorder: OutlineInputBorder( 189 | borderSide: BorderSide(color: Colors.green), 190 | ), 191 | enabledBorder: OutlineInputBorder( 192 | borderSide: BorderSide(color: Colors.blue), 193 | ), 194 | hintText: 'Username', 195 | hintStyle: TextStyle(color: Colors.black38), 196 | ), 197 | ); 198 | } 199 | 200 | Widget password() { 201 | return TextField( 202 | controller: _passwordController, 203 | decoration: const InputDecoration( 204 | focusedBorder: OutlineInputBorder( 205 | borderSide: BorderSide(color: Colors.green), 206 | ), 207 | enabledBorder: OutlineInputBorder( 208 | borderSide: BorderSide(color: Colors.blue), 209 | ), 210 | hintText: 'Password', 211 | hintStyle: TextStyle(color: Colors.black38), 212 | ), 213 | obscureText: true, 214 | ); 215 | } 216 | 217 | Widget loginRow() { 218 | return Row( 219 | children: [ 220 | Expanded( 221 | flex: 2, 222 | child: ElevatedButton( 223 | onPressed: () => _login(false), 224 | style: ElevatedButton.styleFrom( 225 | shape: const RoundedRectangleBorder( 226 | borderRadius: BorderRadius.all(Radius.circular(8)), 227 | ), 228 | padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 229 | backgroundColor: Colors.blue, 230 | ), 231 | child: const Text('Login', style: TextStyle(color: Colors.white)), 232 | ), 233 | ), 234 | const SizedBox(width: 10), 235 | Expanded( 236 | child: autoLoginOption(), 237 | ) 238 | ], 239 | ); 240 | } 241 | 242 | Widget autoLoginOption() { 243 | return FutureBuilder( 244 | future: AppSharedPreferences.kerioAutoLogin, 245 | builder: (context, snapshot) { 246 | final value = snapshot.data ?? false; 247 | return CheckboxListTile( 248 | title: const Text('Auto?'), 249 | value: value, 250 | onChanged: (enabled) async { 251 | await AppSharedPreferences.setKerioAutoLogin(enabled ?? false); 252 | setState(() {}); 253 | }, 254 | ); 255 | }, 256 | ); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /lib/views/leak.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ir_net/data/leak_item.dart'; 3 | import 'package:ir_net/main.dart'; 4 | import 'package:ir_net/views/kerio_login.dart'; 5 | import 'package:touch_mouse_behavior/touch_mouse_behavior.dart'; 6 | import 'package:url_launcher/url_launcher.dart'; 7 | 8 | class LeakView extends StatefulWidget { 9 | const LeakView({super.key}); 10 | 11 | @override 12 | State createState() => _LeakViewState(); 13 | } 14 | 15 | class _LeakViewState extends State { 16 | late TextEditingController textInputController; 17 | 18 | @override 19 | void initState() { 20 | textInputController = TextEditingController(); 21 | bloc.clearLeakInput.listen((_) { 22 | textInputController.clear(); 23 | }); 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | textInputController.dispose(); 30 | super.dispose(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Column( 36 | children: [ 37 | const Text('Leak detection'), 38 | const SizedBox(height: 16), 39 | SizedBox( 40 | width: 400, 41 | child: input(), 42 | ), 43 | const SizedBox(height: 16), 44 | items(), 45 | const KerioLoginView() 46 | ], 47 | ); 48 | } 49 | 50 | Widget items() { 51 | return StreamBuilder>( 52 | stream: bloc.leakChecklist, 53 | builder: (context, snapshot) { 54 | final data = snapshot.data; 55 | if (data == null) { 56 | return const SizedBox.shrink(); 57 | } 58 | return SizedBox( 59 | width: 400, 60 | height: 250, 61 | child: TouchMouseScrollable( 62 | child: ListView.builder( 63 | itemCount: data.length, 64 | itemBuilder: (context, index) { 65 | return item(data[index]); 66 | }, 67 | ), 68 | ), 69 | ); 70 | }, 71 | ); 72 | } 73 | 74 | Widget item(LeakItem item) { 75 | return InkWell( 76 | onTap: () { 77 | launchUrl(Uri.parse(item.url)); 78 | }, 79 | child: Container( 80 | width: 400, 81 | decoration: const BoxDecoration( 82 | shape: BoxShape.rectangle, 83 | color: Colors.black12, 84 | borderRadius: BorderRadius.all(Radius.circular(4)), 85 | ), 86 | padding: const EdgeInsets.all(8), 87 | margin: const EdgeInsets.symmetric(vertical: 4), 88 | child: Row( 89 | children: [ 90 | if (item.status == LeakStatus.failed) 91 | const Icon(Icons.remove_circle_outline, color: Colors.red), 92 | if (item.status == LeakStatus.passed) const Icon(Icons.check, color: Colors.green), 93 | if (item.status == LeakStatus.loading) 94 | const SizedBox( 95 | width: 24, 96 | height: 24, 97 | child: CircularProgressIndicator(), 98 | ), 99 | const SizedBox(width: 8), 100 | Expanded( 101 | child: Text( 102 | item.url, 103 | style: const TextStyle(color: Colors.blueAccent), 104 | )), 105 | IconButton( 106 | onPressed: () => bloc.onDeleteLeakItemClick(item), 107 | icon: Icon( 108 | Icons.highlight_remove_outlined, 109 | color: Colors.red.withAlpha(80), 110 | ), 111 | ) 112 | ], 113 | ), 114 | ), 115 | ); 116 | } 117 | 118 | Widget input() { 119 | return TextField( 120 | controller: textInputController, 121 | onChanged: bloc.onLeakInputChanged, 122 | onSubmitted: (_) => bloc.onAddLeakItemClick(), 123 | decoration: InputDecoration( 124 | focusedBorder: const OutlineInputBorder( 125 | borderSide: BorderSide(color: Colors.green), 126 | ), 127 | enabledBorder: const OutlineInputBorder( 128 | borderSide: BorderSide(color: Colors.blue), 129 | ), 130 | hintText: 'https://developer.google.com', 131 | hintStyle: const TextStyle(color: Colors.black38), 132 | suffixIcon: IconButton(onPressed: bloc.onAddLeakItemClick, icon: const Icon(Icons.add)), 133 | ), 134 | ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/views/options.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:launch_at_startup/launch_at_startup.dart'; 3 | 4 | import '../data/shared_preferences.dart'; 5 | 6 | class AppOptions extends StatefulWidget { 7 | const AppOptions({super.key}); 8 | 9 | @override 10 | State createState() => _AppOptionsState(); 11 | } 12 | 13 | class _AppOptionsState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return SizedBox( 17 | width: 400, 18 | child: Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | showLeakInSysTray(), 22 | launchAtStartup() 23 | ], 24 | ), 25 | ); 26 | } 27 | 28 | Widget launchAtStartup() { 29 | return FutureBuilder( 30 | future: LaunchAtStartup.instance.isEnabled(), 31 | builder: (context, snapshot) { 32 | final value = snapshot.data ?? false; 33 | return CheckboxListTile( 34 | title: const Text('Launch on windows startup?'), 35 | value: value, 36 | onChanged: (enabled) { 37 | if (enabled == true) { 38 | LaunchAtStartup.instance.enable(); 39 | } else { 40 | LaunchAtStartup.instance.disable(); 41 | } 42 | setState(() {}); 43 | }, 44 | ); 45 | }, 46 | ); 47 | } 48 | 49 | Widget showLeakInSysTray() { 50 | return FutureBuilder( 51 | future: AppSharedPreferences.showLeakInSysTray, 52 | builder: (context, snapshot) { 53 | final value = snapshot.data ?? false; 54 | return CheckboxListTile( 55 | title: const Text('Show leak detection on system tray icon?'), 56 | value: value, 57 | onChanged: (enabled) async { 58 | await AppSharedPreferences.setShowLeakInSysTray(enabled ?? false); 59 | setState(() {}); 60 | }, 61 | ); 62 | }, 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "ir_net") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.ir_net") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | # Generated plugin build rules, which manage building the plugins and adding 90 | # them to the application. 91 | include(flutter/generated_plugins.cmake) 92 | 93 | 94 | # === Installation === 95 | # By default, "installing" just makes a relocatable bundle in the build 96 | # directory. 97 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 98 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 99 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 100 | endif() 101 | 102 | # Start with a clean build bundle directory every time. 103 | install(CODE " 104 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 105 | " COMPONENT Runtime) 106 | 107 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 108 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 109 | 110 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 111 | COMPONENT Runtime) 112 | 113 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 114 | COMPONENT Runtime) 115 | 116 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 117 | COMPONENT Runtime) 118 | 119 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 120 | install(FILES "${bundled_library}" 121 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 122 | COMPONENT Runtime) 123 | endforeach(bundled_library) 124 | 125 | # Fully re-copy the assets directory on each build to avoid having stale files 126 | # from a previous install. 127 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 128 | install(CODE " 129 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 130 | " COMPONENT Runtime) 131 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 132 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 133 | 134 | # Install the AOT library on non-Debug builds only. 135 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 136 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 137 | COMPONENT Runtime) 138 | endif() 139 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void fl_register_plugins(FlPluginRegistry* registry) { 15 | g_autoptr(FlPluginRegistrar) screen_retriever_registrar = 16 | fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); 17 | screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); 18 | g_autoptr(FlPluginRegistrar) system_tray_registrar = 19 | fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin"); 20 | system_tray_plugin_register_with_registrar(system_tray_registrar); 21 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 22 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 23 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 24 | g_autoptr(FlPluginRegistrar) window_manager_registrar = 25 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); 26 | window_manager_plugin_register_with_registrar(window_manager_registrar); 27 | } 28 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | screen_retriever 7 | system_tray 8 | url_launcher_linux 9 | window_manager 10 | ) 11 | 12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 13 | ) 14 | 15 | set(PLUGIN_BUNDLED_LIBRARIES) 16 | 17 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 19 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 22 | endforeach(plugin) 23 | 24 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 25 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 27 | endforeach(ffi_plugin) 28 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "ir_net"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "ir_net"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import connectivity_plus 9 | import launch_at_startup 10 | import package_info_plus 11 | import path_provider_foundation 12 | import screen_retriever 13 | import shared_preferences_foundation 14 | import sqflite 15 | import system_tray 16 | import url_launcher_macos 17 | import window_manager 18 | 19 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 20 | ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) 21 | LaunchAtStartupPlugin.register(with: registry.registrar(forPlugin: "LaunchAtStartupPlugin")) 22 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 23 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 24 | ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) 25 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 26 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 27 | SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin")) 28 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 29 | WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) 30 | } 31 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = ir_net 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.irNet 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ir_net 2 | description: Windows tool to show if user is connected to Iran internet or a VPN 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | version: 1.2.2+5 5 | environment: 6 | sdk: ">=3.2.3 <4.0.0" 7 | flutter: "3.29.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | system_tray: ^2.0.1 13 | http: ^1.1.2 14 | connectivity_plus: ^5.0.2 15 | map: ^1.3.3 16 | latlng: ^0.2.0 17 | cached_network_image: ^3.2.1 18 | rxdart: ^0.27.5 19 | launch_at_startup: ^0.0.1 20 | package_info_plus: ^4.2.0 21 | touch_mouse_behavior: ^1.0.0 22 | url_launcher: ^6.1.5 23 | shared_preferences: ^2.0.15 24 | live_event: ^1.0.0 25 | win_toast: ^0.0.2 26 | windows_single_instance: ^1.0.1 27 | dio: ^4.0.6 28 | window_manager: ^0.3.9 29 | flutter_speedtest: ^0.2.0 30 | 31 | dev_dependencies: 32 | flutter_test: 33 | sdk: flutter 34 | flutter_lints: ^2.0.0 35 | msix: ^3.6.3 36 | win32: ^5.11.0 37 | 38 | msix_config: 39 | display_name: IRNet 40 | certificate_path: C:\Workspace\Flutter\ir_net\signing\CERTIFICATE.pfx 41 | certificate_password: IrNetProject 42 | logo_path: C:\Workspace\Flutter\ir_net\assets\app_icon.png 43 | start_menu_icon_path: C:\Workspace\Flutter\ir_net\assets\app_icon.png 44 | tile_icon_path: C:\Workspace\Flutter\ir_net\assets\app_icon.png 45 | publisher_display_name: BuildToApp 46 | identity_name: com.buildtoapp.irnet 47 | msix_version: 1.0.0.0 48 | capabilities: internetClient 49 | install_certificate: true 50 | app_installer: 51 | publish_folder_path: build\windows\runner\Release 52 | 53 | flutter: 54 | uses-material-design: true 55 | assets: 56 | - assets/ 57 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/screenshot.png -------------------------------------------------------------------------------- /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 in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:ir_net/main.dart'; 12 | 13 | void main() { 14 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // // Build our app and trigger a frame. 16 | // await tester.pumpWidget(const MyApp()); 17 | // 18 | // // Verify that our counter starts at 0. 19 | // expect(find.text('0'), findsOneWidget); 20 | // expect(find.text('1'), findsNothing); 21 | // 22 | // // Tap the '+' icon and trigger a frame. 23 | // await tester.tap(find.byIcon(Icons.add)); 24 | // await tester.pump(); 25 | // 26 | // // Verify that our counter has incremented. 27 | // expect(find.text('0'), findsNothing); 28 | // expect(find.text('1'), findsOneWidget); 29 | // }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ir_net 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ir_net", 3 | "short_name": "ir_net", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(ir_net LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "ir_net") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(SET CMP0063 NEW) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | # Generated plugin build rules, which manage building the plugins and adding 56 | # them to the application. 57 | include(flutter/generated_plugins.cmake) 58 | 59 | 60 | # === Installation === 61 | # Support files are copied into place next to the executable, so that it can 62 | # run in place. This is done instead of making a separate bundle (as on Linux) 63 | # so that building and running from within Visual Studio will work. 64 | set(BUILD_BUNDLE_DIR "$") 65 | # Make the "install" step default, as it's required to run. 66 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 67 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 68 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 69 | endif() 70 | 71 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 72 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 73 | 74 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 75 | COMPONENT Runtime) 76 | 77 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 78 | COMPONENT Runtime) 79 | 80 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 81 | COMPONENT Runtime) 82 | 83 | if(PLUGIN_BUNDLED_LIBRARIES) 84 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 85 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 86 | COMPONENT Runtime) 87 | endif() 88 | 89 | # Fully re-copy the assets directory on each build to avoid having stale files 90 | # from a previous install. 91 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 92 | install(CODE " 93 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 94 | " COMPONENT Runtime) 95 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 96 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 97 | 98 | # Install the AOT library on non-Debug builds only. 99 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 100 | CONFIGURATIONS Profile;Release 101 | COMPONENT Runtime) 102 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | void RegisterPlugins(flutter::PluginRegistry* registry) { 19 | ConnectivityPlusWindowsPluginRegisterWithRegistrar( 20 | registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); 21 | LaunchAtStartupPluginRegisterWithRegistrar( 22 | registry->GetRegistrarForPlugin("LaunchAtStartupPlugin")); 23 | ScreenRetrieverPluginRegisterWithRegistrar( 24 | registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); 25 | SystemTrayPluginRegisterWithRegistrar( 26 | registry->GetRegistrarForPlugin("SystemTrayPlugin")); 27 | UrlLauncherWindowsRegisterWithRegistrar( 28 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 29 | WinToastPluginRegisterWithRegistrar( 30 | registry->GetRegistrarForPlugin("WinToastPlugin")); 31 | WindowManagerPluginRegisterWithRegistrar( 32 | registry->GetRegistrarForPlugin("WindowManagerPlugin")); 33 | WindowsSingleInstancePluginRegisterWithRegistrar( 34 | registry->GetRegistrarForPlugin("WindowsSingleInstancePlugin")); 35 | } 36 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | connectivity_plus 7 | launch_at_startup 8 | screen_retriever 9 | system_tray 10 | url_launcher_windows 11 | win_toast 12 | window_manager 13 | windows_single_instance 14 | ) 15 | 16 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 17 | ) 18 | 19 | set(PLUGIN_BUNDLED_LIBRARIES) 20 | 21 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 23 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 26 | endforeach(plugin) 27 | 28 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 29 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 30 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 31 | endforeach(ffi_plugin) 32 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 37 | 38 | # Run the Flutter tool portions of the build. This must not be removed. 39 | add_dependencies(${BINARY_NAME} flutter_assemble) 40 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "BuildToApp" "\0" 93 | VALUE "FileDescription", "ir_net" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "ir_net" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2022 BuildToApp. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "ir_net.exe" "\0" 98 | VALUE "ProductName", "ir_net" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | case WM_CLOSE: 59 | ShowWindow(GetHandle(), SW_HIDE); 60 | return 0; 61 | break; 62 | } 63 | 64 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"ir_net", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaherisaber/ir_net/86083a83e81e53efdf76cd43b1f2096091237ee6/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | std::string utf8_string; 52 | if (target_length == 0 || target_length > utf8_string.max_size()) { 53 | return utf8_string; 54 | } 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | --------------------------------------------------------------------------------