mirror of
https://github.com/yggdrasil-network/crispa-android.git
synced 2025-01-22 07:56:30 +00:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # build.gradle
This commit is contained in:
commit
0825fb814c
@ -8,4 +8,7 @@ The application for connecting to Yggdrasil network on Android.
|
||||
|
||||
Once connected, you can access yggdrasil public services, as the application has peered automatically to default peers within the application.
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/8705683/87849784-62048a80-c8fc-11ea-8eb0-908e69d7595e.png" alt="" height="500" width="">
|
||||
<img src="https://user-images.githubusercontent.com/8705683/87849784-62048a80-c8fc-11ea-8eb0-908e69d7595e.png" alt="" height="500" width="">
|
||||
|
||||
New builds and updates here:
|
||||
https://t.me/yggdrasil_android
|
||||
|
@ -3,13 +3,13 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
applicationId "io.github.chronosx88.yggdrasil"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 30
|
||||
versionCode 6
|
||||
versionName "1.6"
|
||||
targetSdkVersion 29
|
||||
versionCode 18
|
||||
versionName "1.7"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
setProperty("archivesBaseName", project.getParent().name+"-"+versionName)
|
||||
}
|
||||
|
@ -32,6 +32,12 @@
|
||||
android:label="@string/title_activity_dns_list"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name=".CopyLocalNodeInfoActivity"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:label="@string/title_activity_copy_local_node_info"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:screenOrientation="portrait"/>
|
||||
<service
|
||||
android:name=".YggdrasilTunService"
|
||||
android:enabled="true"
|
||||
|
@ -0,0 +1,28 @@
|
||||
package io.github.chronosx88.yggdrasil
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.ListView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import io.github.chronosx88.yggdrasil.models.NodeInfo
|
||||
import io.github.chronosx88.yggdrasil.models.config.CopyInfoAdapter
|
||||
import io.github.chronosx88.yggdrasil.models.config.SelectDNSInfoListAdapter
|
||||
|
||||
class CopyLocalNodeInfoActivity: AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_copy_local_node_info)
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
val ipv6Address = intent.extras!!.getString(MainActivity.IPv6, "")
|
||||
val signingPublicKey = preferences.getString(MainActivity.signingPublicKey, "")
|
||||
val encryptionPublicKey = preferences.getString(MainActivity.encryptionPublicKey, "")
|
||||
var nodeInfoListView = findViewById<ListView>(R.id.nodeInfoList)
|
||||
val nodeInfoList = listOf<NodeInfo>(NodeInfo("IP address", ipv6Address!!), NodeInfo("Encryption Public Key", encryptionPublicKey!!), NodeInfo("Signing Public Key", signingPublicKey!!));
|
||||
var adapter = CopyInfoAdapter(this, nodeInfoList)
|
||||
nodeInfoListView.adapter = adapter
|
||||
}
|
||||
|
||||
}
|
@ -59,11 +59,11 @@ class DNSListActivity : AppCompatActivity() {
|
||||
try {
|
||||
|
||||
for (d in cd) {
|
||||
var ping = ping(d.address, 53)
|
||||
var ping = ping(d.address.hostAddress, 53)
|
||||
d.ping = ping
|
||||
}
|
||||
for (dns in allDNS) {
|
||||
var ping = ping(dns.address, 53)
|
||||
var ping = ping(dns.address.hostAddress, 53)
|
||||
dns.ping = ping
|
||||
runOnUiThread(
|
||||
Runnable
|
||||
@ -110,7 +110,7 @@ class DNSListActivity : AppCompatActivity() {
|
||||
thread(start = true) {
|
||||
var di = DNSInfo(InetAddress.getByName("["+ip+"]"), ccp, "User DNS")
|
||||
try {
|
||||
var ping = ping(di.address, 53)
|
||||
var ping = ping(di.address.hostAddress, 53)
|
||||
di.ping = ping
|
||||
} catch(e: Throwable){
|
||||
di.ping = Int.MAX_VALUE
|
||||
@ -127,7 +127,7 @@ class DNSListActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.save, menu)
|
||||
menuInflater.inflate(R.menu.save_dns, menu)
|
||||
val item = menu.findItem(R.id.saveItem) as MenuItem
|
||||
item.setActionView(R.layout.menu_save)
|
||||
val saveButton = item
|
||||
|
@ -107,19 +107,21 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
val adapter = PeerInfoListAdapter(this, currentPeers.sortedWith(compareBy { it.ping }))
|
||||
peersListView.adapter = adapter
|
||||
|
||||
if (adapter.count > 10) {
|
||||
val item = adapter.getView(0, null, peersListView)
|
||||
item.measure(0, 0)
|
||||
val params = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
(10 * item.measuredHeight).toInt()
|
||||
)
|
||||
peersListView.layoutParams = params
|
||||
}
|
||||
|
||||
if(isStarted && this.currentPeers.isEmpty()) {
|
||||
updatePeers()
|
||||
}
|
||||
val copyAddressButton = findViewById<Button>(R.id.copyIp)
|
||||
copyAddressButton.setOnClickListener {
|
||||
val ip = findViewById<TextView>(R.id.ip)
|
||||
val clipboard: ClipboardManager =
|
||||
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip =
|
||||
ClipData.newPlainText("IP address", ip.text.toString())
|
||||
clipboard.setPrimaryClip(clip)
|
||||
showToast(getString(R.string.address_copied))
|
||||
}
|
||||
|
||||
val editPeersButton = findViewById<Button>(R.id.edit)
|
||||
editPeersButton.setOnClickListener {
|
||||
if(isStarted){
|
||||
@ -150,6 +152,14 @@ class MainActivity : AppCompatActivity() {
|
||||
intent.putStringArrayListExtra(DNS_LIST, serializeDNSInfoSet2StringList(currentDNS))
|
||||
startActivityForResult(intent, DNS_LIST_CODE)
|
||||
}
|
||||
val nodeInfoButton = findViewById<Button>(R.id.nodeInfo)
|
||||
nodeInfoButton.setOnClickListener {
|
||||
if(isStarted) {
|
||||
val intent = Intent(this@MainActivity, CopyLocalNodeInfoActivity::class.java)
|
||||
intent.putExtra(IPv6, findViewById<TextView>(R.id.ip).text.toString())
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
if(isStarted){
|
||||
val ipLayout = findViewById<LinearLayout>(R.id.ipLayout)
|
||||
ipLayout.visibility = View.VISIBLE
|
||||
@ -404,7 +414,7 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
findViewById<Switch>(R.id.staticIP).isChecked =
|
||||
findViewById<SwitchCompat>(R.id.staticIP).isChecked =
|
||||
preferences.getString(STATIC_IP, null) != null
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,18 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.webkit.URLUtil
|
||||
import android.widget.Button
|
||||
import android.widget.ListView
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.hbb20.BuildConfig
|
||||
import com.hbb20.CCPCountry
|
||||
import com.vincentbrison.openlibraries.android.dualcache.Builder
|
||||
import com.vincentbrison.openlibraries.android.dualcache.SizeOf
|
||||
@ -21,32 +24,30 @@ import com.vincentbrison.openlibraries.android.dualcache.JsonSerializer
|
||||
import io.github.chronosx88.yggdrasil.models.PeerInfo
|
||||
import io.github.chronosx88.yggdrasil.models.Status
|
||||
import io.github.chronosx88.yggdrasil.models.config.DropDownAdapter
|
||||
import io.github.chronosx88.yggdrasil.models.config.Peer
|
||||
import io.github.chronosx88.yggdrasil.models.config.SelectPeerInfoListAdapter
|
||||
import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringList2PeerInfoSet
|
||||
import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.ping
|
||||
import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.serializePeerInfoSet2StringList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.lang.reflect.Type
|
||||
import java.net.InetAddress
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.net.UnknownHostException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
class PeerListActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
const val PEER_LIST = "PEER_LIST"
|
||||
const val PEER_LIST_URL = "https://publicpeers.neilalexander.dev/publicnodes.json"
|
||||
const val CACHE_NAME = "PEER_LIST_CACHE"
|
||||
const val ONLINE_PEERINFO_LIST = "ONLINE_PEERINFO_LIST"
|
||||
const val OFFLINE_PEERINFO_LIST = "OFFLINE_PEERINFO_LIST"
|
||||
const val TEST_APP_VERSION = BuildConfig.VERSION_CODE;
|
||||
const val ONLINE_PEERINFO_LIST = "online_peer_info_list"
|
||||
const val OFFLINE_PEERINFO_LIST = "offline_peer_info_list"
|
||||
const val TEST_APP_VERSION = BuildConfig.VERSION_CODE
|
||||
const val RAM_MAX_SIZE = 100000
|
||||
const val DISK_MAX_SIZE = 100000
|
||||
}
|
||||
@ -61,6 +62,8 @@ class PeerListActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private var peerListUrl = PEER_LIST_URL
|
||||
private var peerListPing = true
|
||||
var popup: PopupWindow? = null
|
||||
var adapter: DropDownAdapter? = null
|
||||
|
||||
@ -71,6 +74,13 @@ class PeerListActivity : AppCompatActivity() {
|
||||
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { _ ->
|
||||
addNewPeer()
|
||||
}
|
||||
val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
var peerListUrl: String =
|
||||
preferences.getString(PEER_LIST, "")!!
|
||||
if(!peerListUrl.isNullOrBlank()){
|
||||
this@PeerListActivity.peerListUrl = peerListUrl
|
||||
}
|
||||
var extras = intent.extras
|
||||
var peerList = findViewById<ListView>(R.id.peerList)
|
||||
var adapter = SelectPeerInfoListAdapter(this, arrayListOf(), mutableSetOf())
|
||||
@ -83,73 +93,20 @@ class PeerListActivity : AppCompatActivity() {
|
||||
JsonSerializer(ArrayList<PeerInfo>().javaClass), baseContext
|
||||
).build();
|
||||
|
||||
GlobalScope.launch {
|
||||
GlobalScope.launch() {
|
||||
try {
|
||||
var cp = deserializeStringList2PeerInfoSet(
|
||||
extras!!.getStringArrayList(MainActivity.PEER_LIST)!!
|
||||
)
|
||||
for(pi in cp){
|
||||
var ping = ping(pi.address, pi.port)
|
||||
for (pi in cp) {
|
||||
var ping = ping(pi.hostName, pi.port)
|
||||
pi.ping = ping
|
||||
}
|
||||
try {
|
||||
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)
|
||||
for ((country, peers) in peersMap.entries) {
|
||||
for ((peer, status) in peers) {
|
||||
if (status.up) {
|
||||
for (ccp in countries) {
|
||||
if (ccp.name.toLowerCase()
|
||||
.contains(country.replace(".md", "").replace("-", " "))
|
||||
) {
|
||||
var url = URI(peer)
|
||||
try {
|
||||
var address = InetAddress.getByName(url.host)
|
||||
var peerInfo =
|
||||
PeerInfo(
|
||||
url.scheme,
|
||||
address,
|
||||
url.port,
|
||||
ccp.nameCode
|
||||
)
|
||||
var ping = ping(address, url.port)
|
||||
peerInfo.ping = ping
|
||||
if(cp.contains(peerInfo)){
|
||||
continue
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.addItem(peerInfo)
|
||||
if(adapter.count % 5 == 0) {
|
||||
adapter.sort()
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable){
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var allPeersList = adapter.getAllPeers()
|
||||
var cachePeerInfoList = mutableListOf<PeerInfo>()
|
||||
for(p in allPeersList){
|
||||
if(p.ping<Int.MAX_VALUE){
|
||||
cachePeerInfoList.add(p)
|
||||
}
|
||||
}
|
||||
if(cachePeerInfoList.size>0){
|
||||
peerInfoListCache.put(ONLINE_PEERINFO_LIST, cachePeerInfoList.toList())
|
||||
}
|
||||
} catch (e: FileNotFoundException){
|
||||
e.printStackTrace()
|
||||
var onlinePeerInfoList = peerInfoListCache.get(ONLINE_PEERINFO_LIST)
|
||||
if(onlinePeerInfoList!=null) {
|
||||
for (peerInfo in onlinePeerInfoList) {
|
||||
var ping = ping(peerInfo.address, peerInfo.port)
|
||||
var peerInfoCache = peerInfoListCache.get(ONLINE_PEERINFO_LIST)
|
||||
if (peerInfoCache != null && peerInfoCache.isNotEmpty()) {
|
||||
for (peerInfo in peerInfoCache) {
|
||||
var ping = ping(peerInfo.hostName, peerInfo.port)
|
||||
peerInfo.ping = ping
|
||||
if (cp.contains(peerInfo)) {
|
||||
continue
|
||||
@ -162,17 +119,117 @@ class PeerListActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
var json = downloadJson(this@PeerListActivity.peerListUrl)
|
||||
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)
|
||||
var cachePeerInfoList = mutableListOf<PeerInfo>()
|
||||
for ((country, peers) in peersMap.entries) {
|
||||
for ((peer, status) in peers) {
|
||||
if (status.up) {
|
||||
for (ccp in countries) {
|
||||
if (ccp.name.toLowerCase()
|
||||
.contains(country.replace(".md", "").replace("-", " "))
|
||||
) {
|
||||
if(!peerListPing){
|
||||
return@launch
|
||||
}
|
||||
var url = URI(peer)
|
||||
try {
|
||||
var address = InetAddress.getByName(url.host)
|
||||
var peerInfo =
|
||||
PeerInfo(
|
||||
url.scheme,
|
||||
address,
|
||||
url.port,
|
||||
ccp.nameCode
|
||||
)
|
||||
var ping = ping(url.host, url.port)
|
||||
peerInfo.ping = ping
|
||||
if (cp.contains(peerInfo)) {
|
||||
continue
|
||||
}
|
||||
if (peerInfo.ping < Int.MAX_VALUE) {
|
||||
cachePeerInfoList.add(peerInfo)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.addItem(peerInfo)
|
||||
if (adapter.count % 5 == 0) {
|
||||
adapter.sort()
|
||||
if (cachePeerInfoList.size > 0) {
|
||||
peerInfoListCache.put(
|
||||
ONLINE_PEERINFO_LIST,
|
||||
cachePeerInfoList.toList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is FileNotFoundException, is UnknownHostException -> {
|
||||
var onlinePeerInfoList = peerInfoListCache.get(ONLINE_PEERINFO_LIST)
|
||||
if (onlinePeerInfoList != null) {
|
||||
for (peerInfo in onlinePeerInfoList) {
|
||||
var ping = ping(peerInfo.hostName, peerInfo.port)
|
||||
peerInfo.ping = ping
|
||||
if (cp.contains(peerInfo)) {
|
||||
continue
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.addItem(peerInfo)
|
||||
if (adapter.count % 5 == 0) {
|
||||
adapter.sort()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
e.printStackTrace()
|
||||
}
|
||||
else -> e.printStackTrace()
|
||||
}
|
||||
}
|
||||
var currentPeers = ArrayList(cp.sortedWith(compareBy { it.ping }))
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.addAll(0, currentPeers)
|
||||
}
|
||||
} catch (e: Throwable){
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun editPeerListUrl() {
|
||||
val view: View = LayoutInflater.from(this).inflate(R.layout.edit_peer_list_url_dialog, null)
|
||||
val ab: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
ab.setCancelable(true).setView(view)
|
||||
var ad = ab.show()
|
||||
var saveButton = view.findViewById<Button>(R.id.save)
|
||||
var urlInput = view.findViewById<TextView>(R.id.urlInput)
|
||||
urlInput.text = peerListUrl
|
||||
saveButton.setOnClickListener{
|
||||
|
||||
var url = urlInput.text.toString()
|
||||
if(!URLUtil.isValidUrl(url)){
|
||||
urlInput.error = "The URL is invalid!"
|
||||
return@setOnClickListener;
|
||||
}
|
||||
peerListUrl = url
|
||||
val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
preferences.edit().putString(PEER_LIST, peerListUrl).apply()
|
||||
ad.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNewPeer() {
|
||||
val view: View = LayoutInflater.from(this).inflate(R.layout.new_peer_dialog, null)
|
||||
val countryCode: String = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@ -226,7 +283,7 @@ class PeerListActivity : AppCompatActivity() {
|
||||
GlobalScope.launch {
|
||||
var pi = PeerInfo(schema, InetAddress.getByName(ip), port, ccp)
|
||||
try {
|
||||
var ping = ping(pi.address, pi.port)
|
||||
var ping = ping(pi.hostName, pi.port)
|
||||
pi.ping = ping
|
||||
} catch (e: Throwable){
|
||||
pi.ping = Int.MAX_VALUE
|
||||
@ -277,12 +334,14 @@ class PeerListActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.save, menu)
|
||||
menuInflater.inflate(R.menu.save_peers, menu)
|
||||
val item = menu.findItem(R.id.saveItem) as MenuItem
|
||||
item.setActionView(R.layout.menu_save)
|
||||
val saveButton = item
|
||||
.actionView.findViewById<Button>(R.id.saveButton)
|
||||
saveButton.setOnClickListener {
|
||||
saveButton.isClickable = false
|
||||
cancelPeerListPing()
|
||||
val result = Intent(this, MainActivity::class.java)
|
||||
var adapter = findViewById<ListView>(R.id.peerList).adapter as SelectPeerInfoListAdapter
|
||||
val selectedPeers = adapter.getSelectedPeers()
|
||||
@ -290,8 +349,25 @@ class PeerListActivity : AppCompatActivity() {
|
||||
setResult(Activity.RESULT_OK, result)
|
||||
finish()
|
||||
}
|
||||
|
||||
val editUrl = menu.findItem(R.id.editUrlItem) as MenuItem
|
||||
editUrl.setActionView(R.layout.menu_edit_url)
|
||||
val editUrlButton = editUrl
|
||||
.actionView.findViewById<Button>(R.id.editUrlButton)
|
||||
editUrlButton.setOnClickListener {
|
||||
editPeerListUrl()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun cancelPeerListPing() {
|
||||
peerListPing = false
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
cancelPeerListPing()
|
||||
}
|
||||
}
|
||||
|
||||
class SizeOfPeerList: SizeOf<List<PeerInfo>> {
|
||||
@ -299,9 +375,6 @@ class SizeOfPeerList: SizeOf<List<PeerInfo>> {
|
||||
override fun sizeOf(obj: List<PeerInfo>): Int{
|
||||
var size = 0
|
||||
for (o in obj) {
|
||||
if (o.address != null) {
|
||||
size += o.address.toString().length * 2
|
||||
}
|
||||
if (o.hostName != null) {
|
||||
size += o.hostName.length * 2
|
||||
}
|
||||
@ -317,5 +390,4 @@ class SizeOfPeerList: SizeOf<List<PeerInfo>> {
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class YggdrasilTunService : VpnService() {
|
||||
val blackList = arrayListOf<String>()
|
||||
blackList.add("")
|
||||
config["Peers"] = convertPeerInfoSet2PeerIdSet(peers)
|
||||
config["Listen"] = ""
|
||||
config["Listen"] = arrayListOf<String>()
|
||||
config["AdminListen"] = "tcp://localhost:9001"
|
||||
config["IfName"] = "tun0"
|
||||
if(staticIP) {
|
||||
@ -177,7 +177,7 @@ class YggdrasilTunService : VpnService() {
|
||||
.putString(MainActivity.signingPublicKey, signingPublicKey)
|
||||
.putString(MainActivity.encryptionPrivateKey, encryptionPrivateKey)
|
||||
.putString(MainActivity.encryptionPublicKey, encryptionPublicKey)
|
||||
.putString(MainActivity.STATIC_IP,MainActivity.STATIC_IP).apply()
|
||||
.putString(MainActivity.STATIC_IP, MainActivity.STATIC_IP).apply()
|
||||
} else {
|
||||
val signingPrivateKey = preferences.getString(MainActivity.signingPrivateKey, null)
|
||||
val signingPublicKey = preferences.getString(MainActivity.signingPublicKey, null)
|
||||
|
@ -0,0 +1,13 @@
|
||||
package io.github.chronosx88.yggdrasil.models
|
||||
|
||||
class NodeInfo {
|
||||
|
||||
constructor(key: String, value: String){
|
||||
this.key = key
|
||||
this.value = value
|
||||
}
|
||||
|
||||
var key: String
|
||||
var value: String
|
||||
|
||||
}
|
@ -7,9 +7,13 @@ import java.net.InetAddress
|
||||
|
||||
|
||||
class PeerInfo {
|
||||
|
||||
constructor(){
|
||||
|
||||
}
|
||||
|
||||
constructor(schema: String, address: InetAddress, port: Int, countryCode: String){
|
||||
this.schema = schema
|
||||
this.address = address
|
||||
var a = address.toString();
|
||||
if(a.lastIndexOf('/')>0){
|
||||
this.hostName = a.split("/")[0]
|
||||
@ -22,7 +26,6 @@ class PeerInfo {
|
||||
|
||||
constructor(schema: String, address: InetAddress, port: Int, countryCode: String?, isMeshPeer: Boolean){
|
||||
this.schema = schema
|
||||
this.address = address
|
||||
var a = address.toString();
|
||||
if(a.lastIndexOf('/')>0){
|
||||
this.hostName = a.split("/")[0]
|
||||
@ -34,9 +37,8 @@ class PeerInfo {
|
||||
this.isMeshPeer = isMeshPeer
|
||||
}
|
||||
|
||||
var schema: String
|
||||
var address: InetAddress
|
||||
var hostName: String
|
||||
lateinit var schema: String
|
||||
lateinit var hostName: String
|
||||
var port = 0
|
||||
var countryCode: String?=null
|
||||
var ping: Int = Int.MAX_VALUE
|
||||
|
@ -0,0 +1,67 @@
|
||||
package io.github.chronosx88.yggdrasil.models.config
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import io.github.chronosx88.yggdrasil.R
|
||||
import io.github.chronosx88.yggdrasil.models.NodeInfo
|
||||
|
||||
class CopyInfoAdapter(
|
||||
context: Context,
|
||||
nodeInfoList: List<NodeInfo>,
|
||||
) : ArrayAdapter<NodeInfo?> (context, 0, nodeInfoList) {
|
||||
|
||||
private val mContext: Context = context
|
||||
private var nodeInfoList: MutableList<NodeInfo> = nodeInfoList as MutableList<NodeInfo>
|
||||
|
||||
override fun getItem(position: Int): NodeInfo? {
|
||||
return nodeInfoList[position]
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return nodeInfoList.size
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var copyNodeInfoHolder = CopyInfoHolder()
|
||||
var listItem: View? = convertView
|
||||
if (listItem == null) {
|
||||
listItem = LayoutInflater.from(mContext).inflate(R.layout.copy_node_info_list_item, parent, false)
|
||||
copyNodeInfoHolder.copyButton = listItem.findViewById(R.id.nodeInfoButton) as Button
|
||||
copyNodeInfoHolder.nodeInfoText = listItem.findViewById(R.id.nodeInfoText) as TextView
|
||||
copyNodeInfoHolder.nodeInfoKey = listItem.findViewById(R.id.nodeInfoKey) as TextView
|
||||
listItem.tag = copyNodeInfoHolder
|
||||
} else {
|
||||
copyNodeInfoHolder = listItem.tag as CopyInfoHolder
|
||||
}
|
||||
copyNodeInfoHolder.nodeInfoKey.text = nodeInfoList[position].key
|
||||
copyNodeInfoHolder.nodeInfoText.text = nodeInfoList[position].value
|
||||
copyNodeInfoHolder.copyButton.setOnClickListener{ _ ->
|
||||
val clipboard: ClipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip =
|
||||
ClipData.newPlainText(nodeInfoList[position].key, nodeInfoList[position].value)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
showToast(nodeInfoList[position].key + " " + context.getString(R.string.node_info_copied))
|
||||
}
|
||||
return listItem!!
|
||||
}
|
||||
|
||||
private fun showToast(text: String){
|
||||
val duration = Toast.LENGTH_SHORT
|
||||
val toast = Toast.makeText(context, text, duration)
|
||||
toast.setGravity(Gravity.CENTER, 0, 0)
|
||||
toast.show()
|
||||
}
|
||||
|
||||
class CopyInfoHolder {
|
||||
lateinit var nodeInfoKey: TextView
|
||||
lateinit var nodeInfoText: TextView
|
||||
lateinit var copyButton: Button
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
package io.github.chronosx88.yggdrasil.models.config
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.Gravity
|
||||
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 android.widget.*
|
||||
import io.github.chronosx88.yggdrasil.R
|
||||
import io.github.chronosx88.yggdrasil.models.DNSInfo
|
||||
|
||||
@ -66,6 +66,14 @@ class SelectDNSInfoListAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
dnsInfoHolder.dnsInfoText.setOnClickListener {
|
||||
val clipboard: ClipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip =
|
||||
ClipData.newPlainText("DNS info", dnsId)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
showToast(dnsId + " " + context.getString(R.string.node_info_copied))
|
||||
}
|
||||
dnsInfoHolder.checkbox.isChecked = this.currentDNS.contains(currentDNS)
|
||||
return listItem!!
|
||||
}
|
||||
@ -100,4 +108,11 @@ class SelectDNSInfoListAdapter(
|
||||
lateinit var dnsInfoText: TextView
|
||||
lateinit var ping: TextView
|
||||
}
|
||||
|
||||
private fun showToast(text: String){
|
||||
val duration = Toast.LENGTH_SHORT
|
||||
val toast = Toast.makeText(context, text, duration)
|
||||
toast.setGravity(Gravity.CENTER, 0, 0)
|
||||
toast.show()
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
package io.github.chronosx88.yggdrasil.models.config
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.Gravity
|
||||
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 android.widget.*
|
||||
import io.github.chronosx88.yggdrasil.R
|
||||
import io.github.chronosx88.yggdrasil.models.PeerInfo
|
||||
|
||||
@ -46,12 +46,11 @@ class SelectPeerInfoListAdapter(
|
||||
val currentPeer = allPeers[position]
|
||||
peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
|
||||
val peerId = currentPeer.toString()
|
||||
peerInfoHolder.peerInfoText.text = peerId
|
||||
if(currentPeer.ping == Int.MAX_VALUE){
|
||||
peerInfoHolder.peerInfoText.text = peerId
|
||||
peerInfoHolder.ping.text=""
|
||||
peerInfoHolder.peerInfoText.setTextColor(Color.GRAY)
|
||||
} else {
|
||||
peerInfoHolder.peerInfoText.text = peerId
|
||||
peerInfoHolder.ping.text = currentPeer.ping.toString() + " ms"
|
||||
peerInfoHolder.peerInfoText.setTextColor(Color.WHITE)
|
||||
}
|
||||
@ -66,6 +65,14 @@ class SelectPeerInfoListAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
peerInfoHolder.peerInfoText.setOnClickListener {
|
||||
val clipboard: ClipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip =
|
||||
ClipData.newPlainText("Peer info", peerId)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
showToast(peerId + " " + context.getString(R.string.node_info_copied))
|
||||
}
|
||||
peerInfoHolder.checkbox.isChecked = this.currentPeers.contains(currentPeer)
|
||||
return listItem!!
|
||||
}
|
||||
@ -79,7 +86,9 @@ class SelectPeerInfoListAdapter(
|
||||
}
|
||||
|
||||
fun addItem(peerInfo: PeerInfo){
|
||||
allPeers.add(peerInfo)
|
||||
if(!allPeers.contains(peerInfo)){
|
||||
allPeers.add(peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fun addItem(index: Int, peerInfo: PeerInfo){
|
||||
@ -105,4 +114,10 @@ class SelectPeerInfoListAdapter(
|
||||
lateinit var ping: TextView
|
||||
}
|
||||
|
||||
private fun showToast(text: String){
|
||||
val duration = Toast.LENGTH_SHORT
|
||||
val toast = Toast.makeText(context, text, duration)
|
||||
toast.setGravity(Gravity.CENTER, 0, 0)
|
||||
toast.show()
|
||||
}
|
||||
}
|
@ -77,15 +77,15 @@ class Utils {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ping(address: InetAddress, port:Int): Int {
|
||||
fun ping(hostname: String, port:Int): Int {
|
||||
val start = System.currentTimeMillis()
|
||||
val socket = Socket()
|
||||
try {
|
||||
socket.connect(InetSocketAddress(address, port), 5000)
|
||||
socket.connect(InetSocketAddress(hostname, port), 5000)
|
||||
socket.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
print(address)
|
||||
print(hostname)
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
return (System.currentTimeMillis() - start).toInt()
|
||||
|
26
app/src/main/res/layout/activity_copy_local_node_info.xml
Normal file
26
app/src/main/res/layout/activity_copy_local_node_info.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".CopyLocalNodeInfoActivity"
|
||||
android:background="@color/grey">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_node_info" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -35,10 +35,10 @@
|
||||
android:textColor="@color/dark_30"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/copyIp"
|
||||
android:id="@+id/nodeInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="COPY"
|
||||
android:text="INFO"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="@android:color/transparent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -53,7 +53,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/copyIp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/nodeInfo"
|
||||
android:text=""
|
||||
android:textColor="@color/white"/>
|
||||
</LinearLayout>
|
||||
|
13
app/src/main/res/layout/content_node_info.xml
Normal file
13
app/src/main/res/layout/content_node_info.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
<ListView
|
||||
android:id="@+id/nodeInfoList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:divider="@color/dark_20"
|
||||
android:dividerHeight="2px"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
52
app/src/main/res/layout/copy_node_info_list_item.xml
Normal file
52
app/src/main/res/layout/copy_node_info_list_item.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_toLeftOf="@+id/nodeInfoButton"
|
||||
android:id="@+id/data">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nodeInfoKey"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="10sp"
|
||||
android:text = "nodeInfoKey"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginStart="10dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/nodeInfoText"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingBottom="5dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nodeInfoText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="35dp"
|
||||
android:text="nodeInfoText"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/nodeInfoKey"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/nodeInfoButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="COPY"
|
||||
android:textColor="@color/white"
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="@android:color/transparent"/>
|
||||
|
||||
</RelativeLayout>
|
43
app/src/main/res/layout/edit_peer_list_url_dialog.xml
Normal file
43
app/src/main/res/layout/edit_peer_list_url_dialog.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="10dp"
|
||||
android:background="@color/grey">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_margin="10dp"
|
||||
app:boxBackgroundMode="none"
|
||||
android:background="@drawable/edit_text_rounded_corner"
|
||||
android:textColorHint="@color/white"
|
||||
style="@style/EditText.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/urlInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="URL"
|
||||
android:textColor="@color/white"
|
||||
android:textCursorDrawable="@null"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/url"
|
||||
android:background="@drawable/button_selector"
|
||||
app:backgroundTint="@null"
|
||||
android:text="SAVE"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
13
app/src/main/res/layout/menu_edit_url.xml
Normal file
13
app/src/main/res/layout/menu_edit_url.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical">
|
||||
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/editUrlButton"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="EDIT URL"
|
||||
android:background="@android:color/transparent"/>
|
||||
</RelativeLayout>
|
12
app/src/main/res/menu/save_peers.xml
Normal file
12
app/src/main/res/menu/save_peers.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/editUrlItem"
|
||||
android:title=""
|
||||
app:actionLayout="@layout/menu_edit_url"
|
||||
app:showAsAction="always"/>
|
||||
<item android:id="@+id/saveItem"
|
||||
android:title=""
|
||||
app:actionLayout="@layout/menu_save"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
@ -5,7 +5,8 @@
|
||||
<string name="switch_button_title">SwitchOn</string>
|
||||
<string name="title_activity_peer_list">Edit peers</string>
|
||||
<string name="title_activity_dns_list">Edit DNS</string>
|
||||
<string name="address_copied">Address copied</string>
|
||||
<string name="node_info_copied">copied</string>
|
||||
<string name="schema">Schema</string>
|
||||
<string name="title_activity_about">Yggdrasil</string>
|
||||
<string name="title_activity_copy_local_node_info">Node info</string>
|
||||
</resources>
|
||||
|
@ -1,13 +1,14 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.32'
|
||||
ext.kotlin_version = '1.4.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.0-rc01'
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -17,6 +18,7 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
|
4
fastlane/metadata/android/en-US/changelogs/1.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/1.txt
Normal file
@ -0,0 +1,4 @@
|
||||
v1.0
|
||||
1. Implemented VPN network interface for Yggdrasil tunneling
|
||||
2. Optimized memory buffer
|
||||
3. Added IPv6 info
|
13
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
13
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
@ -0,0 +1,13 @@
|
||||
v1.2
|
||||
* New DNS and new Peer form
|
||||
* fix adding Peers when json url unavailable
|
||||
* DNS dynamic list selection fixes
|
||||
* ping selected Peers and DNS fixes
|
||||
* fixed when app crashed sometimes after ygg switch off
|
||||
* fixed high CPU consumption after switch-off.
|
||||
* prevent NPE fix
|
||||
|
||||
v1.1
|
||||
1. Added dynamic peer list loading and edit functionality
|
||||
2. Layout minor changes
|
||||
3. Peers ping test and sorting by response time
|
3
fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@ -0,0 +1,3 @@
|
||||
v1.3
|
||||
1. open MainActivity after Notification click, task (#12)
|
||||
2. new DNS only number issue
|
3
fastlane/metadata/android/en-US/changelogs/4.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/4.txt
Normal file
@ -0,0 +1,3 @@
|
||||
v1.4
|
||||
* F-Droid support
|
||||
* Android 4.0.3 support (API 15)
|
4
fastlane/metadata/android/en-US/full_description.txt
Normal file
4
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1,4 @@
|
||||
The application for connecting to Yggdrasil network on Android
|
||||
|
||||
Once connected, you can access yggdrasil public services,
|
||||
as the application has peered automatically to default peers within the application
|
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/001.png
Normal file
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/001.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/002.png
Normal file
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/002.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/003.png
Normal file
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/003.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
The application for connecting to Yggdrasil network on Android
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
1
fastlane/metadata/android/en-US/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Yggdrasil
|
@ -4,7 +4,7 @@ all:
|
||||
-go get -u github.com/yggdrasil-network/yggdrasil-go;
|
||||
-cd $(GOPATH)/src/github.com/yggdrasil-network/yggdrasil-go; \
|
||||
go get -v -d ./...; \
|
||||
go get -u github.com/vikulin/yggdrasil-extras@268b006; \
|
||||
go get -u github.com/yggdrasil-network/yggdrasil-extras@005d79c; \
|
||||
ANDROID=true ./build;
|
||||
mv -f $(GOPATH)/src/github.com/yggdrasil-network/yggdrasil-go/yggdrasil.aar yggdrasil.aar;
|
||||
mv -f $(GOPATH)/src/github.com/yggdrasil-network/yggdrasil-go/yggdrasil-sources.jar yggdrasil-sources.jar;
|
||||
|
Loading…
x
Reference in New Issue
Block a user