Merge branch 'main' into patch-2
6
.github/workflows/android.yml
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -4,15 +4,14 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "30.0.3"
|
||||
compileSdkVersion 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "eu.neilalexander.yggdrasil"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 12
|
||||
versionName "0.1-012"
|
||||
targetSdkVersion 34
|
||||
versionCode 19
|
||||
versionName "0.1-019"
|
||||
|
||||
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'
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +60,8 @@ object ConfigurationProxy {
|
|||
{
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
@ -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.Theme_MaterialComponents_DayNight_Dialog))
|
||||
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.Theme_MaterialComponents_DayNight_Dialog))
|
||||
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, _ ->
|
||||
|
|
|
@ -8,6 +8,7 @@ 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 MAIN_CHANNEL_ID = "Yggdrasil Service"
|
||||
|
@ -15,7 +16,7 @@ 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 +25,7 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
|
|||
callback.register()
|
||||
val receiver = YggStateReceiver(this)
|
||||
receiver.register(this)
|
||||
migrateDnsServers(this)
|
||||
}
|
||||
|
||||
fun subscribe() {
|
||||
|
@ -47,8 +49,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 +66,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 +121,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)
|
||||
|
|
|
@ -6,11 +6,11 @@ import android.graphics.Color
|
|||
import android.net.VpnService
|
||||
import android.os.Bundle
|
||||
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
|
||||
|
@ -24,12 +24,11 @@ class MainActivity : AppCompatActivity() {
|
|||
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 fun start() {
|
||||
val intent = Intent(this, PacketTunnelProvider::class.java)
|
||||
|
@ -53,7 +52,6 @@ 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)
|
||||
|
@ -70,6 +68,7 @@ class MainActivity : AppCompatActivity() {
|
|||
startVpnActivity.launch(vpnIntent)
|
||||
} else {
|
||||
start()
|
||||
enabledSwitch.isEnabled = false
|
||||
}
|
||||
}
|
||||
false -> {
|
||||
|
@ -82,7 +81,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()
|
||||
}
|
||||
|
@ -155,11 +154,11 @@ class MainActivity : AppCompatActivity() {
|
|||
"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()
|
||||
if (intent.hasExtra("peers")) {
|
||||
val peers = intent.getStringExtra("peers")
|
||||
if (peers != null && peers != "null") {
|
||||
val peerState = JSONArray(peers)
|
||||
count = peerState.length()
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
|
@ -175,7 +174,6 @@ 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()) {
|
||||
|
@ -184,6 +182,9 @@ class MainActivity : AppCompatActivity() {
|
|||
else -> getString(R.string.main_many_peers, count)
|
||||
}
|
||||
}
|
||||
if (!enabledSwitch.isEnabled) {
|
||||
enabledSwitch.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
@ -282,9 +290,11 @@ open class PacketTunnelProvider: VpnService() {
|
|||
val writerStream = writerStream
|
||||
val writerThread = writerThread
|
||||
if (writerThread == null || writerStream == null) {
|
||||
Log.i(TAG, "Write thread or stream is null")
|
||||
break@writes
|
||||
}
|
||||
if (Thread.currentThread().isInterrupted || !writerStream.fd.valid()) {
|
||||
Log.i(TAG, "Write thread interrupted or file descriptor is invalid")
|
||||
break@writes
|
||||
}
|
||||
try {
|
||||
|
@ -293,6 +303,12 @@ open class PacketTunnelProvider: VpnService() {
|
|||
writerStream.write(buf, 0, len.toInt())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.i(TAG, "Error in write: $e")
|
||||
if (e.toString().contains("ENOBUFS")) {
|
||||
//TODO Check this by some error code
|
||||
//More info about this: https://github.com/AdguardTeam/AdguardForAndroid/issues/724
|
||||
continue
|
||||
}
|
||||
break@writes
|
||||
}
|
||||
}
|
||||
|
@ -308,15 +324,18 @@ open class PacketTunnelProvider: VpnService() {
|
|||
val readerStream = readerStream
|
||||
val readerThread = readerThread
|
||||
if (readerThread == null || readerStream == null) {
|
||||
Log.i(TAG, "Read thread or stream is null")
|
||||
break@reads
|
||||
}
|
||||
if (Thread.currentThread().isInterrupted ||!readerStream.fd.valid()) {
|
||||
Log.i(TAG, "Read thread interrupted or file descriptor is invalid")
|
||||
break@reads
|
||||
}
|
||||
try {
|
||||
val n = readerStream.read(b)
|
||||
yggdrasil.sendBuffer(b, n.toLong())
|
||||
} catch (e: Exception) {
|
||||
Log.i(TAG, "Error in sendBuffer: $e")
|
||||
break@reads
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@ 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 androidx.core.widget.doOnTextChanged
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import org.json.JSONArray
|
||||
|
@ -27,6 +31,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 +48,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,6 +70,30 @@ 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 {
|
||||
|
@ -121,7 +153,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.Theme_MaterialComponents_DayNight_Dialog))
|
||||
builder.setTitle(getString(R.string.peers_remove_title, peer))
|
||||
builder.setPositiveButton(getString(R.string.peers_remove)) { dialog, _ ->
|
||||
config.updateJSON { json ->
|
||||
|
@ -156,7 +188,7 @@ class PeersActivity : AppCompatActivity() {
|
|||
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")
|
||||
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("URI")
|
||||
connectedTableLayout.addView(view)
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +200,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)
|
||||
}
|
||||
|
|
|
@ -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,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||
|
||||
private lateinit var deviceNameEntry: EditText
|
||||
private lateinit var publicKeyLabel: TextView
|
||||
private lateinit var resetConfigurationRow: TableRow
|
||||
private lateinit var resetConfigurationRow: LinearLayoutCompat
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -41,6 +49,24 @@ 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))
|
||||
|
@ -66,11 +92,10 @@ class SettingsActivity : AppCompatActivity() {
|
|||
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 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 +117,40 @@ 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")
|
||||
publicKeyLabel.text = json.optString("PublicKey")
|
||||
}
|
||||
|
||||
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")) {
|
||||
val tree = intent.getStringExtra("pubkey")
|
||||
if (tree != null && tree != "null") {
|
||||
publicKeyLabel.text = intent.getStringExtra("pubkey")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,9 @@
|
|||
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 style="@style/SelectableItemStyle">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/versionLabel"
|
||||
|
@ -397,9 +373,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 +383,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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -42,11 +42,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 +57,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 +73,14 @@
|
|||
<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>
|
||||
</resources>
|
|
@ -42,11 +42,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 +57,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 +73,14 @@
|
|||
<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>
|
||||
</resources>
|
|
@ -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
|
||||
|
|
3
fastlane/metadata/android/en-US/changelogs/15.txt
Normal 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
|
1
fastlane/metadata/android/en-US/changelogs/16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
* Updated core Yggdrasil library to 0.5.4
|
1
fastlane/metadata/android/en-US/changelogs/17.txt
Normal file
|
@ -0,0 +1 @@
|
|||
* Updated core Yggdrasil library to 0.5.6
|
14
fastlane/metadata/android/en-US/changelogs/18.txt
Normal 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
|
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
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 32 KiB |
3
fastlane/metadata/android/ru/changelogs/15.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
* Обновлена библиотека Yggdrasil до 0.5.1
|
||||
* Обновлен интерфейс в соответствии с новой версией библиотеки
|
||||
* Сделаны небольшие исправления в интерфейсе
|
1
fastlane/metadata/android/ru/changelogs/16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
* Обновлена библиотека Yggdrasil до 0.5.4
|
1
fastlane/metadata/android/ru/changelogs/17.txt
Normal file
|
@ -0,0 +1 @@
|
|||
* Обновлена библиотека Yggdrasil до 0.5.6
|
14
fastlane/metadata/android/ru/changelogs/18.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
Обновлена основная библиотека до версии 0.5.7, в которой представлены следующие изменения:
|
||||
|
||||
Добавлено
|
||||
Поддержка WebSocket для пиринга с использованием новой схемы ws:// в Listen и Peers
|
||||
Кроме того, схему wss:// можно использовать для подключения к узлам WebSocket за обратным прокси-сервером HTTPS вроде Nginx
|
||||
|
||||
Изменено
|
||||
В Linux адаптер TUN теперь использует векторизованную чтение/запись, где это возможно, что должно сократить количество времени ЦП, затрачиваемого на системные вызовы, и потенциально повысить пропускную способность
|
||||
Улучшена обработка ошибок соединения, а различные сообщения об ошибках соединения были переписаны для большей ясности
|
||||
Обновление зависимостей
|
||||
|
||||
Исправлено
|
||||
Несколько мультикаст подключений к одной и той же удаленной машине теперь должны работать правильно
|
||||
В некоторых случаях вы можете получить два подключения, одно входящее и одно исходящее, это известное поведение и не вызовет проблем
|
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
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 37 KiB |
|
@ -17,3 +17,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
#android.enableR8.fullMode=false
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -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 0b9c8bd020f971847604e7e1f0e0cfc66e65da49
|