From af82fc6e36e796af1441ff1ccf8bed30aaca14c3 Mon Sep 17 00:00:00 2001 From: hhd <271357901@qq.com> Date: Thu, 3 Nov 2022 11:07:32 +0800 Subject: [PATCH] support andoird --- .gitignore | 14 +- Makefile | 7 + README-ZH.md | 5 +- README.md | 6 +- app/.gitignore | 15 ++ app/.idea/.gitignore | 3 + app/.idea/.name | 1 + app/.idea/compiler.xml | 6 + app/.idea/gradle.xml | 19 ++ app/.idea/jarRepositories.xml | 30 +++ app/.idea/misc.xml | 9 + app/.idea/runConfigurations.xml | 10 + app/.idea/vcs.xml | 6 + app/app/.gitignore | 1 + app/app/proguard-rules.pro | 21 ++ .../cn/openp2p/ExampleInstrumentedTest.kt | 24 +++ app/app/src/main/AndroidManifest.xml | 39 ++++ .../main/java/cn/openp2p/OpenP2PService.kt | 59 ++++++ .../java/cn/openp2p/data/LoginDataSource.kt | 24 +++ .../java/cn/openp2p/data/LoginRepository.kt | 46 +++++ .../src/main/java/cn/openp2p/data/Result.kt | 18 ++ .../cn/openp2p/data/model/LoggedInUser.kt | 9 + .../cn/openp2p/ui/login/LoggedInUserView.kt | 9 + .../java/cn/openp2p/ui/login/LoginActivity.kt | 188 +++++++++++++++++ .../cn/openp2p/ui/login/LoginFormState.kt | 10 + .../java/cn/openp2p/ui/login/LoginResult.kt | 9 + .../cn/openp2p/ui/login/LoginViewModel.kt | 57 ++++++ .../openp2p/ui/login/LoginViewModelFactory.kt | 25 +++ .../drawable-v24/ic_launcher_foreground.xml | 30 +++ .../res/drawable/ic_launcher_background.xml | 170 +++++++++++++++ .../src/main/res/layout/activity_login.xml | 87 ++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3593 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5339 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2636 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4926 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7472 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7909 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11873 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10652 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16570 bytes app/app/src/main/res/values-night/themes.xml | 16 ++ app/app/src/main/res/values/colors.xml | 10 + app/app/src/main/res/values/dimens.xml | 5 + app/app/src/main/res/values/strings.xml | 36 ++++ app/app/src/main/res/values/themes.xml | 16 ++ app/gradle.properties | 19 ++ app/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes app/gradle/wrapper/gradle-wrapper.properties | 6 + app/gradlew | 172 ++++++++++++++++ app/gradlew.bat | 84 ++++++++ app/openp2p.jks | Bin 0 -> 2435 bytes app/settings.gradle | 2 + cmd/openp2p.go | 7 + bandwidthLimit.go => core/bandwidthLimit.go | 2 +- common.go => core/common.go | 6 +- common_test.go => core/common_test.go | 2 +- config.go => core/config.go | 2 +- daemon.go => core/daemon.go | 2 +- errorcode.go => core/errorcode.go | 2 +- handlepush.go => core/handlepush.go | 2 +- holepunch.go => core/holepunch.go | 2 +- install.go => core/install.go | 2 +- log.go => core/log.go | 2 +- core/nat.go | 187 +++++++++++++++++ core/openp2p.go | 97 +++++++++ overlay.go => core/overlay.go | 2 +- p2papp.go => core/p2papp.go | 2 +- p2pappkeys.go => core/p2pappkeys.go | 2 +- p2pnetwork.go => core/p2pnetwork.go | 30 +-- p2ptunnel.go => core/p2ptunnel.go | 8 +- protocol.go => core/protocol.go | 4 +- totp.go => core/totp.go | 2 +- totp_test.go => core/totp_test.go | 2 +- udp.go => core/udp.go | 2 +- underlay.go => core/underlay.go | 2 +- underlay_quic.go => core/underlay_quic.go | 2 +- underlay_tcp.go => core/underlay_tcp.go | 2 +- underlay_tcp6.go => core/underlay_tcp6.go | 2 +- update.go => core/update.go | 2 +- upnp.go => core/upnp.go | 2 +- util_darwin.go => core/util_darwin.go | 2 +- util_linux.go => core/util_linux.go | 6 +- util_windows.go => core/util_windows.go | 2 +- go.mod | 11 +- nat.go | 193 ------------------ openp2p.go | 63 ------ p2pconn.go | 17 -- 90 files changed, 1674 insertions(+), 334 deletions(-) create mode 100644 Makefile create mode 100644 app/.gitignore create mode 100644 app/.idea/.gitignore create mode 100644 app/.idea/.name create mode 100644 app/.idea/compiler.xml create mode 100644 app/.idea/gradle.xml create mode 100644 app/.idea/jarRepositories.xml create mode 100644 app/.idea/misc.xml create mode 100644 app/.idea/runConfigurations.xml create mode 100644 app/.idea/vcs.xml create mode 100644 app/app/.gitignore create mode 100644 app/app/proguard-rules.pro create mode 100644 app/app/src/androidTest/java/cn/openp2p/ExampleInstrumentedTest.kt create mode 100644 app/app/src/main/AndroidManifest.xml create mode 100644 app/app/src/main/java/cn/openp2p/OpenP2PService.kt create mode 100644 app/app/src/main/java/cn/openp2p/data/LoginDataSource.kt create mode 100644 app/app/src/main/java/cn/openp2p/data/LoginRepository.kt create mode 100644 app/app/src/main/java/cn/openp2p/data/Result.kt create mode 100644 app/app/src/main/java/cn/openp2p/data/model/LoggedInUser.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoggedInUserView.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoginFormState.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoginResult.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoginViewModel.kt create mode 100644 app/app/src/main/java/cn/openp2p/ui/login/LoginViewModelFactory.kt create mode 100644 app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/app/src/main/res/layout/activity_login.xml create mode 100644 app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/app/src/main/res/values-night/themes.xml create mode 100644 app/app/src/main/res/values/colors.xml create mode 100644 app/app/src/main/res/values/dimens.xml create mode 100644 app/app/src/main/res/values/strings.xml create mode 100644 app/app/src/main/res/values/themes.xml create mode 100644 app/gradle.properties create mode 100644 app/gradle/wrapper/gradle-wrapper.jar create mode 100644 app/gradle/wrapper/gradle-wrapper.properties create mode 100644 app/gradlew create mode 100644 app/gradlew.bat create mode 100644 app/openp2p.jks create mode 100644 app/settings.gradle create mode 100644 cmd/openp2p.go rename bandwidthLimit.go => core/bandwidthLimit.go (98%) rename common.go => core/common.go (97%) rename common_test.go => core/common_test.go (99%) rename config.go => core/config.go (99%) rename daemon.go => core/daemon.go (94%) rename errorcode.go => core/errorcode.go (97%) rename handlepush.go => core/handlepush.go (96%) rename holepunch.go => core/holepunch.go (99%) rename install.go => core/install.go (95%) rename log.go => core/log.go (99%) create mode 100644 core/nat.go create mode 100644 core/openp2p.go rename overlay.go => core/overlay.go (99%) rename p2papp.go => core/p2papp.go (99%) rename p2pappkeys.go => core/p2pappkeys.go (93%) rename p2pnetwork.go => core/p2pnetwork.go (92%) rename p2ptunnel.go => core/p2ptunnel.go (95%) rename protocol.go => core/protocol.go (99%) rename totp.go => core/totp.go (97%) rename totp_test.go => core/totp_test.go (97%) rename udp.go => core/udp.go (98%) rename underlay.go => core/underlay.go (95%) rename underlay_quic.go => core/underlay_quic.go (99%) rename underlay_tcp.go => core/underlay_tcp.go (99%) rename underlay_tcp6.go => core/underlay_tcp6.go (99%) rename update.go => core/update.go (95%) rename upnp.go => core/upnp.go (99%) rename util_darwin.go => core/util_darwin.go (97%) rename util_linux.go => core/util_linux.go (94%) rename util_windows.go => core/util_windows.go (98%) delete mode 100644 nat.go delete mode 100644 openp2p.go delete mode 100644 p2pconn.go diff --git a/.gitignore b/.gitignore index d59a9f1..6eeee82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,18 @@ __debug_bin __debug_bin.exe # .vscode -openp2p +test/ openp2p.exe* -*.log +*.log* go.sum *.tar.gz *.zip -*.exe \ No newline at end of file +*.exe +config.json +libs/ +*/app/.idea/ +*/app/release/ +openp2p.app.jks +openp2p.aar +openp2p-sources.jar +build.gradle diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3eea790 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +build: + export GOPROXY=https://goproxy.io,direct + go mod tidy + go build cmd/openp2p.go +.PHONY: build + +.DEFAULT_GOAL := build diff --git a/README-ZH.md b/README-ZH.md index a75774d..86bf2d6 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -31,6 +31,7 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络 ## 快速入门 仅需简单4步就能用起来。 下面是一个远程办公例子:在家里连入办公室Windows电脑。 +(另外一个快速入门视频 https://www.bilibili.com/video/BV1Et4y1P7bF/) ### 1.注册 前往 注册新用户,暂无需任何认证 @@ -98,9 +99,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“ go version go1.18.1+ cd到代码根目录,执行 ``` -export GOPROXY=https://goproxy.io,direct -go mod tidy -go build +make ``` ## RoadMap diff --git a/README.md b/README.md index 4b675bd..4668c58 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Your applicaiton can call OpenP2P with a few code to make any internal networks ## Get Started Just 4 simple steps to use. Here's an example of remote work: connecting to an office Windows computer at home. - +(Another quick started vedio https://www.bilibili.com/video/BV1Et4y1P7bF/) ### 1.Register Go to register a new user @@ -106,9 +106,7 @@ The server side has a scheduling model, which calculate bandwith, ping value,st go version go1.18.1+ cd root directory of the socure code and execute ``` -export GOPROXY=https://goproxy.io,direct -go mod tidy -go build +make ``` ## RoadMap diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..643d213 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/app/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/app/.idea/.name b/app/.idea/.name new file mode 100644 index 0000000..a922f9c --- /dev/null +++ b/app/.idea/.name @@ -0,0 +1 @@ +OpenP2P \ No newline at end of file diff --git a/app/.idea/compiler.xml b/app/.idea/compiler.xml new file mode 100644 index 0000000..7d7ec2e --- /dev/null +++ b/app/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml new file mode 100644 index 0000000..b98e941 --- /dev/null +++ b/app/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/.idea/jarRepositories.xml b/app/.idea/jarRepositories.xml new file mode 100644 index 0000000..94ae73a --- /dev/null +++ b/app/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml new file mode 100644 index 0000000..59135fb --- /dev/null +++ b/app/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/.idea/runConfigurations.xml b/app/.idea/runConfigurations.xml new file mode 100644 index 0000000..93e4b17 --- /dev/null +++ b/app/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml new file mode 100644 index 0000000..2e3f692 --- /dev/null +++ b/app/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/app/.gitignore b/app/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/app/proguard-rules.pro b/app/app/proguard-rules.pro new file mode 100644 index 0000000..64b4a05 --- /dev/null +++ b/app/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/app/src/androidTest/java/cn/openp2p/ExampleInstrumentedTest.kt b/app/app/src/androidTest/java/cn/openp2p/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..7148a77 --- /dev/null +++ b/app/app/src/androidTest/java/cn/openp2p/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package cn.openp2p + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("cn.openp2p", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b3487a3 --- /dev/null +++ b/app/app/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 0000000..d3f8410 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/OpenP2PService.kt @@ -0,0 +1,59 @@ +package cn.openp2p + +import android.app.Service +import android.content.Intent +import android.net.VpnService +import android.os.Binder +import android.os.IBinder +import openp2p.Openp2p +import android.util.Log +import cn.openp2p.ui.login.LoginActivity +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class OpenP2PService : VpnService() { + companion object { + private val LOG_TAG = OpenP2PService::class.simpleName + } + + inner class LocalBinder : Binder() { + fun getService(): OpenP2PService = this@OpenP2PService + } + + private val binder = LocalBinder() + private lateinit var network: openp2p.P2PNetwork + override fun onCreate() { + Log.i("xiao","onCreate - Thread ID = " + Thread.currentThread().id) + super.onCreate() + + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.i("xiao", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id) + return super.onStartCommand(intent, flags, startId) + } + + override fun onBind(p0: Intent?): IBinder? { + return binder + } + + override fun onDestroy() { + Log.i("xiao", "onDestroy - Thread ID = " + Thread.currentThread().id) + super.onDestroy() + } + fun onStart(token:String){ + GlobalScope.launch { + do { + network =Openp2p.runAsModule(getExternalFilesDir(null).toString(), token, 0) + val isConnect = network.connect(30000) // ms + Log.i(OpenP2PService.LOG_TAG, "login result: " + isConnect.toString()); + } while(!network?.connect(10000)) + + } + } + fun isConnected(): Boolean{ + if (!::network.isInitialized) return false + return network?.connect(1000) + } + +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/data/LoginDataSource.kt b/app/app/src/main/java/cn/openp2p/data/LoginDataSource.kt new file mode 100644 index 0000000..9a921eb --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/data/LoginDataSource.kt @@ -0,0 +1,24 @@ +package cn.openp2p.data + +import cn.openp2p.data.model.LoggedInUser +import java.io.IOException + +/** + * Class that handles authentication w/ login credentials and retrieves user information. + */ +class LoginDataSource { + + fun login(username: String, password: String): Result { + try { + // TODO: handle loggedInUser authentication + val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe") + return Result.Success(fakeUser) + } catch (e: Throwable) { + return Result.Error(IOException("Error logging in", e)) + } + } + + fun logout() { + // TODO: revoke authentication + } +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/data/LoginRepository.kt b/app/app/src/main/java/cn/openp2p/data/LoginRepository.kt new file mode 100644 index 0000000..fdc7a32 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/data/LoginRepository.kt @@ -0,0 +1,46 @@ +package cn.openp2p.data + +import cn.openp2p.data.model.LoggedInUser + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ + +class LoginRepository(val dataSource: LoginDataSource) { + + // in-memory cache of the loggedInUser object + var user: LoggedInUser? = null + private set + + val isLoggedIn: Boolean + get() = user != null + + init { + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + user = null + } + + fun logout() { + user = null + dataSource.logout() + } + + fun login(username: String, password: String): Result { + // handle login + val result = dataSource.login(username, password) + + if (result is Result.Success) { + setLoggedInUser(result.data) + } + + return result + } + + private fun setLoggedInUser(loggedInUser: LoggedInUser) { + this.user = loggedInUser + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + } +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/data/Result.kt b/app/app/src/main/java/cn/openp2p/data/Result.kt new file mode 100644 index 0000000..d2e6695 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/data/Result.kt @@ -0,0 +1,18 @@ +package cn.openp2p.data + +/** + * A generic class that holds a value with its loading status. + * @param + */ +sealed class Result { + + data class Success(val data: T) : Result() + data class Error(val exception: Exception) : Result() + + override fun toString(): String { + return when (this) { + is Success<*> -> "Success[data=$data]" + is Error -> "Error[exception=$exception]" + } + } +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/data/model/LoggedInUser.kt b/app/app/src/main/java/cn/openp2p/data/model/LoggedInUser.kt new file mode 100644 index 0000000..b3e966a --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/data/model/LoggedInUser.kt @@ -0,0 +1,9 @@ +package cn.openp2p.data.model + +/** + * Data class that captures user information for logged in users retrieved from LoginRepository + */ +data class LoggedInUser( + val userId: String, + val displayName: String +) \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoggedInUserView.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoggedInUserView.kt new file mode 100644 index 0000000..bf6e152 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoggedInUserView.kt @@ -0,0 +1,9 @@ +package cn.openp2p.ui.login + +/** + * User details post authentication that is exposed to the UI + */ +data class LoggedInUserView( + val displayName: String + //... other data fields that may be accessible to the UI +) \ No newline at end of file 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 new file mode 100644 index 0000000..07d0f41 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt @@ -0,0 +1,188 @@ +package cn.openp2p.ui.login + +import android.app.Activity +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.net.Uri +import android.net.VpnService +import android.os.Bundle +import android.os.IBinder +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.EditText +import android.widget.Toast +import androidx.annotation.StringRes +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import cn.openp2p.OpenP2PService +import cn.openp2p.R +import cn.openp2p.databinding.ActivityLoginBinding +import openp2p.Openp2p +import kotlin.concurrent.thread + + +class LoginActivity : AppCompatActivity() { + companion object { + private val LOG_TAG = LoginActivity::class.simpleName + } + private val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + val binder = service as OpenP2PService.LocalBinder + mService = binder.getService() + mService.onStart(mToken) + } + + override fun onServiceDisconnected(className: ComponentName) { + + } + } + private lateinit var loginViewModel: LoginViewModel + private lateinit var binding: ActivityLoginBinding + private lateinit var mService: OpenP2PService + private var mToken: String="" + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityLoginBinding.inflate(layoutInflater) + setContentView(binding.root) + + val token = binding.token + val login = binding.login + val onlineState=binding.onlineState + val openp2pLog = binding.openp2pLog + val profile = binding.profile + val loading = binding.loading + + loginViewModel = ViewModelProvider(this, LoginViewModelFactory()) + .get(LoginViewModel::class.java) + + loginViewModel.loginFormState.observe(this@LoginActivity, Observer { + val loginState = it ?: return@Observer + + // disable login button unless both username / password is valid + login.isEnabled = loginState.isDataValid + + if (loginState.passwordError != null) { + token.error = getString(loginState.passwordError) + } + }) + mToken=token.text.toString() + 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) + + //Complete and destroy login activity once successful + finish() + }) + + profile.setOnClickListener { + val url = "https://console.openp2p.cn/profile" + val i = Intent(Intent.ACTION_VIEW) + i.data = Uri.parse(url) + startActivity(i) + } + token.apply { + afterTextChanged { + loginViewModel.loginDataChanged( + "username.text.toString()", + token.text.toString() + ) + } + +// setOnEditorActionListener { _, actionId, _ -> +// when (actionId) { +// EditorInfo.IME_ACTION_DONE -> +// loginViewModel.login( +// "username.text.toString()", +// token.text.toString() +// ) +// } +// false +// } + +// openp2pLog.setText(getExternalFilesDir(null).toString()) + openp2pLog.setText(R.string.phone_setting) + token.setText(Openp2p.getToken(getExternalFilesDir(null).toString())) + login.setOnClickListener { +// loading.visibility = View.VISIBLE +// loginViewModel.login(username.text.toString(), password.text.toString()) + +// startService(Intent(this, OpenP2PService::class.java)) + val intent = Intent(this@LoginActivity,OpenP2PService::class.java) + + bindService(intent, connection, Context.BIND_AUTO_CREATE) + thread { + do { + Thread.sleep(3000) + if (!::mService.isInitialized) continue + val isConnect = mService.isConnected() + runOnUiThread { + if (isConnect) { + onlineState.setText("在线") + } else { + onlineState.setText("离线") + } + } + } while(true) + } + + } + } + } +// fun listenProgress() { +// Thread { +// while (progress < MsgService.MAX_PROGRESS) { +// progress = msgService.getProgress() +// mProgressBar.setProgress(progress) +// try { +// Thread.sleep(1000) +// } catch (e: InterruptedException) { +// e.printStackTrace() +// } +// } +// }.start() +// } + + private fun updateUiWithUser(model: LoggedInUserView) { + val welcome = getString(R.string.welcome) + val displayName = model.displayName + // TODO : initiate successful logged in experience + Toast.makeText( + applicationContext, + "$welcome $displayName", + Toast.LENGTH_LONG + ).show() + } + + private fun showLoginFailed(@StringRes errorString: Int) { + Toast.makeText(applicationContext, errorString, Toast.LENGTH_SHORT).show() + } +} + +/** + * Extension function to simplify setting an afterTextChanged action to EditText components. + */ +fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) { + this.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(editable: Editable?) { + afterTextChanged.invoke(editable.toString()) + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + }) +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoginFormState.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoginFormState.kt new file mode 100644 index 0000000..1974176 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginFormState.kt @@ -0,0 +1,10 @@ +package cn.openp2p.ui.login + +/** + * Data validation state of the login form. + */ +data class LoginFormState( + val usernameError: Int? = null, + val passwordError: Int? = null, + val isDataValid: Boolean = false +) \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoginResult.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoginResult.kt new file mode 100644 index 0000000..4aeb4c9 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginResult.kt @@ -0,0 +1,9 @@ +package cn.openp2p.ui.login + +/** + * Authentication result : success (user details) or error message. + */ +data class LoginResult( + val success: LoggedInUserView? = null, + val error: Int? = null +) \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModel.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModel.kt new file mode 100644 index 0000000..f6c8d06 --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModel.kt @@ -0,0 +1,57 @@ +package cn.openp2p.ui.login + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import android.util.Patterns +import cn.openp2p.data.LoginRepository +import cn.openp2p.data.Result + +import cn.openp2p.R + +class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() { + + private val _loginForm = MutableLiveData() + val loginFormState: LiveData = _loginForm + + private val _loginResult = MutableLiveData() + val loginResult: LiveData = _loginResult + + fun login(username: String, password: String) { + // can be launched in a separate asynchronous job + val result = loginRepository.login(username, password) + + if (result is Result.Success) { + _loginResult.value = + LoginResult(success = LoggedInUserView(displayName = result.data.displayName)) + } else { + _loginResult.value = LoginResult(error = R.string.login_failed) + } + } + + fun loginDataChanged(username: String, password: String) { +// if (!isUserNameValid(username)) { +// _loginForm.value = LoginFormState(usernameError = R.string.invalid_username) +// } else + if (!isPasswordValid(password)) { + _loginForm.value = LoginFormState(passwordError = R.string.invalid_password) + } else { + _loginForm.value = LoginFormState(isDataValid = true) + } + } + + // A placeholder username validation check + private fun isUserNameValid(username: String): Boolean { + return true + return if (username.contains('@')) { + Patterns.EMAIL_ADDRESS.matcher(username).matches() + } else { + username.isNotBlank() + } + } + + // A placeholder password validation check + private fun isPasswordValid(password: String): Boolean { + return password.length > 5 + } +} \ No newline at end of file diff --git a/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModelFactory.kt b/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModelFactory.kt new file mode 100644 index 0000000..f895e7f --- /dev/null +++ b/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModelFactory.kt @@ -0,0 +1,25 @@ +package cn.openp2p.ui.login + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import cn.openp2p.data.LoginDataSource +import cn.openp2p.data.LoginRepository + +/** + * ViewModel provider factory to instantiate LoginViewModel. + * Required given LoginViewModel has a non-empty constructor + */ +class LoginViewModelFactory : ViewModelProvider.Factory { + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(LoginViewModel::class.java)) { + return LoginViewModel( + loginRepository = LoginRepository( + dataSource = LoginDataSource() + ) + ) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..cc14f03 --- /dev/null +++ b/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/app/src/main/res/drawable/ic_launcher_background.xml b/app/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..a4f78de --- /dev/null +++ b/app/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/app/src/main/res/layout/activity_login.xml b/app/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..a011c23 --- /dev/null +++ b/app/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,87 @@ + + + + + +