mirror of
https://github.com/openp2p-cn/openp2p.git
synced 2026-04-25 19:11:49 +08:00
3.18.4 virtual private network
This commit is contained in:
10
app/.idea/runConfigurations.xml
generated
10
app/.idea/runConfigurations.xml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -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"
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cn.openp2p">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -27,13 +26,21 @@
|
||||
|
||||
<activity
|
||||
android:name=".ui.login.LoginActivity"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<receiver android:name="BootReceiver"
|
||||
android:exported="true"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
27
app/app/src/main/java/cn/openp2p/BootReceiver.kt
Normal file
27
app/app/src/main/java/cn/openp2p/BootReceiver.kt
Normal file
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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<Node>
|
||||
)
|
||||
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<JSONObject>()
|
||||
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? {
|
||||
|
||||
45
app/app/src/main/java/cn/openp2p/log.kt
Normal file
45
app/app/src/main/java/cn/openp2p/log.kt
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
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)
|
||||
}
|
||||
|
||||
loading.visibility = View.GONE
|
||||
if (loginResult.error != null) {
|
||||
showLoginFailed(loginResult.error)
|
||||
}
|
||||
if (loginResult.success != null) {
|
||||
updateUiWithUser(loginResult.success)
|
||||
}
|
||||
setResult(Activity.RESULT_OK)
|
||||
|
||||
//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)
|
||||
|
||||
@@ -13,28 +13,27 @@
|
||||
|
||||
<EditText
|
||||
android:id="@+id/token"
|
||||
android:layout_width="225dp"
|
||||
android:layout_height="46dp"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="45dp"
|
||||
android:hint="Token"
|
||||
android:imeActionLabel="@string/action_sign_in_short"
|
||||
android:imeOptions="actionDone"
|
||||
android:selectAllOnFocus="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="85dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:enabled="false"
|
||||
android:text="@string/action_sign_in"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/token"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:layout_editor_absoluteY="-2dp" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
|
||||
@@ -58,28 +57,36 @@
|
||||
android:id="@+id/openp2pLog"
|
||||
android:layout_width="359dp"
|
||||
android:layout_height="548dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:ems="10"
|
||||
android:inputType="none"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="false"
|
||||
android:inputType="none"
|
||||
android:text="Name"
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/onlineState" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/profile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="打开控制台查看Token"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/token" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/onlineState"
|
||||
android:layout_width="113dp"
|
||||
android:layout_width="85dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName"
|
||||
android:text="未登录"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/profile"
|
||||
app:layout_constraintTop_toBottomOf="@+id/login" />
|
||||
|
||||
|
||||
@@ -10,7 +10,14 @@
|
||||
<string name="invalid_password">Token可以在 https://console.openp2p.cn/profile 获得</string>
|
||||
<string name="login_failed">"Login failed"</string>
|
||||
<string name="phone_setting">"安卓系统默认设置的”杀后台进程“会导致 OpenP2P 在后台运行一会后,被系统杀死进程,导致您的体验受到影响。您可以通过以下方式修改几个设置,解决此问题:
|
||||
|
||||
华为鸿蒙:
|
||||
1. 允许应用后台运行:进入设置 → 搜索进入 应用启动管理 → 关闭 OpenP2P 的 自动管理 开关 → 在弹框中勾选 允许后台活动
|
||||
2. 避免应用被电池优化程序清理:进入设置 → 搜索进入电池优化 → 不允许 →选择所有应用 → 找到无法后台运行的应用 → 设置为不允许
|
||||
3. 关闭省电模式:进入设置 → 电池 → 关闭 省电模式 开关
|
||||
4. 保持设备网络连接:进入设置 → 电池 → 更多电池设置 → 开启 休眠时始终保持网络连接 开关。
|
||||
5. 给后台运行的应用加锁:打开应用后 → 进入多任务界面 → 下拉选中的卡片进行加锁 → 然后点击清理图标清理其他不经常使用的应用
|
||||
6. 设置开发人员选项中相关开关:进入设置 → 搜索进入 开发人员选项 → 找到 不保留活动 开关后关闭 → 并在 后台进程限制 选择 标准限制
|
||||
|
||||
华为手机:
|
||||
进入”设置“,搜索并进入“电池优化“界面,选中 OpenP2P 程序,不允许系统对其进行电池优化;
|
||||
进入”设置“,进入”应用管理“界面,选中 OpenP2P 程序,点击”耗电情况“,开启”允许后台活动“即可;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.5.21"
|
||||
|
||||
ext.kotlin_version = "1.8.20"
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:4.2.1"
|
||||
classpath "com.android.tools.build:gradle:8.1.3"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
6
app/gradle/wrapper/gradle-wrapper.properties
vendored
6
app/gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Oct 22 21:46:24 CST 2022
|
||||
#Tue Dec 05 16:04:08 CST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
Reference in New Issue
Block a user