mirror of
https://github.com/yggdrasil-network/crispa-android.git
synced 2024-11-12 21:41:05 +00:00
1. implemented peers list. iteration#1
This commit is contained in:
parent
9a2f658515
commit
108bb009bb
@ -29,8 +29,16 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ndkVersion "21.2.6472646"
|
ndkVersion "21.2.6472646"
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
task ndkBuild(type: Exec) {
|
task ndkBuild(type: Exec) {
|
||||||
def rootDir = project.rootDir
|
def rootDir = project.rootDir
|
||||||
workingDir = new File(rootDir,"yggdrasil")
|
workingDir = new File(rootDir,"yggdrasil")
|
||||||
@ -41,17 +49,21 @@ gradle.projectsEvaluated {
|
|||||||
tasks.compileDebugKotlin.dependsOn(ndkBuild)
|
tasks.compileDebugKotlin.dependsOn(ndkBuild)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation project(path: ':yggdrasil')
|
implementation project(path: ':yggdrasil')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
|
||||||
|
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
||||||
implementation 'org.hjson:hjson:3.0.0'
|
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
|
implementation 'com.hbb20:ccp:2.4.0'
|
||||||
|
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||||
}
|
}
|
@ -2,8 +2,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="io.github.chronosx88.yggdrasil">
|
package="io.github.chronosx88.yggdrasil">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@ -12,20 +14,28 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
<activity
|
||||||
|
android:name=".PeerListActivity"
|
||||||
|
android:label="@string/title_activity_peer_list"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".YggdrasilTunService"
|
android:name=".YggdrasilTunService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService"/>
|
<action android:name="android.net.VpnService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,56 +1,89 @@
|
|||||||
package io.github.chronosx88.yggdrasil
|
package io.github.chronosx88.yggdrasil
|
||||||
|
|
||||||
import android.R.attr
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.RadioGroup
|
import android.view.Gravity
|
||||||
import android.widget.TextView
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.*
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
const val COMMAND = "COMMAND"
|
||||||
|
const val STOP = "STOP"
|
||||||
|
const val START = "START"
|
||||||
const val PARAM_PINTENT = "pendingIntent"
|
const val PARAM_PINTENT = "pendingIntent"
|
||||||
const val STATUS_START = 1
|
const val STATUS_START = 7
|
||||||
const val STATUS_FINISH = 0
|
const val STATUS_FINISH = 8
|
||||||
|
const val STATUS_STOP = 9
|
||||||
const val IPv6: String = "IPv6"
|
const val IPv6: String = "IPv6"
|
||||||
|
const val PEERS: String = "PEERS"
|
||||||
|
const val PEER_LIST_CODE = 1000
|
||||||
|
const val PEER_LIST = "PEERS_LIST"
|
||||||
|
const val CURRENT_PEERS = "CURRENT_PEERS"
|
||||||
|
const val START_VPN = "START_VPN"
|
||||||
private const val TAG="Yggdrasil"
|
private const val TAG="Yggdrasil"
|
||||||
private const val VPN_REQUEST_CODE = 0x0F
|
private const val VPN_REQUEST_CODE = 0x0F
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var startVpnFlag = false
|
||||||
|
private var currentPeers = arrayListOf<String>()
|
||||||
|
private var isStarted = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
val connectRadioGroup = findViewById<RadioGroup>(R.id.connectRadioGroup)
|
val listView = findViewById<ListView>(R.id.peers)
|
||||||
connectRadioGroup.setOnCheckedChangeListener { group, checkedId ->
|
//save to shared preferences
|
||||||
when (checkedId) {
|
val preferences =
|
||||||
R.id.disconnectButton -> stopVpn()
|
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||||
R.id.connectButton -> startVpn()
|
currentPeers = ArrayList(preferences.getStringSet(CURRENT_PEERS, HashSet())!!)
|
||||||
else -> { // Note the block
|
if(currentPeers.size==0) {
|
||||||
|
currentPeers.add("tcp://194.177.21.156:5066")
|
||||||
}
|
currentPeers.add("tcp://46.151.26.194:60575")
|
||||||
}
|
currentPeers.add("tcp://188.226.125.64:54321")
|
||||||
|
}
|
||||||
|
val adapter = ArrayAdapter(this, R.layout.peers_list_item, currentPeers)
|
||||||
|
listView.adapter = adapter
|
||||||
|
val editBeersButton = findViewById<Button>(R.id.edit)
|
||||||
|
editBeersButton.setOnClickListener {
|
||||||
|
val intent = Intent(this, PeerListActivity::class.java)
|
||||||
|
intent.putStringArrayListExtra(PEER_LIST, currentPeers)
|
||||||
|
startActivityForResult(intent, PEER_LIST_CODE)
|
||||||
|
}
|
||||||
|
if(intent.extras!==null) {
|
||||||
|
startVpnFlag = intent.extras!!.getBoolean(START_VPN, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopVpn(){
|
private fun stopVpn(){
|
||||||
Log.d(TAG,"Stop")
|
Log.d(TAG,"Stop")
|
||||||
val intent = Intent(this, YggdrasilTunService::class.java)
|
val intent = Intent(this, YggdrasilTunService::class.java)
|
||||||
intent.putExtra("COMMAND", "STOP")
|
val TASK_CODE = 100
|
||||||
|
val pi = createPendingResult(TASK_CODE, intent, 0)
|
||||||
|
intent.putExtra(PARAM_PINTENT, pi)
|
||||||
|
intent.putExtra(COMMAND, STOP)
|
||||||
startService(intent)
|
startService(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startVpn(){
|
private fun startVpn(){
|
||||||
Log.d(TAG,"Start")
|
Log.d(TAG,"Start")
|
||||||
|
val ipLayout = findViewById<LinearLayout>(R.id.ipLayout)
|
||||||
|
ipLayout.visibility = View.VISIBLE
|
||||||
val intent= VpnService.prepare(this)
|
val intent= VpnService.prepare(this)
|
||||||
if (intent!=null){
|
if (intent!=null){
|
||||||
startActivityForResult(intent, VPN_REQUEST_CODE);
|
startActivityForResult(intent, VPN_REQUEST_CODE)
|
||||||
}else{
|
}else{
|
||||||
onActivityResult(VPN_REQUEST_CODE, Activity.RESULT_OK, null);
|
onActivityResult(VPN_REQUEST_CODE, Activity.RESULT_OK, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,20 +92,83 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (requestCode == VPN_REQUEST_CODE && resultCode== Activity.RESULT_OK){
|
if (requestCode == VPN_REQUEST_CODE && resultCode== Activity.RESULT_OK){
|
||||||
val intent = Intent(this, YggdrasilTunService::class.java)
|
val intent = Intent(this, YggdrasilTunService::class.java)
|
||||||
val TASK_CODE = 100
|
val TASK_CODE = 100
|
||||||
var pi = createPendingResult(TASK_CODE, intent, 0);
|
val pi = createPendingResult(TASK_CODE, intent, 0)
|
||||||
intent.putExtra("COMMAND", "START")
|
|
||||||
intent.putExtra(PARAM_PINTENT, pi)
|
intent.putExtra(PARAM_PINTENT, pi)
|
||||||
|
intent.putExtra(COMMAND, START)
|
||||||
|
intent.putStringArrayListExtra(PEERS, currentPeers)
|
||||||
startService(intent)
|
startService(intent)
|
||||||
}
|
}
|
||||||
|
if (requestCode == PEER_LIST_CODE && resultCode== Activity.RESULT_OK){
|
||||||
|
if(data!!.extras!=null){
|
||||||
|
var currentPeers = data.extras!!.getStringArrayList(PEER_LIST)
|
||||||
|
if(currentPeers==null || currentPeers.size==0){
|
||||||
|
val text = "No peers selected!"
|
||||||
|
val duration = Toast.LENGTH_SHORT
|
||||||
|
val toast = Toast.makeText(applicationContext, text, duration)
|
||||||
|
toast.setGravity(Gravity.CENTER, 0, 0)
|
||||||
|
toast.show()
|
||||||
|
} else {
|
||||||
|
val adapter = ArrayAdapter(this, R.layout.peers_list_item, currentPeers)
|
||||||
|
val listView = findViewById<ListView>(R.id.peers)
|
||||||
|
listView.adapter = adapter
|
||||||
|
this.currentPeers = currentPeers
|
||||||
|
//save to shared preferences
|
||||||
|
val preferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||||
|
preferences.edit().putStringSet(CURRENT_PEERS, HashSet(this.currentPeers)).apply()
|
||||||
|
if(isStarted){
|
||||||
|
//apply peer changes
|
||||||
|
stopVpn()
|
||||||
|
val i = baseContext.packageManager
|
||||||
|
.getLaunchIntentForPackage(baseContext.packageName)
|
||||||
|
i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
i!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
i.putExtra(START_VPN, true)
|
||||||
|
startActivity(i)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
when (resultCode) {
|
when (resultCode) {
|
||||||
STATUS_START -> print("service started")
|
STATUS_START -> print("service started")
|
||||||
STATUS_FINISH -> {
|
STATUS_FINISH -> {
|
||||||
val result: String = data!!.getStringExtra(IPv6)
|
val result: String = data!!.getStringExtra(IPv6)
|
||||||
findViewById<TextView>(R.id.ip).setText(result)
|
findViewById<TextView>(R.id.ip).text = result
|
||||||
|
isStarted = true
|
||||||
|
}
|
||||||
|
STATUS_STOP -> {
|
||||||
|
isStarted = false
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
else -> { // Note the block
|
else -> { // Note the block
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
menuInflater.inflate(R.menu.main_menu, menu)
|
||||||
|
val item = menu.findItem(R.id.switchId) as MenuItem
|
||||||
|
item.setActionView(R.layout.menu_switch)
|
||||||
|
val switchOn = item
|
||||||
|
.actionView.findViewById<Switch>(R.id.switchOn)
|
||||||
|
if(startVpnFlag){
|
||||||
|
switchOn.isChecked = true
|
||||||
|
startVpnFlag = false
|
||||||
|
startVpn()
|
||||||
|
} else {
|
||||||
|
switchOn.isChecked = false
|
||||||
|
}
|
||||||
|
switchOn.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
|
if (isChecked) {
|
||||||
|
startVpn()
|
||||||
|
} else {
|
||||||
|
stopVpn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
package io.github.chronosx88.yggdrasil
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.ListView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.hbb20.CountryCodePicker
|
||||||
|
import io.github.chronosx88.yggdrasil.models.config.PeerInfo
|
||||||
|
import io.github.chronosx88.yggdrasil.models.config.PeerInfoListAdapter
|
||||||
|
import java.net.Inet4Address
|
||||||
|
|
||||||
|
|
||||||
|
class PeerListActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val peers = arrayListOf(
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("194.177.21.156"),
|
||||||
|
5066,
|
||||||
|
"RU",
|
||||||
|
CountryCodePicker.Language.RUSSIAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("46.151.26.194"),
|
||||||
|
60575,
|
||||||
|
"RU",
|
||||||
|
CountryCodePicker.Language.RUSSIAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("188.226.125.64"),
|
||||||
|
54321,
|
||||||
|
"RU",
|
||||||
|
CountryCodePicker.Language.RUSSIAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("88.201.129.205"),
|
||||||
|
8777,
|
||||||
|
"RU",
|
||||||
|
CountryCodePicker.Language.RUSSIAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("45.11.19.26"),
|
||||||
|
5001,
|
||||||
|
"DE",
|
||||||
|
CountryCodePicker.Language.GERMAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("82.165.69.111"),
|
||||||
|
61216,
|
||||||
|
"DE",
|
||||||
|
CountryCodePicker.Language.GERMAN
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("104.248.15.125"),
|
||||||
|
31337,
|
||||||
|
"US",
|
||||||
|
CountryCodePicker.Language.ENGLISH
|
||||||
|
),
|
||||||
|
PeerInfo(
|
||||||
|
"tcp",
|
||||||
|
Inet4Address.getByName("108.175.10.127"),
|
||||||
|
61216,
|
||||||
|
"US",
|
||||||
|
CountryCodePicker.Language.ENGLISH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_peer_list)
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
|
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
|
||||||
|
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("Action", null).show()
|
||||||
|
}
|
||||||
|
var extras = intent.getExtras()
|
||||||
|
var peerList = findViewById<ListView>(R.id.peerList)
|
||||||
|
if (extras != null) {
|
||||||
|
var currentPeers = extras.getStringArrayList(MainActivity.PEER_LIST)!!
|
||||||
|
var adapter = PeerInfoListAdapter(this, peers, currentPeers)
|
||||||
|
peerList.adapter = adapter
|
||||||
|
} else {
|
||||||
|
var adapter = PeerInfoListAdapter(this, peers, ArrayList())
|
||||||
|
peerList.adapter = adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
menuInflater.inflate(R.menu.save_peers, menu)
|
||||||
|
val item = menu.findItem(R.id.saveItem) as MenuItem
|
||||||
|
item.setActionView(R.layout.menu_save)
|
||||||
|
val saveButton = item
|
||||||
|
.actionView.findViewById<Button>(R.id.saveButton)
|
||||||
|
saveButton.setOnClickListener {
|
||||||
|
val result = Intent(this, MainActivity::class.java)
|
||||||
|
var adapter = findViewById<ListView>(R.id.peerList).adapter as PeerInfoListAdapter
|
||||||
|
val selectedPeers = adapter.getSelectedPeers()
|
||||||
|
if(selectedPeers.size>0) {
|
||||||
|
result.putExtra(MainActivity.PEER_LIST, adapter.getSelectedPeers())
|
||||||
|
setResult(Activity.RESULT_OK, result)
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
val text = "Select at least one peer"
|
||||||
|
val duration = Toast.LENGTH_SHORT
|
||||||
|
val toast = Toast.makeText(applicationContext, text, duration)
|
||||||
|
toast.setGravity(Gravity.CENTER, 0, 0)
|
||||||
|
toast.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,9 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
class YggdrasilTunService : VpnService() {
|
class YggdrasilTunService : VpnService() {
|
||||||
|
|
||||||
|
private lateinit var ygg: Yggdrasil
|
||||||
|
private var isClosed = false
|
||||||
|
|
||||||
/** Maximum packet size is constrained by the MTU, which is given as a signed short. */
|
/** Maximum packet size is constrained by the MTU, which is given as a signed short. */
|
||||||
private val MAX_PACKET_SIZE = Short.MAX_VALUE.toInt()
|
private val MAX_PACKET_SIZE = Short.MAX_VALUE.toInt()
|
||||||
|
|
||||||
@ -33,26 +36,29 @@ class YggdrasilTunService : VpnService() {
|
|||||||
private lateinit var writeCoroutine: CoroutineContext
|
private lateinit var writeCoroutine: CoroutineContext
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (intent?.getStringExtra("COMMAND") == "STOP") {
|
|
||||||
stopVpn()
|
if (intent?.getStringExtra(MainActivity.COMMAND) == MainActivity.STOP) {
|
||||||
}
|
|
||||||
if (intent?.getStringExtra("COMMAND") == "START") {
|
|
||||||
val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT)
|
val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT)
|
||||||
setupTunInterface(pi)
|
stopVpn(pi)
|
||||||
|
}
|
||||||
|
if (intent?.getStringExtra(MainActivity.COMMAND) == MainActivity.START) {
|
||||||
|
val peers = intent.getStringArrayListExtra(MainActivity.PEERS)
|
||||||
|
val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT)
|
||||||
|
ygg = Yggdrasil()
|
||||||
|
setupTunInterface(pi, peers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onStartCommand(intent, flags, startId);
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTunInterface(pi: PendingIntent) {
|
private fun setupTunInterface(pi: PendingIntent, peers: ArrayList<String>) {
|
||||||
pi.send(MainActivity.STATUS_START);
|
pi.send(MainActivity.STATUS_START)
|
||||||
val builder = Builder()
|
val builder = Builder()
|
||||||
val ygg = Yggdrasil()
|
|
||||||
var configJson = Mobile.generateConfigJSON()
|
var configJson = Mobile.generateConfigJSON()
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
|
|
||||||
var config = gson.fromJson(String(configJson), Map::class.java).toMutableMap()
|
var config = gson.fromJson(String(configJson), Map::class.java).toMutableMap()
|
||||||
config = fixConfig(config)
|
config = fixConfig(config, peers)
|
||||||
configJson = gson.toJson(config).toByteArray()
|
configJson = gson.toJson(config).toByteArray()
|
||||||
|
|
||||||
yggConduitEndpoint = ygg.startJSON(configJson)
|
yggConduitEndpoint = ygg.startJSON(configJson)
|
||||||
@ -67,13 +73,14 @@ class YggdrasilTunService : VpnService() {
|
|||||||
tunInputStream = FileInputStream(tunInterface!!.fileDescriptor)
|
tunInputStream = FileInputStream(tunInterface!!.fileDescriptor)
|
||||||
tunOutputStream = FileOutputStream(tunInterface!!.fileDescriptor)
|
tunOutputStream = FileOutputStream(tunInterface!!.fileDescriptor)
|
||||||
readCoroutine = GlobalScope.launch {
|
readCoroutine = GlobalScope.launch {
|
||||||
var buffer = ByteArray(2048)
|
val buffer = ByteArray(2048)
|
||||||
while (true) {
|
try{
|
||||||
try{
|
while (true) {
|
||||||
readPacketsFromTun(buffer)
|
readPacketsFromTun(buffer)
|
||||||
} catch (e: IOException){
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
} catch (e: IOException){
|
||||||
|
e.printStackTrace()
|
||||||
|
tunInputStream!!.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeCoroutine = GlobalScope.launch {
|
writeCoroutine = GlobalScope.launch {
|
||||||
@ -85,11 +92,8 @@ class YggdrasilTunService : VpnService() {
|
|||||||
pi.send(this, MainActivity.STATUS_FINISH, intent)
|
pi.send(this, MainActivity.STATUS_FINISH, intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fixConfig(config: MutableMap<Any?, Any?>): MutableMap<Any?, Any?> {
|
private fun fixConfig(config: MutableMap<Any?, Any?>, peers: ArrayList<String>): MutableMap<Any?, Any?> {
|
||||||
val peers = arrayListOf<String>();
|
|
||||||
peers.add("tcp://194.177.21.156:5066")
|
|
||||||
peers.add("tcp://46.151.26.194:60575")
|
|
||||||
peers.add("tcp://188.226.125.64:54321")
|
|
||||||
val whiteList = arrayListOf<String>()
|
val whiteList = arrayListOf<String>()
|
||||||
whiteList.add("")
|
whiteList.add("")
|
||||||
val blackList = arrayListOf<String>()
|
val blackList = arrayListOf<String>()
|
||||||
@ -120,11 +124,11 @@ class YggdrasilTunService : VpnService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun readPacketsFromTun(buffer: ByteArray) {
|
private fun readPacketsFromTun(buffer: ByteArray) {
|
||||||
if(tunInputStream != null) {
|
if(!isClosed) {
|
||||||
// Read the outgoing packet from the input stream.
|
// Read the outgoing packet from the input stream.
|
||||||
var length = tunInputStream!!.read(buffer)
|
val length = tunInputStream!!.read(buffer)
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
var byteBuffer = ByteBuffer.allocate(length);
|
val byteBuffer = ByteBuffer.allocate(length)
|
||||||
byteBuffer.put(buffer, 0, length)
|
byteBuffer.put(buffer, 0, length)
|
||||||
yggConduitEndpoint.send(byteBuffer.array())
|
yggConduitEndpoint.send(byteBuffer.array())
|
||||||
} else {
|
} else {
|
||||||
@ -140,12 +144,19 @@ class YggdrasilTunService : VpnService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopVpn() {
|
private fun stopVpn(pi: PendingIntent) {
|
||||||
readCoroutine.cancel()
|
isClosed = true;
|
||||||
writeCoroutine.cancel()
|
readCoroutine!!.cancel()
|
||||||
|
writeCoroutine!!.cancel()
|
||||||
|
tunInputStream!!.close()
|
||||||
|
tunOutputStream!!.close()
|
||||||
tunInterface!!.close()
|
tunInterface!!.close()
|
||||||
tunInterface = null
|
tunInterface = null
|
||||||
stopSelf()
|
//this hack due to https://github.com/yggdrasil-network/yggdrasil-go/issues/714 bug
|
||||||
|
ygg.startAutoconfigure()
|
||||||
|
ygg.stop()
|
||||||
|
val intent: Intent = Intent()
|
||||||
|
pi.send(this, MainActivity.STATUS_STOP, intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package io.github.chronosx88.yggdrasil.models.config
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.hbb20.CCPCountry
|
||||||
|
import com.hbb20.CountryCodePicker
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
|
|
||||||
|
class PeerInfo {
|
||||||
|
constructor(schema: String, address: InetAddress, port: Int, countryCode: String, language: CountryCodePicker.Language){
|
||||||
|
this.schema = schema
|
||||||
|
this.address = address
|
||||||
|
this.port = port
|
||||||
|
this.countryCode = countryCode
|
||||||
|
this.language = language
|
||||||
|
}
|
||||||
|
var schema: String
|
||||||
|
var address: InetAddress
|
||||||
|
var port = 0
|
||||||
|
var countryCode: String
|
||||||
|
var language: CountryCodePicker.Language
|
||||||
|
var ping: Float = Float.MAX_VALUE
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return this.schema+":/"+address.toString()+":"+port
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCountry(context: Context): CCPCountry? {
|
||||||
|
return CCPCountry.getCountryForNameCodeFromLibraryMasterList(context, language, countryCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package io.github.chronosx88.yggdrasil.models.config
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import io.github.chronosx88.yggdrasil.R
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|
||||||
|
class PeerInfoListAdapter(
|
||||||
|
context: Context,
|
||||||
|
allPeers: List<PeerInfo>,
|
||||||
|
currentPeers: ArrayList<String>
|
||||||
|
) : ArrayAdapter<PeerInfo?> (context, 0, allPeers) {
|
||||||
|
|
||||||
|
private val mContext: Context = context
|
||||||
|
private var allPeers: List<PeerInfo> = allPeers
|
||||||
|
private var currentPeers: ArrayList<String> = currentPeers
|
||||||
|
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
|
||||||
|
var listItem: View? = convertView
|
||||||
|
if (listItem == null) {
|
||||||
|
listItem = LayoutInflater.from(mContext).inflate(R.layout.peers_list_item_edit, parent, false)
|
||||||
|
}
|
||||||
|
val currentPeer = allPeers[position]
|
||||||
|
val image: ImageView = listItem?.findViewById(R.id.countryFlag) as ImageView
|
||||||
|
image.setImageResource(currentPeer.getCountry(mContext)!!.flagID)
|
||||||
|
val name = listItem.findViewById(R.id.peerInfoText) as TextView
|
||||||
|
val peerId = currentPeer.toString()
|
||||||
|
name.text = peerId
|
||||||
|
val checkbox = listItem.findViewById(R.id.checkbox) as CheckBox
|
||||||
|
checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
|
if(isChecked){
|
||||||
|
if(!currentPeers.contains(peerId)){
|
||||||
|
currentPeers.add(peerId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(currentPeers.contains(peerId)){
|
||||||
|
currentPeers.remove(peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(this.currentPeers.contains(peerId)){
|
||||||
|
checkbox.isChecked = true
|
||||||
|
}
|
||||||
|
return listItem
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getSelectedPeers(): ArrayList<String> {
|
||||||
|
return currentPeers
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
app/src/main/res/drawable/rounded_corner.xml
Normal file
16
app/src/main/res/drawable/rounded_corner.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/grey" />
|
||||||
|
|
||||||
|
<solid android:color="@color/dark_10" />
|
||||||
|
|
||||||
|
<padding
|
||||||
|
android:left="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:bottom="1dp"
|
||||||
|
android:top="1dp" />
|
||||||
|
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
</shape>
|
@ -6,78 +6,92 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/grey"
|
android:background="@color/grey"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/ipLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/connectRadioGroup"
|
android:layout_margin="20dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:background="@drawable/rounded_corner"
|
||||||
|
android:gravity="left"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:gravity="left">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/ipLabel"
|
android:id="@+id/ipLabel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:background="@color/dark_10"
|
android:background="@android:color/transparent"
|
||||||
android:elevation="8dp"
|
android:elevation="8dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
android:text="Your IP address:"
|
android:text="Your IP address:"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/dark_30" />
|
||||||
android:paddingLeft="20dp"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/ip"
|
android:id="@+id/ip"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:background="@color/dark_10"
|
android:background="@android:color/transparent"
|
||||||
android:elevation="8dp"
|
android:elevation="8dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:paddingLeft="20dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/connectRadioGroup"
|
app:layout_constraintBottom_toTopOf="@+id/connectRadioGroup"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
<RadioGroup
|
|
||||||
android:id="@+id/connectRadioGroup"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/out_line"
|
android:layout_margin="20dp"
|
||||||
android:checkedButton="@+id/offer"
|
android:background="@drawable/rounded_corner"
|
||||||
android:orientation="horizontal"
|
android:gravity="left"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:orientation="vertical"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toBottomOf="@id/ipLayout">
|
||||||
tools:layout_editor_absoluteX="50dp">
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<RadioButton
|
<TextView
|
||||||
android:id="@+id/disconnectButton"
|
android:id="@+id/ipPeers"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="40dp"
|
||||||
android:layout_marginLeft="1dp"
|
android:background="@android:color/transparent"
|
||||||
android:layout_marginTop="1dp"
|
android:elevation="8dp"
|
||||||
android:layout_marginBottom="1dp"
|
android:gravity="center_vertical"
|
||||||
android:layout_weight="1"
|
android:text="Peers:"
|
||||||
android:background="@drawable/toggle_widget_background"
|
android:textColor="@color/dark_30"
|
||||||
android:button="@null"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:gravity="center"
|
app:layout_constraintEnd_toStartOf="@+id/edit"
|
||||||
android:text="@string/disconnect_button"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:textColor="@color/white"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:checked="true"/>
|
<Button
|
||||||
|
android:id="@+id/edit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="EDIT"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:background="@android:color/transparent"/>
|
||||||
|
|
||||||
<RadioButton
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
android:id="@+id/connectButton"
|
<ListView
|
||||||
android:layout_width="0dp"
|
android:id="@+id/peers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="1dp"
|
android:gravity="center_vertical"
|
||||||
android:layout_marginRight="1dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:layout_marginBottom="1dp"
|
android:dividerHeight="0dp"
|
||||||
android:layout_weight="1"
|
android:divider="@null"
|
||||||
android:background="@drawable/toggle_widget_background"
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
android:button="@null"
|
|
||||||
android:gravity="center"
|
</LinearLayout>
|
||||||
android:text="@string/connect_button"
|
|
||||||
android:textColor="@color/white" />
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
40
app/src/main/res/layout/activity_peer_list.xml
Normal file
40
app/src/main/res/layout/activity_peer_list.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".PeerListActivity"
|
||||||
|
android:background="@color/grey">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/AppTheme.AppBarOverlay">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/content_peer_list" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|right"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
app:backgroundTint="@color/green"
|
||||||
|
app:tint="@color/white"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:elevation="6dp"
|
||||||
|
app:fabSize="normal"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
13
app/src/main/res/layout/content_peer_list.xml
Normal file
13
app/src/main/res/layout/content_peer_list.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/peerList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:divider="@color/dark_20"
|
||||||
|
android:dividerHeight="2px"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
13
app/src/main/res/layout/menu_save.xml
Normal file
13
app/src/main/res/layout/menu_save.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/saveButton"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:text="SAVE"
|
||||||
|
android:background="@android:color/transparent"/>
|
||||||
|
</RelativeLayout>
|
14
app/src/main/res/layout/menu_switch.xml
Normal file
14
app/src/main/res/layout/menu_switch.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/switchOn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:theme="@style/SwitchTheme"/>
|
||||||
|
</RelativeLayout>
|
9
app/src/main/res/layout/peers_list_item.xml
Normal file
9
app/src/main/res/layout/peers_list_item.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:minHeight="25dp"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/white"/>
|
39
app/src/main/res/layout/peers_list_item_edit.xml
Normal file
39
app/src/main/res/layout/peers_list_item_edit.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleX="0.4"
|
||||||
|
android:scaleY="0.4"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginStart="5dp">
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/SwitchTheme"
|
||||||
|
android:scaleX="3"
|
||||||
|
android:scaleY="3"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:background="@android:color/transparent"/>
|
||||||
|
</FrameLayout>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/countryFlag"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="5dp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/peerInfoText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:minHeight="45dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/white"/>
|
||||||
|
</LinearLayout>
|
9
app/src/main/res/menu/main_menu.xml
Normal file
9
app/src/main/res/menu/main_menu.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/switchId"
|
||||||
|
android:title=""
|
||||||
|
app:actionLayout="@layout/menu_switch"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
8
app/src/main/res/menu/save_peers.xml
Normal file
8
app/src/main/res/menu/save_peers.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/saveItem"
|
||||||
|
android:title=""
|
||||||
|
app:actionLayout="@layout/menu_save"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
</menu>
|
@ -9,4 +9,5 @@
|
|||||||
<color name="dark_5">#555555</color>
|
<color name="dark_5">#555555</color>
|
||||||
<color name="dark_10">#666666</color>
|
<color name="dark_10">#666666</color>
|
||||||
<color name="dark_20">#777777</color>
|
<color name="dark_20">#777777</color>
|
||||||
|
<color name="dark_30">#acacac</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
3
app/src/main/res/values/dimens.xml
Normal file
3
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
</resources>
|
@ -2,4 +2,14 @@
|
|||||||
<string name="app_name">Yggdrasil</string>
|
<string name="app_name">Yggdrasil</string>
|
||||||
<string name="connect_button">Connect</string>
|
<string name="connect_button">Connect</string>
|
||||||
<string name="disconnect_button">Disconnect</string>
|
<string name="disconnect_button">Disconnect</string>
|
||||||
|
<string name="switch_button_title">SwitchOn</string>
|
||||||
|
<string name="title_activity_peer_list">Edit peers</string>
|
||||||
|
<!-- Strings used for fragments for navigation -->
|
||||||
|
<string name="first_fragment_label">First Fragment</string>
|
||||||
|
<string name="second_fragment_label">Second Fragment</string>
|
||||||
|
<string name="next">Next</string>
|
||||||
|
<string name="previous">Previous</string>
|
||||||
|
|
||||||
|
<string name="hello_first_fragment">Hello first fragment</string>
|
||||||
|
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -8,4 +8,17 @@
|
|||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SwitchTheme" parent="Theme.AppCompat.Light">
|
||||||
|
<item name="android:colorControlActivated">@color/green</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.NoActionBar">
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||||
|
|
||||||
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user