1. implemented peers list serialization/deserialization. iteration 3

This commit is contained in:
vadym 2020-06-21 11:43:33 -07:00
parent 28eb0903ef
commit 2f357be07e
6 changed files with 161 additions and 87 deletions

View File

@ -14,6 +14,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.gson.Gson import com.google.gson.Gson
import io.github.chronosx88.yggdrasil.models.PeerInfo import io.github.chronosx88.yggdrasil.models.PeerInfo
import io.github.chronosx88.yggdrasil.models.config.PeerInfoListAdapter
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashSet import kotlin.collections.HashSet
@ -22,7 +23,6 @@ import kotlin.collections.HashSet
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
companion object { companion object {
const val COMMAND = "COMMAND" const val COMMAND = "COMMAND"
const val STOP = "STOP" const val STOP = "STOP"
const val START = "START" const val START = "START"
@ -34,14 +34,34 @@ class MainActivity : AppCompatActivity() {
const val PEERS: String = "PEERS" const val PEERS: String = "PEERS"
const val PEER_LIST_CODE = 1000 const val PEER_LIST_CODE = 1000
const val PEER_LIST = "PEERS_LIST" const val PEER_LIST = "PEERS_LIST"
const val CURRENT_PEERS = "CURRENT_PEERS" const val CURRENT_PEERS = "CURRENT_PEER_INFO"
const val START_VPN = "START_VPN" const val START_VPN = "START_VPN"
private const val TAG="Yggdrasil" private const val TAG="Yggdrasil"
private const val VPN_REQUEST_CODE = 0x0F private const val VPN_REQUEST_CODE = 0x0F
@JvmStatic
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
}
@JvmStatic
fun serializePeerInfoList2StringList(list: ArrayList<PeerInfo>): ArrayList<String> {
var gson = Gson()
var out = ArrayList<String>()
for(p in list) {
out.add(gson.toJson(p))
}
return out
}
} }
private var startVpnFlag = false private var startVpnFlag = false
private var currentPeers = arrayListOf<String>() private var currentPeers = arrayListOf<PeerInfo>()
private var isStarted = false private var isStarted = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -51,18 +71,14 @@ class MainActivity : AppCompatActivity() {
//save to shared preferences //save to shared preferences
val preferences = val preferences =
PreferenceManager.getDefaultSharedPreferences(this.baseContext) PreferenceManager.getDefaultSharedPreferences(this.baseContext)
currentPeers = ArrayList(preferences.getStringSet(CURRENT_PEERS, HashSet())!!) currentPeers = deserializeStringList2PeerInfoList(ArrayList(preferences.getStringSet(CURRENT_PEERS, HashSet())!!))
if(currentPeers.size==0) {
currentPeers.add("tcp://194.177.21.156:5066") val adapter = PeerInfoListAdapter(this, currentPeers)
currentPeers.add("tcp://46.151.26.194:60575")
currentPeers.add("tcp://188.226.125.64:54321")
}
val adapter = ArrayAdapter(this, R.layout.peers_list_item, currentPeers)
listView.adapter = adapter listView.adapter = adapter
val editBeersButton = findViewById<Button>(R.id.edit) val editPeersButton = findViewById<Button>(R.id.edit)
editBeersButton.setOnClickListener { editPeersButton.setOnClickListener {
val intent = Intent(this, PeerListActivity::class.java) val intent = Intent(this, PeerListActivity::class.java)
intent.putStringArrayListExtra(PEER_LIST, currentPeers) intent.putStringArrayListExtra(PEER_LIST, serializePeerInfoList2StringList(currentPeers))
startActivityForResult(intent, PEER_LIST_CODE) startActivityForResult(intent, PEER_LIST_CODE)
} }
if(intent.extras!==null) { if(intent.extras!==null) {
@ -95,32 +111,33 @@ class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == VPN_REQUEST_CODE && resultCode== Activity.RESULT_OK){ if (requestCode == VPN_REQUEST_CODE && resultCode== Activity.RESULT_OK){
if(currentPeers.size==0){
showNoPeersSelected()
return
}
val intent = Intent(this, YggdrasilTunService::class.java) val intent = Intent(this, YggdrasilTunService::class.java)
val TASK_CODE = 100 val TASK_CODE = 100
val pi = createPendingResult(TASK_CODE, intent, 0) val pi = createPendingResult(TASK_CODE, intent, 0)
intent.putExtra(PARAM_PINTENT, pi) intent.putExtra(PARAM_PINTENT, pi)
intent.putExtra(COMMAND, START) intent.putExtra(COMMAND, START)
intent.putStringArrayListExtra(PEERS, currentPeers) intent.putStringArrayListExtra(PEERS, serializePeerInfoList2StringList(currentPeers))
startService(intent) startService(intent)
} }
if (requestCode == PEER_LIST_CODE && resultCode== Activity.RESULT_OK){ if (requestCode == PEER_LIST_CODE && resultCode== Activity.RESULT_OK){
if(data!!.extras!=null){ if(data!!.extras!=null){
var currentPeers = data.extras!!.getStringArrayList(PEER_LIST) var currentPeers = data.extras!!.getStringArrayList(PEER_LIST)
if(currentPeers==null || currentPeers.size==0){ if(currentPeers==null || currentPeers.size==0){
val text = "No peers selected!" showNoPeersSelected()
val duration = Toast.LENGTH_SHORT
val toast = Toast.makeText(applicationContext, text, duration)
toast.setGravity(Gravity.CENTER, 0, 0)
toast.show()
} else { } else {
val adapter = ArrayAdapter(this, R.layout.peers_list_item, currentPeers) this.currentPeers = deserializeStringList2PeerInfoList(currentPeers)
val adapter = PeerInfoListAdapter(this, this.currentPeers)
val listView = findViewById<ListView>(R.id.peers) val listView = findViewById<ListView>(R.id.peers)
listView.adapter = adapter listView.adapter = adapter
this.currentPeers = currentPeers
//save to shared preferences //save to shared preferences
val preferences = val preferences =
PreferenceManager.getDefaultSharedPreferences(this.baseContext) PreferenceManager.getDefaultSharedPreferences(this.baseContext)
preferences.edit().putStringSet(CURRENT_PEERS, HashSet(this.currentPeers)).apply() preferences.edit().putStringSet(CURRENT_PEERS, HashSet(currentPeers)).apply()
if(isStarted){ if(isStarted){
//apply peer changes //apply peer changes
stopVpn() stopVpn()
@ -177,22 +194,12 @@ class MainActivity : AppCompatActivity() {
return true return true
} }
fun deserializeStringList2PeerInfoList(list: ArrayList<String>): ArrayList<PeerInfo> { fun showNoPeersSelected(){
var gson = Gson() val text = "No peers selected!"
var out = ArrayList<PeerInfo>() val duration = Toast.LENGTH_SHORT
for(s in list) { val toast = Toast.makeText(applicationContext, text, duration)
out.add(gson.fromJson(s, PeerInfo::class.java)) toast.setGravity(Gravity.CENTER, 0, 0)
} toast.show()
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

@ -17,7 +17,7 @@ import com.google.gson.reflect.TypeToken
import com.hbb20.CCPCountry import com.hbb20.CCPCountry
import io.github.chronosx88.yggdrasil.models.PeerInfo import io.github.chronosx88.yggdrasil.models.PeerInfo
import io.github.chronosx88.yggdrasil.models.Status import io.github.chronosx88.yggdrasil.models.Status
import io.github.chronosx88.yggdrasil.models.config.PeerInfoListAdapter import io.github.chronosx88.yggdrasil.models.config.SelectPeerInfoListAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -136,24 +136,22 @@ class PeerListActivity : AppCompatActivity() {
} }
if (extras != null) { if (extras != null) {
var cp = extras.getStringArrayList(MainActivity.PEER_LIST)!! var cp = MainActivity.deserializeStringList2PeerInfoList(extras.getStringArrayList(MainActivity.PEER_LIST)!!)
var currentPeers = ArrayList(cp) var currentPeers = ArrayList(cp)
for(peerInfo in allPeers){ for(peerInfo in allPeers){
if(currentPeers.contains(peerInfo.toString())){ if(currentPeers.contains(peerInfo)){
currentPeers.remove(peerInfo.toString()) currentPeers.remove(peerInfo)
} }
} }
for(cp in currentPeers){ for(currentPeer in currentPeers){
var url = URI(cp) allPeers.add(0, currentPeer)
var peerInfo = PeerInfo(url.scheme, InetAddress.getByName(url.host), url.port, "RU")
allPeers.add(0, peerInfo)
} }
var adapter = PeerInfoListAdapter(instance, allOnlinePeers, cp) var adapter = SelectPeerInfoListAdapter(instance, allOnlinePeers, cp)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
peerList.adapter = adapter peerList.adapter = adapter
} }
} else { } else {
var adapter = PeerInfoListAdapter(instance, allOnlinePeers, ArrayList()) var adapter = SelectPeerInfoListAdapter(instance, allOnlinePeers, ArrayList())
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
peerList.adapter = adapter peerList.adapter = adapter
} }
@ -170,10 +168,10 @@ class PeerListActivity : AppCompatActivity() {
.actionView.findViewById<Button>(R.id.saveButton) .actionView.findViewById<Button>(R.id.saveButton)
saveButton.setOnClickListener { saveButton.setOnClickListener {
val result = Intent(this, MainActivity::class.java) val result = Intent(this, MainActivity::class.java)
var adapter = findViewById<ListView>(R.id.peerList).adapter as PeerInfoListAdapter var adapter = findViewById<ListView>(R.id.peerList).adapter as SelectPeerInfoListAdapter
val selectedPeers = adapter.getSelectedPeers() val selectedPeers = adapter.getSelectedPeers()
if(selectedPeers.size>0) { if(selectedPeers.size>0) {
result.putExtra(MainActivity.PEER_LIST, adapter.getSelectedPeers()) result.putExtra(MainActivity.PEER_LIST, MainActivity.serializePeerInfoList2StringList(adapter.getSelectedPeers()))
setResult(Activity.RESULT_OK, result) setResult(Activity.RESULT_OK, result)
finish() finish()
} else { } else {

View File

@ -7,6 +7,7 @@ import android.os.ParcelFileDescriptor
import android.system.OsConstants import android.system.OsConstants
import com.google.gson.Gson import com.google.gson.Gson
import dummy.ConduitEndpoint import dummy.ConduitEndpoint
import io.github.chronosx88.yggdrasil.models.PeerInfo
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -27,6 +28,15 @@ class YggdrasilTunService : VpnService() {
companion object { companion object {
private const val TAG = "Yggdrasil-service" private const val TAG = "Yggdrasil-service"
@JvmStatic
fun convertPeerInfoList2PeerIdList(list: ArrayList<PeerInfo>): ArrayList<String> {
var out = ArrayList<String>()
for(p in list) {
out.add(p.toString())
}
return out
}
} }
private var tunInterface: ParcelFileDescriptor? = null private var tunInterface: ParcelFileDescriptor? = null
private lateinit var yggConduitEndpoint: ConduitEndpoint private lateinit var yggConduitEndpoint: ConduitEndpoint
@ -42,7 +52,7 @@ class YggdrasilTunService : VpnService() {
stopVpn(pi) stopVpn(pi)
} }
if (intent?.getStringExtra(MainActivity.COMMAND) == MainActivity.START) { if (intent?.getStringExtra(MainActivity.COMMAND) == MainActivity.START) {
val peers = intent.getStringArrayListExtra(MainActivity.PEERS) val peers = MainActivity.deserializeStringList2PeerInfoList(intent.getStringArrayListExtra(MainActivity.PEERS))
val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT) val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT)
ygg = Yggdrasil() ygg = Yggdrasil()
setupTunInterface(pi, peers) setupTunInterface(pi, peers)
@ -51,7 +61,7 @@ class YggdrasilTunService : VpnService() {
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
private fun setupTunInterface(pi: PendingIntent, peers: ArrayList<String>) { private fun setupTunInterface(pi: PendingIntent, peers: ArrayList<PeerInfo>) {
pi.send(MainActivity.STATUS_START) pi.send(MainActivity.STATUS_START)
val builder = Builder() val builder = Builder()
@ -92,13 +102,13 @@ class YggdrasilTunService : VpnService() {
pi.send(this, MainActivity.STATUS_FINISH, intent) pi.send(this, MainActivity.STATUS_FINISH, intent)
} }
private fun fixConfig(config: MutableMap<Any?, Any?>, peers: ArrayList<String>): MutableMap<Any?, Any?> { private fun fixConfig(config: MutableMap<Any?, Any?>, peers: ArrayList<PeerInfo>): MutableMap<Any?, Any?> {
val whiteList = arrayListOf<String>() val whiteList = arrayListOf<String>()
whiteList.add("") whiteList.add("")
val blackList = arrayListOf<String>() val blackList = arrayListOf<String>()
blackList.add("") blackList.add("")
config["Peers"] = peers config["Peers"] = convertPeerInfoList2PeerIdList(peers)
config["Listen"] = "" config["Listen"] = ""
config["AdminListen"] = "tcp://localhost:9001" config["AdminListen"] = "tcp://localhost:9001"
config["IfName"] = "tun0" config["IfName"] = "tun0"

View File

@ -15,20 +15,17 @@ import java.util.ArrayList
class PeerInfoListAdapter( class PeerInfoListAdapter(
context: Context, context: Context,
allPeers: List<PeerInfo>, allPeers: List<PeerInfo>
currentPeers: ArrayList<String>
) : ArrayAdapter<PeerInfo?> (context, 0, allPeers) { ) : ArrayAdapter<PeerInfo?> (context, 0, allPeers) {
private val mContext: Context = context private val mContext: Context = context
private var allPeers: List<PeerInfo> = allPeers private var allPeers: List<PeerInfo> = allPeers
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 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, parent, false)
peerInfoHolder.checkbox = listItem.findViewById(R.id.checkbox) as CheckBox
peerInfoHolder.countryFlag = listItem.findViewById(R.id.countryFlag) as ImageView peerInfoHolder.countryFlag = listItem.findViewById(R.id.countryFlag) as ImageView
peerInfoHolder.peerInfoText = listItem.findViewById(R.id.peerInfoText) as TextView peerInfoHolder.peerInfoText = listItem.findViewById(R.id.peerInfoText) as TextView
listItem.tag = peerInfoHolder listItem.tag = peerInfoHolder
@ -39,30 +36,10 @@ class PeerInfoListAdapter(
peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID) peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
val peerId = currentPeer.toString() val peerId = currentPeer.toString()
peerInfoHolder.peerInfoText.text = peerId peerInfoHolder.peerInfoText.text = peerId
peerInfoHolder.checkbox.setOnCheckedChangeListener { _, isChecked ->
if(isChecked){
if(!currentPeers.contains(peerId)){
currentPeers.add(peerId)
}
} else {
if(currentPeers.contains(peerId)){
currentPeers.remove(peerId)
}
}
}
peerInfoHolder.checkbox.isChecked = this.currentPeers.contains(peerId)
if(this.currentPeers.contains(peerId)){
print(peerId)
}
return listItem!! return listItem!!
} }
fun getSelectedPeers(): ArrayList<String> {
return currentPeers
}
class PeerInfoHolder { class PeerInfoHolder {
lateinit var checkbox: CheckBox
lateinit var countryFlag: ImageView lateinit var countryFlag: ImageView
lateinit var peerInfoText: TextView lateinit var peerInfoText: TextView
} }

View File

@ -0,0 +1,67 @@
package io.github.chronosx88.yggdrasil.models.config
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
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
class SelectPeerInfoListAdapter(
context: Context,
allPeers: List<PeerInfo>,
currentPeers: ArrayList<PeerInfo>
) : ArrayAdapter<PeerInfo?> (context, 0, allPeers) {
private val mContext: Context = context
private var allPeers: List<PeerInfo> = allPeers
private var currentPeers: ArrayList<PeerInfo> = 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]
peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
val peerId = currentPeer.toString()
peerInfoHolder.peerInfoText.text = peerId
peerInfoHolder.checkbox.setOnCheckedChangeListener { _, isChecked ->
if(isChecked){
if(!currentPeers.contains(currentPeer)){
currentPeers.add(currentPeer)
}
} else {
if(currentPeers.contains(currentPeer)){
currentPeers.remove(currentPeer)
}
}
}
peerInfoHolder.checkbox.isChecked = this.currentPeers.contains(currentPeer)
return listItem!!
}
fun getSelectedPeers(): ArrayList<PeerInfo> {
return currentPeers
}
class PeerInfoHolder {
lateinit var checkbox: CheckBox
lateinit var countryFlag: ImageView
lateinit var peerInfoText: TextView
}
}

View File

@ -1,9 +1,24 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/countryFlag"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="20dp"
android:scaleX="0.7"
android:scaleY="0.7"/>
<TextView
android:id="@+id/peerInfoText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="20dp" android:layout_marginStart="10dp"
android:paddingEnd="20dp" android:paddingEnd="20dp"
android:minHeight="25dp" android:minHeight="22dp"
android:textSize="13sp" android:textSize="13sp"
android:textColor="@color/white"/> android:textColor="@color/white"/>
</LinearLayout>