Show host addresses
This commit is contained in:
@@ -28,6 +28,7 @@ import be.mygod.vpnhotspot.net.IpNeighbour
|
|||||||
import be.mygod.vpnhotspot.net.IpNeighbourMonitor
|
import be.mygod.vpnhotspot.net.IpNeighbourMonitor
|
||||||
import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
||||||
import be.mygod.vpnhotspot.net.TetherType
|
import be.mygod.vpnhotspot.net.TetherType
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
|
||||||
class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, IpNeighbourMonitor.Callback {
|
class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, IpNeighbourMonitor.Callback {
|
||||||
inner class Data : BaseObservable() {
|
inner class Data : BaseObservable() {
|
||||||
@@ -56,6 +57,9 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
|
|
||||||
val ssid @Bindable get() = binder?.service?.ssid ?: getText(R.string.repeater_inactive)
|
val ssid @Bindable get() = binder?.service?.ssid ?: getText(R.string.repeater_inactive)
|
||||||
val password @Bindable get() = binder?.service?.password ?: ""
|
val password @Bindable get() = binder?.service?.password ?: ""
|
||||||
|
val addresses @Bindable get(): String {
|
||||||
|
return NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
fun onStatusChanged() {
|
fun onStatusChanged() {
|
||||||
notifyPropertyChanged(BR.switchEnabled)
|
notifyPropertyChanged(BR.switchEnabled)
|
||||||
@@ -67,6 +71,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
notifyPropertyChanged(BR.ssid)
|
notifyPropertyChanged(BR.ssid)
|
||||||
notifyPropertyChanged(BR.password)
|
notifyPropertyChanged(BR.password)
|
||||||
p2pInterface = group?.`interface`
|
p2pInterface = group?.`interface`
|
||||||
|
notifyPropertyChanged(BR.addresses)
|
||||||
adapter.p2p = group?.clientList ?: emptyList()
|
adapter.p2p = group?.clientList ?: emptyList()
|
||||||
adapter.recreate()
|
adapter.recreate()
|
||||||
}
|
}
|
||||||
@@ -80,7 +85,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
val ip = neighbour?.ip
|
val ip = neighbour?.ip
|
||||||
|
|
||||||
val icon get() = TetherType.ofInterface(iface, p2pInterface).icon
|
val icon get() = TetherType.ofInterface(iface, p2pInterface).icon
|
||||||
val title get() = listOf(ip, mac).filter { !it.isNullOrEmpty() }.joinToString()
|
val title get() = listOf(ip, mac).filter { !it.isNullOrEmpty() }.joinToString("\t\t")
|
||||||
val description get() = getString(when (neighbour?.state) {
|
val description get() = getString(when (neighbour?.state) {
|
||||||
IpNeighbour.State.INCOMPLETE, null -> R.string.connected_state_incomplete
|
IpNeighbour.State.INCOMPLETE, null -> R.string.connected_state_incomplete
|
||||||
IpNeighbour.State.VALID -> R.string.connected_state_valid
|
IpNeighbour.State.VALID -> R.string.connected_state_valid
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import be.mygod.vpnhotspot.databinding.FragmentTetheringBinding
|
|||||||
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
|
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
|
||||||
import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
||||||
import be.mygod.vpnhotspot.net.TetherType
|
import be.mygod.vpnhotspot.net.TetherType
|
||||||
|
import java.net.Inet4Address
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
|
||||||
class TetheringFragment : Fragment(), ServiceConnection {
|
class TetheringFragment : Fragment(), ServiceConnection {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -42,11 +44,11 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
private open class DefaultSorter<T : Comparable<T>> : BaseSorter<T>() {
|
private open class DefaultSorter<T : Comparable<T>> : BaseSorter<T>() {
|
||||||
override fun compareNonNull(o1: T, o2: T): Int = o1.compareTo(o2)
|
override fun compareNonNull(o1: T, o2: T): Int = o1.compareTo(o2)
|
||||||
}
|
}
|
||||||
private object StringSorter : DefaultSorter<String>()
|
private object TetheredInterfaceSorter : DefaultSorter<TetheredInterface>()
|
||||||
|
|
||||||
inner class Data(val iface: String) : BaseObservable() {
|
inner class Data(val iface: TetheredInterface) : BaseObservable() {
|
||||||
val icon: Int get() = TetherType.ofInterface(iface).icon
|
val icon: Int get() = TetherType.ofInterface(iface.name).icon
|
||||||
var active = binder?.active?.contains(iface) == true
|
val active = binder?.active?.contains(iface.name) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InterfaceViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
|
private class InterfaceViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
|
||||||
@@ -59,10 +61,9 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
val context = itemView.context
|
val context = itemView.context
|
||||||
val data = binding.data!!
|
val data = binding.data!!
|
||||||
if (data.active) context.startService(Intent(context, TetheringService::class.java)
|
if (data.active) context.startService(Intent(context, TetheringService::class.java)
|
||||||
.putExtra(TetheringService.EXTRA_REMOVE_INTERFACE, data.iface))
|
.putExtra(TetheringService.EXTRA_REMOVE_INTERFACE, data.iface.name))
|
||||||
else ContextCompat.startForegroundService(context, Intent(context, TetheringService::class.java)
|
else ContextCompat.startForegroundService(context, Intent(context, TetheringService::class.java)
|
||||||
.putExtra(TetheringService.EXTRA_ADD_INTERFACE, data.iface))
|
.putExtra(TetheringService.EXTRA_ADD_INTERFACE, data.iface.name))
|
||||||
data.active = !data.active
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private class ManageViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
private class ManageViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
@@ -73,12 +74,18 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
override fun onClick(v: View?) = itemView.context.startActivity(Intent()
|
override fun onClick(v: View?) = itemView.context.startActivity(Intent()
|
||||||
.setClassName("com.android.settings", "com.android.settings.Settings\$TetherSettingsActivity"))
|
.setClassName("com.android.settings", "com.android.settings.Settings\$TetherSettingsActivity"))
|
||||||
}
|
}
|
||||||
|
class TetheredInterface(val name: String, lookup: Map<String, NetworkInterface>) : Comparable<TetheredInterface> {
|
||||||
|
val addresses = lookup[name]!!.formatAddresses()
|
||||||
|
|
||||||
|
override fun compareTo(other: TetheredInterface) = name.compareTo(other.name)
|
||||||
|
}
|
||||||
inner class TetheringAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
inner class TetheringAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private val tethered = SortedList(String::class.java, StringSorter)
|
private val tethered = SortedList(TetheredInterface::class.java, TetheredInterfaceSorter)
|
||||||
|
|
||||||
fun update(data: Set<String>) {
|
fun update(data: Set<String>) {
|
||||||
|
val lookup = NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
|
||||||
tethered.clear()
|
tethered.clear()
|
||||||
tethered.addAll(data)
|
tethered.addAll(data.map { TetheredInterface(it, lookup) })
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +111,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
private var binder: TetheringService.TetheringBinder? = null
|
private var binder: TetheringService.TetheringBinder? = null
|
||||||
val adapter = TetheringAdapter()
|
val adapter = TetheringAdapter()
|
||||||
private val receiver = broadcastReceiver { _, intent ->
|
private val receiver = broadcastReceiver { _, intent ->
|
||||||
adapter.update(ConnectivityManagerHelper.getTetheredIfaces(intent.extras).toSet())
|
adapter.update(ConnectivityManagerHelper.getTetheredIfaces(intent.extras))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import android.support.annotation.DrawableRes
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.net.Inet4Address
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
|
||||||
fun debugLog(tag: String?, message: String?) {
|
fun debugLog(tag: String?, message: String?) {
|
||||||
if (BuildConfig.DEBUG) Log.d(tag, message)
|
if (BuildConfig.DEBUG) Log.d(tag, message)
|
||||||
@@ -27,6 +29,14 @@ fun intentFilter(vararg actions: String): IntentFilter {
|
|||||||
@BindingAdapter("android:src")
|
@BindingAdapter("android:src")
|
||||||
fun setImageResource(imageView: ImageView, @DrawableRes resource: Int) = imageView.setImageResource(resource)
|
fun setImageResource(imageView: ImageView, @DrawableRes resource: Int) = imageView.setImageResource(resource)
|
||||||
|
|
||||||
|
fun NetworkInterface.formatAddresses() =
|
||||||
|
(this.interfaceAddresses.asSequence()
|
||||||
|
.filter { !it.address.isLinkLocalAddress }
|
||||||
|
.map { "${it.address.hostAddress}/${it.networkPrefixLength}" }
|
||||||
|
.toList() +
|
||||||
|
listOfNotNull(this.hardwareAddress?.joinToString(":") { "%02x".format(it) }))
|
||||||
|
.joinToString("\n")
|
||||||
|
|
||||||
private const val NOISYSU_TAG = "NoisySU"
|
private const val NOISYSU_TAG = "NoisySU"
|
||||||
private const val NOISYSU_SUFFIX = "SUCCESS\n"
|
private const val NOISYSU_SUFFIX = "SUCCESS\n"
|
||||||
fun loggerSu(command: String): String? {
|
fun loggerSu(command: String): String? {
|
||||||
|
|||||||
@@ -76,6 +76,24 @@
|
|||||||
android:text="@{data.password}"
|
android:text="@{data.password}"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
tools:text="p4ssW0rd"/>
|
tools:text="p4ssW0rd"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="0"
|
||||||
|
android:layout_row="2"
|
||||||
|
android:text="@string/repeater_addresses"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:layout_row="2"
|
||||||
|
android:focusable="false"
|
||||||
|
android:text="@{data.addresses}"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
tools:text="192.168.49.1/24\n01:23:45:ab:cd:ef"/>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -29,8 +29,9 @@
|
|||||||
android:layout_height="0dp"/>
|
android:layout_height="0dp"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
android:text="@{client.title}"
|
android:text="@{client.title}"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
tools:text="192.168.49.123, 01:23:45:ab:cd:ef"/>
|
tools:text="192.168.49.123\t\t01:23:45:ab:cd:ef"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -26,19 +26,37 @@
|
|||||||
android:layout_width="16dp"
|
android:layout_width="16dp"
|
||||||
android:layout_height="0dp"/>
|
android:layout_height="0dp"/>
|
||||||
|
|
||||||
<Switch
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{data.iface.name}"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
tools:text="wlan0"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{data.iface.addresses}"
|
||||||
|
tools:text="192.168.43.1/24\n01:23:45:ab:cd:ef"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:checked="@{data.active}"
|
android:checked="@{data.active}"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"/>
|
||||||
android:text="@{data.iface}"
|
|
||||||
tools:text="wlan0"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
android:text="@string/tethering_manage"/>
|
android:text="@string/tethering_manage"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<string name="repeater_ssid">中继名称</string>
|
<string name="repeater_ssid">中继名称</string>
|
||||||
<string name="repeater_password">中继密码</string>
|
<string name="repeater_password">中继密码</string>
|
||||||
|
<string name="repeater_addresses">中继地址</string>
|
||||||
<string name="repeater_wps_dialog_title">输入 PIN</string>
|
<string name="repeater_wps_dialog_title">输入 PIN</string>
|
||||||
<string name="repeater_wps_dialog_pbc">一键加密</string>
|
<string name="repeater_wps_dialog_pbc">一键加密</string>
|
||||||
<string name="repeater_wps_success_pbc">请在 2 分钟内在需要连接的设备上使用一键加密以连接到此中继。</string>
|
<string name="repeater_wps_success_pbc">请在 2 分钟内在需要连接的设备上使用一键加密以连接到此中继。</string>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<string name="repeater_ssid">Network name</string>
|
<string name="repeater_ssid">Network name</string>
|
||||||
<string name="repeater_password">Password</string>
|
<string name="repeater_password">Password</string>
|
||||||
|
<string name="repeater_addresses">Addresses</string>
|
||||||
<string name="repeater_wps">WPS</string>
|
<string name="repeater_wps">WPS</string>
|
||||||
<string name="repeater_wps_dialog_title">Enter PIN</string>
|
<string name="repeater_wps_dialog_title">Enter PIN</string>
|
||||||
<string name="repeater_wps_dialog_pbc">Push Button</string>
|
<string name="repeater_wps_dialog_pbc">Push Button</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user