From 32ac19a7a523adf8e744eb9c419da68f4f1e1612 Mon Sep 17 00:00:00 2001 From: wanghongen Date: Sun, 2 Jul 2023 06:15:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=89=E5=8D=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .metadata | 18 +- android/.gitignore | 13 + android/app/build.gradle | 72 +++++ android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 +++ .../kotlin/com/network/proxy/MainActivity.kt | 79 +++++ .../com/network/proxy/ProxyVpnService.kt | 60 ++++ .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 10407 bytes .../src/main/res/mipmap-ldpi/ic_launcher.png | Bin 0 -> 3403 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 5472 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 16563 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 32064 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 51323 bytes .../app/src/main/res/values-night/styles.xml | 18 ++ android/app/src/main/res/values/styles.xml | 18 ++ android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle | 31 ++ android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle | 11 + lib/main.dart | 49 +-- lib/network/bin/server.dart | 32 +- lib/network/channel.dart | 31 +- lib/network/handler.dart | 7 +- lib/network/http/body_reader.dart | 6 + lib/network/http/http.dart | 2 + lib/network/util/request_rewrite.dart | 2 +- lib/network/util/system_proxy.dart | 52 ++-- lib/ui/{ => desktop}/left/domain.dart | 11 +- lib/ui/{ => desktop}/left/path.dart | 0 .../{ => desktop}/toolbar/launch/launch.dart | 24 +- .../{ => desktop}/toolbar/setting/filter.dart | 2 +- .../toolbar/setting/request_rewrite.dart | 0 .../toolbar/setting/setting.dart | 16 +- .../{ => desktop}/toolbar/setting/theme.dart | 16 +- lib/ui/{ => desktop}/toolbar/ssl/ssl.dart | 0 lib/ui/{ => desktop}/toolbar/toolbar.dart | 11 +- lib/ui/mobile.dart | 122 ++++++++ lib/ui/mobile/filter.dart | 219 ++++++++++++++ lib/ui/mobile/request.dart | 285 ++++++++++++++++++ lib/ui/mobile/request_rewrite.dart | 284 +++++++++++++++++ lib/ui/mobile/ssl.dart | 75 +++++ lib/ui/panel.dart | 72 +++-- lib/utils/platform.dart | 7 + linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 20 +- pubspec.yaml | 2 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 53 files changed, 1602 insertions(+), 167 deletions(-) create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/network/proxy/MainActivity.kt create mode 100644 android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-ldpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle rename lib/ui/{ => desktop}/left/domain.dart (94%) rename lib/ui/{ => desktop}/left/path.dart (100%) rename lib/ui/{ => desktop}/toolbar/launch/launch.dart (65%) rename lib/ui/{ => desktop}/toolbar/setting/filter.dart (99%) rename lib/ui/{ => desktop}/toolbar/setting/request_rewrite.dart (100%) rename lib/ui/{ => desktop}/toolbar/setting/setting.dart (89%) rename lib/ui/{ => desktop}/toolbar/setting/theme.dart (76%) rename lib/ui/{ => desktop}/toolbar/ssl/ssl.dart (100%) rename lib/ui/{ => desktop}/toolbar/toolbar.dart (82%) create mode 100644 lib/ui/mobile.dart create mode 100644 lib/ui/mobile/filter.dart create mode 100644 lib/ui/mobile/request.dart create mode 100644 lib/ui/mobile/request_rewrite.dart create mode 100644 lib/ui/mobile/ssl.dart create mode 100644 lib/utils/platform.dart diff --git a/.metadata b/.metadata index 1dea856..25d90ec 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff + revision: 796c8ef79279f9c774545b3771238c3098dbefab channel: stable project_type: app @@ -13,17 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - - platform: linux - create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - - platform: macos - create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - - platform: windows - create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff - base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab + - platform: android + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab # User provided section diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..85a89f6 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,72 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + namespace "com.network.proxy" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.network.proxy" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..77172ea --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/network/proxy/MainActivity.kt b/android/app/src/main/kotlin/com/network/proxy/MainActivity.kt new file mode 100644 index 0000000..21f926b --- /dev/null +++ b/android/app/src/main/kotlin/com/network/proxy/MainActivity.kt @@ -0,0 +1,79 @@ +package com.network.proxy + +import android.content.Intent +import android.net.VpnService +import android.os.Bundle +import android.util.Log +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel + + +class MainActivity : FlutterActivity() { + private val CHANNEL = "com.proxy/proxyVpn" + + private val VPN_REQUEST_CODE: Int = 24 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + prepareVpn() + } + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) + .setMethodCallHandler { call, result -> + when (call.method) { + "startVpn" -> { + val host = call.argument("proxyHost") + val port = call.argument("proxyPort") + startVpn(host!!, port!!) + } + + "stopVpn" -> { + stopVpn() + } + + else -> { + result.notImplemented() + } + } + } + } + + /** + * 准备vpn
+ * 设备可能弹出连接vpn提示 + */ + private fun prepareVpn() { + val intent = VpnService.prepare(this@MainActivity) + if (intent != null) { + startActivityForResult(intent, VPN_REQUEST_CODE) + } + } + + /** + * 启动vpn服务 + */ + private fun startVpn(host: String, port: Int) { + Log.i("com.network.proxy", "startVpn") + val intent = Intent(this, ProxyVpnService::class.java) + intent.putExtra(ProxyVpnService.ProxyHost, host) + intent.putExtra(ProxyVpnService.ProxyPort, port) + startService(intent) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + } + + /** + * 停止vpn服务 + */ + private fun stopVpn() { + startService(Intent(this@MainActivity, ProxyVpnService::class.java).also { + it.action = ProxyVpnService.ACTION_DISCONNECT + }) + } + +} diff --git a/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt new file mode 100644 index 0000000..740408f --- /dev/null +++ b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt @@ -0,0 +1,60 @@ +package com.network.proxy + +import android.content.Intent +import android.net.ProxyInfo +import android.net.VpnService +import android.os.Build +import android.os.ParcelFileDescriptor + +class ProxyVpnService : VpnService() { + private lateinit var vpnInterface: ParcelFileDescriptor + + companion object { + const val ProxyHost = "ProxyHost" + const val ProxyPort = "ProxyPort" + + /** + * 动作:断开连接 + */ + const val ACTION_DISCONNECT = "DISCONNECT" + } + + override fun onDestroy() { + super.onDestroy() + disconnect() + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + return if (intent?.action == ACTION_DISCONNECT) { + disconnect() + START_NOT_STICKY + } else { + connect(intent?.getStringExtra(ProxyHost)!!, intent.getIntExtra(ProxyPort, 0)) + START_STICKY + } + } + + private fun disconnect() { + vpnInterface.close() + } + + private fun connect(proxyHost: String, proxyPort: Int) { + vpnInterface = createVpnInterface(proxyHost, proxyPort) + } + + private fun createVpnInterface(proxyHost: String, proxyPort: Int): ParcelFileDescriptor { + return Builder() + .addAddress("10.0.0.2", 32) + .addRoute("0.0.0.0", 0) + .setSession(baseContext.applicationInfo.name) + .also { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + it.addDisallowedApplication(baseContext.packageName) + .setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort)) + } + } + .establish() ?: throw IllegalStateException("无法初始化vpnInterface") + } + + +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..3964e4b4be2c381895cf3fd5a377497adf71b79f GIT binary patch literal 10407 zcmV;YC|K8tP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8xC?H8hK~#9!?YwuC9oK#5`MLL2dbx9MG=OeoB9R0E5(EQC zi9w=7k)pNqN0{JiBM>oQK!w_xe@c@BM}E@B91Jga7|{(?k4A zU%OFuhpX z!#Hk^!mwFjWu(oNHg?)~oDt9SC-V8+(OjW;Iw&?R%2HeW;9uYO{|$idyYD`Jbm)xK zb=wxjdQYWVzqVShUu7)Ye9vvmW_*k>p56J8^c0*k!oGDxL9Vs5~5=rOTlct zS{{$2I*ibom3MO_bwgJ{U zDCr=C#kF+;J58Wwko6e?PZ9VYm0HO0lfwkf{rLGFq}z@aF2=YRyMSa7pDGRu)E6ch z9X`S2_;_eg-)ky0{m1T}-bWvO_^+$~T>+CY#&g$QU^xm ziDW=%B(V`VNE}=oTf;1CGgd$?-*5DJSC8Y?V9C_JUWk-YNy2>;_Be3`zr8=0M+#}yUc ze%*Gi+q;FPVg?}vXU~oDrTZV?)cFaDZR?m_jELfx(KD|xdhSeI44kjE6f(b6?CO5~ zp|AY(!Cd`wz}d6!u3e|c7ao(P{yTey_V|9Ohq}xV$}BO87>SfVQYa7-jl>9pBVz(- zsa8h0VoNXo>b9%#9f1%gd5~bLghWa~shHuVBQFuv7Ex5V?S@^v>+M(4T*%_8Na2(9a=1zmh#Dn1Plj3YVw$3yrle+R5mOY*C>cFQe^&{C zL12&)q!1`6QBv{5@t9HQe0u*oh9fwakPcXc#z&6Qgz zSIRiPPhAAm2#8dPj&-{TIySj49zFBB5$^x;uDd?!{@)7FJ@W}bXTfdZ){N+}%2 z#gPt)n)fbdey-ZP2jBOIwPyQ{Z3I~t$C1QwjF3x4gs{nv!a{dPGe+0(J&&AIV==O5 zmBHemtwVF)PEa}d^6}?CwR6k5;A0>Ecc1x-KmYCeKMNq&Yabe$jlY`f+SuV2dzlXn zLRs9%;#z}G4Oc9n!x@?i9=mo7u>Xo1Sih#5e8xq{WT%xBNGUMZA)oW8Ru^e$X-XC; zL0FNj)PfR!j`yTqczxclf(CzQ~aBi~h{C%#iYoMv6jk>kC zLV=D^Vv#_Vp*l@pZ;@NByPn;bZJ?A7a8fW5XBG&VT(}SdAuR3f?Tn94(c9C5kcrh` zSOSPNgw_|UOpcDA*oClyTqc{o$FRgy8c!Ak0%>Z9Xbz0V;$hN{b6kauJ&Z7P_HN`{ zy(W&F9RAEufBV;f7cO4=TgISw-1&xej(#99y;g%$-`yN5VEUd|mmGxjHU zzW3vqi`V~_X@0|&Yp$A){QuO~zsa>;9%C#Ljg1%a%mP<#Tf_hG-dowTeKWbhM+%7) z0!Jp4GoFr`n7K@EzOubGxKSMz&<|ZfUUAG>g zoMkXqK4S^%ArlkR2n;VAewHv6I9?8c!6321K_H2(MH`Fbxp=PR+}Se&&86Jzp34vX z@W_!L{&WDnUSD=!}De`_jui{f!6t{@;C#pFH?Dx?1Jv z(brhFWi#1AApy|XCAY$Yj$=-~{yMHBIC$`C_Uzuu_}F<)oH~Q!Wbi#7D+C&g6=1AE z8bc|UCpNK|o}Ac~4aDCaJNnH0PX*AL4OhQ&AAK{1HAL* zy-ZGwGdVs^p^(GRxF`uyl7J$`5+=y>2f5;BKq*OcOEZq+@ch%yGIn~H!~gIQFMsQC zYG=;F>^!DYoP%+9?Ys8TV@H1S<^bLL&JVh?b?>jb z`!}q~i6Htz}{WF`pPk?3uSuO4=^%5ju-gE zI%Z;Wg4a);B+{A<8#j^3W{_4O3`i+xZ7p*B{v8wo$qPr05;zV@xkx3DN+OZCj*sWN zOpKq~+~o6roOtc|nKuP!?Z$mKRLTBk@7m3x77J7yK-Bu;PbPy{OFqx(^Du?otUP5-5R#teLH=Z zZKl-T#uHCG$-D2li~h9(44)lla&i(s2vRPD@8SAP`C+R04{%H2z5d!eX>W34tpW z`*v?28{EU6{^v&!DueJn7A!=OKsqi1>o;)bm8X9;5Vb!5USBoM>w2xb7Dl(WwzXiw zDnyH9yog(G*hy0%ijS+h+za3;twIJl0TJGL=DJI~WUd6ub(DRgXBWSffs zT7g}*P@vG=&O%g2ybvI9A-EmAy%gJ8Id$wfT5DoQQi=Od`;Ugo3cr;xV)|mzbKGV| zV1PId32O`7dix#*I-76>APrJzgpf(VwJDih$%HS8HIC!3W!siyH{VReY=GoDY`yLp zy4S9uJUNMz&Ee%TGUhAMv$Co`CJA|6vfQV%`rJSO|2eMYHH=3 zH}BzxKmIYgIzcwmMOf9ixdPHFmU`NI{`DmQMfGaQ&*av%w>C31Jxf<}hU@ok#Ze&9 zNib}fOA3x?WT+LT(WPr&E;BATRsy=>Wvc|I$HwUD?xd@)FFCIyb)7UFHUj^}kyJ`B z5QQ<-N|j2r#?0&tQ5cfVW@&G4qf~4{D2b7hkAC28{_rm!puNzE41BaGfR|@%HmqL) z(Bk|;pOmuTDNVdE&A$D+&|#IysgR~(0oU~tb8cjdhCcdM$I1XLR(VZa_*$%EM#n~3 zyKxYq(kfQ2+>PZ_-uN49EQ{p|Gt<*(ZCG4bB%909(%M2xYcr0LNTrfEq?5lh0$K;S$wG zJ1~HhlBVWnO3h8=a=E0mSc<>@gnq!vx274oUaMn`Nivpfn8^whSIdYN78lCYYjrw1 zJL&H3N|S<21*0S`8Dp@3N&~p-NZx(>_5Axk`+H=*87VEfe4g3K^Oy3VN~N03w&bW) z7U*orvwPbHd{-gm4ullMT2n5Usnx2Cj*Rf~OD}Qm>^Yj6nrUlorM;sA*Y{{`Z6%k_ zB^>DDTI`L$N#A3wWqx*!zBT>0uA7$WjStXZRpMS3eV&klj;;B zW0UTW6%u0&2pC%5&Cr^5MyIDy(!+Hes*EG3kwU3Pfl?9~zVC%PjzB2d+FF;)R9LZ$_)->IC8sycS_ss|d}kGFSjs%a7HpW5u8TG_kuXRb zSgaMP>ewK&D|Fd(HyRSK*2#%>Y}?4(>?DPZ4_04lL77bU+|2ZNE%2O7E}ui&R63O+ zQ3aQBYKqKC09Ll)-UzI>Jd*e$9komdmtt3=Q6{sU#F( zR}`~4(a5DtlABG=Gc-6rec@qjArHEKDM0zA!o=&ZAD^_=^ap{D6r{4$Dhs~&XTj>} zcX{m>zJHY=H_|f@F5Hq$MLAu>def{kjrMN z*XtN-$YyfHIz~x}=Or1dyjUT~WbiY2>g8E4l(T6nzvXtvsfi+3+78kj-6*eV`CjFH~+Jp;{dW@7}RM%_I^>ns9 zvnoJsZQU=R7fiiYlVCB%QjhD@>va~ZRmLYLh@uG3^Jr=+v2Dv13WYq5Qc2uamj=(( z(YO?_Fp7{u5M+W&to1^W9G1F^c7=(g(;mho@k=#=d1_3?7-E}bIVA)Vi@_wyqp-x< zVsw1IR;&EDx4Yw!RRL;gYk6^DynKFoYO1fZvz?cZyh^NNa@j1MT^(%Nv;p7ulb%<4 zabmUjvf8>`UQF5|f_N~sHZO`2s^MOi=?#~5SreLt!+KIwx0E6i=RbAuhEH?C5MQ?Yi>HD`+Y;F)*-(uI^59xg4(N zp`={mMq;HPzmoG@49>+|X({&Dq>m1q9Udl%V|x4hm-U<_AoU9Aa;d|JwJ?5uly5xn z4d&-uhu`zZmS!?^(yLRlVzhUcHDetQf zeB(hgJ~@Gx@e%?n1S0iqlWM@1EPgdBTG?yA*!7k^Lt+I?O-_<8vwY>he3>U7dz`6>2@EEsYl5V$$#MmdUTcGU3M`Z36Yai`pRnzZ5YB2g5_OCgRo2C3X$l{ zORC}unvjh8DLOhk*|Bp6^?IF$ANm%AA7VZ4U;RoSd3^{ltkwd-q+jwB>I)ALKY~V}3CEw}11Mul>sP zHyj|J&k-AqQnGPf3Fl}qXp$azrBY#Lc9yVSXJLMUa=Fa>+&sowe9vQQYKmH|Mn0Fr zaUEi9>FevEyQiD5UMEpF48j^D%Vf`5i)px-jF$qoAPI=JLB~xBDi)z6j&j(zVFQCh zgPb~bk|!SfHkoX|&Rsj`@9#x9Nl`pHGWyqR)(m`eaQ%jtSMKqf9yi_gfsJFsrw)JM z=iU>1{Nwjx0Y@sFhIb06)~ifRO))w;!pO)d#Zr-MHcPQsB$v$+_yL(rfaAI-)sP_) zD+;5C+1XjjY`1$rHOMTBuS>(DP@7lqtU3dHUS8Y zHae*frA!;1No6oOHKj*JM(3xerau3|kr)5`GoSr*d~uX`%i|sI`B;8t?Ch5V$K3H} z|LY&IZlE70jm}c2WM_m-3e!faPpt%_5jyn|rLeLQRPFKw2$^;)SbEJ`L#0yV$cr!X z%+t@XW?&81T)UrAQ;DRg@HB^Ee*=+I-| ze*B4he*1s=orR0n{!L3wrhUFfloeQ$ zau2D}-Aw;_<@ASGQpl3hR0ujcI@q;q7ny9pqYppE@R>7|no8sgxm2w($)-q&km=xp z)>v#ZgDuizvLr7m7Ru%M=bnH5-XlkkeDA5Jo?6v7F34i{{OrF8aJ)Nb=F2~GZus;z zC5Hn?DFWZqElo|wHf`K|XyD*A=Br=*DporD)^GkAzT+j@iZKYA+}V?8$9BaMSGbRr z7%h+_g{=`9tVI$hfmSAwWm)f*OBSUZm!SlCU4THP z^gxJYFufs1V-n6|gaE0CwVoI`fBu(e&X0cm^Pm6x8+wnp{q9eAL~)DjXEuhozgyAr zU_M_GT1X-TajiyJnPFz)Je7r6idm0hKF5*6PxJ2E4)Qyn{dGE9Tk%{!376^Ad@^+* z(l!gJlwebj5@~N=pryfDjS@?Zth9(s$7`%i${T61QlwM1iB4oNoIZVqAN}A5>rM%}q!ImLyvWlen}sCK+iG0wZi?a&qc3kACOzzx>cIedtZYi`aL~T|IT<^v;Fq zvW_)F|1N0i?lvk%XdFTu@T!x9cng{ z=xxFvh_KqnQb%AB8UhJmq&R{>cVQt zjV(z{r^Ip}OdF#YYn6XEb^7GLS)85`5)oz!`FP#N4Oiz&rLI?Bc&X+7|NXx_aBAYr z@dqAw;KCq1J6F9r$P_c47XdZYF+Sbmx!67ybZD8x>Ei|{a;<{dg)=!eQp>bTXZX}n zZQK^+=6eWa%%Zj!>tcn#rj>J}2-m_=5ejRIRJyX*UV z!&*3CBICP`OR-qm*;FWI&!0bE?&;}S259$XLqB)$Z8zU2gRJ_}R~})mTA^)V7t|f1 zSS?$UAn9YL?VlKMg+SvVtdEtjAQfe!kq!vI9ZQH1;93bf#IYe644$c@>SdrzAVUH( zPY{*yDvO+%ox`;fryk?S8aJvVjm1|E0@%5CAJ@F^4uV1s6ZmM~=ito;`P$!ojsN<{ z6ZEyWQ7_jiOUsXc{G9upXI_}SAf~8Z@!E+q+ir;TE)cFsn-H;%gGlREU;5<>EaE>==h4{?hWclBtF8% z7y^(PJWIs>!D+f=LK~3_E1}xXL4$k_CgaQ zr%!YF+HMx>HI5uR#>m7Zp%fHbn#pAXrl%&!yBTKZ=b3XAb4KLdV)K98w&y)xHpWa; z>*0yBr;hgi)tCSAz~H7W&f9J}fVF~3wPqiA?D5Ii%8v8L&YZpgpuxcb5l1n`N;+Gb zc>f*O@Mm9onC@mpq^m^ABO<^W7mxHtTO+9hwMNrZs^vbtiqa-tA|Fbt@I;~j1SY~0 z7G*Voj1VFuHZ^9-bM&JKTrwn-ihg%8Rlp7rYm}V-*bzf z_>Dgh`>uF|&h}Q$pE+fJ_=E2cyyKRex$jxs*J2t&V z1RW1v08q73sb?~QG&-)^$exNTi{EX@Issw%kynJ$E1}VrTETo+N9D4(*&G9#HZwj~AvEB4 zIcho}MqzZq>7+>Lh9*Qfb+Y*kz6}v}o*-LtBHYWSr^Xt$^(6p3^~866?eZ&jZ>d%) z$0sHxe)94QM^4^$^K}y+{=^3^V40u$pC5YdTaQ2a>(&hhrIbnAPAasuDCs24IBU{* zn}pN}sZhosZG?_yi7UfgvAKtzyZbiQ^>^Z`27$Ke9nathi6hdEthC8Qe=3W0ba!*$ z#v6I~A08y|eR_Ml`Sy4ID{BVUuzCA7I@hk}cmCinh>S~XM>n_`qS#_cre1YSsuLxp zNGJn*1uF<4>I2vq9F#>N<>QDL2^GRD?5t$gI?@1ncC86gZ(nB=z2RFGFG z!vdD>hnG?&chg&gurRQ89ru3XlgLC))Kf zp-FV96nt%C&`GVQv1k(!#Uc58Gkf>$p1bkj?h66hd;Q+?2X8va_U+qwv%`1Hd+L6s z)z3EBnOY=_O>RtcMU?0*sTgE2Xr0IeJ{s3lG0_ZNtvT-f*LTp_)|B3=nRFkNkW00f zNavqdHVh>qZMhp7BV&$~^!4^)j7D3F6)K(TcUars%lBU#C!fzVUDa5t(K_v#+9;81 z(Fj{7u$GLdGd6jS{@(U4t{Le6lZ)3aKl!Ooz9l%H{*6CXGgDLV>*^iQ8cDTbw!$IO z0*t~(;R=h@Au^U^tihv(sm;)owS4+Rcd%(~H;Sa;CuHMBl%$O$mAGI8x-phQDy6Vu zgvDr$UC|X0BC$Lr1RX7T@_`}~I1pEHY?ypWhP8+|0#iqtMKXkVW}f-^vnX4Awymw@ zKm5vvZoA~`bl&o==Xc%p5%I{kzx(eO%DheZO>(MSrA`*z7|XGO$fP2a0mll2tS6~C zTBKGU<)(wzuw}3pC6XPLLZV1yBO%i-JZXTUZ4#JG;u;A~iCkgKa$8oU6H8Jh-$f*? zz!;4q3>{4p>N6-^BQPcrHm#3uVGE0%A_l>$smSec}?=yLP^9SM~*CfRKXSk3g* z45x>Osn%*lQHWHMT#%(&tuZ@0N3~YL_Z^HjRH_xq3snRLC7@ofGdVSd@B6&#-S6hI z?K`N2HBO#5hONwS=Gb$DI&BmHS_|q?Otn&BYH}P`SGaQL*5|fv82D--kY9YG^}i)R zCno0d+PeFC)@+!LjOwy-#ZVG9{lIW^ zpQEX{iFV&5@O?6w4B1>VSJdcO2$@b{G{zHcV`AQ5FdTaRFmv;B7;70DA7|sxAiJ-; zhNoXT!RYuT));ElD$_GF9Dn5qT5DQcnrSV$=UdR9d literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6b7540a8bb81569717dc5472d64fea3a981c42 GIT binary patch literal 3403 zcmV-R4Ycx!P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x48%!9K~z}7t(SR_T-ABSfA_uDue)b@W_p&MeV;*tRBPWI@9;=zL>=wJa%XBFvs3K^5l%erb1KfN?4R_qInzWO{wu6{9+<4cMl$Dh5 z?fW)RU0Fo;kzO8t{HM5f4Fe}Xkkd!^K0B+UyhrAWB)Ip^*4UCWXuV_RMoM3c}7)$m{^j-$et$Z zpJdt0B33Po(O&MdeCd1wmPx?0(c%-2CkO{EcD=QWXta#V%1V?#O>G4~|HUD4fhzn! zDaB<~<;Rb_w`SGqKiPWd!1mGKSh5W_e91f&H=mETEWO4xi&3cwA~MLTrEOfjav`0a zjZ?=sb*z`7;s~*t3h)aH81Ty%w{qpRp9Ep*(h9D>``^ig+DLf{)6Zfh`-z`^``F^S zZ5KWH_+O_A(?2{066*F?{mg4j1y-t;6*E1adgNB_{>*iBHr0XSg9CWb*wjGJ;UoED zS%oX>6LQ@AVjiILyNsWW6Lk8~sbQ3#!SyYCyBe>kvEx^}4u1Rg+aLUR$*%p>eKFsz zdNy3y8qTH9vTkK7tC!8Esi_)60MkGzV~PnQU{G06&c1gJkRBN2^(X(0t^e=@JGO79 zW6nIP8frm9CYfevu%Fv*S&Pbzaj5$^+K%8VgJ`6JiF3n?Qt{E}dXMiN{t%#O^QB*_ zZ(nd#DxT!}PjqnmpIuHYTEhN=?-5Uo6RWN|&qQIS9kgj|Zs6e7S4r-8o0S``rF!Wi zUViZ=%hJ}~j)B3vIW4SNHJd%VcjEa0lo_Hd8Z%FwI26154=;aas$}cd z-x(|}F8jRaX}TJ1KD+)8khG6wm|SwnJer%D=gE}D zrPE0szV~{fkq}~lD1`yop#ZV!Dl+K|hr4^IudmB%r(lcvj&}CHwTpw>USr3bZ*b$? zUm#jpnHRJu5DjRQGB89i0wyb#T*%gKuj8vCEG5QBUr(52#CT^fHDwI$fUBIJ$IH|Hb-e` zlv~#?=6gTdNmXSH>8jQ%rvMd&!(BQ(!pen9*uQfR$~K5qRTHf!r>LYTuNf%-)6)V& z&fgzBc9gjb=7VLNe^3y`spGwvhDm8@DUA&cDANE9)%8_uefpeuA2f>9MHw3 zw!>vB7k~%8>v3-U9Phu^jj2pb%fi<_bLY*$v@Cwz2nKAdU?9&78nl@Qq@bm(`8=zh zhv)eqV44;iu35rAZrX-n1({kopnSQoqXjgG>th-QRk0XVljSkV=g*{WChagSS`8zA zYCQ5RDL_d9ppgfu6oO!)P^O9Y6^rI}P-JeVIIPbHV|*e$8jDpF<`|$9_>$M66gHU- z<|7%ON+z)afdVp>2cilQEe5{h;JF^+fikW9`U;GKt-4xbgae#ssIUJ_PdFR`1zHJ4 zLD0%9Y_384{5n(APu?p?#1oW6%b1E=HLY*>1|uUQBoaw7=`30Vv>*sUJAjA~RymyU zL3;|2W$Jg&^q&R+Lkuv$bzKI|43bWzab1_P(lVNxn)3VDA1+yPB1uDI!?Y+c3bf`e zTHDw_t&s0|Q_j`-a+(Hb21j$-U*ElF3Q$Mqyo2dPI+seMg8iq@V26XCpt7QZ=Ef!n zOmo>JL(`Z_r&ENBil(_v0Rd1xB9bT52cjD)%h{56R9=2}Y!qA!i zJjO|Vp)gQjD2iMEtCX@8{=e5%ERd+jBr(dVY(XI(AA#i)hyo$!0`EZ(t)B4GIEpZT88_{0Pg$u~<+&|#{K$#?@2-fXJ&~j%UeXb7A}!8%T-@=5 z=(HQY)$aG+=fsKQbY0jFGJlrOOsmUQvy5J{v6l)@Z@3^EL2gd+x8?!V*ti2VfjuP!`qoTQvx{gk2 zT3d-!R6-;))n>-dj8GYifg_UhGC%F!xBvdO3pz95qDad(?*H2Z-`V)x41l`u;_JU! zKkJG|6M;sRCV(d<9uU$7A(h6+4HL_a6HSj%6z|8#B?vevqP|Zknw-Bm#@6{gX2dw_79I#ty#Zzi#RM885sF=b!Y7} zN-5MU+jjJ4#H@e#={?w03rVFl8ck07D6*(R9GhaHl|~tYr9H|dOTZZ+BK<^?CrI}7 zQ4t7ZrW2T1mpi_47lEpB+~OjBxbZ2Hrw4g=d@S1+h};}^%(v@HjU{hwfAK$x!o})^ z|K9xNKVEm;_f$}!ysRr`(Bdl(JnkMFbj+c1Iea5(P};&-KY%jwi8|ROLYPGG33{U# zsS{i>zkyYk&f=2!b11JY0R#FDALYcZ-E?)$pm%VHH+H_m;n6fbXB8#Y7wFR?V`x5jWf43$lrhMHWVJpbHGq2u}~;1WuOqQUB7|E#29xkm``!E z4DX3&sHkh9Z(Q;&^fjV=(y0k9nt$ob&F!TEV156`o08LQH-Gu3J9KgBhVh)>V1nmk zDg))Zgd|CsJ;rzM|03dLFf0o@U}FS8dHG(dL=azt=MX6_1~cECIvS#+RfeF>A!1|b z1S)hm@TG29NPSKD9o{;ZFS_m47TzrVXxOF)Jr4gk$H!@6b ze;MQKSH@rgK=rAVi<;CZAoDdO=2Yp-8VQ$>jGfxT4Zvh?*2Fg!F&eMR`K>(;Ni h`JTVJDLuLW{{bu8Ckw`<*;N1l002ovPDHLkV1h{mg5dxF literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..62a2d6d264d4e00e9046935dba1a817b0478ba93 GIT binary patch literal 5472 zcmV-m6`$&fP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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
W==Dt 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

E9l5i3f}7=li77ZZK151I?k!_Hql0}4 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 Y1Ar|sij_qhX8-^I07*qoM6N<$g0r0NLI3~& literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..f7eb7f6 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/lib/main.dart b/lib/main.dart index b8cac0a..194e3ea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,9 +4,11 @@ import 'package:chinese_font_library/chinese_font_library.dart'; import 'package:flutter/material.dart'; import 'package:network_proxy/network/bin/server.dart'; import 'package:network_proxy/ui/component/split_view.dart'; -import 'package:network_proxy/ui/left/domain.dart'; +import 'package:network_proxy/ui/desktop/left/domain.dart'; +import 'package:network_proxy/ui/desktop/toolbar/toolbar.dart'; +import 'package:network_proxy/ui/mobile.dart'; import 'package:network_proxy/ui/panel.dart'; -import 'package:network_proxy/ui/toolbar/toolbar.dart'; +import 'package:network_proxy/utils/platform.dart'; import 'package:window_manager/window_manager.dart'; import 'network/channel.dart'; @@ -15,19 +17,20 @@ import 'network/http/http.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - //设置窗口大小 - await windowManager.ensureInitialized(); + if (Platforms.isDesktop()) { + //设置窗口大小 + await windowManager.ensureInitialized(); - WindowOptions windowOptions = WindowOptions( - minimumSize: const Size(980, 600), - size: Platform.isMacOS ? const Size(1200, 750) : const Size(1080, 650), - center: true, - titleBarStyle: - Platform.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal); - windowManager.waitUntilReadyToShow(windowOptions, () async { - await windowManager.show(); - await windowManager.focus(); - }); + WindowOptions windowOptions = WindowOptions( + minimumSize: const Size(980, 600), + size: Platform.isMacOS ? const Size(1200, 750) : const Size(1080, 650), + center: true, + titleBarStyle: Platform.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal); + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); + } runApp(const FluentApp()); } @@ -41,7 +44,7 @@ class FluentApp extends StatelessWidget { @override Widget build(BuildContext context) { var lightTheme = ThemeData.light(useMaterial3: true); - var darkTheme = ThemeData.dark(useMaterial3: false); + var darkTheme = ThemeData.dark(useMaterial3: !Platforms.isDesktop()); if (Platform.isWindows) { lightTheme = lightTheme.useSystemChineseFont(); darkTheme = darkTheme.useSystemChineseFont(); @@ -56,23 +59,22 @@ class FluentApp extends StatelessWidget { theme: lightTheme, darkTheme: darkTheme, themeMode: currentMode, - home: const NetworkHomePage(), + home: Platforms.isDesktop() ? const DesktopHomePage() : const MobileHomePage(), ); }); } } -class NetworkHomePage extends StatefulWidget { - const NetworkHomePage({super.key}); +class DesktopHomePage extends StatefulWidget { + const DesktopHomePage({super.key}); @override - State createState() => _NetworkHomePagePageState(); + State createState() => _DesktopHomePagePageState(); } -class _NetworkHomePagePageState extends State - implements EventListener { +class _DesktopHomePagePageState extends State implements EventListener { final domainStateKey = GlobalKey(); - final NetworkTabController panel = NetworkTabController(); + final NetworkTabController panel = NetworkTabController(tabStyle: const TextStyle(fontSize: 18)); late ProxyServer proxyServer; @@ -94,8 +96,7 @@ class _NetworkHomePagePageState extends State @override Widget build(BuildContext context) { - final domainWidget = DomainWidget( - key: domainStateKey, proxyServer: proxyServer, panel: panel); + final domainWidget = DomainWidget(key: domainStateKey, proxyServer: proxyServer, panel: panel); return Scaffold( appBar: Tab( diff --git a/lib/network/bin/server.dart b/lib/network/bin/server.dart index 41fd508..a2f036b 100644 --- a/lib/network/bin/server.dart +++ b/lib/network/bin/server.dart @@ -3,6 +3,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:network_proxy/network/util/host_filter.dart'; +import 'package:network_proxy/utils/platform.dart'; +import 'package:path_provider/path_provider.dart'; import '../channel.dart'; import '../handler.dart'; @@ -29,16 +31,23 @@ class ProxyServer { bool get enableSsl => _enableSsl; - File homeDir() { - var userHome = Platform.environment['HOME'] ?? Platform.environment['USERPROFILE']; + Future homeDir() async { + String? userHome; + if (Platforms.isDesktop()) { + userHome = Platform.environment['HOME'] ?? Platform.environment['USERPROFILE']; + } else { + userHome = (await getApplicationSupportDirectory()).path; + } + var separator = Platform.pathSeparator; return File("${userHome!}$separator.proxypin"); } /// 配置文件 - File configFile() { + Future configFile() async { var separator = Platform.pathSeparator; - return File("${homeDir().path}${separator}config.cnf"); + var home = await homeDir(); + return File("${home.path}${separator}config.cnf"); } /// 是否启用ssl @@ -95,8 +104,11 @@ class ProxyServer { /// 刷新配置文件 flushConfig() async { - var file = configFile(); - await file.create(recursive: true); + var file = await configFile(); + var exists = await file.exists(); + if (!exists) { + file = await file.create(recursive: true); + } HostFilter.whitelist.toJson(); HostFilter.blacklist.toJson(); var json = jsonEncode(toJson()); @@ -106,7 +118,7 @@ class ProxyServer { /// 加载配置文件 Future _loadConfig() async { - var file = configFile(); + var file = await configFile(); var exits = await file.exists(); if (!exits) { return; @@ -123,7 +135,8 @@ class ProxyServer { /// 加载请求重写配置文件 Future _loadRequestRewriteConfig() async { - var file = File('${homeDir().path}${Platform.pathSeparator}request_rewrite.json'); + var home = await homeDir(); + var file = File('${home.path}${Platform.pathSeparator}request_rewrite.json'); var exits = await file.exists(); if (!exits) { return; @@ -137,7 +150,8 @@ class ProxyServer { /// 保存请求重写配置文件 flushRequestRewriteConfig() async { - var file = File('${homeDir().path}${Platform.pathSeparator}request_rewrite.json'); + var home = await homeDir(); + var file = File('${home.path}${Platform.pathSeparator}request_rewrite.json'); bool exists = await file.exists(); if (!exists) { await file.create(recursive: true); diff --git a/lib/network/channel.dart b/lib/network/channel.dart index 4945b24..4a8fec7 100644 --- a/lib/network/channel.dart +++ b/lib/network/channel.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; -import 'package:logger/logger.dart'; import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/network/util/attribute_keys.dart'; import 'package:network_proxy/network/util/crts.dart'; @@ -13,15 +12,7 @@ import 'handler.dart'; ///处理I/O事件或截获I/O操作 abstract class ChannelHandler { - var log = Logger( - printer: PrettyPrinter( - methodCount: 0, - errorMethodCount: 8, - lineLength: 120, - colors: true, - printEmojis: false, - excludeBox: {Level.info: true, Level.debug: true}, - )); + var log = logger; void channelActive(Channel channel) {} @@ -51,7 +42,9 @@ class Channel { final int remotePort; Channel(this._socket) - : _id = DateTime.now().millisecondsSinceEpoch + Random().nextInt(999999), + : _id = DateTime + .now() + .millisecondsSinceEpoch + Random().nextInt(999999), remoteAddress = _socket.remoteAddress, remotePort = _socket.remotePort; @@ -130,7 +123,7 @@ class ChannelPipeline extends ChannelHandler { return; } if (data is HttpRequest) { - data.hostAndPort ??= getHostAndPort(data); + data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data); data.remoteDomain = data.hostAndPort?.url; data.requestUrl = data.uri.startsWith("/") ? '${data.remoteDomain}${data.uri}' : data.uri; try { @@ -178,7 +171,7 @@ class HostAndPort { String domain = url; String? scheme; //域名格式 直接解析 - if (url.startsWith(httpScheme)) { + if (url.startsWith(httpScheme) || url.startsWith(httpsScheme)) { //httpScheme scheme = url.startsWith(httpsScheme) ? httpsScheme : httpScheme; domain = url.substring(scheme.length).split("/")[0]; @@ -202,11 +195,11 @@ class HostAndPort { @override bool operator ==(Object other) => identical(this, other) || - other is HostAndPort && - runtimeType == other.runtimeType && - scheme == other.scheme && - host == other.host && - port == other.port; + other is HostAndPort && + runtimeType == other.runtimeType && + scheme == other.scheme && + host == other.host && + port == other.port; @override int get hashCode => scheme.hashCode ^ host.hashCode ^ port.hashCode; @@ -292,7 +285,7 @@ class Network { //客户端ssl Channel remoteChannel = channel.getAttribute(channel.id); remoteChannel.secureSocket = - await SecureSocket.secure(remoteChannel.socket, onBadCertificate: (certificate) => true); + await SecureSocket.secure(remoteChannel.socket, onBadCertificate: (certificate) => true); remoteChannel.pipeline.listen(remoteChannel); //服务端ssl diff --git a/lib/network/handler.dart b/lib/network/handler.dart index 6378d4f..9293d59 100644 --- a/lib/network/handler.dart +++ b/lib/network/handler.dart @@ -56,7 +56,8 @@ class HttpChannelHandler extends ChannelHandler { Future forward(Channel channel, HttpRequest httpRequest) async { channel.putAttribute(AttributeKeys.request, httpRequest); - if (httpRequest.uri == 'http://proxy.pin/ssl') { + if (httpRequest.uri == 'http://proxy.pin/ssl' || + httpRequest.requestUrl == 'http://127.0.0.1:${channel.socket.port}/ssl') { _crtDownload(channel, httpRequest); return; } @@ -65,12 +66,13 @@ class HttpChannelHandler extends ChannelHandler { //实现抓包代理转发 if (httpRequest.method != HttpMethod.connect) { + // log.i("[${channel.id}] ${httpRequest.requestUrl}"); + var replaceBody = requestRewrites?.findRequestReplaceWith(httpRequest.path); if (replaceBody?.isNotEmpty == true) { httpRequest.body = utf8.encode(replaceBody!); } - // log.i("[${channel.id}] ${remoteChannel.getAttribute(AttributeKeys.uri)}"); listener?.onRequest(channel, httpRequest); //实现抓包代理转发 await remoteChannel.write(httpRequest); @@ -132,6 +134,7 @@ class HttpResponseProxyHandler extends ChannelHandler { @override void channelRead(Channel channel, HttpResponse msg) { msg.request = clientChannel.getAttribute(AttributeKeys.request); + msg.request?.response= msg; // log.i("[${clientChannel.id}] Response ${msg.bodyAsString}"); var replaceBody = requestRewrites?.findResponseReplaceWith(msg.request?.path); diff --git a/lib/network/http/body_reader.dart b/lib/network/http/body_reader.dart index 26cca57..24caba4 100644 --- a/lib/network/http/body_reader.dart +++ b/lib/network/http/body_reader.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:network_proxy/network/http/http.dart'; import '../../utils/num.dart'; +import '../channel.dart'; import 'codec.dart'; class Result { @@ -29,6 +30,11 @@ class BodyReader { : _state = message.headers.isChunked ? ReaderState.readChunkSize : ReaderState.readFixedLengthContent; Result readBody(Uint8List data) { + if (_bodyBuffer.length > Codec.maxBodyLength) { + _bodyBuffer.clear(); + throw Exception('Body length exceeds ${Codec.maxBodyLength}'); + } + _offset = 0; //chunked编码 diff --git a/lib/network/http/http.dart b/lib/network/http/http.dart index 1814341..2a2da12 100644 --- a/lib/network/http/http.dart +++ b/lib/network/http/http.dart @@ -51,10 +51,12 @@ class HttpRequest extends HttpMessage { final String uri; late HttpMethod method; late String requestUrl; + String? path; HostAndPort? hostAndPort; final DateTime requestTime = DateTime.now(); String? remoteDomain; + HttpResponse? response; HttpRequest(this.method, this.uri, String protocolVersion) : super(protocolVersion); diff --git a/lib/network/util/request_rewrite.dart b/lib/network/util/request_rewrite.dart index d8a4300..7c9200c 100644 --- a/lib/network/util/request_rewrite.dart +++ b/lib/network/util/request_rewrite.dart @@ -1,5 +1,5 @@ class RequestRewrites { - bool enabled = false; + bool enabled = true; final List rules = []; load(Map? map) { diff --git a/lib/network/util/system_proxy.dart b/lib/network/util/system_proxy.dart index d8bbeb9..c155f1b 100644 --- a/lib/network/util/system_proxy.dart +++ b/lib/network/util/system_proxy.dart @@ -4,53 +4,41 @@ import 'package:network_proxy/utils/ip.dart'; import 'package:proxy_manager/proxy_manager.dart'; class SystemProxy { + static String? _hardwarePort; /// 设置系统代理 static void setSystemProxy(int port, bool enableSsl) async { if (Platform.isMacOS) { - _setProxyServerMacOS("127.0.0.1:$port", enableSsl); + _setProxyServerMacOS(port, enableSsl); } else if (Platform.isWindows) { _setProxyServerWindows(port, enableSsl); } } - static Future _setProxyServerMacOS( - String proxyServer, bool enableSsl) async { - var match = RegExp(r"^(?:http://)?(?.+):(?\d+)$") - .firstMatch(proxyServer); - if (match == null) { - print('proxyServer parse error!'); - return false; - } - var host = match.namedGroup('host'); - var port = match.namedGroup('port'); - var name = await hardwarePort(); + static Future _setProxyServerMacOS(int port, bool enableSsl) async { + _hardwarePort = await hardwarePort(); var results = await Process.run('bash', [ '-c', _concatCommands([ - 'networksetup -setwebproxy $name $host $port', - enableSsl == true - ? 'networksetup -setsecurewebproxy $name $host $port' - : '', - 'networksetup -setproxybypassdomains $name 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 127.0.0.1 localhost *.local timestamp.apple.com sequoia.apple.com seed-sequoia.siri.apple.com *.google.com', + 'networksetup -setwebproxy $_hardwarePort 127.0.0.1 $port', + enableSsl == true ? 'networksetup -setsecurewebproxy $_hardwarePort 127.0.0.1 $port' : '', + 'networksetup -setproxybypassdomains $_hardwarePort 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 127.0.0.1 localhost *.local timestamp.apple.com sequoia.apple.com seed-sequoia.siri.apple.com *.google.com', ]) ]); - print( - 'set proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stdout}'); + print('set proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stdout}'); return results.exitCode == 0; } - static Future setProxyEnableMacOS( - bool proxyEnable, bool enableSsl) async { + static Future setProxyEnableMacOS(bool proxyEnable, bool enableSsl) async { var proxyMode = proxyEnable ? 'on' : 'off'; - var name = await hardwarePort(); + _hardwarePort ??= await hardwarePort(); + print('set proxyEnable: $proxyEnable, name: $_hardwarePort'); + var results = await Process.run('bash', [ '-c', _concatCommands([ - 'networksetup -setwebproxystate $name $proxyMode', - enableSsl - ? 'networksetup -setsecurewebproxystate $name $proxyMode' - : '', + 'networksetup -setwebproxystate $_hardwarePort $proxyMode', + enableSsl ? 'networksetup -setsecurewebproxystate $_hardwarePort $proxyMode' : '', ]) ]); return results.exitCode == 0; @@ -76,17 +64,14 @@ class SystemProxy { 'networksetup -listnetworkserviceorder |grep "Device: $name" -A 1 |grep "Hardware Port" |awk -F ": " \'{print \$2}\'', ]) ]); - + print(results); return results.stdout.toString().split(", ")[0]; } - static Future _setProxyServerWindows( - int proxyPort, bool enableSsl) async { + static Future _setProxyServerWindows(int proxyPort, bool enableSsl) async { ProxyManager manager = ProxyManager(); await manager.setAsSystemProxy(ProxyTypes.http, "127.0.0.1", proxyPort); - if (enableSsl) { - // await manager.setAsSystemProxy(ProxyTypes.https, "127.0.0.1", 8888); - } + var results = await Process.run('reg', [ 'add', 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', @@ -99,8 +84,7 @@ class SystemProxy { '/f', ]); - print( - 'set proxyServer $proxyPort, exitCode: ${results.exitCode}, stdout: ${results.stderr}'); + print('set proxyServer $proxyPort, exitCode: ${results.exitCode}, stdout: ${results.stderr}'); return results.exitCode == 0; } diff --git a/lib/ui/left/domain.dart b/lib/ui/desktop/left/domain.dart similarity index 94% rename from lib/ui/left/domain.dart rename to lib/ui/desktop/left/domain.dart index 3099bdd..7002a03 100644 --- a/lib/ui/left/domain.dart +++ b/lib/ui/desktop/left/domain.dart @@ -3,14 +3,13 @@ import 'dart:collection'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/channel.dart'; import 'package:network_proxy/network/http/http.dart'; +import 'package:network_proxy/network/util/attribute_keys.dart'; import 'package:network_proxy/network/util/host_filter.dart'; import 'package:network_proxy/ui/component/transition.dart'; -import 'package:network_proxy/ui/left/path.dart'; - -import '../../network/channel.dart'; -import '../../network/util/attribute_keys.dart'; -import '../panel.dart'; +import 'package:network_proxy/ui/desktop/left/path.dart'; +import 'package:network_proxy/ui/panel.dart'; ///左侧域名 class DomainWidget extends StatefulWidget { @@ -143,7 +142,7 @@ class _HeaderBodyState extends State { visualDensity: const VisualDensity(vertical: -3.6), title: Text(title, textAlign: TextAlign.left, - style: const TextStyle(fontSize: 16), + style: const TextStyle(fontSize: 14), maxLines: 1, overflow: TextOverflow.ellipsis), onTap: () { diff --git a/lib/ui/left/path.dart b/lib/ui/desktop/left/path.dart similarity index 100% rename from lib/ui/left/path.dart rename to lib/ui/desktop/left/path.dart diff --git a/lib/ui/toolbar/launch/launch.dart b/lib/ui/desktop/toolbar/launch/launch.dart similarity index 65% rename from lib/ui/toolbar/launch/launch.dart rename to lib/ui/desktop/toolbar/launch/launch.dart index bdf211a..f5f62e8 100644 --- a/lib/ui/toolbar/launch/launch.dart +++ b/lib/ui/desktop/toolbar/launch/launch.dart @@ -5,8 +5,11 @@ import 'package:window_manager/window_manager.dart'; class SocketLaunch extends StatefulWidget { final ProxyServer proxyServer; + final int size; + final Function? onStart; + final Function? onStop; - const SocketLaunch({super.key, required this.proxyServer}); + const SocketLaunch({super.key, required this.proxyServer, this.size = 25, this.onStart, this.onStop}); @override State createState() { @@ -22,6 +25,7 @@ class _SocketLaunchState extends State with WindowListener { super.initState(); windowManager.addListener(this); widget.proxyServer.start(); + widget.onStart?.call(); } @override @@ -31,23 +35,25 @@ class _SocketLaunchState extends State with WindowListener { } @override - void onWindowClose() { + void onWindowClose() async { + print("onWindowClose"); + await widget.proxyServer.stop(); started = false; - widget.proxyServer.stop(); - setState(() {}); } @override Widget build(BuildContext context) { return IconButton( tooltip: started ? "停止" : "启动", - icon: Icon( - started ? Icons.stop : Icons.play_arrow_sharp, - color: started ? Colors.red : Colors.green, - size: 25, - ), + icon: Icon(started ? Icons.stop : Icons.play_arrow_sharp, + color: started ? Colors.red : Colors.green, size: widget.size.toDouble()), onPressed: () async { Future result = started ? widget.proxyServer.stop() : widget.proxyServer.start(); + if (started) { + widget.onStop?.call(); + } else { + widget.onStart?.call(); + } result.then((value) => setState(() { started = !started; })); diff --git a/lib/ui/toolbar/setting/filter.dart b/lib/ui/desktop/toolbar/setting/filter.dart similarity index 99% rename from lib/ui/toolbar/setting/filter.dart rename to lib/ui/desktop/toolbar/setting/filter.dart index 997e896..cdb2dd8 100644 --- a/lib/ui/toolbar/setting/filter.dart +++ b/lib/ui/desktop/toolbar/setting/filter.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/util/host_filter.dart'; -import '../../../network/util/host_filter.dart'; class FilterDialog extends StatefulWidget { final ProxyServer proxyServer; diff --git a/lib/ui/toolbar/setting/request_rewrite.dart b/lib/ui/desktop/toolbar/setting/request_rewrite.dart similarity index 100% rename from lib/ui/toolbar/setting/request_rewrite.dart rename to lib/ui/desktop/toolbar/setting/request_rewrite.dart diff --git a/lib/ui/toolbar/setting/setting.dart b/lib/ui/desktop/toolbar/setting/setting.dart similarity index 89% rename from lib/ui/toolbar/setting/setting.dart rename to lib/ui/desktop/toolbar/setting/setting.dart index f6ef990..d35c820 100644 --- a/lib/ui/toolbar/setting/setting.dart +++ b/lib/ui/desktop/toolbar/setting/setting.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:network_proxy/network/bin/server.dart'; -import 'package:network_proxy/ui/toolbar/setting/request_rewrite.dart'; -import 'package:network_proxy/ui/toolbar/setting/theme.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/request_rewrite.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/theme.dart'; import 'package:url_launcher/url_launcher.dart'; import 'filter.dart'; @@ -26,11 +26,8 @@ class _SettingState extends State { offset: const Offset(10, 30), itemBuilder: (context) { return [ - PopupMenuItem(padding: const EdgeInsets.all(0), child: PortWidget(proxyServer: widget.proxyServer)), - const PopupMenuItem( - padding: EdgeInsets.all(0), - child: ThemeSetting(), - ), + PopupMenuItem(padding: const EdgeInsets.all(0), child: PortWidget(proxyServer: widget.proxyServer, textStyle: const TextStyle(fontSize: 13))), + const PopupMenuItem(padding: EdgeInsets.all(0), child: ThemeSetting(dense: true)), PopupMenuItem( padding: const EdgeInsets.all(0), child: ListTile( @@ -97,8 +94,9 @@ class _SettingState extends State { class PortWidget extends StatefulWidget { final ProxyServer proxyServer; + final TextStyle? textStyle; - const PortWidget({super.key, required this.proxyServer}); + const PortWidget({super.key, required this.proxyServer, this.textStyle}); @override State createState() { @@ -135,7 +133,7 @@ class _PortState extends State { Widget build(BuildContext context) { return Row(children: [ const Padding(padding: EdgeInsets.only(left: 16)), - const Text("端口号:", style: TextStyle(fontSize: 13)), + Text("端口号:", style: widget.textStyle), SizedBox( width: 80, child: TextFormField( diff --git a/lib/ui/toolbar/setting/theme.dart b/lib/ui/desktop/toolbar/setting/theme.dart similarity index 76% rename from lib/ui/toolbar/setting/theme.dart rename to lib/ui/desktop/toolbar/setting/theme.dart index 5d45702..f344439 100644 --- a/lib/ui/toolbar/setting/theme.dart +++ b/lib/ui/desktop/toolbar/setting/theme.dart @@ -1,15 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:network_proxy/main.dart'; -import '../../../main.dart'; class ThemeSetting extends StatelessWidget { - const ThemeSetting({Key? key}) : super(key: key); + final bool dense; + + const ThemeSetting({Key? key, this.dense = false}) : super(key: key); @override Widget build(BuildContext context) { return PopupMenuButton( tooltip: themeNotifier.value.name, - surfaceTintColor: Colors.white70, + surfaceTintColor: Theme.of(context).colorScheme.onPrimary, offset: const Offset(150, 0), itemBuilder: (BuildContext context) { return [ @@ -30,10 +32,10 @@ class ThemeSetting extends StatelessWidget { }), ]; }, - child: const ListTile( - title: Text("主题"), - trailing: Icon(Icons.arrow_right), - dense: true, + child: ListTile( + title: const Text("主题"), + trailing: const Icon(Icons.arrow_right), + dense: dense, )); } } diff --git a/lib/ui/toolbar/ssl/ssl.dart b/lib/ui/desktop/toolbar/ssl/ssl.dart similarity index 100% rename from lib/ui/toolbar/ssl/ssl.dart rename to lib/ui/desktop/toolbar/ssl/ssl.dart diff --git a/lib/ui/toolbar/toolbar.dart b/lib/ui/desktop/toolbar/toolbar.dart similarity index 82% rename from lib/ui/toolbar/toolbar.dart rename to lib/ui/desktop/toolbar/toolbar.dart index b030b68..73406ad 100644 --- a/lib/ui/toolbar/toolbar.dart +++ b/lib/ui/desktop/toolbar/toolbar.dart @@ -2,11 +2,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:network_proxy/ui/toolbar/setting/setting.dart'; -import 'package:network_proxy/ui/toolbar/ssl/ssl.dart'; +import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/setting.dart'; +import 'package:network_proxy/ui/desktop/toolbar/ssl/ssl.dart'; import 'package:window_manager/window_manager.dart'; -import '../../network/bin/server.dart'; import '../left/domain.dart'; import 'launch/launch.dart'; @@ -41,6 +41,11 @@ class _ToolbarState extends State { windowManager.blur(); return; } + if (event.isKeyPressed(LogicalKeyboardKey.metaLeft) && event.isKeyPressed(LogicalKeyboardKey.keyQ)) { + print(" windowManager.close()"); + windowManager.close(); + return; + } } diff --git a/lib/ui/mobile.dart b/lib/ui/mobile.dart new file mode 100644 index 0000000..d514a73 --- /dev/null +++ b/lib/ui/mobile.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/channel.dart'; +import 'package:network_proxy/network/handler.dart'; +import 'package:network_proxy/network/http/http.dart'; +import 'package:network_proxy/network/util/host_filter.dart'; +import 'package:network_proxy/ui/desktop/toolbar/launch/launch.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/setting.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/theme.dart'; +import 'package:network_proxy/ui/mobile/filter.dart'; +import 'package:network_proxy/ui/mobile/ssl.dart'; + +import 'mobile/request.dart'; +import 'mobile/request_rewrite.dart'; + +class MobileHomePage extends StatefulWidget { + const MobileHomePage({super.key}); + + @override + State createState() { + return MobileHomeState(); + } +} + +class MobileHomeState extends State implements EventListener { + static const MethodChannel proxyVpnChannel = MethodChannel('com.proxy/proxyVpn'); + + late ProxyServer proxyServer; + final requestStateKey = GlobalKey(); + + @override + void onRequest(Channel channel, HttpRequest request) { + requestStateKey.currentState!.add(channel, request); + } + + @override + void onResponse(Channel channel, HttpResponse response) { + requestStateKey.currentState!.addResponse(channel, response); + } + + @override + void initState() { + proxyServer = ProxyServer(listener: this); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(centerTitle: true, title: const Text("ProxyPin"), actions: [ + IconButton( + tooltip: "清理", + icon: const Icon(Icons.cleaning_services_outlined), + onPressed: () => requestStateKey.currentState?.clean()), + IconButton( + tooltip: "Https代理", + icon: const Icon(Icons.https), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) { + return MobileSslWidget(proxyServer: proxyServer); + }), + ); + }) + ]), + drawer: drawer(), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + child: SocketLaunch( + proxyServer: proxyServer, + size: 38, + onStart: () { + proxyVpnChannel.invokeMethod("startVpn", {"proxyHost": "127.0.0.1", "proxyPort": proxyServer.port}); + }, + onStop: () { + proxyVpnChannel.invokeMethod("stopVpn"); + }, + )), + body: RequestWidget(key: requestStateKey, proxyServer: proxyServer)); + } + + Drawer drawer() { + return Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration(color: Theme.of(context).colorScheme.primaryContainer), + child: const Text('设置'), + ), + PortWidget(proxyServer: proxyServer), + const ThemeSetting(), + ListTile( + title: const Text("域名白名单"), + trailing: const Icon(Icons.arrow_right), + onTap: () => _filter(HostFilter.whitelist)), + ListTile( + title: const Text("域名黑名单"), + trailing: const Icon(Icons.arrow_right), + onTap: () => _filter(HostFilter.blacklist)), + ListTile(title: const Text("请求重写"), trailing: const Icon(Icons.arrow_right), onTap: () => _reqeustRewrite()) + ], + )); + } + + void _filter(HostList hostList) { + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) { + return MobileFilterWidget(proxyServer: proxyServer, hostList: hostList); + }), + ); + } + + void _reqeustRewrite() { + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) { + return MobileRequestRewrite(proxyServer: proxyServer); + }), + ); + } +} diff --git a/lib/ui/mobile/filter.dart b/lib/ui/mobile/filter.dart new file mode 100644 index 0000000..394279f --- /dev/null +++ b/lib/ui/mobile/filter.dart @@ -0,0 +1,219 @@ +import 'package:flutter/material.dart'; +import 'package:network_proxy/network/bin/server.dart'; + +import '../../../network/util/host_filter.dart'; + +class MobileFilterWidget extends StatefulWidget { + final ProxyServer proxyServer; + final HostList hostList; + + const MobileFilterWidget({super.key, required this.proxyServer, required this.hostList}); + + @override + State createState() => _MobileFilterState(); +} + +class _MobileFilterState extends State { + final ValueNotifier hostEnableNotifier = ValueNotifier(false); + + @override + void dispose() { + hostEnableNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + var title = widget.hostList.runtimeType == Whites ? "白名单" : "黑名单"; + var subtitle = widget.hostList.runtimeType == Whites ? "只代理白名单中的域名, 白名单启用黑名单将会失效" : "黑名单中的域名不会代理"; + return Scaffold( + appBar: AppBar(title: const Text("域名过滤", style: TextStyle(fontSize: 16))), + body: Container( + padding: const EdgeInsets.all(10), + child: DomainFilter( + title: title, + subtitle: subtitle, + hostList: widget.hostList, + proxyServer: widget.proxyServer, + hostEnableNotifier: hostEnableNotifier), + ) + ); + } +} + +class DomainFilter extends StatefulWidget { + final String title; + final String subtitle; + final HostList hostList; + final ProxyServer proxyServer; + final ValueNotifier hostEnableNotifier; + + const DomainFilter( + {super.key, + required this.title, + required this.subtitle, + required this.hostList, + required this.hostEnableNotifier, + required this.proxyServer}); + + @override + State createState() { + return _DomainFilterState(); + } +} + +class _DomainFilterState extends State { + late DomainList domainList; + bool changed = false; + + @override + Widget build(BuildContext context) { + domainList = DomainList(widget.hostList); + + return Column( + children: [ + ListTile( + title: Text(widget.title), + subtitle: Text(widget.subtitle, style: const TextStyle(fontSize: 12)), + titleAlignment: ListTileTitleAlignment.center, + ), + const SizedBox(height: 10), + ValueListenableBuilder( + valueListenable: widget.hostEnableNotifier, + builder: (_, bool enable, __) { + return SwitchListTile( + title: const Text('是否启用'), + dense: true, + value: widget.hostList.enabled, + onChanged: (value) { + widget.hostList.enabled = value; + changed = true; + widget.hostEnableNotifier.value = !widget.hostEnableNotifier.value; + }); + }), + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + FilledButton.icon( + icon: const Icon(Icons.add, size: 14), + onPressed: () { + add(); + }, + label: const Text("增加", style: TextStyle(fontSize: 12))), + const SizedBox(width: 10), + TextButton.icon( + icon: const Icon(Icons.remove, size: 14), + label: const Text("删除", style: TextStyle(fontSize: 12)), + onPressed: () { + if (domainList.selected().isEmpty) { + return; + } + changed = true; + setState(() { + widget.hostList.removeIndex(domainList.selected()); + }); + }) + ]), + domainList + ], + ); + } + + @override + void dispose() { + if (changed) { + widget.proxyServer.flushConfig(); + } + super.dispose(); + } + + void add() { + GlobalKey formKey = GlobalKey(); + String? host; + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + scrollable: true, + content: Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + key: formKey, + child: Column(children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'Host', hintText: '*.example.com'), + onSaved: (val) => host = val) + ]))), + actions: [ + FilledButton( + child: const Text("添加"), + onPressed: () { + (formKey.currentState as FormState).save(); + if (host != null && host!.isNotEmpty) { + try { + changed = true; + widget.hostList.add(host!); + setState(() {}); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString()))); + } + } + Navigator.of(context).pop(); + }), + ElevatedButton( + child: const Text("关闭"), + onPressed: () { + Navigator.of(context).pop(); + }) + ]); + }); + } +} + +///域名列表 +class DomainList extends StatefulWidget { + final HostList hostList; + + DomainList(this.hostList) : super(key: GlobalKey<_DomainListState>()); + + @override + State createState() => _DomainListState(); + + List selected() { + var state = (key as GlobalKey<_DomainListState>).currentState; + List list = []; + state?.selected.forEach((key, value) { + if (value == true) { + list.add(key); + } + }); + return list; + } +} + +class _DomainListState extends State { + late Map selected = {}; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 10), + height: 300, + child: SingleChildScrollView( + child: DataTable( + border: TableBorder.symmetric(outside: BorderSide(width: 1, color: Theme.of(context).highlightColor)), + columns: const [ + DataColumn(label: Text('域名')), + ], + rows: List.generate( + widget.hostList.list.length, + (index) => DataRow( + cells: [DataCell(Text(widget.hostList.list[index].pattern.replaceAll(".*", "*")))], + selected: selected[index] == true, + onSelectChanged: (value) { + setState(() { + selected[index] = value!; + }); + })), + ))); + } +} diff --git a/lib/ui/mobile/request.dart b/lib/ui/mobile/request.dart new file mode 100644 index 0000000..83af6e3 --- /dev/null +++ b/lib/ui/mobile/request.dart @@ -0,0 +1,285 @@ +import 'dart:collection'; + +import 'package:date_format/date_format.dart'; +import 'package:flutter/material.dart'; +import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/http/http.dart'; + +import '../../network/channel.dart'; +import '../panel.dart'; + +class RequestWidget extends StatefulWidget { + final ProxyServer proxyServer; + + const RequestWidget({super.key, required this.proxyServer}); + + @override + State createState() { + return RequestWidgetState(); + } +} + +class RequestWidgetState extends State { + final tabs = [ + const Tab(child: Text('全部请求')), + const Tab(child: Text('域名列表')), + ]; + + GlobalKey requestSequenceKey = GlobalKey(); + GlobalKey domainListKey = GlobalKey(); + + static List container = []; + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: tabs.length, + child: Scaffold( + appBar: AppBar(title: TabBar(tabs: tabs)), + body: TabBarView( + children: [ + RequestSequence(key: requestSequenceKey, list: container), + DomainList(key: domainListKey, list: container), + ], + ), + )); + } + + ///添加请求 + add(Channel channel, HttpRequest request) { + container.add(request); + requestSequenceKey.currentState?.add(request); + domainListKey.currentState?.add(request); + } + + ///添加响应 + addResponse(Channel channel, HttpResponse response) { + response.request?.response = response; + requestSequenceKey.currentState?.addResponse(response); + domainListKey.currentState?.addResponse(response); + } + + ///清理 + clean() { + setState(() { + domainListKey.currentState?.clean(); + requestSequenceKey.currentState?.clean(); + container.clear(); + }); + } +} + +class RequestSequence extends StatefulWidget { + final List list; + + const RequestSequence({super.key, required this.list}); + + @override + State createState() { + return RequestSequenceState(); + } +} + +class RequestSequenceState extends State { + GlobalKey listKey = GlobalKey(); + Map> indexes = HashMap(); + + late Queue list = Queue(); + + @override + initState() { + super.initState(); + print("initState ${widget.list.length}"); + list.addAll(widget.list.reversed); + } + + ///添加请求 + add(HttpRequest request) { + list.addFirst(request); + listKey.currentState?.insertItem(0); + } + + ///添加响应 + addResponse(HttpResponse response) { + response.request?.response = response; + var state = indexes.remove(response.request); + state?.currentState?.change(response); + } + + clean() { + setState(() { + list.clear(); + indexes.clear(); + listKey.currentState?.removeAllItems((context, animation) => Container()); + }); + } + + @override + Widget build(BuildContext context) { + return AnimatedList( + key: listKey, + initialItemCount: list.length, + itemBuilder: (context, index, Animation animation) { + GlobalKey key = GlobalKey(); + indexes[list.elementAt(index)] = key; + return Container( + decoration: + BoxDecoration(border: Border(bottom: BorderSide(width: 0.5, color: Theme.of(context).focusColor))), + child: RequestRow(key: key, request: list.elementAt(index))); + }); + } +} + +class RequestRow extends StatefulWidget { + final HttpRequest request; + + const RequestRow({super.key, required this.request}); + + @override + State createState() { + return RequestRowState(); + } +} + +class RequestRowState extends State { + late HttpRequest request; + HttpResponse? response; + + change(HttpResponse response) { + setState(() { + this.response = response; + }); + } + + @override + void initState() { + request = widget.request; + response = request.response; + super.initState(); + } + + @override + Widget build(BuildContext context) { + var title = '${request.method.name} ${request.requestUrl}'; + var time = formatDate(request.requestTime, [HH, ':', nn, ':', ss]); + return ListTile( + leading: Icon(getIcon(response), size: 16, color: Colors.green), + title: Text(title, overflow: TextOverflow.ellipsis, maxLines: 1), + subtitle: Text( + '$time - [${response?.status.code ?? ''}] ${response?.contentType.name.toUpperCase() ?? ''} ${response?.costTime() ?? ''} ', + maxLines: 1), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) { + return NetworkTabController( + httpRequest: request, + httpResponse: response, + title: const Text("抓包详情", style: TextStyle(fontSize: 16))); + })); + }); + } + + IconData getIcon(HttpResponse? response) { + var map = { + ContentType.json: Icons.data_object, + ContentType.html: Icons.html, + ContentType.js: Icons.javascript, + ContentType.image: Icons.image, + ContentType.text: Icons.text_fields, + ContentType.css: Icons.css, + ContentType.font: Icons.font_download, + }; + if (response == null) { + return Icons.question_mark; + } + var contentType = response.contentType; + return map[contentType] ?? Icons.http; + } +} + +class DomainList extends StatefulWidget { + final List list; + + const DomainList({super.key, required this.list}); + + @override + State createState() { + return DomainListState(); + } +} + +class DomainListState extends State { + GlobalKey requestSequenceKey = GlobalKey(); + + Map> containerMap = {}; + + LinkedHashSet container = LinkedHashSet(); + HostAndPort? showHostAndPort; + + @override + initState() { + super.initState(); + for (var request in widget.list) { + var hostAndPort = request.hostAndPort!; + container.add(hostAndPort); + var list = containerMap[hostAndPort]; + if (list == null) { + list = []; + containerMap[hostAndPort] = list; + } + list.add(request); + } + } + + add(HttpRequest request) { + var hostAndPort = request.hostAndPort!; + container.add(hostAndPort); + var list = containerMap[hostAndPort]; + if (list == null) { + list = []; + containerMap[hostAndPort] = list; + } + list.add(request); + if (showHostAndPort == request.hostAndPort) { + requestSequenceKey.currentState?.add(request); + } + + setState(() {}); + } + + addResponse(HttpResponse response) { + if (showHostAndPort == response.request?.hostAndPort) { + requestSequenceKey.currentState?.addResponse(response); + } + } + + clean() { + setState(() { + containerMap.clear(); + container.clear(); + }); + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: container.length, + itemBuilder: (context, index) { + var time = formatDate( + containerMap[container.elementAt(index)]!.last.requestTime, [m, '/', d, ' ', HH, ':', nn, ':', ss]); + return ListTile( + title: Text(container.elementAt(index).url, maxLines: 1, overflow: TextOverflow.ellipsis), + trailing: const Icon(Icons.chevron_right), + subtitle: Text("最后请求时间: $time, 次数: ${containerMap[container.elementAt(index)]!.length}", + maxLines: 1, overflow: TextOverflow.ellipsis), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) { + showHostAndPort = container.elementAt(index); + return Scaffold( + appBar: AppBar(title: const Text("请求列表")), + body: RequestSequence(key: requestSequenceKey, list: containerMap[container.elementAt(index)]!)); + })); + }); + }); + } +} diff --git a/lib/ui/mobile/request_rewrite.dart b/lib/ui/mobile/request_rewrite.dart new file mode 100644 index 0000000..1539db6 --- /dev/null +++ b/lib/ui/mobile/request_rewrite.dart @@ -0,0 +1,284 @@ +import 'package:flutter/material.dart'; +import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/util/request_rewrite.dart'; + +class MobileRequestRewrite extends StatefulWidget { + final ProxyServer proxyServer; + + const MobileRequestRewrite({super.key, required this.proxyServer}); + + @override + State createState() => _MobileRequestRewriteState(); +} + +class _MobileRequestRewriteState extends State { + late RequestRuleList requestRuleList; + late ValueNotifier enableNotifier; + bool changed = false; + + @override + void initState() { + super.initState(); + requestRuleList = RequestRuleList(widget.proxyServer.requestRewrites); + enableNotifier = ValueNotifier(widget.proxyServer.requestRewrites.enabled); + } + + @override + void dispose() { + if (changed || enableNotifier.value != widget.proxyServer.requestRewrites.enabled) { + widget.proxyServer.requestRewrites.enabled = enableNotifier.value; + widget.proxyServer.flushRequestRewriteConfig(); + } + + enableNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("请求重写")), + body: Container( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + child: ValueListenableBuilder( + valueListenable: enableNotifier, + builder: (_, bool v, __) { + return SwitchListTile( + contentPadding: const EdgeInsets.only(left: 2), + title: const Text('是否启用请求重写'), + value: enableNotifier.value, + onChanged: (value) { + enableNotifier.value = value; + }); + })), + const SizedBox(height: 10), + Row(children: [ + FilledButton.icon( + icon: const Icon(Icons.add), + onPressed: () { + add(); + }, + label: const Text("增加")), + const SizedBox(width: 10), + OutlinedButton.icon( + onPressed: () { + var selectedIndex = requestRuleList.currentSelectedIndex(); + add(selectedIndex); + }, + icon: const Icon(Icons.edit), + label: const Text("编辑")), + TextButton.icon( + icon: const Icon(Icons.remove), + label: const Text("删除"), + onPressed: () { + var removeSelected = requestRuleList.removeSelected(); + if (removeSelected.isEmpty) { + return; + } + + changed = true; + setState(() { + widget.proxyServer.requestRewrites.removeIndex(removeSelected); + requestRuleList.changeState(); + }); + }) + ]), + const SizedBox(height: 10), + requestRuleList, + ], + ))); + } + + void add([int currentIndex = -1]) { + showDialog( + context: context, + builder: (BuildContext context) { + return RuleAddDialog( + requestRewrites: widget.proxyServer.requestRewrites, + currentIndex: currentIndex, + onChange: () { + changed = true; + requestRuleList.changeState(); + }); + }); + } +} + +///请求重写规则添加对话框 +class RuleAddDialog extends StatelessWidget { + final RequestRewrites requestRewrites; + final int currentIndex; + final Function onChange; + + const RuleAddDialog({super.key, required this.currentIndex, required this.onChange, required this.requestRewrites}); + + @override + Widget build(BuildContext context) { + GlobalKey formKey = GlobalKey(); + RequestRewriteRule? rule; + if (currentIndex >= 0) { + rule = requestRewrites.rules[currentIndex]; + } + + ValueNotifier enableNotifier = ValueNotifier(rule == null || rule.enabled); + String? url = rule?.url; + String? requestBody = rule?.requestBody; + String? responseBody = rule?.responseBody; + + return AlertDialog( + title: const Text("添加请求重写规则", style: TextStyle(fontSize: 16)), + scrollable: true, + content: Form( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ValueListenableBuilder( + valueListenable: enableNotifier, + builder: (_, bool enable, __) { + return SwitchListTile( + contentPadding: const EdgeInsets.only(left: 0), + title: const Text('是否启用', textAlign: TextAlign.start), + value: enable, + onChanged: (value) { + enableNotifier.value = value; + }); + }), + TextFormField( + decoration: const InputDecoration(labelText: 'URL', hintText: '/api/v1/*'), + validator: (val) { + if (val == null || val.isEmpty) { + return 'URL不能为空'; + } + return null; + }, + initialValue: url, + onSaved: (val) => url = val), + TextFormField( + initialValue: requestBody, + decoration: const InputDecoration(labelText: '请求体替换为:'), + onSaved: (val) => requestBody = val), + TextFormField( + initialValue: responseBody, + minLines: 3, + maxLines: 15, + decoration: const InputDecoration(labelText: '响应体替换为:', hintText: '{"code":"200","data":{}}'), + onSaved: (val) => responseBody = val) + ])), + actions: [ + FilledButton( + child: const Text("保存"), + onPressed: () { + if ((formKey.currentState as FormState).validate()) { + (formKey.currentState as FormState).save(); + + if (currentIndex >= 0) { + requestRewrites.rules[currentIndex] = RequestRewriteRule(enableNotifier.value, url!, + requestBody: requestBody, responseBody: responseBody); + } else { + requestRewrites.addRule(RequestRewriteRule(enableNotifier.value, url!, + requestBody: requestBody, responseBody: responseBody)); + } + + enableNotifier.dispose(); + onChange.call(); + Navigator.of(context).pop(); + } + }), + ElevatedButton( + child: const Text("关闭"), + onPressed: () { + Navigator.of(context).pop(); + }) + ]); + } +} + +class RequestRuleList extends StatefulWidget { + final RequestRewrites requestRewrites; + + RequestRuleList(this.requestRewrites) : super(key: GlobalKey<_RequestRuleListState>()); + + @override + State createState() => _RequestRuleListState(); + + List removeSelected() { + var state = (key as GlobalKey<_RequestRuleListState>).currentState; + List list = []; + state?.selected.forEach((key, value) { + if (value == true) { + list.add(key); + } + }); + state?.selected.clear(); + return list; + } + + int currentSelectedIndex() { + var state = (key as GlobalKey<_RequestRuleListState>).currentState; + return state?.currentSelectedIndex ?? -1; + } + + changeState() { + var state = (key as GlobalKey<_RequestRuleListState>).currentState; + state?.changeState(); + } +} + +class _RequestRuleListState extends State { + final Map selected = {}; + int currentSelectedIndex = -1; + + changeState() { + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 10), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: DataTable( + dataRowMaxHeight: 100, + border: TableBorder.symmetric(outside: BorderSide(width: 1, color: Theme.of(context).highlightColor)), + columns: const [ + DataColumn(label: Text('启用')), + DataColumn(label: Text('URL')), + DataColumn(label: Text('请求体')), + DataColumn(label: Text('响应体')), + ], + rows: List.generate( + widget.requestRewrites.rules.length, + (index) => DataRow( + cells: [ + DataCell(Text(widget.requestRewrites.rules[index].enabled ? "是" : "否")), + DataCell(ConstrainedBox( + constraints: const BoxConstraints(minWidth: 60), + child: Text(widget.requestRewrites.rules[index].url))), + DataCell(SelectableText.rich( + TextSpan(text: widget.requestRewrites.rules[index].requestBody), + style: const TextStyle(fontSize: 12))), + DataCell(Container( + constraints: const BoxConstraints(maxWidth: 300), + padding: const EdgeInsetsDirectional.all(10), + child: SelectableText.rich( + TextSpan(text: widget.requestRewrites.rules[index].responseBody), + style: const TextStyle(fontSize: 12)), + )) + ], + selected: selected[index] == true, + onSelectChanged: (value) { + setState(() { + selected[index] = value!; + currentSelectedIndex = index; + }); + })), + ))); + } +} diff --git a/lib/ui/mobile/ssl.dart b/lib/ui/mobile/ssl.dart new file mode 100644 index 0000000..5c5ba9b --- /dev/null +++ b/lib/ui/mobile/ssl.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:network_proxy/network/bin/server.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MobileSslWidget extends StatefulWidget { + final ProxyServer proxyServer; + + const MobileSslWidget({super.key, required this.proxyServer}); + + @override + State createState() => _MobileSslState(); +} + +class _MobileSslState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Https代理"), + centerTitle: true, + ), + body: Column(children: [ + _Switch(proxyServer: widget.proxyServer), + ExpansionTile( + title: const Text("安装根证书"), + initiallyExpanded: true, + childrenPadding: const EdgeInsets.only(left: 20), + expandedAlignment: Alignment.topLeft, + expandedCrossAxisAlignment: CrossAxisAlignment.start, + shape: const Border(), + children: [ + TextButton(onPressed: () => _downloadCert(), child: const Text("1. 下载根证书安装到本系统")), + TextButton(onPressed: () {}, child: const Text("2. 去系统设置信任根证书")), + ]) + ])); + } + + void _downloadCert() async { + launchUrl(Uri.parse("http://127.0.0.1:${widget.proxyServer.port}/ssl"), mode: LaunchMode.externalApplication); + } +} + +class _Switch extends StatefulWidget { + final ProxyServer proxyServer; + + const _Switch({Key? key, required this.proxyServer}) : super(key: key); + + @override + State<_Switch> createState() => _SwitchState(); +} + +class _SwitchState extends State<_Switch> { + bool changed = false; + + @override + Widget build(BuildContext context) { + return SwitchListTile( + hoverColor: Colors.transparent, + title: const Text("启用Https代理", style: TextStyle(fontSize: 16)), + value: widget.proxyServer.enableSsl, + onChanged: (val) { + widget.proxyServer.enableSsl = val; + changed = true; + setState(() {}); + }); + } + + @override + void dispose() { + super.dispose(); + if (changed) { + widget.proxyServer.flushConfig(); + } + } +} diff --git a/lib/ui/panel.dart b/lib/ui/panel.dart index af5b5fc..84953ef 100644 --- a/lib/ui/panel.dart +++ b/lib/ui/panel.dart @@ -6,17 +6,23 @@ import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/utils/lang.dart'; class NetworkTabController extends StatefulWidget { - final tabs = [ - const Tab(child: Text('General', style: TextStyle(fontSize: 18))), - const Tab(child: Text('Request', style: TextStyle(fontSize: 18))), - const Tab(child: Text('Response', style: TextStyle(fontSize: 18))), - const Tab(child: Text('Cookies', style: TextStyle(fontSize: 18))), + final tabs = [ + 'General', + 'Request', + 'Response', + 'Cookies', ]; final ValueWrap request = ValueWrap(); final ValueWrap response = ValueWrap(); + final Widget? title; + final TextStyle? tabStyle; - NetworkTabController() : super(key: GlobalKey()); + NetworkTabController({HttpRequest? httpRequest, HttpResponse? httpResponse, this.title, this.tabStyle}) + : super(key: GlobalKey()) { + request.set(httpRequest); + response.set(httpResponse); + } void change(HttpRequest? request, HttpResponse? response) { this.request.set(request); @@ -31,26 +37,46 @@ class NetworkTabController extends StatefulWidget { } } -class NetworkTabState extends State { +class NetworkTabState extends State with SingleTickerProviderStateMixin { + late TabController _tabController; + void changeState() { setState(() {}); } + @override + void initState() { + super.initState(); + _tabController = TabController(length: widget.tabs.length, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return DefaultTabController( - length: widget.tabs.length, - child: Scaffold( - appBar: AppBar(title: TabBar(tabs: widget.tabs)), - body: TabBarView( - children: [ - general(), - request(), - response(), - cookies(), - ], - ), - )); + var tabBar = TabBar( + controller: _tabController, + labelPadding: const EdgeInsets.symmetric(horizontal: 10), + tabs: widget.tabs.map((title) => Tab(child: Text(title, style: widget.tabStyle, maxLines: 1))).toList(), + ); + + Widget appBar = widget.title == null ? tabBar : AppBar(title: widget.title, bottom: tabBar); + return Scaffold( + appBar: appBar as PreferredSizeWidget?, + body: TabBarView( + controller: _tabController, + children: [ + general(), + request(), + response(), + cookies(), + ], + ), + ); } Widget general() { @@ -138,8 +164,7 @@ class NetworkTabState extends State { Widget? getBody(String type, HttpMessage message) { if (message.body?.isNotEmpty == true) { if (message.contentType == ContentType.image) { - return expansionTile("$type Body", - [Image.memory(Uint8List.fromList(message.body ?? []), fit: BoxFit.cover, width: 200, height: 200)]); + return expansionTile("$type Body", [Image.memory(Uint8List.fromList(message.body ?? []), fit: BoxFit.cover)]); } else { try { if (message.contentType == ContentType.json) { @@ -168,8 +193,7 @@ class NetworkTabState extends State { } Widget rowWidget(final String name, String? value) { - return Row( - children: [ + return Row(children: [ Expanded(flex: 2, child: SelectableText(name)), Expanded(flex: 4, child: SelectableText(value ?? '')) ]); diff --git a/lib/utils/platform.dart b/lib/utils/platform.dart new file mode 100644 index 0000000..15ec409 --- /dev/null +++ b/lib/utils/platform.dart @@ -0,0 +1,7 @@ +import 'dart:io'; + +class Platforms { + static bool isDesktop() { + return Platform.isWindows || Platform.isMacOS || Platform.isLinux; + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index d76db18..6139551 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) proxy_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ProxyManagerPlugin"); + proxy_manager_plugin_register_with_registrar(proxy_manager_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 7432170..6fb4385 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux + proxy_manager screen_retriever url_launcher_linux window_manager diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index f7e5268..229ee96 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,6 +7,7 @@ import Foundation import file_selector_macos import path_provider_foundation +import proxy_manager import screen_retriever import url_launcher_macos import window_manager @@ -14,6 +15,7 @@ import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + ProxyManagerPlugin.register(with: registry.registrar(forPlugin: "ProxyManagerPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 02032b4..65f3b80 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,10 +117,18 @@ packages: dependency: "direct main" description: name: file_selector - sha256: "2b9acc3587127132da2a8d88d069172c49e32f977211cdf8f61f4b8e68e2a165" + sha256: "1d2fde93dddf634a9c3c0faa748169d7ac0d83757135555707e52f02c017ad4f" url: "https://pub.dev" source: hosted - version: "0.9.4" + version: "0.9.5" + file_selector_android: + dependency: transitive + description: + name: file_selector_android + sha256: "65d41d2fbed893c5eb8842674ed08b920dc7d276b6c7e74ee8b1759dce4b2067" + url: "https://pub.dev" + source: hosted + version: "0.5.0" file_selector_ios: dependency: transitive description: @@ -178,10 +186,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_test: dependency: "direct dev" description: flutter @@ -521,10 +529,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "95096fede562cbb65f30d38b62d819a458f59ba9fe4a317f6cee669710f6676b" + sha256: "9eef00e393e7f9308309ce9a8b2398c9ee3ca78b50c96e8b4f9873945693ac88" url: "https://pub.dev" source: hosted - version: "0.3.4" + version: "0.3.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ccdcdeb..d2c2385 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: logger: ^1.4.0 date_format: ^2.0.7 file_selector: ^0.9.3 - window_manager: ^0.3.4 + window_manager: ^0.3.5 path_provider: ^2.0.15 url_launcher: ^6.1.11 proxy_manager: ^0.0.3 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 0d3b5b1..bb1a7e2 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -14,6 +15,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + ProxyManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ProxyManagerPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index c0677f7..8cd3f07 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + proxy_manager screen_retriever url_launcher_windows window_manager