Compare commits

...

16 commits

Author SHA1 Message Date
Leo Heitmann Ruiz
222d9d90bc
Add icon for F-Droid (#79)
Some checks failed
Build / Build (push) Has been cancelled
2025-02-10 23:19:27 +00:00
Revertron
6ddc878fde
Prepared the 0.5.12 release. (#77)
* Prepared the 0.5.12 release.
2025-01-08 20:56:34 +01:00
Revertron
055a74ea69
UI improvements (#74)
* Fixed connected peers status in PeersActivity.
* Fixed display of new generated public key in SettingsActivity.
* Made more reliable display of connectivity state on main screen.
* Added a note about not configured peers. Changed all dialog buttons to greenish color.
* Click on a version row now opens URL of the project on GitHub.
* Changed the wording of no peer notification.
2025-01-08 20:13:56 +01:00
Poussinou
94db1facd7
Update readme.md (#41)
Signed-off-by: Poussinou <fravincent77@yahoo.fr>
Co-authored-by: Revertron <105154+Revertron@users.noreply.github.com>
2024-11-25 19:26:15 +01:00
Revertron
e211111d60
Updated release-notes for F-Droid. (#69) 2024-10-23 19:11:31 +02:00
Revertron
cdc12a8e7e
Prepare for release 0.5.9 (#68)
Updated the yggdrasil-go submodule. Fixed #64, updated some deps.
2024-10-23 18:10:01 +02:00
Revertron
34756b2193
Returned the yggdrasil-go submodule. (#63)
* Returned the yggdrasil-go submodule.
2024-08-19 00:07:45 +02:00
Revertron
060c096cdf
Preparation for release 0.5.7 (#62)
* Updated DNS.
* Removed tree info from main screen.
* Added a link to public peers site.
* Updated version and changelogs.
2024-08-09 00:24:22 +02:00
Neil Alexander
6a66960666
Version 0.1-017 (Yggdrasil 0.5.6) 2024-05-31 23:49:23 +01:00
Neil Alexander
a70563fba2
Check out Yggdrasil master branch instead of develop 2024-05-31 23:32:38 +01:00
Neil Alexander
ca45b37baa
Use peer count to track enabled/connected, remove unused submodule 2024-05-31 23:32:03 +01:00
Vasyl Gello
d1ebc977fd
Update core library and tag new version 0.1-016 (#53)
* Bump yggdrasil-go submodule to 0.5.4
* Version 0.1-016
2023-11-29 21:49:24 +01:00
Revertron
f164e05e5c
Updated graddle and some deps. (#54)
* Updated graddle and some deps.

* Updated Java version for CI (11 -> 17).
2023-11-29 21:42:06 +01:00
Revertron
8dfa2f638a
One fix and updates for F-Droid. (#50)
Fixed password getter (crashes for old configs).
Changed publishing material for F-Droid.
Incremented version.
2023-10-29 14:42:37 +01:00
Revertron
f4e1a75cfc
Changes for updating to Yggdrasil 0.5. (#49) 2023-10-28 23:31:32 +02:00
Revertron
9df80c0612
Fixed UI for Android TV (for d-pad usage). (#47) 2023-09-08 14:48:52 +02:00
43 changed files with 564 additions and 180 deletions

View file

@ -23,7 +23,7 @@ jobs:
with:
repository: yggdrasil-network/yggdrasil-go
path: yggdrasil-go
ref: develop
ref: master
fetch-depth: 0
- name: Setup Go environment
@ -34,10 +34,10 @@ jobs:
go install golang.org/x/mobile/cmd/gomobile@latest
~/go/bin/gomobile init
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle

View file

@ -4,15 +4,14 @@ plugins {
}
android {
compileSdkVersion 29
buildToolsVersion "30.0.3"
compileSdkVersion 34
defaultConfig {
applicationId "eu.neilalexander.yggdrasil"
minSdkVersion 21
targetSdkVersion 29
versionCode 13
versionName "0.1-013"
targetSdkVersion 34
versionCode 20
versionName "0.1-020"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -46,17 +45,18 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
namespace 'eu.neilalexander.yggdrasil'
}
dependencies {
implementation fileTree(include: ['*.aar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.preference:preference-ktx:1.1.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.preference:preference-ktx:1.2.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.neilalexander.yggdrasil">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<application
android:name=".GlobalApplication"
@ -23,8 +24,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SettingsActivity" android:parentActivityName=".MainActivity" />
<activity android:name=".PeersActivity" android:parentActivityName=".MainActivity" />
<activity android:name=".SettingsActivity" android:parentActivityName=".MainActivity" android:exported="false" />
<activity android:name=".PeersActivity" android:parentActivityName=".MainActivity" android:exported="false" />
<activity android:name=".DnsActivity" android:exported="false" />
<activity android:name=".TileServiceActivity" android:theme="@android:style/Theme.NoDisplay"
android:allowTaskReparenting="true"
@ -42,6 +43,7 @@
<service
android:name=".PacketTunnelProvider"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="systemExempted"
android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService" />

View file

@ -32,14 +32,12 @@ object ConfigurationProxy {
val newJson = JSONObject(String(Mobile.generateConfigJSON()))
updateJSON { json ->
json.put("PrivateKey", newJson.getString("PrivateKey"))
json.put("PublicKey", newJson.getString("PublicKey"))
}
}
fun setKeys(privateKey: String, publicKey: String) {
fun setKeys(privateKey: String) {
updateJSON { json ->
json.put("PrivateKey", privateKey)
json.put("PublicKey", publicKey)
}
}
@ -57,12 +55,13 @@ object ConfigurationProxy {
json.put("IfMTU", 65535)
if (json.getJSONArray("MulticastInterfaces").get(0) is String) {
var ar = JSONArray()
val ar = JSONArray()
ar.put(0, JSONObject("""
{
"Regex": ".*",
"Beacon": true,
"Listen": true
"Listen": true,
"Password": ""
}
""".trimIndent()))
json.put("MulticastInterfaces", ar)
@ -94,4 +93,12 @@ object ConfigurationProxy {
(json.getJSONArray("MulticastInterfaces").get(0) as JSONObject).put("Beacon", value)
}
}
var multicastPassword: String
get() = (json.getJSONArray("MulticastInterfaces").get(0) as JSONObject).optString("Password")
set(value) {
updateJSON { json ->
(json.getJSONArray("MulticastInterfaces").get(0) as JSONObject).put("Password", value)
}
}
}

View file

@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager
import com.google.android.material.textfield.TextInputEditText
const val KEY_DNS_SERVERS = "dns_servers"
const val KEY_DNS_VERSION = "dns_version"
const val KEY_ENABLE_CHROME_FIX = "enable_chrome_fix"
class DnsActivity : AppCompatActivity() {
@ -41,10 +42,10 @@ class DnsActivity : AppCompatActivity() {
val descriptionRevertron = getString(R.string.dns_server_info_revertron)
// Here we can add some other DNS servers in a future
defaultDnsServers = hashMapOf(
"302:7991::53" to Pair(getString(R.string.location_amsterdam), descriptionRevertron),
"302:db60::53" to Pair(getString(R.string.location_prague), descriptionRevertron),
"300:6223::53" to Pair(getString(R.string.location_bratislava), descriptionRevertron),
"301:1088::53" to Pair(getString(R.string.location_buffalo), descriptionRevertron),
"308:62:45:62::" to Pair(getString(R.string.location_amsterdam), descriptionRevertron),
"308:84:68:55::" to Pair(getString(R.string.location_frankfurt), descriptionRevertron),
"308:25:40:bd::" to Pair(getString(R.string.location_bratislava), descriptionRevertron),
"308:c8:48:45::" to Pair(getString(R.string.location_buffalo), descriptionRevertron),
)
serversTableLayout = findViewById(R.id.configuredDnsTableLayout)
@ -56,7 +57,7 @@ class DnsActivity : AppCompatActivity() {
addServerButton.setOnClickListener {
val view = inflater.inflate(R.layout.dialog_add_dns_server, null)
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.setView(view)
builder.setPositiveButton(getString(R.string.add)) { _, _ ->
@ -127,7 +128,7 @@ class DnsActivity : AppCompatActivity() {
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
builder.setTitle(getString(R.string.dns_remove_title, server))
builder.setPositiveButton(getString(R.string.remove)) { dialog, _ ->
servers.removeAt(button.tag as Int)
@ -175,7 +176,7 @@ class DnsActivity : AppCompatActivity() {
}
}
view.setOnLongClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
builder.setTitle(getString(R.string.dns_server_info_dialog_title))
builder.setMessage("${infoPair.first}\n\n${infoPair.second}")
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ ->

View file

@ -8,14 +8,16 @@ import android.os.Build
import android.service.quicksettings.TileService
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager
const val PREF_KEY_ENABLED = "enabled"
const val PREF_KEY_PEERS_NOTE = "peers_note"
const val MAIN_CHANNEL_ID = "Yggdrasil Service"
class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
private lateinit var config: ConfigurationProxy
private var currentState: State = State.Disabled
var updaterConnections: Int = 0
private var updaterConnections: Int = 0
override fun onCreate() {
super.onCreate()
@ -24,6 +26,7 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
callback.register()
val receiver = YggStateReceiver(this)
receiver.register(this)
migrateDnsServers(this)
}
fun subscribe() {
@ -47,8 +50,10 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
@RequiresApi(Build.VERSION_CODES.N)
override fun onStateChange(state: State) {
if (state != currentState) {
val componentName = ComponentName(this, YggTileService::class.java)
TileService.requestListeningState(this, componentName)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val componentName = ComponentName(this, YggTileService::class.java)
TileService.requestListeningState(this, componentName)
}
if (state != State.Disabled) {
val notification = createServiceNotification(this, state)
@ -62,13 +67,39 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
}
}
fun migrateDnsServers(context: Context) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
if (preferences.getInt(KEY_DNS_VERSION, 0) >= 1) {
return
}
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
if (serverString!!.isNotEmpty()) {
// Replacing old Revertron's servers by new ones
val newServers = serverString
.replace("300:6223::53", "308:25:40:bd::")
.replace("302:7991::53", "308:62:45:62::")
.replace("302:db60::53", "308:84:68:55::")
.replace("301:1088::53", "308:c8:48:45::")
val editor = preferences.edit()
editor.putInt(KEY_DNS_VERSION, 1)
if (newServers != serverString) {
editor.putString(KEY_DNS_SERVERS, newServers)
}
editor.apply()
}
}
fun createServiceNotification(context: Context, state: State): Notification {
createNotificationChannels(context)
val intent = Intent(context, MainActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, flags)
val text = when (state) {
State.Disabled -> context.getText(R.string.tile_disabled)
@ -91,7 +122,11 @@ fun createPermissionMissingNotification(context: Context): Notification {
val intent = Intent(context, MainActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, flags)
return NotificationCompat.Builder(context, MAIN_CHANNEL_ID)
.setShowWhen(false)

View file

@ -1,16 +1,19 @@
package eu.neilalexander.yggdrasil
import android.app.Activity
import android.app.AlertDialog
import android.content.*
import android.graphics.Color
import android.net.Uri
import android.net.VpnService
import android.os.Bundle
import android.view.ContextThemeWrapper
import android.widget.Switch
import android.widget.TableRow
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.content.edit
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
@ -18,18 +21,19 @@ import eu.neilalexander.yggdrasil.PacketTunnelProvider.Companion.STATE_INTENT
import mobile.Mobile
import org.json.JSONArray
const val APP_WEB_URL = "https://github.com/yggdrasil-network/yggdrasil-android"
class MainActivity : AppCompatActivity() {
private lateinit var enabledSwitch: Switch
private lateinit var enabledLabel: TextView
private lateinit var ipAddressLabel: TextView
private lateinit var subnetLabel: TextView
private lateinit var coordinatesLabel: TextView
private lateinit var peersLabel: TextView
private lateinit var peersRow: TableRow
private lateinit var peersRow: LinearLayoutCompat
private lateinit var dnsLabel: TextView
private lateinit var dnsRow: TableRow
private lateinit var settingsRow: TableRow
private lateinit var dnsRow: LinearLayoutCompat
private lateinit var settingsRow: LinearLayoutCompat
private lateinit var versionRow: LinearLayoutCompat
private fun start() {
val intent = Intent(this, PacketTunnelProvider::class.java)
@ -53,12 +57,12 @@ class MainActivity : AppCompatActivity() {
enabledLabel = findViewById(R.id.yggdrasilStatusLabel)
ipAddressLabel = findViewById(R.id.ipAddressValue)
subnetLabel = findViewById(R.id.subnetValue)
coordinatesLabel = findViewById(R.id.coordinatesValue)
peersLabel = findViewById(R.id.peersValue)
peersRow = findViewById(R.id.peersTableRow)
dnsLabel = findViewById(R.id.dnsValue)
dnsRow = findViewById(R.id.dnsTableRow)
settingsRow = findViewById(R.id.settingsTableRow)
versionRow = findViewById(R.id.versionTableRow)
enabledLabel.setTextColor(Color.GRAY)
@ -70,6 +74,7 @@ class MainActivity : AppCompatActivity() {
startVpnActivity.launch(vpnIntent)
} else {
start()
enabledSwitch.isEnabled = false
}
}
false -> {
@ -82,7 +87,7 @@ class MainActivity : AppCompatActivity() {
preferences.edit(commit = true) { putBoolean(PREF_KEY_ENABLED, isChecked) }
}
val enableYggdrasilPanel = findViewById<TableRow>(R.id.enableYggdrasilPanel)
val enableYggdrasilPanel = findViewById<LinearLayoutCompat>(R.id.enableYggdrasilPanel)
enableYggdrasilPanel.setOnClickListener {
enabledSwitch.toggle()
}
@ -105,6 +110,11 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
}
versionRow.isClickable = true
versionRow.setOnClickListener {
openUrlInBrowser(APP_WEB_URL)
}
ipAddressLabel.setOnLongClickListener {
val clipboard: ClipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("ip", ipAddressLabel.text)
@ -153,15 +163,16 @@ class MainActivity : AppCompatActivity() {
override fun onReceive(context: Context?, intent: Intent) {
when (intent.getStringExtra("type")) {
"state" -> {
enabledLabel.text = if (intent.getBooleanExtra("started", false)) {
var count = 0
if (intent.hasExtra("dht")) {
val dht = intent.getStringExtra("dht")
if (dht != null && dht != "null") {
val dhtState = JSONArray(dht)
count = dhtState.length()
}
val peerState = JSONArray(intent.getStringExtra("peers") ?: "[]")
var count = 0
for (i in 0..<peerState.length()) {
val peer = peerState.getJSONObject(i)
if (peer.getString("IP").isNotEmpty()) {
count += 1
}
}
enabledLabel.text = if (intent.getBooleanExtra("started", false)) {
showPeersNoteIfNeeded(peerState.length())
if (count == 0) {
enabledLabel.setTextColor(Color.RED)
getString(R.string.main_no_connectivity)
@ -175,17 +186,52 @@ class MainActivity : AppCompatActivity() {
}
ipAddressLabel.text = intent.getStringExtra("ip") ?: "N/A"
subnetLabel.text = intent.getStringExtra("subnet") ?: "N/A"
coordinatesLabel.text = intent.getStringExtra("coords") ?: "[]"
if (intent.hasExtra("peers")) {
val peerState = JSONArray(intent.getStringExtra("peers") ?: "[]")
peersLabel.text = when (val count = peerState.length()) {
peersLabel.text = when (count) {
0 -> getString(R.string.main_no_peers)
1 -> getString(R.string.main_one_peer)
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()
}
}
}

View file

@ -1,7 +1,8 @@
package eu.neilalexander.yggdrasil
import android.content.*
import android.content.Intent
import android.net.VpnService
import android.net.wifi.WifiManager
import android.os.Build
import android.os.ParcelFileDescriptor
import android.system.OsConstants
@ -42,6 +43,7 @@ open class PacketTunnelProvider: VpnService() {
private var parcel: ParcelFileDescriptor? = null
private var readerStream: FileInputStream? = null
private var writerStream: FileOutputStream? = null
private var multicastLock: WifiManager.MulticastLock? = null
override fun onCreate() {
super.onCreate()
@ -101,6 +103,13 @@ open class PacketTunnelProvider: VpnService() {
val notification = createServiceNotification(this, State.Enabled)
startForeground(SERVICE_NOTIFICATION_ID, notification)
// Acquire multicast lock
val wifi = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
multicastLock = wifi.createMulticastLock("Yggdrasil").apply {
setReferenceCounted(false)
acquire()
}
Log.d(TAG, config.getJSON().toString())
yggdrasil.startJSON(config.getJSONByteArray())
@ -163,16 +172,7 @@ open class PacketTunnelProvider: VpnService() {
updater()
}
var intent = Intent(STATE_INTENT)
intent.putExtra("type", "state")
intent.putExtra("started", true)
intent.putExtra("ip", yggdrasil.addressString)
intent.putExtra("subnet", yggdrasil.subnetString)
intent.putExtra("coords", yggdrasil.coordsString)
intent.putExtra("peers", yggdrasil.peersJSON)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
intent = Intent(YGG_STATE_INTENT)
var intent = Intent(YGG_STATE_INTENT)
intent.putExtra("state", STATE_ENABLED)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
@ -221,6 +221,7 @@ open class PacketTunnelProvider: VpnService() {
stopForeground(true)
stopSelf()
multicastLock?.release()
}
private fun connect() {
@ -231,27 +232,34 @@ open class PacketTunnelProvider: VpnService() {
}
private fun updater() {
try {
Thread.sleep(500)
} catch (_: InterruptedException) {
return
}
var lastStateUpdate = System.currentTimeMillis()
updates@ while (started.get()) {
val treeJSON = yggdrasil.treeJSON
if ((application as GlobalApplication).needUiUpdates()) {
val intent = Intent(STATE_INTENT)
intent.putExtra("type", "state")
intent.putExtra("started", true)
intent.putExtra("ip", yggdrasil.addressString)
intent.putExtra("subnet", yggdrasil.subnetString)
intent.putExtra("coords", yggdrasil.coordsString)
intent.putExtra("pubkey", yggdrasil.publicKeyString)
intent.putExtra("peers", yggdrasil.peersJSON)
intent.putExtra("dht", yggdrasil.dhtjson)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
val curTime = System.currentTimeMillis()
if (lastStateUpdate + 10000 < curTime) {
val intent = Intent(YGG_STATE_INTENT)
var state = STATE_ENABLED
val dht = yggdrasil.dhtjson
if (dht != null && dht != "null") {
val dhtState = JSONArray(dht)
val count = dhtState.length()
if (yggdrasil.routingEntries > 0) {
state = STATE_CONNECTED
}
if (treeJSON != null && treeJSON != "null") {
val treeState = JSONArray(treeJSON)
val count = treeState.length()
if (count > 1)
state = STATE_CONNECTED
}

View file

@ -5,17 +5,27 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.ContextThemeWrapper
import android.view.KeyEvent
import android.view.LayoutInflater
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.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.material.textfield.TextInputEditText
import org.json.JSONArray
import org.json.JSONObject
class PeersActivity : AppCompatActivity() {
private lateinit var config: ConfigurationProxy
private lateinit var inflater: LayoutInflater
@ -27,6 +37,7 @@ class PeersActivity : AppCompatActivity() {
private lateinit var configuredTableLabel: TextView
private lateinit var multicastListenSwitch: Switch
private lateinit var multicastBeaconSwitch: Switch
private lateinit var passwordEdit: EditText
private lateinit var addPeerButton: ImageButton
override fun onCreate(savedInstanceState: Bundle?) {
@ -43,6 +54,9 @@ class PeersActivity : AppCompatActivity() {
configuredTableLayout = findViewById(R.id.configuredPeersTableLayout)
configuredTableLabel = findViewById(R.id.configuredPeersLabel)
val discoveryLink = findViewById<TextView>(R.id.peers_discovery_link)
discoveryLink.movementMethod = LinkMovementMethod.getInstance()
multicastListenSwitch = findViewById(R.id.enableMulticastListen)
multicastListenSwitch.setOnCheckedChangeListener { button, _ ->
config.multicastListen = button.isChecked
@ -62,12 +76,36 @@ class PeersActivity : AppCompatActivity() {
multicastListenPanel.setOnClickListener {
multicastListenSwitch.toggle()
}
passwordEdit = findViewById(R.id.passwordEdit)
passwordEdit.setText(config.multicastPassword)
passwordEdit.doOnTextChanged { text, _, _, _ ->
config.multicastPassword = text.toString()
}
passwordEdit.setOnKeyListener { _, keyCode, _ ->
(keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER)
}
findViewById<View>(R.id.passwordTableRow).setOnKeyListener { _, keyCode, event ->
Log.i("Key", keyCode.toString())
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
passwordEdit.requestFocus()
true
} else {
false
}
} else {
false
}
}
addPeerButton = findViewById(R.id.addPeerButton)
addPeerButton.setOnClickListener {
val view = inflater.inflate(R.layout.dialog_addpeer, null)
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.setView(view)
builder.setPositiveButton(getString(R.string.peers_add)) { dialog, _ ->
@ -121,7 +159,7 @@ class PeersActivity : AppCompatActivity() {
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.YggdrasilDialogs))
builder.setTitle(getString(R.string.peers_remove_title, peer))
builder.setPositiveButton(getString(R.string.peers_remove)) { dialog, _ ->
config.updateJSON { json ->
@ -148,16 +186,25 @@ class PeersActivity : AppCompatActivity() {
connectedTableLabel.text = getString(R.string.peers_no_connected_title)
}
else -> {
connectedTableLayout.visibility = View.VISIBLE
connectedTableLabel.text = getString(R.string.peers_connected_title)
var connected = false
connectedTableLayout.removeAllViewsInLayout()
for (peer in peers) {
val view = inflater.inflate(R.layout.peers_connected, null)
val ip = peer.getString("IP")
view.findViewById<TextView>(R.id.addressLabel).text = ip
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("Remote")
connectedTableLayout.addView(view)
// Only connected peers have IPs
if (ip.isNotEmpty()) {
view.findViewById<TextView>(R.id.addressLabel).text = ip
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("URI")
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)
}
}
}
@ -168,7 +215,9 @@ class PeersActivity : AppCompatActivity() {
when (intent.getStringExtra("type")) {
"state" -> {
if (intent.hasExtra("peers")) {
val peersArray = JSONArray(intent.getStringExtra("peers") ?: "[]")
val peers1 = intent.getStringExtra("peers")
//Log.i("PeersActivity", "Peers json: $peers1")
val peersArray = JSONArray(peers1 ?: "[]")
val array = Array(peersArray.length()) { i ->
peersArray.getJSONObject(i)
}

View file

@ -1,15 +1,23 @@
package eu.neilalexander.yggdrasil
import android.app.AlertDialog
import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.ContextThemeWrapper
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.widget.*
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.widget.doOnTextChanged
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import org.json.JSONObject
class SettingsActivity : AppCompatActivity() {
@ -18,7 +26,8 @@ class SettingsActivity : AppCompatActivity() {
private lateinit var deviceNameEntry: EditText
private lateinit var publicKeyLabel: TextView
private lateinit var resetConfigurationRow: TableRow
private lateinit var resetConfigurationRow: LinearLayoutCompat
private var publicKeyReset = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -41,9 +50,27 @@ class SettingsActivity : AppCompatActivity() {
}
}
deviceNameEntry.setOnKeyListener { view, keyCode, event ->
(keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER)
}
findViewById<View>(R.id.deviceNameTableRow).setOnKeyListener { view, keyCode, event ->
Log.i("Key", keyCode.toString())
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
deviceNameEntry.requestFocus()
true
} else {
false
}
} else {
false
}
}
resetConfigurationRow.setOnClickListener {
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.setView(view)
builder.setPositiveButton(getString(R.string.settings_reset)) { dialog, _ ->
@ -59,18 +86,18 @@ class SettingsActivity : AppCompatActivity() {
findViewById<View>(R.id.resetKeysRow).setOnClickListener {
config.resetKeys()
publicKeyReset = true
updateView()
}
findViewById<View>(R.id.setKeysRow).setOnClickListener {
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 publicKey = view.findViewById<EditText>(R.id.public_key)
builder.setTitle(getString(R.string.set_keys))
builder.setView(view)
builder.setPositiveButton(getString(R.string.save)) { dialog, _ ->
config.setKeys(privateKey.text.toString(), publicKey.text.toString())
config.setKeys(privateKey.text.toString())
updateView()
dialog.dismiss()
}
@ -92,13 +119,44 @@ class SettingsActivity : AppCompatActivity() {
}
private fun updateView() {
val nodeinfo = config.getJSON().optJSONObject("NodeInfo")
val json = config.getJSON()
val nodeinfo = json.optJSONObject("NodeInfo")
if (nodeinfo != null) {
deviceNameEntry.setText(nodeinfo.getString("name"), TextView.BufferType.EDITABLE)
} else {
deviceNameEntry.setText("", TextView.BufferType.EDITABLE)
}
publicKeyLabel.text = config.getJSON().getString("PublicKey")
var key = json.optString("PrivateKey")
if (key.isNotEmpty()) {
key = key.substring(key.length / 2)
}
publicKeyLabel.text = key
}
override fun onResume() {
super.onResume()
LocalBroadcastManager.getInstance(this).registerReceiver(
receiver, IntentFilter(PacketTunnelProvider.STATE_INTENT)
)
(application as GlobalApplication).subscribe()
}
override fun onPause() {
super.onPause()
(application as GlobalApplication).unsubscribe()
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
}
// 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() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.hasExtra("pubkey") && !publicKeyReset) {
val tree = intent.getStringExtra("pubkey")
if (tree != null && tree != "null") {
publicKeyLabel.text = intent.getStringExtra("pubkey")
}
}
}
}
}

View file

@ -223,6 +223,7 @@
android:layout_marginStart="16pt"
android:layout_marginLeft="16pt"
android:layout_marginTop="4pt"
android:layout_marginBottom="4pt"
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:alpha="0.7"

View file

@ -52,8 +52,9 @@
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<TableLayout
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/connectedPeersTableLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8pt"
@ -70,7 +71,7 @@
android:paddingBottom="2pt"
android:showDividers="middle">
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/enableYggdrasilPanel"
style="@style/SelectableSwitchItemStyle">
@ -92,9 +93,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow style="@style/SelectableItemStyle">
<androidx.appcompat.widget.LinearLayoutCompat style="@style/SelectableItemStyle">
<TextView
android:id="@+id/yggdrasilStatusLabel"
@ -106,16 +107,16 @@
android:textSize="14sp"
android:textStyle="bold" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
</TableLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
<Space
android:layout_width="match_parent"
android:layout_height="32px" />
<TextView
android:id="@+id/statisticsSectionLabel"
android:id="@+id/networkInfoSectionLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16pt"
@ -125,13 +126,14 @@
android:layout_marginBottom="2pt"
android:alpha="0.7"
android:paddingRight="8pt"
android:text="@string/main_statistics"
android:text="@string/main_network_info"
android:textAllCaps="true"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<TableLayout
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/configuredPeersTableLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8pt"
@ -148,7 +150,7 @@
android:paddingBottom="2pt"
android:showDividers="middle">
<TableRow style="@style/SelectableItemStyle">
<androidx.appcompat.widget.LinearLayoutCompat style="@style/SelectableItemStyle">
<TextView
android:id="@+id/ipAddressLabel"
@ -172,9 +174,9 @@
android:textAlignment="viewEnd"
android:textIsSelectable="true"
android:textSize="14sp" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow style="@style/SelectableItemStyle">
<androidx.appcompat.widget.LinearLayoutCompat style="@style/SelectableItemStyle">
<TextView
android:id="@+id/subnetLabel"
@ -199,35 +201,9 @@
android:textIsSelectable="true"
android:textSize="14sp" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow style="@style/SelectableItemStyle">
<TextView
android:id="@+id/coordinatesLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/main_coordinates"
android:textColor="?attr/textDefault" />
<TextView
android:id="@+id/coordinatesValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:alpha="0.5"
android:ellipsize="end"
android:maxLines="1"
android:scrollHorizontally="false"
android:selectAllOnFocus="true"
android:singleLine="true"
android:text="[]"
android:textAlignment="viewEnd"
android:textIsSelectable="true" />
</TableRow>
</TableLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
<Space
android:layout_width="match_parent"
@ -249,8 +225,9 @@
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<TableLayout
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/configurationTableLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8pt"
@ -258,7 +235,6 @@
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:background="@drawable/rounded"
android:clickable="true"
android:divider="#46878787"
android:dividerPadding="4pt"
android:paddingLeft="4pt"
@ -267,7 +243,7 @@
android:paddingBottom="2pt"
android:showDividers="middle">
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/peersTableRow"
style="@style/SelectableItemStyle">
@ -302,9 +278,9 @@
android:scaleX="1.2"
android:scaleY="1.2"
app:srcCompat="@drawable/ic_baseline_chevron_right_24" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/dnsTableRow"
style="@style/SelectableItemStyle">
@ -339,9 +315,9 @@
android:scaleX="1.2"
android:scaleY="1.2"
app:srcCompat="@drawable/ic_baseline_chevron_right_24" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/settingsTableRow"
style="@style/SelectableItemStyle">
@ -373,9 +349,11 @@
android:scaleY="1.2"
app:srcCompat="@drawable/ic_baseline_chevron_right_24" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow style="@style/SelectableItemStyle">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/versionTableRow"
style="@style/SelectableItemStyle">
<TextView
android:id="@+id/versionLabel"
@ -397,9 +375,9 @@
android:text="@string/main_unknown"
android:textAlignment="textEnd" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
</TableLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
<TextView
android:layout_width="match_parent"
@ -407,6 +385,7 @@
android:layout_marginStart="16pt"
android:layout_marginLeft="16pt"
android:layout_marginTop="4pt"
android:layout_marginBottom="4pt"
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:alpha="0.7"

View file

@ -137,6 +137,23 @@
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<TextView
android:id="@+id/peers_discovery_link"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16pt"
android:layout_marginLeft="16pt"
android:layout_marginTop="4pt"
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:layout_marginBottom="4pt"
android:alpha="0.7"
android:paddingRight="8pt"
android:text="@string/peers_discovery_hint"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<Space
android:layout_width="match_parent"
android:layout_height="32px" />
@ -217,6 +234,37 @@
</TableRow>
<TableRow
android:id="@+id/passwordTableRow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:paddingStart="4pt"
android:paddingTop="4pt"
android:paddingEnd="4pt"
android:paddingBottom="4pt">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/peers_multicast_password_hint"
android:textColor="?attr/textDefault" />
<EditText
android:id="@+id/passwordEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:ems="10"
android:hint="@string/tap_to_edit"
android:inputType="textVisiblePassword"
android:padding="0pt"
android:textAlignment="textEnd"
android:textSize="14sp" />
</TableRow>
</TableLayout>
<TextView
@ -225,6 +273,7 @@
android:layout_marginStart="16pt"
android:layout_marginLeft="16pt"
android:layout_marginTop="4pt"
android:layout_marginBottom="4pt"
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:alpha="0.7"

View file

@ -173,6 +173,7 @@
android:id="@+id/publicKeyLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:fontFamily="monospace"
android:text="@string/public_key"
android:textSize="14sp" />
@ -216,8 +217,9 @@
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="12sp" />
<TableLayout
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/backupTableLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8pt"
@ -225,7 +227,6 @@
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:background="@drawable/rounded"
android:clickable="true"
android:divider="#46878787"
android:dividerPadding="4pt"
android:paddingLeft="4pt"
@ -234,7 +235,7 @@
android:paddingBottom="2pt"
android:showDividers="middle">
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/resetKeysRow"
style="@style/SelectableItemStyle">
@ -242,9 +243,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/regenerate_keys" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/setKeysRow"
style="@style/SelectableItemStyle">
@ -252,9 +253,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/set_keys" />
</TableRow>
</androidx.appcompat.widget.LinearLayoutCompat>
<TableRow
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/resetConfigurationRow"
style="@style/SelectableItemStyle">
@ -263,15 +264,16 @@
android:layout_height="wrap_content"
android:text="@string/reset_configuration"
android:textColor="@android:color/holo_red_dark" />
</TableRow>
</TableLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16pt"
android:layout_marginLeft="16pt"
android:layout_marginTop="2pt"
android:layout_marginTop="4pt"
android:layout_marginBottom="4pt"
android:layout_marginEnd="8pt"
android:layout_marginRight="8pt"
android:alpha="0.7"

View file

@ -12,15 +12,6 @@
android:paddingTop="4pt"
android:paddingBottom="4pt">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/public_key_label" />
<EditText
android:id="@+id/public_key"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -29,6 +29,8 @@
<string name="main_no_peers">Нет пиров</string>
<string name="main_one_peer">1 пир</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">Добавить</string>
<string name="peers_remove_title">Убрать %s?</string>
@ -42,11 +44,10 @@
<string name="settings_reset">Сброс</string>
<string name="main_status">Состояние</string>
<string name="main_enable_yggdrasil">Включить Yggdrasil</string>
<string name="main_statistics">Статистика</string>
<string name="main_network_info">Адрес и сеть</string>
<string name="main_not_available">Н</string>
<string name="main_ip">Адрес</string>
<string name="main_subnet">Подсеть</string>
<string name="main_coordinates">Координаты</string>
<string name="main_configuration">Конфигурация</string>
<string name="main_peers">Пиры</string>
<string name="main_dns_servers">Серверы DNS</string>
@ -58,7 +59,9 @@
<string name="discoverable_over_multicast">Находимый через multicast</string>
<string name="search_for_multicast_peers">Искать пиров через multicast</string>
<string name="configured_peers_hint">Yggdrasil будет пытаться подключаться к этим пирам автоматически. Если вы добавите несколько пиров, ваше устройство может быть использовано для переноса данных между другими узлами сети. Чтобы этого избежать настройте только один пир.</string>
<string name="peer_connectivity_hint">Пиры могут быть найдены с помощью Multicast если они находятся в той же Wi-Fi сети, либо через USB. Трафик в мобильной сети может быть платным. Вы можете отключить мобильные данные в настройках устройства.</string>
<string name="peers_discovery_hint">Вы можете найти публичные пиры <a href="https://publicpeers.neilalexander.dev/">по этой ссылке</a>.</string>
<string name="peer_connectivity_hint">Пиры могут быть найдены с помощью Multicast если они находятся в той же Wi-Fi сети, либо через USB. У них должен быть одинаковый пароль. Трафик в мобильной сети может быть платным. Вы можете отключить мобильные данные в настройках устройства.</string>
<string name="peers_multicast_password_hint">Пароль</string>
<string name="node_info">Об узле</string>
<string name="device_name">Название устройства</string>
<string name="tap_to_edit">Нажмите для изменения</string>
@ -72,15 +75,15 @@
<string name="tile_enabled">Включено (Нет подключения)</string>
<string name="tile_connected">Подключено</string>
<string name="location_amsterdam">Амстердам, Нидерланды</string>
<string name="location_prague">Прага, Чехия</string>
<string name="location_frankfurt">Франкфурт, Германия</string>
<string name="location_bratislava">Братислава, Словакия</string>
<string name="location_buffalo">Баффало, США</string>
<string name="location_buffalo">Буффало, США</string>
<string name="channel_name">Сервис VPN</string>
<string name="channel_description">Главный канал нотификаций сервиса</string>
<string name="permission_notification_text">Нажмите здесь чтобы включить Yggdrasil.</string>
<string name="add_peer_help">Введите полный URI пира для добавления. Yggdrasil будет автоматически подключаться к нему при запуске.</string>
<string name="public_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="no_browser_found_toast">Не найден браузер для открытия ссылки!</string>
</resources>

View file

@ -5,6 +5,7 @@
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="green">#5FBF9F</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="hintlight">#F2F1F5</color>

View file

@ -29,6 +29,8 @@
<string name="main_no_peers">No peers</string>
<string name="main_one_peer">1 peer</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">Add</string>
<string name="peers_remove_title">Remove %s?</string>
@ -42,11 +44,10 @@
<string name="settings_reset">Reset</string>
<string name="main_status">Status</string>
<string name="main_enable_yggdrasil">Enable Yggdrasil</string>
<string name="main_statistics">Statistics</string>
<string name="main_network_info">Network info</string>
<string name="main_not_available">N/A</string>
<string name="main_ip">IP</string>
<string name="main_subnet">Subnet</string>
<string name="main_coordinates">Coordinates</string>
<string name="main_configuration">Configuration</string>
<string name="main_peers">Peers</string>
<string name="main_dns_servers">DNS servers</string>
@ -58,7 +59,9 @@
<string name="discoverable_over_multicast">Discoverable over multicast</string>
<string name="search_for_multicast_peers">Search for multicast peers</string>
<string name="configured_peers_hint">Yggdrasil will automatically attempt to connect to configured peers when started. If you configure more than one peer, your device may carry traffic on behalf of other network nodes. Avoid this by configuring only a single peer.</string>
<string name="peer_connectivity_hint">Multicast peers will be discovered on the same Wi-Fi network or via USB. Data charges may apply when using mobile data. You can prevent data usage in the device settings.</string>
<string name="peers_discovery_hint">You can find public peers by opening <a href="https://publicpeers.neilalexander.dev/">this link</a>.</string>
<string name="peer_connectivity_hint">Multicast peers will be discovered on the same Wi-Fi network or via USB. They must have the same password. Data charges may apply when using mobile data. You can prevent data usage in the device settings.</string>
<string name="peers_multicast_password_hint">Password</string>
<string name="node_info">Node Info</string>
<string name="device_name">Device Name</string>
<string name="tap_to_edit">Tap to edit</string>
@ -72,15 +75,15 @@
<string name="tile_enabled">Enabled (No connectivity)</string>
<string name="tile_connected">Connected</string>
<string name="location_amsterdam">Amsterdam, NL</string>
<string name="location_prague">Prague, CZ</string>
<string name="location_frankfurt">Frankfurt, DE</string>
<string name="location_bratislava">Bratislava, SK</string>
<string name="location_buffalo">Buffalo, US</string>
<string name="channel_name">VPN Service</string>
<string name="channel_description">Main channel for foreground notification</string>
<string name="permission_notification_text">Tap here to enable Yggdrasil.</string>
<string name="add_peer_help">Enter the full URI of the peer to add. Yggdrasil will automatically connect to this peer when started.</string>
<string name="public_key_label">Public key:</string>
<string name="private_key_label">Private key:</string>
<string name="set_keys">Set your own keys</string>
<string name="set_keys">Set your own key</string>
<string name="save">Save</string>
<string name="no_browser_found_toast">No browser found to open the URL!</string>
</resources>

View file

@ -18,4 +18,8 @@
<item name="tableBackgroundColor">@color/white</item>
<item name="textDefault">@color/black</item>
</style>
<style name="YggdrasilDialogs" parent="@style/Theme.MaterialComponents.DayNight.Dialog">
<item name="colorPrimary">@color/green</item>
</style>
</resources>

View file

@ -1,12 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.7.20'
ext.kotlin_version = '1.9.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.2"
classpath 'com.android.tools.build:gradle:8.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View file

@ -0,0 +1,3 @@
* Updated core Yggdrasil library to 0.5.1
* Updated UI to reflect changes in new version
* Fixed small bugs in UI

View file

@ -0,0 +1 @@
* Updated core Yggdrasil library to 0.5.4

View file

@ -0,0 +1 @@
* Updated core Yggdrasil library to 0.5.6

View file

@ -0,0 +1,14 @@
Updated core library to 0.5.7, presenting these changes:
Added
WebSocket support for peerings, by using the new ws:// scheme in Listen and Peers
Additionally, the wss:// scheme can be used to connect to a WebSocket peer behind a HTTPS reverse proxy
Changed
On Linux, the TUN adapter now uses vectorised reads/writes where possible, which should reduce the amount of CPU time spent on syscalls and potentially improve throughput
Link error handling has been improved and various link error messages have been rewritten to be clearer
Upgrade dependencies
Fixed
Multiple multicast connections to the same remote machine should now work correctly
You may get two connections in some cases, one inbound and one outbound, this is known and will not cause problems

View 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

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

@ -0,0 +1,3 @@
* Обновлена библиотека Yggdrasil до 0.5.1
* Обновлен интерфейс в соответствии с новой версией библиотеки
* Сделаны небольшие исправления в интерфейсе

View file

@ -0,0 +1 @@
* Обновлена библиотека Yggdrasil до 0.5.4

View file

@ -0,0 +1 @@
* Обновлена библиотека Yggdrasil до 0.5.6

View file

@ -0,0 +1,14 @@
Обновлена основная библиотека до версии 0.5.7, в которой представлены следующие изменения:
Добавлено
Поддержка WebSocket для пиринга с использованием новой схемы ws:// в Listen и Peers
Кроме того, схему wss:// можно использовать для подключения к узлам WebSocket за обратным прокси-сервером HTTPS вроде Nginx
Изменено
В Linux адаптер TUN теперь использует векторизованную чтение/запись, где это возможно, что должно сократить количество времени ЦП, затрачиваемого на системные вызовы, и потенциально повысить пропускную способность
Улучшена обработка ошибок соединения, а различные сообщения об ошибках соединения были переписаны для большей ясности
Обновление зависимостей
Исправлено
Несколько мультикаст подключений к одной и той же удаленной машине теперь должны работать правильно
В некоторых случаях вы можете получить два подключения, одно входящее и одно исходящее, это известное поведение и не вызовет проблем

View file

@ -0,0 +1,23 @@
Обновлена основная библиотека до версии 0.5.9, в которой представлены следующие изменения:
Изменено
Алгоритм маршрутизации был обновлен с учетом стоимости соединения с RTT, что должно отдавать предпочтения соединениям с меньшей задержкой соединениям с большей задержкой, когда это возможно
Расчетная стоимость представляет собой среднее значение RTT соединения, но новые соединения изначально оцениваются выше, так что можно избежать проблем с нестабильными узлами
Стоимость соединения используется только при наличии нескольких следующих переходов и будет игнорироваться, если есть только один путь без петель к месту назначения
Эта версия совместима с существующими узлами v0.5.x, но будет иметь наилучшие результаты при пиринге с узлами, которые также работают под управлением последней версии
Команда getPeers теперь будет сообщать рассчитанную стоимость соединения для каждого заданного пира
Обновлены зависимости
Исправлено
Обнаружение локальных пиров теперь должно снова работать при сборке Yggdrasil как фреймворка Android
Обнаружение локальных пиров теперь будет правильно игнорировать интерфейсы, которые не помечены как работающие
Эфемерные соединения, такие как добавленные мультикастом, больше не будут пытаться быстро переподключаться в цикле, устранена проблема высокой загрузки ЦП
Интерфейс TUN больше не будет прекращать работу при срабатывании ошибки чтения пакета с помощью векторизованного чтения
Опция AllowedPublicKeys снова больше не будет применяться к локальным пирам, как изначально предполагалось
Потенциальный краш при отключении пиринговых соединений был исправлен
Избыточный системный вызов для установки MTU в OpenBSD был удален
Исправления в приложении для Android
Исправлен случайный сбой при запуске/остановке
Обновлены некоторые зависимости
Обновлен Android API до 34

View file

@ -0,0 +1,19 @@
Обновлена основная библиотека до версии 0.5.12, в которой представлены следующие изменения:
Исправлено
Исправлена регрессия синхронизации, которая приводит к более высокому уровню служебного трафика в простое
Исправления в приложении Android
Исправления и улучшения пользовательского интерфейса
Обновлены некоторые зависимости
Обновления с предыдущих версий:
Изменено
Алгоритм выбора родителя теперь выбирает нового родителя только в том случае, если это дает большую экономическую выгоду, что должно помочь стабилизировать дерево
Фильтры Блума теперь периодически распространяются повторно, чтобы избежать застревания узлов в плохом состоянии
Исправлено
Утечка памяти, вызванная пропущенной очисткой карты ответов пиров
Другие исправления ошибок с распространением фильтра Блума для фильтров вне дерева
Пиринг с использованием TLS теперь снова поддерживают TLS 1.2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

View file

@ -16,4 +16,8 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.code.style=official
#android.enableR8.fullMode=false
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View file

@ -1,6 +1,6 @@
#Mon Jun 14 15:11:35 BST 2021
#Mon Nov 27 01:27:23 CET 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

@ -1 +1 @@
Subproject commit 14f1cd4696a37b0f7fdcb067fac337c46953f8af
Subproject commit 213f72b8403ff55a5e38a0fa7d1cd0a093ac4666

View file

@ -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