From 28eb0903efd327a0b6deed0bddd71fff830bb110 Mon Sep 17 00:00:00 2001 From: vadym Date: Sun, 21 Jun 2020 06:54:16 -0700 Subject: [PATCH] 1. implemented peers list load from json, iteration #2 https://publicpeers.neilalexander.dev/publicnodes.json --- app/build.gradle | 26 ++-- .../chronosx88/yggdrasil/MainActivity.kt | 30 ++++- .../chronosx88/yggdrasil/PeerListActivity.kt | 114 +++++++++++++----- .../yggdrasil/YggdrasilTunService.kt | 4 +- .../yggdrasil/models/{config => }/PeerInfo.kt | 23 +++- .../chronosx88/yggdrasil/models/Status.kt | 5 + .../models/config/PeerInfoListAdapter.kt | 31 +++-- .../res/drawable/checkbox_rounded_corner.xml | 16 +++ ...rner.xml => info_panel_rounded_corner.xml} | 0 app/src/main/res/layout/activity_main.xml | 4 +- .../main/res/layout/peers_list_item_edit.xml | 5 +- 11 files changed, 187 insertions(+), 71 deletions(-) rename app/src/main/java/io/github/chronosx88/yggdrasil/models/{config => }/PeerInfo.kt (52%) create mode 100644 app/src/main/java/io/github/chronosx88/yggdrasil/models/Status.kt create mode 100644 app/src/main/res/drawable/checkbox_rounded_corner.xml rename app/src/main/res/drawable/{rounded_corner.xml => info_panel_rounded_corner.xml} (100%) diff --git a/app/build.gradle b/app/build.gradle index 4f29cfb..d3e972e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt index 0b3a4c0..7337d2a 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt @@ -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): ArrayList { + var gson = Gson() + var out = ArrayList() + for(s in list) { + out.add(gson.fromJson(s, PeerInfo::class.java)) + } + return out + } + + fun serializePeerInfoList2StringSet(list: ArrayList): Set { + var gson = Gson() + var out = TreeSet() + for(p in list) { + out.add(gson.toJson(p)) + } + return out + } + } diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/PeerListActivity.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/PeerListActivity.kt index 7d129c9..53ded57 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/PeerListActivity.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/PeerListActivity.kt @@ -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(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(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>>() {}.type + val peersMap: Map> = Gson().fromJson(json, mapType) + val allOnlinePeers = arrayListOf() + 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 + } + } } } diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt index 87eaa86..78f16b6 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt @@ -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() diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfo.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt similarity index 52% rename from app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfo.kt rename to app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt index 0a13347..25ae017 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfo.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/Status.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/Status.kt new file mode 100644 index 0000000..ecb73d8 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/Status.kt @@ -0,0 +1,5 @@ +package io.github.chronosx88.yggdrasil.models + +class Status { + var up: Boolean = false +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt index ec6ec82..250a6d2 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt @@ -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 = 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 { + fun getSelectedPeers(): ArrayList { return currentPeers } + class PeerInfoHolder { + lateinit var checkbox: CheckBox + lateinit var countryFlag: ImageView + lateinit var peerInfoText: TextView + } + } \ No newline at end of file diff --git a/app/src/main/res/drawable/checkbox_rounded_corner.xml b/app/src/main/res/drawable/checkbox_rounded_corner.xml new file mode 100644 index 0000000..f28fc22 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_rounded_corner.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_corner.xml b/app/src/main/res/drawable/info_panel_rounded_corner.xml similarity index 100% rename from app/src/main/res/drawable/rounded_corner.xml rename to app/src/main/res/drawable/info_panel_rounded_corner.xml diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4416a63..bf6e0e8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -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"> diff --git a/app/src/main/res/layout/peers_list_item_edit.xml b/app/src/main/res/layout/peers_list_item_edit.xml index 2ac26d0..573da2e 100644 --- a/app/src/main/res/layout/peers_list_item_edit.xml +++ b/app/src/main/res/layout/peers_list_item_edit.xml @@ -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"> + android:background="@android:color/transparent" + android:checked="false"/>