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 { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':yggdrasil') implementation project(path: ':yggdrasil')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2' implementation "androidx.preference:preference-ktx:1.1.1"
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
implementation 'com.google.android.material:material:1.3.0-alpha01' implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.hbb20:ccp:2.4.0' 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 android.widget.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager 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() { class MainActivity : AppCompatActivity() {
@ -122,10 +127,11 @@ class MainActivity : AppCompatActivity() {
val i = baseContext.packageManager val i = baseContext.packageManager
.getLaunchIntentForPackage(baseContext.packageName) .getLaunchIntentForPackage(baseContext.packageName)
i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 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) i.putExtra(START_VPN, true)
startActivity(i)
finish() finish()
startActivity(i)
} }
} }
} }
@ -161,7 +167,7 @@ class MainActivity : AppCompatActivity() {
} else { } else {
switchOn.isChecked = false switchOn.isChecked = false
} }
switchOn.setOnCheckedChangeListener { buttonView, isChecked -> switchOn.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) { if (isChecked) {
startVpn() startVpn()
} else { } else {
@ -171,4 +177,22 @@ class MainActivity : AppCompatActivity() {
return true 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 androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.hbb20.CountryCodePicker import com.google.gson.Gson
import io.github.chronosx88.yggdrasil.models.config.PeerInfo 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 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.Inet4Address
import java.net.InetAddress
import java.net.URI
import java.net.URL
import java.nio.charset.Charset
class PeerListActivity : AppCompatActivity() { class PeerListActivity : AppCompatActivity() {
companion object { companion object {
val peers = arrayListOf( const val PEER_LIST_URL = "https://publicpeers.neilalexander.dev/publicnodes.json"
var allPeers = arrayListOf(
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("194.177.21.156"), Inet4Address.getByName("194.177.21.156"),
5066, 5066,
"RU", "RU"
CountryCodePicker.Language.RUSSIAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("46.151.26.194"), Inet4Address.getByName("46.151.26.194"),
60575, 60575,
"RU", "RU"
CountryCodePicker.Language.RUSSIAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("188.226.125.64"), Inet4Address.getByName("188.226.125.64"),
54321, 54321,
"RU", "RU"
CountryCodePicker.Language.RUSSIAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("88.201.129.205"), Inet4Address.getByName("88.201.129.205"),
8777, 8777,
"RU", "RU"
CountryCodePicker.Language.RUSSIAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("45.11.19.26"), Inet4Address.getByName("45.11.19.26"),
5001, 5001,
"DE", "DE"
CountryCodePicker.Language.GERMAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("82.165.69.111"), Inet4Address.getByName("82.165.69.111"),
61216, 61216,
"DE", "DE"
CountryCodePicker.Language.GERMAN
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("104.248.15.125"), Inet4Address.getByName("104.248.15.125"),
31337, 31337,
"US", "US"
CountryCodePicker.Language.ENGLISH
), ),
PeerInfo( PeerInfo(
"tcp", "tcp",
Inet4Address.getByName("108.175.10.127"), Inet4Address.getByName("108.175.10.127"),
61216, 61216,
"US", "US"
CountryCodePicker.Language.ENGLISH
) )
) )
} }
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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_peer_list) setContentView(R.layout.activity_peer_list)
setSupportActionBar(findViewById(R.id.toolbar)) setSupportActionBar(findViewById(R.id.toolbar))
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view -> findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show() .setAction("Action", null).show()
} }
var extras = intent.getExtras() var extras = intent.extras
var peerList = findViewById<ListView>(R.id.peerList) var peerList = findViewById<ListView>(R.id.peerList)
if (extras != null) { var instance = this
var currentPeers = extras.getStringArrayList(MainActivity.PEER_LIST)!! GlobalScope.launch {
var adapter = PeerInfoListAdapter(this, peers, currentPeers) var json = downloadJson(PEER_LIST_URL)
peerList.adapter = adapter var countries = CCPCountry.getLibraryMasterCountriesEnglish()
} else { val mapType: Type = object :
var adapter = PeerInfoListAdapter(this, peers, ArrayList()) TypeToken<Map<String?, Map<String, Status>>>() {}.type
peerList.adapter = adapter 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) { private fun stopVpn(pi: PendingIntent) {
isClosed = true; isClosed = true;
readCoroutine!!.cancel() readCoroutine.cancel()
writeCoroutine!!.cancel() writeCoroutine.cancel()
tunInputStream!!.close() tunInputStream!!.close()
tunOutputStream!!.close() tunOutputStream!!.close()
tunInterface!!.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 android.content.Context
import com.hbb20.CCPCountry import com.hbb20.CCPCountry
@ -7,26 +7,37 @@ import java.net.InetAddress
class PeerInfo { 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.schema = schema
this.address = address this.address = address
this.port = port this.port = port
this.countryCode = countryCode this.countryCode = countryCode
this.language = language
} }
var schema: String var schema: String
var address: InetAddress var address: InetAddress
var port = 0 var port = 0
var countryCode: String var countryCode: String
var language: CountryCodePicker.Language
var ping: Float = Float.MAX_VALUE var ping: Float = Float.MAX_VALUE
override fun toString(): String { 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? { 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.ImageView
import android.widget.TextView import android.widget.TextView
import io.github.chronosx88.yggdrasil.R import io.github.chronosx88.yggdrasil.R
import io.github.chronosx88.yggdrasil.models.PeerInfo
import java.util.ArrayList import java.util.ArrayList
@ -23,19 +24,22 @@ class PeerInfoListAdapter(
private var currentPeers: ArrayList<String> = currentPeers private var currentPeers: ArrayList<String> = currentPeers
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var peerInfoHolder = PeerInfoHolder()
var listItem: View? = convertView var listItem: View? = convertView
if (listItem == null) { if (listItem == null) {
listItem = LayoutInflater.from(mContext).inflate(R.layout.peers_list_item_edit, parent, false) 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 currentPeer = allPeers[position]
val image: ImageView = listItem?.findViewById(R.id.countryFlag) as ImageView peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
image.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
val name = listItem.findViewById(R.id.peerInfoText) as TextView
val peerId = currentPeer.toString() val peerId = currentPeer.toString()
name.text = peerId peerInfoHolder.peerInfoText.text = peerId
val checkbox = listItem.findViewById(R.id.checkbox) as CheckBox peerInfoHolder.checkbox.setOnCheckedChangeListener { _, isChecked ->
checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
if(isChecked){ if(isChecked){
if(!currentPeers.contains(peerId)){ if(!currentPeers.contains(peerId)){
currentPeers.add(peerId) currentPeers.add(peerId)
@ -46,14 +50,21 @@ class PeerInfoListAdapter(
} }
} }
} }
peerInfoHolder.checkbox.isChecked = this.currentPeers.contains(peerId)
if(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 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="20dp" android:layout_margin="20dp"
android:background="@drawable/rounded_corner" android:background="@drawable/info_panel_rounded_corner"
android:gravity="left" android:gravity="left"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -49,7 +49,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="20dp" android:layout_margin="20dp"
android:background="@drawable/rounded_corner" android:background="@drawable/info_panel_rounded_corner"
android:gravity="left" android:gravity="left"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/ipLayout"> app:layout_constraintTop_toBottomOf="@id/ipLayout">

View File

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