mirror of
https://github.com/yggdrasil-network/yggdrasil-android.git
synced 2025-04-28 14:15:08 +03:00
Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
222d9d90bc | ||
![]() |
6ddc878fde | ||
![]() |
055a74ea69 | ||
![]() |
94db1facd7 | ||
![]() |
e211111d60 | ||
![]() |
cdc12a8e7e | ||
![]() |
34756b2193 |
23 changed files with 238 additions and 41 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "libs/yggdrasil-go"]
|
||||||
|
path = libs/yggdrasil-go
|
||||||
|
url = https://github.com/yggdrasil-network/yggdrasil-go
|
|
@ -9,9 +9,9 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "eu.neilalexander.yggdrasil"
|
applicationId "eu.neilalexander.yggdrasil"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 34
|
||||||
versionCode 18
|
versionCode 20
|
||||||
versionName "0.1-018"
|
versionName "0.1-020"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -51,12 +51,12 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.aar'], dir: 'libs')
|
implementation fileTree(include: ['*.aar'], dir: 'libs')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.12.0'
|
implementation 'androidx.core:core-ktx:1.13.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'com.google.android.material:material:1.5.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
testImplementation 'junit:junit:4.+'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".GlobalApplication"
|
android:name=".GlobalApplication"
|
||||||
|
@ -42,6 +43,7 @@
|
||||||
<service
|
<service
|
||||||
android:name=".PacketTunnelProvider"
|
android:name=".PacketTunnelProvider"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
|
android:foregroundServiceType="systemExempted"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService" />
|
<action android:name="android.net.VpnService" />
|
||||||
|
|
|
@ -55,7 +55,7 @@ object ConfigurationProxy {
|
||||||
json.put("IfMTU", 65535)
|
json.put("IfMTU", 65535)
|
||||||
|
|
||||||
if (json.getJSONArray("MulticastInterfaces").get(0) is String) {
|
if (json.getJSONArray("MulticastInterfaces").get(0) is String) {
|
||||||
var ar = JSONArray()
|
val ar = JSONArray()
|
||||||
ar.put(0, JSONObject("""
|
ar.put(0, JSONObject("""
|
||||||
{
|
{
|
||||||
"Regex": ".*",
|
"Regex": ".*",
|
||||||
|
|
|
@ -57,7 +57,7 @@ class DnsActivity : AppCompatActivity() {
|
||||||
addServerButton.setOnClickListener {
|
addServerButton.setOnClickListener {
|
||||||
val view = inflater.inflate(R.layout.dialog_add_dns_server, null)
|
val view = inflater.inflate(R.layout.dialog_add_dns_server, null)
|
||||||
val input = view.findViewById<TextInputEditText>(R.id.addDnsInput)
|
val input = view.findViewById<TextInputEditText>(R.id.addDnsInput)
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.dns_add_server_dialog_title))
|
builder.setTitle(getString(R.string.dns_add_server_dialog_title))
|
||||||
builder.setView(view)
|
builder.setView(view)
|
||||||
builder.setPositiveButton(getString(R.string.add)) { _, _ ->
|
builder.setPositiveButton(getString(R.string.add)) { _, _ ->
|
||||||
|
@ -128,7 +128,7 @@ class DnsActivity : AppCompatActivity() {
|
||||||
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
||||||
|
|
||||||
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
|
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.dns_remove_title, server))
|
builder.setTitle(getString(R.string.dns_remove_title, server))
|
||||||
builder.setPositiveButton(getString(R.string.remove)) { dialog, _ ->
|
builder.setPositiveButton(getString(R.string.remove)) { dialog, _ ->
|
||||||
servers.removeAt(button.tag as Int)
|
servers.removeAt(button.tag as Int)
|
||||||
|
@ -176,7 +176,7 @@ class DnsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view.setOnLongClickListener {
|
view.setOnLongClickListener {
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.dns_server_info_dialog_title))
|
builder.setTitle(getString(R.string.dns_server_info_dialog_title))
|
||||||
builder.setMessage("${infoPair.first}\n\n${infoPair.second}")
|
builder.setMessage("${infoPair.first}\n\n${infoPair.second}")
|
||||||
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.core.app.NotificationCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
|
||||||
const val PREF_KEY_ENABLED = "enabled"
|
const val PREF_KEY_ENABLED = "enabled"
|
||||||
|
const val PREF_KEY_PEERS_NOTE = "peers_note"
|
||||||
const val MAIN_CHANNEL_ID = "Yggdrasil Service"
|
const val MAIN_CHANNEL_ID = "Yggdrasil Service"
|
||||||
|
|
||||||
class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
|
class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package eu.neilalexander.yggdrasil
|
package eu.neilalexander.yggdrasil
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.ContextThemeWrapper
|
||||||
import android.widget.Switch
|
import android.widget.Switch
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
@ -18,6 +21,7 @@ import eu.neilalexander.yggdrasil.PacketTunnelProvider.Companion.STATE_INTENT
|
||||||
import mobile.Mobile
|
import mobile.Mobile
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
|
||||||
|
const val APP_WEB_URL = "https://github.com/yggdrasil-network/yggdrasil-android"
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var enabledSwitch: Switch
|
private lateinit var enabledSwitch: Switch
|
||||||
|
@ -29,6 +33,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var dnsLabel: TextView
|
private lateinit var dnsLabel: TextView
|
||||||
private lateinit var dnsRow: LinearLayoutCompat
|
private lateinit var dnsRow: LinearLayoutCompat
|
||||||
private lateinit var settingsRow: LinearLayoutCompat
|
private lateinit var settingsRow: LinearLayoutCompat
|
||||||
|
private lateinit var versionRow: LinearLayoutCompat
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
val intent = Intent(this, PacketTunnelProvider::class.java)
|
val intent = Intent(this, PacketTunnelProvider::class.java)
|
||||||
|
@ -57,6 +62,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
dnsLabel = findViewById(R.id.dnsValue)
|
dnsLabel = findViewById(R.id.dnsValue)
|
||||||
dnsRow = findViewById(R.id.dnsTableRow)
|
dnsRow = findViewById(R.id.dnsTableRow)
|
||||||
settingsRow = findViewById(R.id.settingsTableRow)
|
settingsRow = findViewById(R.id.settingsTableRow)
|
||||||
|
versionRow = findViewById(R.id.versionTableRow)
|
||||||
|
|
||||||
enabledLabel.setTextColor(Color.GRAY)
|
enabledLabel.setTextColor(Color.GRAY)
|
||||||
|
|
||||||
|
@ -68,6 +74,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
startVpnActivity.launch(vpnIntent)
|
startVpnActivity.launch(vpnIntent)
|
||||||
} else {
|
} else {
|
||||||
start()
|
start()
|
||||||
|
enabledSwitch.isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false -> {
|
false -> {
|
||||||
|
@ -103,6 +110,11 @@ class MainActivity : AppCompatActivity() {
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
versionRow.isClickable = true
|
||||||
|
versionRow.setOnClickListener {
|
||||||
|
openUrlInBrowser(APP_WEB_URL)
|
||||||
|
}
|
||||||
|
|
||||||
ipAddressLabel.setOnLongClickListener {
|
ipAddressLabel.setOnLongClickListener {
|
||||||
val clipboard: ClipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard: ClipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText("ip", ipAddressLabel.text)
|
val clip = ClipData.newPlainText("ip", ipAddressLabel.text)
|
||||||
|
@ -151,15 +163,16 @@ class MainActivity : AppCompatActivity() {
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
when (intent.getStringExtra("type")) {
|
when (intent.getStringExtra("type")) {
|
||||||
"state" -> {
|
"state" -> {
|
||||||
enabledLabel.text = if (intent.getBooleanExtra("started", false)) {
|
val peerState = JSONArray(intent.getStringExtra("peers") ?: "[]")
|
||||||
var count = 0
|
var count = 0
|
||||||
if (intent.hasExtra("peers")) {
|
for (i in 0..<peerState.length()) {
|
||||||
val peers = intent.getStringExtra("peers")
|
val peer = peerState.getJSONObject(i)
|
||||||
if (peers != null && peers != "null") {
|
if (peer.getString("IP").isNotEmpty()) {
|
||||||
val peerState = JSONArray(peers)
|
count += 1
|
||||||
count = peerState.length()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enabledLabel.text = if (intent.getBooleanExtra("started", false)) {
|
||||||
|
showPeersNoteIfNeeded(peerState.length())
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
enabledLabel.setTextColor(Color.RED)
|
enabledLabel.setTextColor(Color.RED)
|
||||||
getString(R.string.main_no_connectivity)
|
getString(R.string.main_no_connectivity)
|
||||||
|
@ -174,15 +187,51 @@ class MainActivity : AppCompatActivity() {
|
||||||
ipAddressLabel.text = intent.getStringExtra("ip") ?: "N/A"
|
ipAddressLabel.text = intent.getStringExtra("ip") ?: "N/A"
|
||||||
subnetLabel.text = intent.getStringExtra("subnet") ?: "N/A"
|
subnetLabel.text = intent.getStringExtra("subnet") ?: "N/A"
|
||||||
if (intent.hasExtra("peers")) {
|
if (intent.hasExtra("peers")) {
|
||||||
val peerState = JSONArray(intent.getStringExtra("peers") ?: "[]")
|
peersLabel.text = when (count) {
|
||||||
peersLabel.text = when (val count = peerState.length()) {
|
|
||||||
0 -> getString(R.string.main_no_peers)
|
0 -> getString(R.string.main_no_peers)
|
||||||
1 -> getString(R.string.main_one_peer)
|
1 -> getString(R.string.main_one_peer)
|
||||||
else -> getString(R.string.main_many_peers, count)
|
else -> getString(R.string.main_many_peers, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!enabledSwitch.isEnabled) {
|
||||||
|
enabledSwitch.isEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPeersNoteIfNeeded(peerCount: Int) {
|
||||||
|
if (peerCount > 0) return
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this@MainActivity.baseContext)
|
||||||
|
if (!preferences.getBoolean(PREF_KEY_PEERS_NOTE, false)) {
|
||||||
|
this@MainActivity.runOnUiThread {
|
||||||
|
val builder: AlertDialog.Builder =
|
||||||
|
AlertDialog.Builder(ContextThemeWrapper(this@MainActivity, R.style.YggdrasilDialogs))
|
||||||
|
builder.setTitle(getString(R.string.main_add_some_peers_title))
|
||||||
|
builder.setMessage(getString(R.string.main_add_some_peers_message))
|
||||||
|
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
// Mark this note as shown
|
||||||
|
preferences.edit().apply {
|
||||||
|
putBoolean(PREF_KEY_PEERS_NOTE, true)
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openUrlInBrowser(url: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
data = Uri.parse(url)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
// Handle the exception if no browser is found
|
||||||
|
Toast.makeText(this, getText(R.string.no_browser_found_toast), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,11 @@ open class PacketTunnelProvider: VpnService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updater() {
|
private fun updater() {
|
||||||
|
try {
|
||||||
Thread.sleep(500)
|
Thread.sleep(500)
|
||||||
|
} catch (_: InterruptedException) {
|
||||||
|
return
|
||||||
|
}
|
||||||
var lastStateUpdate = System.currentTimeMillis()
|
var lastStateUpdate = System.currentTimeMillis()
|
||||||
updates@ while (started.get()) {
|
updates@ while (started.get()) {
|
||||||
val treeJSON = yggdrasil.treeJSON
|
val treeJSON = yggdrasil.treeJSON
|
||||||
|
|
|
@ -5,7 +5,6 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -13,13 +12,20 @@ import android.view.ContextThemeWrapper
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.EditText
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.Switch
|
||||||
|
import android.widget.TableLayout
|
||||||
|
import android.widget.TableRow
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
|
||||||
class PeersActivity : AppCompatActivity() {
|
class PeersActivity : AppCompatActivity() {
|
||||||
private lateinit var config: ConfigurationProxy
|
private lateinit var config: ConfigurationProxy
|
||||||
private lateinit var inflater: LayoutInflater
|
private lateinit var inflater: LayoutInflater
|
||||||
|
@ -99,7 +105,7 @@ class PeersActivity : AppCompatActivity() {
|
||||||
addPeerButton.setOnClickListener {
|
addPeerButton.setOnClickListener {
|
||||||
val view = inflater.inflate(R.layout.dialog_addpeer, null)
|
val view = inflater.inflate(R.layout.dialog_addpeer, null)
|
||||||
val input = view.findViewById<TextInputEditText>(R.id.addPeerInput)
|
val input = view.findViewById<TextInputEditText>(R.id.addPeerInput)
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.peers_add_peer))
|
builder.setTitle(getString(R.string.peers_add_peer))
|
||||||
builder.setView(view)
|
builder.setView(view)
|
||||||
builder.setPositiveButton(getString(R.string.peers_add)) { dialog, _ ->
|
builder.setPositiveButton(getString(R.string.peers_add)) { dialog, _ ->
|
||||||
|
@ -153,7 +159,7 @@ class PeersActivity : AppCompatActivity() {
|
||||||
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
||||||
|
|
||||||
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
|
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.peers_remove_title, peer))
|
builder.setTitle(getString(R.string.peers_remove_title, peer))
|
||||||
builder.setPositiveButton(getString(R.string.peers_remove)) { dialog, _ ->
|
builder.setPositiveButton(getString(R.string.peers_remove)) { dialog, _ ->
|
||||||
config.updateJSON { json ->
|
config.updateJSON { json ->
|
||||||
|
@ -180,16 +186,25 @@ class PeersActivity : AppCompatActivity() {
|
||||||
connectedTableLabel.text = getString(R.string.peers_no_connected_title)
|
connectedTableLabel.text = getString(R.string.peers_no_connected_title)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
connectedTableLayout.visibility = View.VISIBLE
|
var connected = false
|
||||||
connectedTableLabel.text = getString(R.string.peers_connected_title)
|
|
||||||
|
|
||||||
connectedTableLayout.removeAllViewsInLayout()
|
connectedTableLayout.removeAllViewsInLayout()
|
||||||
for (peer in peers) {
|
for (peer in peers) {
|
||||||
val view = inflater.inflate(R.layout.peers_connected, null)
|
val view = inflater.inflate(R.layout.peers_connected, null)
|
||||||
val ip = peer.getString("IP")
|
val ip = peer.getString("IP")
|
||||||
|
// Only connected peers have IPs
|
||||||
|
if (ip.isNotEmpty()) {
|
||||||
view.findViewById<TextView>(R.id.addressLabel).text = ip
|
view.findViewById<TextView>(R.id.addressLabel).text = ip
|
||||||
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("URI")
|
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("URI")
|
||||||
connectedTableLayout.addView(view)
|
connectedTableLayout.addView(view)
|
||||||
|
connected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connected) {
|
||||||
|
connectedTableLayout.visibility = View.VISIBLE
|
||||||
|
connectedTableLabel.text = getString(R.string.peers_connected_title)
|
||||||
|
} else {
|
||||||
|
connectedTableLayout.visibility = View.GONE
|
||||||
|
connectedTableLabel.text = getString(R.string.peers_no_connected_title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
private lateinit var deviceNameEntry: EditText
|
private lateinit var deviceNameEntry: EditText
|
||||||
private lateinit var publicKeyLabel: TextView
|
private lateinit var publicKeyLabel: TextView
|
||||||
private lateinit var resetConfigurationRow: LinearLayoutCompat
|
private lateinit var resetConfigurationRow: LinearLayoutCompat
|
||||||
|
private var publicKeyReset = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -69,7 +70,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
resetConfigurationRow.setOnClickListener {
|
resetConfigurationRow.setOnClickListener {
|
||||||
val view = inflater.inflate(R.layout.dialog_resetconfig, null)
|
val view = inflater.inflate(R.layout.dialog_resetconfig, null)
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
builder.setTitle(getString(R.string.settings_warning_title))
|
builder.setTitle(getString(R.string.settings_warning_title))
|
||||||
builder.setView(view)
|
builder.setView(view)
|
||||||
builder.setPositiveButton(getString(R.string.settings_reset)) { dialog, _ ->
|
builder.setPositiveButton(getString(R.string.settings_reset)) { dialog, _ ->
|
||||||
|
@ -85,12 +86,13 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
findViewById<View>(R.id.resetKeysRow).setOnClickListener {
|
findViewById<View>(R.id.resetKeysRow).setOnClickListener {
|
||||||
config.resetKeys()
|
config.resetKeys()
|
||||||
|
publicKeyReset = true
|
||||||
updateView()
|
updateView()
|
||||||
}
|
}
|
||||||
|
|
||||||
findViewById<View>(R.id.setKeysRow).setOnClickListener {
|
findViewById<View>(R.id.setKeysRow).setOnClickListener {
|
||||||
val view = inflater.inflate(R.layout.dialog_set_keys, null)
|
val view = inflater.inflate(R.layout.dialog_set_keys, null)
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
|
||||||
val privateKey = view.findViewById<EditText>(R.id.private_key)
|
val privateKey = view.findViewById<EditText>(R.id.private_key)
|
||||||
builder.setTitle(getString(R.string.set_keys))
|
builder.setTitle(getString(R.string.set_keys))
|
||||||
builder.setView(view)
|
builder.setView(view)
|
||||||
|
@ -125,7 +127,11 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
deviceNameEntry.setText("", TextView.BufferType.EDITABLE)
|
deviceNameEntry.setText("", TextView.BufferType.EDITABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKeyLabel.text = json.optString("PublicKey")
|
var key = json.optString("PrivateKey")
|
||||||
|
if (key.isNotEmpty()) {
|
||||||
|
key = key.substring(key.length / 2)
|
||||||
|
}
|
||||||
|
publicKeyLabel.text = key
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -145,7 +151,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
// To be able to get public key from running Yggdrasil we use this receiver, as we don't have this field in config
|
// To be able to get public key from running Yggdrasil we use this receiver, as we don't have this field in config
|
||||||
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
if (intent.hasExtra("pubkey")) {
|
if (intent.hasExtra("pubkey") && !publicKeyReset) {
|
||||||
val tree = intent.getStringExtra("pubkey")
|
val tree = intent.getStringExtra("pubkey")
|
||||||
if (tree != null && tree != "null") {
|
if (tree != null && tree != "null") {
|
||||||
publicKeyLabel.text = intent.getStringExtra("pubkey")
|
publicKeyLabel.text = intent.getStringExtra("pubkey")
|
||||||
|
|
|
@ -351,7 +351,9 @@
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat style="@style/SelectableItemStyle">
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/versionTableRow"
|
||||||
|
style="@style/SelectableItemStyle">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/versionLabel"
|
android:id="@+id/versionLabel"
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
<string name="main_no_peers">Нет пиров</string>
|
<string name="main_no_peers">Нет пиров</string>
|
||||||
<string name="main_one_peer">1 пир</string>
|
<string name="main_one_peer">1 пир</string>
|
||||||
<string name="main_many_peers">%d пира/пиров</string>
|
<string name="main_many_peers">%d пира/пиров</string>
|
||||||
|
<string name="main_add_some_peers_title">Внимание</string>
|
||||||
|
<string name="main_add_some_peers_message">Не настроено ни одного пира. Если не будет обнаруживаемых пиров в этой сети, то вам надо будет добавить пир вручную, чтобы подключение к Yggdrasil работало как положено.</string>
|
||||||
<string name="peers_add_peer">Добавить пира в конфиг</string>
|
<string name="peers_add_peer">Добавить пира в конфиг</string>
|
||||||
<string name="peers_add">Добавить</string>
|
<string name="peers_add">Добавить</string>
|
||||||
<string name="peers_remove_title">Убрать %s?</string>
|
<string name="peers_remove_title">Убрать %s?</string>
|
||||||
|
@ -83,4 +85,5 @@
|
||||||
<string name="private_key_label">Приватный ключ:</string>
|
<string name="private_key_label">Приватный ключ:</string>
|
||||||
<string name="set_keys">Установить свой ключ</string>
|
<string name="set_keys">Установить свой ключ</string>
|
||||||
<string name="save">Сохранить</string>
|
<string name="save">Сохранить</string>
|
||||||
|
<string name="no_browser_found_toast">Не найден браузер для открытия ссылки!</string>
|
||||||
</resources>
|
</resources>
|
|
@ -5,6 +5,7 @@
|
||||||
<color name="purple_700">#FF3700B3</color>
|
<color name="purple_700">#FF3700B3</color>
|
||||||
<color name="teal_200">#FF03DAC5</color>
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
<color name="teal_700">#FF018786</color>
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="green">#5FBF9F</color>
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
<color name="hintlight">#F2F1F5</color>
|
<color name="hintlight">#F2F1F5</color>
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
<string name="main_no_peers">No peers</string>
|
<string name="main_no_peers">No peers</string>
|
||||||
<string name="main_one_peer">1 peer</string>
|
<string name="main_one_peer">1 peer</string>
|
||||||
<string name="main_many_peers">%d peers</string>
|
<string name="main_many_peers">%d peers</string>
|
||||||
|
<string name="main_add_some_peers_title">Note</string>
|
||||||
|
<string name="main_add_some_peers_message">No peers are configured. If there are no multicast peers nearby, you will need to manually configure peers in order for Yggdrasil to connect and work properly.</string>
|
||||||
<string name="peers_add_peer">Add Configured Peer</string>
|
<string name="peers_add_peer">Add Configured Peer</string>
|
||||||
<string name="peers_add">Add</string>
|
<string name="peers_add">Add</string>
|
||||||
<string name="peers_remove_title">Remove %s?</string>
|
<string name="peers_remove_title">Remove %s?</string>
|
||||||
|
@ -83,4 +85,5 @@
|
||||||
<string name="private_key_label">Private key:</string>
|
<string name="private_key_label">Private key:</string>
|
||||||
<string name="set_keys">Set your own key</string>
|
<string name="set_keys">Set your own key</string>
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
|
<string name="no_browser_found_toast">No browser found to open the URL!</string>
|
||||||
</resources>
|
</resources>
|
|
@ -18,4 +18,8 @@
|
||||||
<item name="tableBackgroundColor">@color/white</item>
|
<item name="tableBackgroundColor">@color/white</item>
|
||||||
<item name="textDefault">@color/black</item>
|
<item name="textDefault">@color/black</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="YggdrasilDialogs" parent="@style/Theme.MaterialComponents.DayNight.Dialog">
|
||||||
|
<item name="colorPrimary">@color/green</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
|
@ -1,6 +1,6 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.9.10'
|
ext.kotlin_version = '1.9.20'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
23
fastlane/metadata/android/en-US/changelogs/19.txt
Normal file
23
fastlane/metadata/android/en-US/changelogs/19.txt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Updated core library to 0.5.9, presenting these changes:
|
||||||
|
|
||||||
|
Changed
|
||||||
|
The routing algorithm has been updated with RTT-aware link costing, which should prefer lower latency links over higher latency links where possible
|
||||||
|
The calculated cost is an average of the link RTT, but newly established links are costed higher to begin with, such that unstable peerings can be avoided
|
||||||
|
Link costs are only used where multiple next-hops are available and will be ignored if there is only one loop-free path to the destination
|
||||||
|
This is protocol-compatible with existing v0.5.x nodes but will have the best results when peering with nodes that are also running the latest version
|
||||||
|
The getPeers endpoint will now report the calculated link cost for each given peer
|
||||||
|
Upgrade dependencies
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
Multicast discovery should now work again when building Yggdrasil as an Android framework
|
||||||
|
Multicast discovery will now correctly ignore interfaces that are not marked as running
|
||||||
|
Ephemeral links, such as those added by multicast, will no longer try to reconnect in a fast loop, fixing a high CPU issue
|
||||||
|
The TUN interface will no longer stop working when hitting a segment read error from vectorised reads
|
||||||
|
The AllowedPublicKeys option will once again no longer apply to multicast peerings, as was originally intended
|
||||||
|
A potential panic when shutting down peering links has been fixed
|
||||||
|
A redundant system call for setting MTU on OpenBSD has been removed
|
||||||
|
|
||||||
|
Fixes in Android app
|
||||||
|
Fixed occasional crash on start/stop
|
||||||
|
Updated some dependencies
|
||||||
|
Updated Android API to 34
|
19
fastlane/metadata/android/en-US/changelogs/20.txt
Normal file
19
fastlane/metadata/android/en-US/changelogs/20.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Updated core library to 0.5.12, presenting these changes:
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
A timing regression which causes a higher level of idle protocol traffic on each peering has been fixed
|
||||||
|
|
||||||
|
Fixes in Android app
|
||||||
|
Some UI fixes and improvements
|
||||||
|
Updated some dependencies
|
||||||
|
|
||||||
|
Updates from previous versions:
|
||||||
|
|
||||||
|
Changed
|
||||||
|
The parent selection algorithm now only chooses a new parent if there is a larger cost benefit to doing so, which should help to stabilise the tree
|
||||||
|
The bloom filters are now repropagated periodically, to avoid nodes getting stuck with bad state
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
A memory leak caused by missed cleanup of the peer response map has been fixed
|
||||||
|
Other bug fixes with bloom filter propagation for off-tree filters and zero vs one bits
|
||||||
|
TLS-based peering connections now support TLS 1.2 again
|
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
23
fastlane/metadata/android/ru/changelogs/19.txt
Normal file
23
fastlane/metadata/android/ru/changelogs/19.txt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Обновлена основная библиотека до версии 0.5.9, в которой представлены следующие изменения:
|
||||||
|
|
||||||
|
Изменено
|
||||||
|
Алгоритм маршрутизации был обновлен с учетом стоимости соединения с RTT, что должно отдавать предпочтения соединениям с меньшей задержкой соединениям с большей задержкой, когда это возможно
|
||||||
|
Расчетная стоимость представляет собой среднее значение RTT соединения, но новые соединения изначально оцениваются выше, так что можно избежать проблем с нестабильными узлами
|
||||||
|
Стоимость соединения используется только при наличии нескольких следующих переходов и будет игнорироваться, если есть только один путь без петель к месту назначения
|
||||||
|
Эта версия совместима с существующими узлами v0.5.x, но будет иметь наилучшие результаты при пиринге с узлами, которые также работают под управлением последней версии
|
||||||
|
Команда getPeers теперь будет сообщать рассчитанную стоимость соединения для каждого заданного пира
|
||||||
|
Обновлены зависимости
|
||||||
|
|
||||||
|
Исправлено
|
||||||
|
Обнаружение локальных пиров теперь должно снова работать при сборке Yggdrasil как фреймворка Android
|
||||||
|
Обнаружение локальных пиров теперь будет правильно игнорировать интерфейсы, которые не помечены как работающие
|
||||||
|
Эфемерные соединения, такие как добавленные мультикастом, больше не будут пытаться быстро переподключаться в цикле, устранена проблема высокой загрузки ЦП
|
||||||
|
Интерфейс TUN больше не будет прекращать работу при срабатывании ошибки чтения пакета с помощью векторизованного чтения
|
||||||
|
Опция AllowedPublicKeys снова больше не будет применяться к локальным пирам, как изначально предполагалось
|
||||||
|
Потенциальный краш при отключении пиринговых соединений был исправлен
|
||||||
|
Избыточный системный вызов для установки MTU в OpenBSD был удален
|
||||||
|
|
||||||
|
Исправления в приложении для Android
|
||||||
|
Исправлен случайный сбой при запуске/остановке
|
||||||
|
Обновлены некоторые зависимости
|
||||||
|
Обновлен Android API до 34
|
19
fastlane/metadata/android/ru/changelogs/20.txt
Normal file
19
fastlane/metadata/android/ru/changelogs/20.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Обновлена основная библиотека до версии 0.5.12, в которой представлены следующие изменения:
|
||||||
|
|
||||||
|
Исправлено
|
||||||
|
Исправлена регрессия синхронизации, которая приводит к более высокому уровню служебного трафика в простое
|
||||||
|
|
||||||
|
Исправления в приложении Android
|
||||||
|
Исправления и улучшения пользовательского интерфейса
|
||||||
|
Обновлены некоторые зависимости
|
||||||
|
|
||||||
|
Обновления с предыдущих версий:
|
||||||
|
|
||||||
|
Изменено
|
||||||
|
Алгоритм выбора родителя теперь выбирает нового родителя только в том случае, если это дает большую экономическую выгоду, что должно помочь стабилизировать дерево
|
||||||
|
Фильтры Блума теперь периодически распространяются повторно, чтобы избежать застревания узлов в плохом состоянии
|
||||||
|
|
||||||
|
Исправлено
|
||||||
|
Утечка памяти, вызванная пропущенной очисткой карты ответов пиров
|
||||||
|
Другие исправления ошибок с распространением фильтра Блума для фильтров вне дерева
|
||||||
|
Пиринг с использованием TLS теперь снова поддерживают TLS 1.2
|
1
libs/yggdrasil-go
Submodule
1
libs/yggdrasil-go
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 213f72b8403ff55a5e38a0fa7d1cd0a093ac4666
|
21
readme.md
21
readme.md
|
@ -1,4 +1,23 @@
|
||||||
## build instructions
|
Yggdrasil Android
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6 network. It is lightweight, self-arranging, supported on multiple platforms and allows pretty much any IPv6-capable application to communicate securely with other Yggdrasil nodes. Yggdrasil does not require you to have IPv6 Internet connectivity - it also works over IPv4.
|
||||||
|
|
||||||
|
This app allows you to connect to Yggdrasil Network and use any service located in this network. It works as VPN service, but all your usual traffic will go trough your provider, not through Yggdrasil Network.
|
||||||
|
|
||||||
|
Also, it is not a goal of the Yggdrasil project to provide anonymity. Direct peers over the Internet will be able to see your IP address and may be able to use this information to determine your location or identity. Multicast-discovered peerings on the same network will typically expose your device MAC address. Other nodes on the network may be able to discern some information about which nodes you are peered with.
|
||||||
|
|
||||||
|
All traffic sent across the Yggdrasil network is encrypted end-to-end. Assuming that our crypto is solid, it cannot be decrypted or read by any intermediate nodes, and can only be decrypted by the recipient for which it was intended. However, please note that Yggdrasil has not been officially externally audited.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Get it on F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/eu.neilalexander.yggdrasil/)
|
||||||
|
|
||||||
|
Or get the APK from the [Releases Section](https://github.com/yggdrasil-network/yggdrasil-android/releases/latest).
|
||||||
|
|
||||||
|
## Build Instructions
|
||||||
|
|
||||||
* install gomobile
|
* install gomobile
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue