Implement displaying tethering errors
This commit is contained in:
@@ -7,5 +7,5 @@ abstract class Data : BaseObservable() {
|
||||
abstract val title: CharSequence
|
||||
abstract val text: CharSequence
|
||||
abstract val active: Boolean
|
||||
abstract val selectable: Boolean
|
||||
open val selectable get() = true
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ class InterfaceManager(private val parent: TetheringFragment, val iface: String)
|
||||
override val title get() = iface
|
||||
override val text get() = addresses
|
||||
override val active get() = parent.tetheringBinder?.isActive(iface) == true
|
||||
override val selectable get() = true
|
||||
}
|
||||
|
||||
val addresses = parent.ifaceLookup[iface]?.formatAddresses() ?: ""
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
|
||||
import be.mygod.vpnhotspot.databinding.ListitemManageTetherBinding
|
||||
import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding
|
||||
|
||||
abstract class Manager {
|
||||
@@ -29,7 +28,7 @@ abstract class Manager {
|
||||
InterfaceManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
|
||||
VIEW_TYPE_MANAGE -> ManageBar.ViewHolder(inflater.inflate(R.layout.listitem_manage, parent, false))
|
||||
VIEW_TYPE_WIFI, VIEW_TYPE_USB, VIEW_TYPE_BLUETOOTH, VIEW_TYPE_WIFI_LEGACY ->
|
||||
TetherManager.ViewHolder(ListitemManageTetherBinding.inflate(inflater, parent, false))
|
||||
TetherManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
|
||||
VIEW_TYPE_LOCAL_ONLY_HOTSPOT -> @TargetApi(26) {
|
||||
LocalOnlyHotspotManager.ViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.databinding.BaseObservable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
@@ -18,18 +17,21 @@ import android.view.View
|
||||
import android.widget.Toast
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.databinding.ListitemManageTetherBinding
|
||||
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
||||
import be.mygod.vpnhotspot.util.setPaddingStart
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
abstract class TetherManager private constructor(protected val parent: TetheringFragment) : Manager(),
|
||||
TetheringManager.OnStartTetheringCallback {
|
||||
class ViewHolder(val binding: ListitemManageTetherBinding) : RecyclerView.ViewHolder(binding.root),
|
||||
class ViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
|
||||
View.OnClickListener {
|
||||
init {
|
||||
itemView.setPaddingStart(itemView.resources.getDimensionPixelOffset(
|
||||
R.dimen.listitem_manage_tether_padding_start))
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@@ -72,10 +74,11 @@ abstract class TetherManager private constructor(protected val parent: Tethering
|
||||
/**
|
||||
* A convenient class to delegate stuff to BaseObservable.
|
||||
*/
|
||||
inner class Data : BaseObservable() {
|
||||
val tetherType get() = this@TetherManager.tetherType
|
||||
val title get() = this@TetherManager.title
|
||||
val isStarted get() = this@TetherManager.isStarted
|
||||
inner class Data : be.mygod.vpnhotspot.manage.Data() {
|
||||
override val icon get() = tetherType.icon
|
||||
override val title get() = this@TetherManager.title
|
||||
override var text: CharSequence = ""
|
||||
override val active get() = isStarted
|
||||
}
|
||||
|
||||
val data = Data()
|
||||
@@ -97,6 +100,27 @@ abstract class TetherManager private constructor(protected val parent: Tethering
|
||||
(viewHolder as ViewHolder).manager = this
|
||||
}
|
||||
|
||||
fun updateErrorMessage(errored: List<String>) {
|
||||
data.text = errored.filter { TetherType.ofInterface(it) == tetherType }.joinToString("\n") {
|
||||
val error = TetheringManager.getLastTetherError(it)
|
||||
"$it: " + when (error) {
|
||||
TetheringManager.TETHER_ERROR_NO_ERROR -> "TETHER_ERROR_NO_ERROR"
|
||||
TetheringManager.TETHER_ERROR_UNKNOWN_IFACE -> "TETHER_ERROR_UNKNOWN_IFACE"
|
||||
TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL -> "TETHER_ERROR_SERVICE_UNAVAIL"
|
||||
TetheringManager.TETHER_ERROR_UNSUPPORTED -> "TETHER_ERROR_UNSUPPORTED"
|
||||
TetheringManager.TETHER_ERROR_UNAVAIL_IFACE -> "TETHER_ERROR_UNAVAIL_IFACE"
|
||||
TetheringManager.TETHER_ERROR_MASTER_ERROR -> "TETHER_ERROR_MASTER_ERROR"
|
||||
TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR -> "TETHER_ERROR_TETHER_IFACE_ERROR"
|
||||
TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR -> "TETHER_ERROR_UNTETHER_IFACE_ERROR"
|
||||
TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR -> "TETHER_ERROR_ENABLE_NAT_ERROR"
|
||||
TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR -> "TETHER_ERROR_DISABLE_NAT_ERROR"
|
||||
TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR -> "TETHER_ERROR_IFACE_CFG_ERROR"
|
||||
else -> app.getString(R.string.failure_reason_unknown, error)
|
||||
}
|
||||
}
|
||||
data.notifyChange()
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
class Wifi(parent: TetheringFragment) : TetherManager(parent) {
|
||||
override val title get() = parent.getString(R.string.tethering_manage_wifi)
|
||||
|
||||
@@ -47,7 +47,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
||||
TetherManager.WifiLegacy(this@TetheringFragment)
|
||||
}
|
||||
|
||||
fun update(activeIfaces: List<String>, localOnlyIfaces: List<String>) {
|
||||
fun update(activeIfaces: List<String>, localOnlyIfaces: List<String>, erroredIfaces: List<String>) {
|
||||
ifaceLookup = try {
|
||||
NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
|
||||
} catch (e: SocketException) {
|
||||
@@ -67,7 +67,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
||||
list.add(ManageBar)
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
list.addAll(tetherManagers)
|
||||
tetherManagers.forEach { it.onTetheringStarted() }
|
||||
tetherManagers.forEach { it.updateErrorMessage(erroredIfaces) }
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < 26) {
|
||||
list.add(wifiManagerLegacy)
|
||||
@@ -89,7 +89,8 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
||||
val adapter = ManagerAdapter()
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
adapter.update(TetheringManager.getTetheredIfaces(intent.extras),
|
||||
TetheringManager.getLocalOnlyTetheredIfaces(intent.extras))
|
||||
TetheringManager.getLocalOnlyTetheredIfaces(intent.extras),
|
||||
intent.extras.getStringArrayList(TetheringManager.EXTRA_ERRORED_TETHER))
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@@ -97,7 +98,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
||||
binding.interfaces.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
binding.interfaces.itemAnimator = DefaultItemAnimator()
|
||||
binding.interfaces.adapter = adapter
|
||||
adapter.update(emptyList(), emptyList())
|
||||
adapter.update(emptyList(), emptyList(), emptyList())
|
||||
ServiceForegroundConnector(this, this, TetheringService::class)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@@ -41,10 +41,36 @@ object TetheringManager {
|
||||
*/
|
||||
const val ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"
|
||||
private const val EXTRA_ACTIVE_TETHER_LEGACY = "activeArray"
|
||||
/**
|
||||
* gives a String[] listing all the interfaces currently in local-only
|
||||
* mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
|
||||
*/
|
||||
@RequiresApi(26)
|
||||
private const val EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray"
|
||||
/**
|
||||
* gives a String[] listing all the interfaces currently tethered
|
||||
* (ie, has DHCPv4 support and packets potentially forwarded/NATed)
|
||||
*/
|
||||
@RequiresApi(26)
|
||||
private const val EXTRA_ACTIVE_TETHER = "tetherArray"
|
||||
/**
|
||||
* gives a String[] listing all the interfaces we tried to tether and
|
||||
* failed. Use {@link #getLastTetherError} to find the error code
|
||||
* for any interfaces listed here.
|
||||
*/
|
||||
const val EXTRA_ERRORED_TETHER = "erroredArray"
|
||||
const val TETHER_ERROR_NO_ERROR = 0
|
||||
const val TETHER_ERROR_UNKNOWN_IFACE = 1
|
||||
const val TETHER_ERROR_SERVICE_UNAVAIL = 2
|
||||
const val TETHER_ERROR_UNSUPPORTED = 3
|
||||
const val TETHER_ERROR_UNAVAIL_IFACE = 4
|
||||
const val TETHER_ERROR_MASTER_ERROR = 5
|
||||
const val TETHER_ERROR_TETHER_IFACE_ERROR = 6
|
||||
const val TETHER_ERROR_UNTETHER_IFACE_ERROR = 7
|
||||
const val TETHER_ERROR_ENABLE_NAT_ERROR = 8
|
||||
const val TETHER_ERROR_DISABLE_NAT_ERROR = 9
|
||||
const val TETHER_ERROR_IFACE_CFG_ERROR = 10
|
||||
const val TETHER_ERROR_PROVISION_FAILED = 11
|
||||
|
||||
const val TETHERING_WIFI = 0
|
||||
/**
|
||||
@@ -68,6 +94,9 @@ object TetheringManager {
|
||||
private val stopTethering by lazy {
|
||||
ConnectivityManager::class.java.getDeclaredMethod("stopTethering", Int::class.java)
|
||||
}
|
||||
private val getLastTetherError by lazy {
|
||||
ConnectivityManager::class.java.getDeclaredMethod("getLastTetherError", String::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs tether provisioning for the given type if needed and then starts tethering if
|
||||
@@ -126,6 +155,16 @@ object TetheringManager {
|
||||
stopTethering.invoke(app.connectivity, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a more detailed error code after a Tethering or Untethering
|
||||
* request asynchronously failed.
|
||||
*
|
||||
* @param iface The name of the interface of interest
|
||||
* @return error The error code of the last error tethering or untethering the named
|
||||
* interface
|
||||
*/
|
||||
fun getLastTetherError(iface: String): Int = getLastTetherError.invoke(app.connectivity, iface) as Int
|
||||
|
||||
fun getTetheredIfaces(extras: Bundle) = extras.getStringArrayList(
|
||||
if (Build.VERSION.SDK_INT >= 26) EXTRA_ACTIVE_TETHER else EXTRA_ACTIVE_TETHER_LEGACY)!!
|
||||
fun getLocalOnlyTetheredIfaces(extras: Bundle) =
|
||||
|
||||
@@ -37,6 +37,8 @@ fun Bundle.put(key: String, map: Array<String>): Bundle {
|
||||
return this
|
||||
}
|
||||
|
||||
fun View.setPaddingStart(value: Int) = setPaddingRelative(value, paddingTop, paddingEnd, paddingBottom)
|
||||
|
||||
@BindingAdapter("android:src")
|
||||
fun setImageResource(imageView: ImageView, @DrawableRes resource: Int) = imageView.setImageResource(resource)
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<data>
|
||||
<variable
|
||||
name="data"
|
||||
type="be.mygod.vpnhotspot.manage.TetherManager.Data"/>
|
||||
</data>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:padding="16dp"
|
||||
android:paddingStart="56dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@{data.tetherType.icon}"
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
tools:src="@drawable/ic_device_network_wifi"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="0dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@{data.title}"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
tools:text="@string/tethering_manage_wifi"/>
|
||||
|
||||
<Switch
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:checked="@{data.isStarted}"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
4
mobile/src/main/res/values/dimen.xml
Normal file
4
mobile/src/main/res/values/dimen.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="listitem_manage_tether_padding_start">56dp</dimen>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user