diff --git a/.gitignore b/.gitignore index 6eeee82..caa7e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,9 @@ libs/ openp2p.app.jks openp2p.aar openp2p-sources.jar -build.gradle +wintun/ +wintun.dll +.vscode/ +app/.idea/ +*_debug_bin* +cmd/openp2p \ No newline at end of file diff --git a/README-ZH.md b/README-ZH.md index 037095a..564d1d9 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -19,9 +19,9 @@ [查看详细](#安全性) ### 4. 轻量 -文件大小2MB+,运行内存2MB+;全部在应用层实现,没有虚拟网卡,没有内核程序 +文件大小2MB+,运行内存2MB+;它可以仅跑在应用层,或者配合wintun驱动使用组网功能 ### 5. 跨平台 -因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64 +因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64、s390x、ppc64le ### 6. 高效 P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境,无论NAT1-4(Cone或Symmetric),UDP或TCP打洞,UPNP,IPv6都支持。依靠Quic协议优秀的拥塞算法,能在糟糕的网络环境获得高带宽低延时。 @@ -128,13 +128,15 @@ CGO_ENABLED=0 env GOOS=linux GOARCH=amd64 go build -o openp2p --ldflags '-s -w ' 6. 客户端提供WebUI 7. ~~支持自有服务器,开源服务器程序~~(100%) 8. 共享节点调度模型优化,对不同的运营商优化 -9. 方便二次开发,提供API和lib +9. ~~方便二次开发,提供API和lib~~(100%) 10. ~~应用层支持UDP协议,实现很简单,但UDP应用较少暂不急~~(100%) -11. 底层通信支持KCP协议,目前仅支持Quic;KCP专门对延时优化,被游戏加速器广泛使用,可以牺牲一定的带宽降低延时 +11. ~~底层通信支持KCP协议,目前仅支持Quic;KCP专门对延时优化,被游戏加速器广泛使用,可以牺牲一定的带宽降低延时~~(100%) 12. ~~支持Android系统,让旧手机焕发青春变成移动网关~~(100%) -13. 支持Windows网上邻居共享文件 -14. 内网直连优化,用处不大,估计就用户测试时用到 +13. ~~支持Windows网上邻居共享文件~~(100%) +14. ~~内网直连优化~~(100%) 15. ~~支持UPNP~~(100%) +16. ~~支持Android~~(100%) +17. 支持IOS 远期计划: 1. 利用区块链技术去中心化,让共享设备的用户有收益,从而促进更多用户共享,达到正向闭环。 diff --git a/README.md b/README.md index d9c1393..6ed7887 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ The code is open source, the P2P tunnel uses TLS1.3+AES double encryption, and t [details](#Safety) ### 4. Lightweight -2MB+ filesize, 2MB+ memory. It runs at appllication layer, no vitrual NIC, no kernel driver. +2MB+ filesize, 2MB+ memory. It could only runs at application layer, or uses wintun driver for SDWAN. ### 5. Cross-platform -Benefit from lightweight, it easily supports most of major OS, like Windows, Linux, MacOS, also most of CPU architecture, like 386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64. +Benefit from lightweight, it easily supports most of major OS, like Windows, Linux, MacOS, also most of CPU architecture, like 386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64、s390x、ppc64le. ### 6. Efficient P2P direct connection lets your devices make good use of bandwidth. Your device can be connected in any network environments, even supports NAT1-4 (Cone or Symmetric),UDP or TCP punching,UPNP,IPv6. Relying on the excellent congestion algorithm of the Quic protocol, high bandwidth and low latency can be obtained in a bad network environment. @@ -136,14 +136,15 @@ Short-Term: 6. Provide WebUI on client side. 7. ~~Support private server, open source server program.~~(100%) 8. Optimize our share scheduling model for different network operators. -9. Provide REST APIs and libary for secondary development. +9. ~~Provide REST APIs and libary for secondary development.~~(100%) 10. ~~Support UDP at application layer, it is easy to implement but not urgent due to only a few applicaitons using UDP protocol.~~(100%) -11. Support KCP protocol underlay, currently support Quic only. KCP focus on delay optimization,which has been widely used as game accelerator,it can sacrifice part of bandwidth to reduce timelag. +11. ~~Support KCP protocol underlay, currently support Quic only. KCP focus on delay optimization,which has been widely used as game accelerator,it can sacrifice part of bandwidth to reduce timelag. ~~(100%) 12. ~~Support Android platform, let the phones to be mobile gateway.~~(100%) -13. Support SMB Windows neighborhood. -14. Direct connection on intranet, for testing. +13. ~~Support SMB Windows neighborhood.~~(100%) +14. ~~Direct connection on intranet, for testing.~~(100%) 15. ~~Support UPNP.~~(100%) - +16. ~~Support Android~~(100%) +17. Support IOS Long-Term: 1. Use blockchain technology to decentralize, so that users who share equipment have benefits, thereby promoting more users to share, and achieving a positive closed loop. diff --git a/USAGE-ZH.md b/USAGE-ZH.md index 052ad09..0b6b91a 100644 --- a/USAGE-ZH.md +++ b/USAGE-ZH.md @@ -43,7 +43,7 @@ nohup ./openp2p -d -node OFFICEPC1 -token TOKEN & ``` { "network": { - "Node": "hhd1207-222", + "Node": "YOUR-NODE-NAME", "Token": "TOKEN", "ShareBandwidth": 0, "ServerHost": "api.openp2p.cn", @@ -87,7 +87,8 @@ systemctl stop firewalld.service systemctl start firewalld.service firewall-cmd --state ``` - +## 停止 +TODO: windows linux macos ## 卸载 ``` ./openp2p uninstall diff --git a/USAGE.md b/USAGE.md index 37da7e6..a6f2778 100644 --- a/USAGE.md +++ b/USAGE.md @@ -46,7 +46,7 @@ Configuration example ``` { "network": { - "Node": "hhd1207-222", + "Node": "YOUR-NODE-NAME", "Token": "TOKEN", "ShareBandwidth": 0, "ServerHost": "api.openp2p.cn", diff --git a/app/.idea/runConfigurations.xml b/app/.idea/runConfigurations.xml deleted file mode 100644 index 93e4b17..0000000 --- a/app/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/README.md b/app/README.md index 501757e..126e433 100644 --- a/app/README.md +++ b/app/README.md @@ -1,7 +1,11 @@ ## Build +depends on openjdk 11, gradle 8.1.3, ndk 21 ``` -cd core + +go install golang.org/x/mobile/cmd/gomobile@latest +gomobile init go get -v golang.org/x/mobile/bind +cd core gomobile bind -target android -v if [[ $? -ne 0 ]]; then echo "build error" diff --git a/app/app/build.gradle b/app/app/build.gradle index 8d2be38..fc8f9ca 100644 --- a/app/app/build.gradle +++ b/app/app/build.gradle @@ -1,62 +1,63 @@ -plugins { - id 'com.android.application' - id 'kotlin-android' -} - -android { - signingConfigs { - release { - storeFile file('C:\\work\\src\\openp2p-client\\app\\openp2p.jks') - storePassword 'YOUR-PASSWORD' - keyAlias 'openp2p.keys' - keyPassword 'YOUR-PASSWORD' - } - } - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - applicationId "cn.openp2p" - minSdkVersion 16 - targetSdkVersion 30 - versionCode 1 - versionName "2718281828" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled true - shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.release - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - viewBinding true - } -} - -dependencies { - implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.1' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.2.1' - implementation 'androidx.annotation:annotation:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.1' - implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - implementation files('libs\\openp2p-sources.jar') +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +android { + signingConfigs { + release { + storeFile file('C:\\work\\src\\openp2p-client\\app\\openp2p.jks') + storePassword 'YOUR-PASSWORD' + keyAlias 'openp2p.keys' + keyPassword 'YOUR-PASSWORD' + } + } + compileSdkVersion 31 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "cn.openp2p" + minSdkVersion 16 + targetSdkVersion 31 + versionCode 1 + versionName "2718281828" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + namespace "cn.openp2p" + } + + buildTypes { + release { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation files('libs\\openp2p-sources.jar') } \ No newline at end of file diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml index b3487a3..86cc154 100644 --- a/app/app/src/main/AndroidManifest.xml +++ b/app/app/src/main/AndroidManifest.xml @@ -1,12 +1,11 @@ - + - + + android:label="@string/app_name" + android:exported="true"> + + + + + \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/BootReceiver.kt b/app/app/src/main/java/cn/openp2p/BootReceiver.kt new file mode 100644 index 0000000..f498f81 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/BootReceiver.kt @@ -0,0 +1,27 @@ +package cn.openp2p + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.net.VpnService +import android.os.Build +import android.os.Bundle +import android.util.Log +import cn.openp2p.ui.login.LoginActivity +class BootReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { +// Logger.log("pp onReceive "+intent.action.toString()) + Log.i("onReceive","start "+intent.action.toString()) + // if (Intent.ACTION_BOOT_COMPLETED == intent.action) { + // Log.i("onReceive","match "+intent.action.toString()) + // VpnService.prepare(context) + // val intent = Intent(context, OpenP2PService::class.java) + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // context.startForegroundService(intent) + // } else { + // context.startService(intent) + // } + // } + Log.i("onReceive","end "+intent.action.toString()) + } +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/OpenP2PService.kt b/app/app/src/main/java/cn/openp2p/OpenP2PService.kt index d2542dd..6660496 100644 --- a/app/app/src/main/java/cn/openp2p/OpenP2PService.kt +++ b/app/app/src/main/java/cn/openp2p/OpenP2PService.kt @@ -4,9 +4,12 @@ import android.app.* import android.content.Context import android.content.Intent import android.graphics.Color +import java.io.IOException +import android.net.VpnService import android.os.Binder import android.os.Build import android.os.IBinder +import android.os.ParcelFileDescriptor import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat @@ -14,9 +17,22 @@ import cn.openp2p.ui.login.LoginActivity import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import openp2p.Openp2p +import java.io.FileInputStream +import java.io.FileOutputStream +import java.nio.ByteBuffer +import kotlinx.coroutines.* +import org.json.JSONObject +data class Node(val name: String, val ip: String, val resource: String? = null) -class OpenP2PService : Service() { + +data class Network( + val id: Long, + val name: String, + val gateway: String, + val Nodes: List +) +class OpenP2PService : VpnService() { companion object { private val LOG_TAG = OpenP2PService::class.simpleName } @@ -29,10 +45,12 @@ class OpenP2PService : Service() { private lateinit var network: openp2p.P2PNetwork private lateinit var mToken: String private var running:Boolean =true + private var sdwanRunning:Boolean =false + private var vpnInterface: ParcelFileDescriptor? = null + private var sdwanJob: Job? = null override fun onCreate() { Log.i(LOG_TAG, "onCreate - Thread ID = " + Thread.currentThread().id) - var channelId: String? = null - channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + var channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("kim.hsl", "ForegroundService") } else { "" @@ -41,7 +59,7 @@ class OpenP2PService : Service() { val pendingIntent = PendingIntent.getActivity( this, 0, - notificationIntent, 0 + notificationIntent, PendingIntent.FLAG_IMMUTABLE ) val notification = channelId?.let { @@ -54,7 +72,7 @@ class OpenP2PService : Service() { startForeground(1337, notification) super.onCreate() - + refreshSDWAN() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -62,23 +80,172 @@ class OpenP2PService : Service() { LOG_TAG, "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id ) + startOpenP2P(null) return super.onStartCommand(intent, flags, startId) } override fun onBind(p0: Intent?): IBinder? { - val token = p0?.getStringExtra("token") - Log.i(LOG_TAG, "onBind - Thread ID = " + Thread.currentThread().id + token) + Log.i(LOG_TAG, "onBind token=$token") + startOpenP2P(token) + return binder + } + + private fun startOpenP2P(token : String?): Boolean { + if (sdwanRunning) { + return true + } + + Log.i(LOG_TAG, "startOpenP2P - Thread ID = " + Thread.currentThread().id + token) + val oldToken = Openp2p.getToken(getExternalFilesDir(null).toString()) + Log.i(LOG_TAG, "startOpenP2P oldtoken=$oldToken newtoken=$token") + if (oldToken=="0" && token==null){ + return false + } + sdwanRunning = true + // runSDWAN() GlobalScope.launch { - network = Openp2p.runAsModule(getExternalFilesDir(null).toString(), token, 0, 1) + network = Openp2p.runAsModule( + getExternalFilesDir(null).toString(), + token, + 0, + 1 + ) // /storage/emulated/0/Android/data/cn.openp2p/files/ val isConnect = network.connect(30000) // ms - Log.i(OpenP2PService.LOG_TAG, "login result: " + isConnect.toString()); + Log.i(LOG_TAG, "login result: " + isConnect.toString()); do { Thread.sleep(1000) - }while(network.connect(30000)&&running) + } while (network.connect(30000) && running) stopSelf() } - return binder + return false + } + + private fun refreshSDWAN() { + GlobalScope.launch { + Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN start"); + while (true) { + Log.i(OpenP2PService.LOG_TAG, "waiting new sdwan config"); + val buf = ByteArray(4096) + val buffLen = Openp2p.getAndroidSDWANConfig(buf) + Log.i(OpenP2PService.LOG_TAG, "closing running sdwan instance"); + sdwanRunning = false + vpnInterface?.close() + vpnInterface = null + Thread.sleep(10000) + runSDWAN(buf.copyOfRange(0,buffLen.toInt() )) + } + Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN end"); + } + } + private suspend fun readTunLoop() { + val inputStream = FileInputStream(vpnInterface?.fileDescriptor).channel + if (inputStream==null){ + Log.i(OpenP2PService.LOG_TAG, "open FileInputStream error: "); + return + } + Log.d(LOG_TAG, "read tun loop start") + val buffer = ByteBuffer.allocate(4096) + val byteArrayRead = ByteArray(4096) + while (sdwanRunning) { + buffer.clear() + val readBytes = inputStream.read(buffer) + if (readBytes <= 0) { +// Log.i(OpenP2PService.LOG_TAG, "inputStream.read error: ") + delay(1) + continue + } + buffer.flip() + buffer.get(byteArrayRead,0,readBytes) + Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidRead: %d", readBytes)) + Openp2p.androidRead(byteArrayRead, readBytes.toLong()) + + } + Log.d(LOG_TAG, "read tun loop end") + } + + private fun runSDWAN(buf:ByteArray) { + sdwanRunning=true + sdwanJob=GlobalScope.launch(context = Dispatchers.IO) { + Log.i(OpenP2PService.LOG_TAG, "runSDWAN start:${buf.decodeToString()}"); + try{ + var builder = Builder() + val jsonObject = JSONObject(buf.decodeToString()) + val id = jsonObject.getLong("id") + val name = jsonObject.getString("name") + val gateway = jsonObject.getString("gateway") + val nodesArray = jsonObject.getJSONArray("Nodes") + + val nodesList = mutableListOf() + for (i in 0 until nodesArray.length()) { + nodesList.add(nodesArray.getJSONObject(i)) + } + + val myNodeName = Openp2p.getAndroidNodeName() + Log.i(OpenP2PService.LOG_TAG, "getAndroidNodeName:${myNodeName}"); + val nodeList = nodesList.map { + val nodeName = it.getString("name") + val nodeIp = it.getString("ip") + if (nodeName==myNodeName){ + builder.addAddress(nodeIp, 24) + } + val nodeResource = if (it.has("resource")) it.getString("resource") else null + val parts = nodeResource?.split("/") + if (parts?.size == 2) { + val ipAddress = parts[0] + val subnetMask = parts[1] + builder.addRoute(ipAddress, subnetMask.toInt()) + Log.i(OpenP2PService.LOG_TAG, "sdwan addRoute:${ipAddress},${subnetMask.toInt()}"); + } + Node(nodeName, nodeIp, nodeResource) + } + + val network = Network(id, name, gateway, nodeList) + println(network) + Log.i(OpenP2PService.LOG_TAG, "onBind"); + builder.addDnsServer("8.8.8.8") + builder.addRoute("10.2.3.0", 24) +// builder.addRoute("0.0.0.0", 0); + builder.setSession(LOG_TAG!!) + builder.setMtu(1420) + vpnInterface = builder.establish() + if (vpnInterface==null){ + Log.e(OpenP2PService.LOG_TAG, "start vpnservice error: "); + } + val outputStream = FileOutputStream(vpnInterface?.fileDescriptor).channel + if (outputStream==null){ + Log.e(OpenP2PService.LOG_TAG, "open FileOutputStream error: "); + return@launch + } + + val byteArrayWrite = ByteArray(4096) + launch { + readTunLoop() + } + + Log.d(LOG_TAG, "write tun loop start") + while (sdwanRunning) { + val len = Openp2p.androidWrite(byteArrayWrite) + Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidWrite: %d",len)); + val writeBytes = outputStream?.write(ByteBuffer.wrap(byteArrayWrite)) + if (writeBytes != null && writeBytes <= 0) { + Log.i(OpenP2PService.LOG_TAG, "outputStream?.write error: "); + continue + } + } + outputStream.close() +// 关闭 VPN 接口 + vpnInterface?.close() +// 置空变量以释放资源 + vpnInterface = null + Log.d(LOG_TAG, "write tun loop end") + }catch (e: Exception) { + // 捕获异常并记录 + Log.e("VPN Connection", "发生异常: ${e.message}") + } + Log.i(OpenP2PService.LOG_TAG, "runSDWAN end"); + } + } override fun onDestroy() { @@ -93,12 +260,13 @@ class OpenP2PService : Service() { } fun isConnected(): Boolean { if (!::network.isInitialized) return false - return network?.connect(1000) + return network.connect(1000) } fun stop() { running=false stopSelf() + Openp2p.stop() } @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(channelId: String, channelName: String): String? { diff --git a/app/app/src/main/java/cn/openp2p/log.kt b/app/app/src/main/java/cn/openp2p/log.kt new file mode 100644 index 0000000..28a26fc --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/log.kt @@ -0,0 +1,45 @@ +package cn.openp2p +import android.content.* +import java.io.File +import java.io.FileWriter +import java.text.SimpleDateFormat +import java.util.* +import android.app.* +import android.content.Context +import android.content.Intent +import android.graphics.Color +import java.io.IOException +import android.net.VpnService +import android.os.Binder +import android.os.Build +import android.os.IBinder +import android.os.ParcelFileDescriptor +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import cn.openp2p.ui.login.LoginActivity +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import openp2p.Openp2p +import java.io.FileInputStream +import java.io.FileOutputStream +import java.nio.ByteBuffer +import kotlinx.coroutines.* +import org.json.JSONObject + +object Logger { + private val logFile: File = File("app.log") + + fun log(message: String) { + val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()) + val logMessage = "$timestamp: $message\n" + + try { + val fileWriter = FileWriter(logFile, true) + fileWriter.append(logMessage) + fileWriter.close() + } catch (e: Exception) { + e.printStackTrace() + } + } +} diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt index 93cd3f5..5db1f81 100644 --- a/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt @@ -5,6 +5,7 @@ import android.app.Activity import android.app.ActivityManager import android.app.Notification import android.app.PendingIntent +import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent @@ -25,6 +26,7 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import cn.openp2p.Logger import cn.openp2p.OpenP2PService import cn.openp2p.R import cn.openp2p.databinding.ActivityLoginBinding @@ -51,6 +53,14 @@ class LoginActivity : AppCompatActivity() { private lateinit var loginViewModel: LoginViewModel private lateinit var binding: ActivityLoginBinding private lateinit var mService: OpenP2PService + @RequiresApi(Build.VERSION_CODES.O) + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == 0 && resultCode == Activity.RESULT_OK) { + startService(Intent(this, OpenP2PService::class.java)) + } + } + @RequiresApi(Build.VERSION_CODES.O) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -78,22 +88,18 @@ class LoginActivity : AppCompatActivity() { token.error = getString(loginState.passwordError) } }) - val intent1 = VpnService.prepare(this) ?: return - loginViewModel.loginResult.observe(this@LoginActivity, Observer { - val loginResult = it ?: return@Observer - - loading.visibility = View.GONE - if (loginResult.error != null) { - showLoginFailed(loginResult.error) - } - if (loginResult.success != null) { - updateUiWithUser(loginResult.success) - } - setResult(Activity.RESULT_OK) + openp2pLog.setText(R.string.phone_setting) + val intent = VpnService.prepare(this) + if (intent != null) + { + Log.i("openp2p", "VpnService.prepare need permission"); + startActivityForResult(intent, 0) + } + else { + Log.i("openp2p", "VpnService.prepare ready"); + onActivityResult(0, Activity.RESULT_OK, null) + } - //Complete and destroy login activity once successful - finish() - }) profile.setOnClickListener { val url = "https://console.openp2p.cn/profile" @@ -110,17 +116,23 @@ class LoginActivity : AppCompatActivity() { } openp2pLog.setText(R.string.phone_setting) - token.setText(Openp2p.getToken(getExternalFilesDir(null).toString())) + login.setOnClickListener { if (login.text.toString()=="退出"){ -// val intent = Intent(this@LoginActivity, OpenP2PService::class.java) -// stopService(intent) + // val intent = Intent(this@LoginActivity, OpenP2PService::class.java) + // stopService(intent) Log.i(LOG_TAG, "quit") mService.stop() - unbindService(connection) + val intent = Intent(this@LoginActivity, OpenP2PService::class.java) stopService(intent) + // 解绑服务 + unbindService(connection) + + // 结束当前 Activity + finish() // 或者使用 finishAffinity() 来结束整个应用程序 exitAPP() + // finishAffinity() } login.setText("退出") @@ -139,13 +151,20 @@ class LoginActivity : AppCompatActivity() { if (isConnect) { onlineState.setText("在线") } else { - onlineState.setText("离线") + onlineState.setText("正在登录") } } } while (true) } } + val tokenText = Openp2p.getToken(getExternalFilesDir(null).toString()) + token.setText(tokenText.toString()) + // Check token length and automatically click login if length > 10 + if (tokenText.length > 10) { +// Logger.log("performClick ") + login.performClick() + } } } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) diff --git a/app/app/src/main/res/layout/activity_login.xml b/app/app/src/main/res/layout/activity_login.xml index a011c23..494dff9 100644 --- a/app/app/src/main/res/layout/activity_login.xml +++ b/app/app/src/main/res/layout/activity_login.xml @@ -13,28 +13,27 @@ + app:layout_constraintTop_toTopOf="parent" />