1. implemented peers list load from json, iteration #2

https://publicpeers.neilalexander.dev/publicnodes.json
This commit is contained in:
vadym 2020-06-21 06:54:16 -07:00
parent 4f53e50e3e
commit 28eb0903ef
11 changed files with 187 additions and 71 deletions

View File

@ -39,31 +39,21 @@ android {
}
task ndkBuild(type: Exec) {
def rootDir = project.rootDir
workingDir = new File(rootDir,"yggdrasil")
commandLine 'make'
}
gradle.projectsEvaluated {
tasks.compileDebugKotlin.dependsOn(ndkBuild)
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':yggdrasil')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "androidx.preference:preference-ktx:1.1.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.hbb20:ccp:2.4.0'
implementation "androidx.preference:preference-ktx:1.1.1"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

View File

@ -12,6 +12,11 @@ import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import com.google.gson.Gson
import io.github.chronosx88.yggdrasil.models.PeerInfo
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
class MainActivity : AppCompatActivity() {
@ -122,10 +127,11 @@ class MainActivity : AppCompatActivity() {
val i = baseContext.packageManager
.getLaunchIntentForPackage(baseContext.packageName)
i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
i!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
i.putExtra(START_VPN, true)
startActivity(i)
finish()
startActivity(i)
}
}
}
@ -161,7 +167,7 @@ class MainActivity : AppCompatActivity() {
} else {
switchOn.isChecked = false
}
switchOn.setOnCheckedChangeListener { buttonView, isChecked ->
switchOn.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
startVpn()
} else {
@ -171,4 +177,22 @@ class MainActivity : AppCompatActivity() {
return true
}
fun deserializeStringList2PeerInfoList(list: ArrayList<String>): ArrayList<PeerInfo> {
var gson = Gson()
var out = ArrayList<PeerInfo>()
for(s in list) {
out.add(gson.fromJson(s, PeerInfo::class.java))
}
return out
}
fun serializePeerInfoList2StringSet(list: ArrayList<PeerInfo>): Set<String> {
var gson = Gson()
var out = TreeSet<String>()
for(p in list) {
out.add(gson.toJson(p))
}
return out
}
}

View File

@ -12,94 +12,152 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.hbb20.CountryCodePicker
import io.github.chronosx88.yggdrasil.models.config.PeerInfo
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.hbb20.CCPCountry
import io.github.chronosx88.yggdrasil.models.PeerInfo
import io.github.chronosx88.yggdrasil.models.Status
import io.github.chronosx88.yggdrasil.models.config.PeerInfoListAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.lang.reflect.Type
import java.net.Inet4Address
import java.net.InetAddress
import java.net.URI
import java.net.URL
import java.nio.charset.Charset
class PeerListActivity : AppCompatActivity() {
companion object {
val peers = arrayListOf(
const val PEER_LIST_URL = "https://publicpeers.neilalexander.dev/publicnodes.json"
var allPeers = arrayListOf(
PeerInfo(
"tcp",
Inet4Address.getByName("194.177.21.156"),
5066,
"RU",
CountryCodePicker.Language.RUSSIAN
"RU"
),
PeerInfo(
"tcp",
Inet4Address.getByName("46.151.26.194"),
60575,
"RU",
CountryCodePicker.Language.RUSSIAN
"RU"
),
PeerInfo(
"tcp",
Inet4Address.getByName("188.226.125.64"),
54321,
"RU",
CountryCodePicker.Language.RUSSIAN
"RU"
),
PeerInfo(
"tcp",
Inet4Address.getByName("88.201.129.205"),
8777,
"RU",
CountryCodePicker.Language.RUSSIAN
"RU"
),
PeerInfo(
"tcp",
Inet4Address.getByName("45.11.19.26"),
5001,
"DE",
CountryCodePicker.Language.GERMAN
"DE"
),
PeerInfo(
"tcp",
Inet4Address.getByName("82.165.69.111"),
61216,
"DE",
CountryCodePicker.Language.GERMAN
"DE"
),
PeerInfo(
"tcp",
Inet4Address.getByName("104.248.15.125"),
31337,
"US",
CountryCodePicker.Language.ENGLISH
"US"
),
PeerInfo(
"tcp",
Inet4Address.getByName("108.175.10.127"),
61216,
"US",
CountryCodePicker.Language.ENGLISH
"US"
)
)
}
fun downloadJson(link: String): String {
URL(link).openStream().use { input ->
var outStream = ByteArrayOutputStream()
outStream.use { output ->
input.copyTo(output)
}
return String(outStream.toByteArray(), Charset.forName("UTF-8"))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_peer_list)
setSupportActionBar(findViewById(R.id.toolbar))
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
var extras = intent.getExtras()
var extras = intent.extras
var peerList = findViewById<ListView>(R.id.peerList)
if (extras != null) {
var currentPeers = extras.getStringArrayList(MainActivity.PEER_LIST)!!
var adapter = PeerInfoListAdapter(this, peers, currentPeers)
peerList.adapter = adapter
} else {
var adapter = PeerInfoListAdapter(this, peers, ArrayList())
peerList.adapter = adapter
var instance = this
GlobalScope.launch {
var json = downloadJson(PEER_LIST_URL)
var countries = CCPCountry.getLibraryMasterCountriesEnglish()
val mapType: Type = object :
TypeToken<Map<String?, Map<String, Status>>>() {}.type
val peersMap: Map<String, Map<String, Status>> = Gson().fromJson(json, mapType)
val allOnlinePeers = arrayListOf<PeerInfo>()
for ((country, peers) in peersMap.entries) {
println("$country:")
for ((peer, status) in peers) {
if(status.up){
for (ccp in countries){
if(ccp.name.toLowerCase().contains(country.replace(".md",""))){
var url = URI(peer)
var peerInfo = PeerInfo(url.scheme, InetAddress.getByName(url.host), url.port, ccp.nameCode)
allOnlinePeers.add(peerInfo)
}
}
}
}
}
if(allOnlinePeers.size>0){
allPeers = allOnlinePeers
}
if (extras != null) {
var cp = extras.getStringArrayList(MainActivity.PEER_LIST)!!
var currentPeers = ArrayList(cp)
for(peerInfo in allPeers){
if(currentPeers.contains(peerInfo.toString())){
currentPeers.remove(peerInfo.toString())
}
}
for(cp in currentPeers){
var url = URI(cp)
var peerInfo = PeerInfo(url.scheme, InetAddress.getByName(url.host), url.port, "RU")
allPeers.add(0, peerInfo)
}
var adapter = PeerInfoListAdapter(instance, allOnlinePeers, cp)
withContext(Dispatchers.Main) {
peerList.adapter = adapter
}
} else {
var adapter = PeerInfoListAdapter(instance, allOnlinePeers, ArrayList())
withContext(Dispatchers.Main) {
peerList.adapter = adapter
}
}
}
}

View File

@ -146,8 +146,8 @@ class YggdrasilTunService : VpnService() {
private fun stopVpn(pi: PendingIntent) {
isClosed = true;
readCoroutine!!.cancel()
writeCoroutine!!.cancel()
readCoroutine.cancel()
writeCoroutine.cancel()
tunInputStream!!.close()
tunOutputStream!!.close()
tunInterface!!.close()

View File

@ -1,4 +1,4 @@
package io.github.chronosx88.yggdrasil.models.config
package io.github.chronosx88.yggdrasil.models
import android.content.Context
import com.hbb20.CCPCountry
@ -7,26 +7,37 @@ import java.net.InetAddress
class PeerInfo {
constructor(schema: String, address: InetAddress, port: Int, countryCode: String, language: CountryCodePicker.Language){
constructor(schema: String, address: InetAddress, port: Int, countryCode: String){
this.schema = schema
this.address = address
this.port = port
this.countryCode = countryCode
this.language = language
}
var schema: String
var address: InetAddress
var port = 0
var countryCode: String
var language: CountryCodePicker.Language
var ping: Float = Float.MAX_VALUE
override fun toString(): String {
return this.schema+":/"+address.toString()+":"+port
var a = address.toString();
if(a.indexOf("/")>0){
return this.schema+"://"+a.split("/")[0]+":"+port
} else {
if(a.contains(":")) {
return this.schema + "://[" + a.substring(1) + "]:" + port
} else {
return this.schema + ":/" + a + ":" + port
}
}
}
override fun equals(other: Any?): Boolean {
return toString() == other.toString()
}
fun getCountry(context: Context): CCPCountry? {
return CCPCountry.getCountryForNameCodeFromLibraryMasterList(context, language, countryCode)
return CCPCountry.getCountryForNameCodeFromLibraryMasterList(context, CountryCodePicker.Language.ENGLISH, countryCode)
}
}

View File

@ -0,0 +1,5 @@
package io.github.chronosx88.yggdrasil.models
class Status {
var up: Boolean = false
}

View File

@ -9,6 +9,7 @@ import android.widget.CheckBox
import android.widget.ImageView
import android.widget.TextView
import io.github.chronosx88.yggdrasil.R
import io.github.chronosx88.yggdrasil.models.PeerInfo
import java.util.ArrayList
@ -23,19 +24,22 @@ class PeerInfoListAdapter(
private var currentPeers: ArrayList<String> = currentPeers
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var peerInfoHolder = PeerInfoHolder()
var listItem: View? = convertView
if (listItem == null) {
listItem = LayoutInflater.from(mContext).inflate(R.layout.peers_list_item_edit, parent, false)
peerInfoHolder.checkbox = listItem.findViewById(R.id.checkbox) as CheckBox
peerInfoHolder.countryFlag = listItem.findViewById(R.id.countryFlag) as ImageView
peerInfoHolder.peerInfoText = listItem.findViewById(R.id.peerInfoText) as TextView
listItem.tag = peerInfoHolder
} else {
peerInfoHolder = listItem.tag as PeerInfoHolder
}
val currentPeer = allPeers[position]
val image: ImageView = listItem?.findViewById(R.id.countryFlag) as ImageView
image.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
val name = listItem.findViewById(R.id.peerInfoText) as TextView
peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
val peerId = currentPeer.toString()
name.text = peerId
val checkbox = listItem.findViewById(R.id.checkbox) as CheckBox
checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
peerInfoHolder.peerInfoText.text = peerId
peerInfoHolder.checkbox.setOnCheckedChangeListener { _, isChecked ->
if(isChecked){
if(!currentPeers.contains(peerId)){
currentPeers.add(peerId)
@ -46,14 +50,21 @@ class PeerInfoListAdapter(
}
}
}
peerInfoHolder.checkbox.isChecked = this.currentPeers.contains(peerId)
if(this.currentPeers.contains(peerId)){
checkbox.isChecked = true
print(peerId)
}
return listItem
return listItem!!
}
public fun getSelectedPeers(): ArrayList<String> {
fun getSelectedPeers(): ArrayList<String> {
return currentPeers
}
class PeerInfoHolder {
lateinit var checkbox: CheckBox
lateinit var countryFlag: ImageView
lateinit var peerInfoText: TextView
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke
android:width="2dp"
android:color="@android:color/transparent" />
<solid android:color="@color/white" />
<padding
android:left="1dp"
android:right="1dp"
android:bottom="1dp"
android:top="1dp" />
<corners android:radius="3dp" />
</shape>

View File

@ -12,7 +12,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="@drawable/rounded_corner"
android:background="@drawable/info_panel_rounded_corner"
android:gravity="left"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
@ -49,7 +49,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="@drawable/rounded_corner"
android:background="@drawable/info_panel_rounded_corner"
android:gravity="left"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/ipLayout">

View File

@ -9,7 +9,7 @@
android:layout_height="wrap_content"
android:scaleX="0.4"
android:scaleY="0.4"
android:background="@color/white"
android:background="@drawable/checkbox_rounded_corner"
android:layout_marginStart="5dp">
<CheckBox
android:id="@+id/checkbox"
@ -19,7 +19,8 @@
android:scaleX="3"
android:scaleY="3"
android:layout_margin="10dp"
android:background="@android:color/transparent"/>
android:background="@android:color/transparent"
android:checked="false"/>
</FrameLayout>
<ImageView
android:id="@+id/countryFlag"