Implement connected devices (buggy debug version)
This commit is contained in:
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -93,7 +93,7 @@
|
|||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import android.app.Service
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.wifi.p2p.WifiP2pGroup
|
import android.net.NetworkInfo
|
||||||
import android.net.wifi.p2p.WifiP2pManager
|
import android.net.wifi.p2p.*
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.support.v4.app.NotificationCompat
|
import android.support.v4.app.NotificationCompat
|
||||||
@@ -45,18 +45,61 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
|
|
||||||
private lateinit var p2pManager: WifiP2pManager
|
private lateinit var p2pManager: WifiP2pManager
|
||||||
private lateinit var channel: WifiP2pManager.Channel
|
private lateinit var channel: WifiP2pManager.Channel
|
||||||
private lateinit var group: WifiP2pGroup
|
lateinit var group: WifiP2pGroup
|
||||||
private var receiver: BroadcastReceiver? = null
|
lateinit var clients: MutableCollection<WifiP2pDevice>
|
||||||
private val binder = HotspotBinder()
|
private val binder = HotspotBinder()
|
||||||
|
private var receiverRegistered = false
|
||||||
|
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when (intent.action) {
|
||||||
|
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION ->
|
||||||
|
if (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, 0) ==
|
||||||
|
WifiP2pManager.WIFI_P2P_STATE_DISABLED) clean() // group may be enabled by other apps
|
||||||
|
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION ->{
|
||||||
|
clients = intent.getParcelableExtra<WifiP2pDeviceList>(WifiP2pManager.EXTRA_P2P_DEVICE_LIST).deviceList
|
||||||
|
binder.data?.onClientsChanged()
|
||||||
|
}
|
||||||
|
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
|
||||||
|
val info = intent.getParcelableExtra<WifiP2pInfo>(WifiP2pManager.EXTRA_WIFI_P2P_INFO)
|
||||||
|
val net = intent.getParcelableExtra<NetworkInfo>(WifiP2pManager.EXTRA_NETWORK_INFO)
|
||||||
|
val group = intent.getParcelableExtra<WifiP2pGroup>(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)
|
||||||
|
val downstream = group.`interface`
|
||||||
|
if (net.isConnected && downstream != null && this@HotspotService.downstream == null) {
|
||||||
|
this@HotspotService.downstream = downstream
|
||||||
|
if (noisySu("echo 1 >/proc/sys/net/ipv4/ip_forward",
|
||||||
|
"ip route add default dev $upstream scope link table 62",
|
||||||
|
"ip route add $route dev $downstream scope link table 62",
|
||||||
|
"ip route add broadcast 255.255.255.255 dev $downstream scope link table 62",
|
||||||
|
"ip rule add from $route lookup 62",
|
||||||
|
"iptables -N vpnhotspot_fwd",
|
||||||
|
"iptables -A vpnhotspot_fwd -i $upstream -o $downstream -m state --state ESTABLISHED,RELATED -j ACCEPT",
|
||||||
|
"iptables -A vpnhotspot_fwd -i $downstream -o $upstream -j ACCEPT",
|
||||||
|
"iptables -I FORWARD -j vpnhotspot_fwd",
|
||||||
|
"iptables -t nat -A PREROUTING -i $downstream -p tcp --dport 53 -j DNAT --to-destination $dns",
|
||||||
|
"iptables -t nat -A PREROUTING -i $downstream -p udp --dport 53 -j DNAT --to-destination $dns")) {
|
||||||
|
doStart(group)
|
||||||
|
} else startFailure("Something went wrong, please check logcat.")
|
||||||
|
}
|
||||||
|
group.`interface`
|
||||||
|
binder.data?.onGroupChanged()
|
||||||
|
Log.d(TAG, "${intent.action}: $info, $net, $group")
|
||||||
|
}
|
||||||
|
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
|
||||||
|
val info = intent.getParcelableExtra<WifiP2pInfo>(WifiP2pManager.EXTRA_WIFI_P2P_INFO)
|
||||||
|
Log.d(TAG, "${intent.action}: $info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: do something
|
// TODO: do something to these hardcoded strings
|
||||||
|
private var downstream: String? = null
|
||||||
private val upstream = "tun0"
|
private val upstream = "tun0"
|
||||||
private val downstream = "p2p0"
|
|
||||||
private val route = "192.168.49.0/24"
|
private val route = "192.168.49.0/24"
|
||||||
private val dns = "8.8.8.8:53"
|
private val dns = "8.8.8.8:53"
|
||||||
|
|
||||||
private var status = Status.IDLE
|
var status = Status.IDLE
|
||||||
set(value) {
|
private set(value) {
|
||||||
field = value
|
field = value
|
||||||
binder.data?.onStatusChanged()
|
binder.data?.onStatusChanged()
|
||||||
}
|
}
|
||||||
@@ -76,20 +119,13 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (status != Status.IDLE) return START_NOT_STICKY
|
if (status != Status.IDLE) return START_NOT_STICKY
|
||||||
status = Status.STARTING
|
status = Status.STARTING
|
||||||
initReceiver()
|
if (!receiverRegistered) {
|
||||||
if (!noisySu("echo 1 >/proc/sys/net/ipv4/ip_forward",
|
registerReceiver(receiver, createIntentFilter(
|
||||||
"ip route add default dev $upstream scope link table 62",
|
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
||||||
"ip route add $route dev $downstream scope link table 62",
|
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION,
|
||||||
"ip route add broadcast 255.255.255.255 dev $downstream scope link table 62",
|
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION,
|
||||||
"ip rule add from $route lookup 62",
|
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION))
|
||||||
"iptables -N vpnhotspot_fwd",
|
receiverRegistered = true
|
||||||
"iptables -A vpnhotspot_fwd -i $upstream -o $downstream -m state --state ESTABLISHED,RELATED -j ACCEPT",
|
|
||||||
"iptables -A vpnhotspot_fwd -i $downstream -o $upstream -j ACCEPT",
|
|
||||||
"iptables -I FORWARD -j vpnhotspot_fwd",
|
|
||||||
"iptables -t nat -A PREROUTING -i $downstream -p tcp --dport 53 -j DNAT --to-destination $dns",
|
|
||||||
"iptables -t nat -A PREROUTING -i $downstream -p udp --dport 53 -j DNAT --to-destination $dns")) {
|
|
||||||
startFailure("Something went wrong, please check logcat.")
|
|
||||||
return START_NOT_STICKY
|
|
||||||
}
|
}
|
||||||
p2pManager.requestGroupInfo(channel, {
|
p2pManager.requestGroupInfo(channel, {
|
||||||
when {
|
when {
|
||||||
@@ -115,70 +151,48 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
startForeground(0, NotificationCompat.Builder(this@HotspotService, CHANNEL).build())
|
startForeground(0, NotificationCompat.Builder(this@HotspotService, CHANNEL).build())
|
||||||
clean()
|
clean()
|
||||||
}
|
}
|
||||||
private fun doStart() {
|
private fun doStart() = p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener {
|
||||||
p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener, WifiP2pManager.GroupInfoListener {
|
override fun onFailure(reason: Int) = startFailure("Failed to create P2P group (reason: $reason)")
|
||||||
override fun onFailure(reason: Int) = startFailure("Failed to create P2P group (reason: $reason)")
|
override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire
|
||||||
|
})
|
||||||
private var tries = 0
|
|
||||||
override fun onSuccess() = p2pManager.requestGroupInfo(channel, this)
|
|
||||||
override fun onGroupInfoAvailable(group: WifiP2pGroup?) {
|
|
||||||
if (group != null && group.isGroupOwner) doStart(group) else if (tries < 10) {
|
|
||||||
Thread.sleep(30L shl tries++)
|
|
||||||
onSuccess()
|
|
||||||
} else startFailure("Unexpected group: $group")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
private fun doStart(group: WifiP2pGroup) {
|
private fun doStart(group: WifiP2pGroup) {
|
||||||
status = Status.ACTIVE
|
|
||||||
this.group = group
|
this.group = group
|
||||||
|
clients = group.clientList
|
||||||
|
status = Status.ACTIVE
|
||||||
startForeground(1, NotificationCompat.Builder(this@HotspotService, CHANNEL)
|
startForeground(1, NotificationCompat.Builder(this@HotspotService, CHANNEL)
|
||||||
|
.setWhen(0)
|
||||||
.setColor(ContextCompat.getColor(this@HotspotService, R.color.colorPrimary))
|
.setColor(ContextCompat.getColor(this@HotspotService, R.color.colorPrimary))
|
||||||
.setContentTitle(group.networkName)
|
.setContentTitle(group.networkName)
|
||||||
.setSubText(group.passphrase)
|
.setContentText(group.passphrase)
|
||||||
.setSmallIcon(R.drawable.ic_device_wifi_tethering)
|
.setSmallIcon(R.drawable.ic_device_wifi_tethering)
|
||||||
.setContentIntent(PendingIntent.getActivity(this, 0,
|
.setContentIntent(PendingIntent.getActivity(this, 0,
|
||||||
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initReceiver() {
|
|
||||||
return
|
|
||||||
receiver = object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
when (intent.action) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
registerReceiver(receiver, createIntentFilter(
|
|
||||||
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
|
||||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION,
|
|
||||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION,
|
|
||||||
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun clean() {
|
private fun clean() {
|
||||||
if (!noisySu("iptables -t nat -D PREROUTING -i $downstream -p tcp --dport 53 -j DNAT --to-destination $dns",
|
if (receiverRegistered) {
|
||||||
"iptables -t nat -D PREROUTING -i $downstream -p udp --dport 53 -j DNAT --to-destination $dns",
|
unregisterReceiver(receiver)
|
||||||
"iptables -D FORWARD -j vpnhotspot_fwd",
|
receiverRegistered = false
|
||||||
"iptables -F vpnhotspot_fwd",
|
}
|
||||||
"iptables -X vpnhotspot_fwd",
|
if (downstream != null)
|
||||||
"ip rule del from $route lookup 62",
|
if (noisySu("iptables -t nat -D PREROUTING -i $downstream -p tcp --dport 53 -j DNAT --to-destination $dns",
|
||||||
"ip route del broadcast 255.255.255.255 dev $downstream scope link table 62",
|
"iptables -t nat -D PREROUTING -i $downstream -p udp --dport 53 -j DNAT --to-destination $dns",
|
||||||
"ip route del $route dev $downstream scope link table 62",
|
"iptables -D FORWARD -j vpnhotspot_fwd",
|
||||||
"ip route del default dev $upstream scope link table 62"))
|
"iptables -F vpnhotspot_fwd",
|
||||||
Toast.makeText(this, "Something went wrong, please check logcat.", Toast.LENGTH_SHORT).show()
|
"iptables -X vpnhotspot_fwd",
|
||||||
|
"ip rule del from $route lookup 62",
|
||||||
|
"ip route del broadcast 255.255.255.255 dev $downstream scope link table 62",
|
||||||
|
"ip route del $route dev $downstream scope link table 62",
|
||||||
|
"ip route del default dev $upstream scope link table 62")) {
|
||||||
|
downstream = null
|
||||||
|
} else Toast.makeText(this, "Something went wrong, please check logcat.", Toast.LENGTH_SHORT).show()
|
||||||
status = Status.IDLE
|
status = Status.IDLE
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
if (status != Status.IDLE) binder.shutdown()
|
if (status != Status.IDLE) binder.shutdown()
|
||||||
if (receiver != null) {
|
|
||||||
unregisterReceiver(receiver)
|
|
||||||
receiver = null
|
|
||||||
}
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,17 @@ import android.content.ServiceConnection
|
|||||||
import android.databinding.BaseObservable
|
import android.databinding.BaseObservable
|
||||||
import android.databinding.Bindable
|
import android.databinding.Bindable
|
||||||
import android.databinding.DataBindingUtil
|
import android.databinding.DataBindingUtil
|
||||||
|
import android.net.wifi.p2p.WifiP2pDevice
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
|
import android.support.v7.widget.DefaultItemAnimator
|
||||||
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import be.mygod.vpnhotspot.databinding.ClientBinding
|
||||||
import be.mygod.vpnhotspot.databinding.MainActivityBinding
|
import be.mygod.vpnhotspot.databinding.MainActivityBinding
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), ServiceConnection {
|
class MainActivity : AppCompatActivity(), ServiceConnection {
|
||||||
@@ -37,23 +44,66 @@ class MainActivity : AppCompatActivity(), ServiceConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val running get() = binder?.status == HotspotService.Status.ACTIVE
|
||||||
|
val ssid: String @Bindable get() = if (running) binder!!.service.group.networkName else ""
|
||||||
|
val password: String @Bindable get() = if (running) binder!!.service.group.passphrase else ""
|
||||||
|
val clients @Bindable get() = if (running) binder!!.service.clients else null
|
||||||
|
|
||||||
fun onStatusChanged() {
|
fun onStatusChanged() {
|
||||||
notifyPropertyChanged(BR.switchEnabled)
|
notifyPropertyChanged(BR.switchEnabled)
|
||||||
notifyPropertyChanged(BR.serviceStarted)
|
notifyPropertyChanged(BR.serviceStarted)
|
||||||
|
onGroupChanged()
|
||||||
|
onClientsChanged()
|
||||||
}
|
}
|
||||||
fun onBinderChanged() {
|
fun onGroupChanged() {
|
||||||
onStatusChanged()
|
notifyPropertyChanged(BR.ssid)
|
||||||
|
notifyPropertyChanged(BR.password)
|
||||||
}
|
}
|
||||||
|
fun onClientsChanged() = adapter.fetchClients()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientViewHolder(val binding: ClientBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
inner class ClientAdapter : RecyclerView.Adapter<ClientViewHolder>() {
|
||||||
|
private var owner: WifiP2pDevice? = null
|
||||||
|
private lateinit var clients: MutableCollection<WifiP2pDevice>
|
||||||
|
|
||||||
|
fun fetchClients() {
|
||||||
|
val binder = binder!!
|
||||||
|
if (data.running) {
|
||||||
|
owner = binder.service.group.owner
|
||||||
|
clients = binder.service.clients
|
||||||
|
} else owner = null
|
||||||
|
notifyDataSetChanged() // recreate everything
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
|
ClientViewHolder(ClientBinding.inflate(LayoutInflater.from(parent.context)))
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ClientViewHolder, position: Int) {
|
||||||
|
holder.binding.device = when (position) {
|
||||||
|
0 -> owner
|
||||||
|
else -> clients.elementAt(position - 1)
|
||||||
|
}
|
||||||
|
holder.binding.executePendingBindings()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = if (owner == null) 0 else 1 + clients.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var binding: MainActivityBinding
|
private lateinit var binding: MainActivityBinding
|
||||||
private val data = Data()
|
private val data = Data()
|
||||||
|
private val adapter = ClientAdapter()
|
||||||
private var binder: HotspotService.HotspotBinder? = null
|
private var binder: HotspotService.HotspotBinder? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
|
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
|
||||||
binding.data = data
|
binding.data = data
|
||||||
|
binding.clients.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
|
val animator = DefaultItemAnimator()
|
||||||
|
animator.supportsChangeAnimations = false // prevent fading-in/out when rebinding
|
||||||
|
binding.clients.itemAnimator = animator
|
||||||
|
binding.clients.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@@ -70,11 +120,12 @@ class MainActivity : AppCompatActivity(), ServiceConnection {
|
|||||||
val binder = service as HotspotService.HotspotBinder
|
val binder = service as HotspotService.HotspotBinder
|
||||||
binder.data = data
|
binder.data = data
|
||||||
this.binder = binder
|
this.binder = binder
|
||||||
data.onBinderChanged()
|
data.onStatusChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName?) {
|
override fun onServiceDisconnected(name: ComponentName?) {
|
||||||
binder?.data = null
|
binder?.data = null
|
||||||
binder = null
|
binder = null
|
||||||
|
data.onStatusChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ echo $NOISYSU_SUFFIX""")
|
|||||||
var out = process.inputStream.bufferedReader().use { it.readLine() }
|
var out = process.inputStream.bufferedReader().use { it.readLine() }
|
||||||
val result = out != NOISYSU_SUFFIX
|
val result = out != NOISYSU_SUFFIX
|
||||||
out = out.removeSuffix(NOISYSU_SUFFIX)
|
out = out.removeSuffix(NOISYSU_SUFFIX)
|
||||||
if (!out.isNullOrBlank()) Log.i("noisySu", out)
|
if (!out.isNullOrBlank()) Log.i(NOISYSU_TAG, out)
|
||||||
val err = process.errorStream.bufferedReader().use { it.readLine() }
|
val err = process.errorStream.bufferedReader().use { it.readLine() }
|
||||||
if (!err.isNullOrBlank()) Log.e("noisySu", err)
|
if (!err.isNullOrBlank()) Log.e(NOISYSU_TAG, err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
46
mobile/src/main/res/layout/client.xml
Normal file
46
mobile/src/main/res/layout/client.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="device"
|
||||||
|
type="android.net.wifi.p2p.WifiP2pDevice"/>
|
||||||
|
</data>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{device.deviceName}"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
|
tools:text="Device name"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{device.deviceAddress}"
|
||||||
|
tools:text="xx:xx:xx:xx:xx:xx"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{device.primaryDeviceType}"
|
||||||
|
tools:text="Primary type"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{device.secondaryDeviceType}"
|
||||||
|
tools:text="Secondary type"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{Integer.toString(device.status)}"
|
||||||
|
tools:text="Status"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
android:id="@+id/toolbar">
|
android:id="@+id/toolbar">
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/switchService"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
@@ -54,11 +53,11 @@
|
|||||||
android:layout_row="0"/>
|
android:layout_row="0"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textSsid"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_column="2"
|
android:layout_column="2"
|
||||||
android:layout_row="0"
|
android:layout_row="0"
|
||||||
|
android:text="@{data.ssid}"
|
||||||
tools:text="DIRECT-rAnd0m"/>
|
tools:text="DIRECT-rAnd0m"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -70,11 +69,11 @@
|
|||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textPassword"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_column="2"
|
android:layout_column="2"
|
||||||
android:layout_row="1"
|
android:layout_row="1"
|
||||||
|
android:text="@{data.password}"
|
||||||
tools:text="p4ssW0rd"/>
|
tools:text="p4ssW0rd"/>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
|
||||||
@@ -93,11 +92,15 @@
|
|||||||
android:backgroundTint="?android:attr/textColorSecondary"/>
|
android:backgroundTint="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/clients"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingBottom="8dp"
|
android:paddingBottom="8dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="8dp"/>
|
android:paddingTop="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
tools:listitem="@layout/client"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user