Merge branch 'v3' into q-beta

This commit is contained in:
Mygod
2019-04-04 19:37:27 +08:00
28 changed files with 676 additions and 358 deletions

View File

@@ -7,14 +7,10 @@ import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.graphics.Typeface
import android.location.LocationManager
import android.os.Build
import android.os.IBinder
import android.provider.Settings
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.TypefaceSpan
import android.view.View
import android.widget.Toast
import androidx.core.content.getSystemService
@@ -25,7 +21,6 @@ import be.mygod.vpnhotspot.DebugHelper
import be.mygod.vpnhotspot.LocalOnlyHotspotService
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.formatAddresses
import be.mygod.vpnhotspot.widget.SmartSnackbar
@@ -80,15 +75,7 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager()
private val lookup: Map<String, NetworkInterface> get() = parent.ifaceLookup
override val icon get() = R.drawable.ic_action_perm_scan_wifi
override val title: CharSequence get() {
val configuration = binder?.configuration ?: return parent.getString(R.string.tethering_temp_hotspot)
return SpannableStringBuilder("${configuration.SSID} - ").apply {
val start = length
append(configuration.preSharedKey)
setSpan(if (Build.VERSION.SDK_INT >= 28) TypefaceSpan(Typeface.MONOSPACE) else
TypefaceSpan("monospace"), start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
override val title: CharSequence get() = parent.getString(R.string.tethering_temp_hotspot)
override val text: CharSequence get() {
return lookup[binder?.iface ?: return ""]?.formatAddresses() ?: ""
}
@@ -103,7 +90,7 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager()
override val type get() = VIEW_TYPE_LOCAL_ONLY_HOTSPOT
private val data = Data()
private var binder: LocalOnlyHotspotService.Binder? = null
internal var binder: LocalOnlyHotspotService.Binder? = null
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
viewHolder as ViewHolder

View File

@@ -8,6 +8,7 @@ import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.wifi.WifiConfiguration
import android.net.wifi.p2p.WifiP2pGroup
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.os.Parcelable
@@ -24,10 +25,8 @@ import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.get
import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.*
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding
import be.mygod.vpnhotspot.net.wifi.P2pSupplicantConfiguration
import be.mygod.vpnhotspot.net.wifi.WifiP2pDialogFragment
import be.mygod.vpnhotspot.net.wifi.configuration.*
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.formatAddresses
import be.mygod.vpnhotspot.widget.SmartSnackbar
@@ -54,7 +53,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
else -> false
}
val ssid @Bindable get() = binder?.group?.networkName ?: ""
val addresses: CharSequence @Bindable get() {
return try {
NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: ""
@@ -62,12 +60,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
""
}
}
var oc: CharSequence
@Bindable get() {
val oc = RepeaterService.operatingChannel
return if (oc in 1..165) oc.toString() else ""
}
set(value) = app.pref.edit().putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()).apply()
fun onStatusChanged() {
notifyPropertyChanged(BR.switchEnabled)
@@ -75,7 +67,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
notifyPropertyChanged(BR.addresses)
}
fun onGroupChanged(group: WifiP2pGroup? = null) {
notifyPropertyChanged(BR.ssid)
p2pInterface = group?.`interface`
notifyPropertyChanged(BR.addresses)
}
@@ -101,22 +92,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
fun wps() {
if (binder?.active == true) WpsDialogFragment().show(parent, TetheringFragment.REPEATER_WPS)
}
fun editConfigurations() {
val group = binder?.group
if (group != null) try {
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
holder.config = config
WifiP2pDialogFragment().withArg(WifiP2pDialogFragment.Arg(WifiConfiguration().apply {
SSID = group.networkName
preSharedKey = config.psk
})).show(parent, TetheringFragment.REPEATER_EDIT_CONFIGURATION)
return
} catch (e: RuntimeException) {
Timber.w(e)
}
SmartSnackbar.make(R.string.repeater_configure_failure).show()
}
}
@Parcelize
@@ -177,19 +152,35 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
}
}
fun onEditResult(which: Int, data: Intent?) {
when (which) {
DialogInterface.BUTTON_POSITIVE -> try {
val master = holder.config ?: return
val config = AlertDialogFragment.getRet<WifiP2pDialogFragment.Arg>(data!!).configuration
val configuration: WifiConfiguration? get() {
val group = binder?.group
if (group != null) try {
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
holder.config = config
return newWifiApConfiguration(group.networkName, config.psk).apply {
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used
if (Build.VERSION.SDK_INT >= 23) {
apBand = AP_BAND_ANY
apChannel = RepeaterService.operatingChannel
}
}
} catch (e: RuntimeException) {
Timber.w(e)
}
SmartSnackbar.make(R.string.repeater_configure_failure).show()
return null
}
fun updateConfiguration(config: WifiConfiguration) {
holder.config?.let { master ->
if (binder?.group?.networkName != config.SSID || master.psk != config.preSharedKey) try {
master.update(config.SSID, config.preSharedKey)
binder!!.group = null
} catch (e: Exception) {
Timber.w(e)
SmartSnackbar.make(e).show()
}
DialogInterface.BUTTON_NEUTRAL -> binder!!.resetCredentials()
holder.config = null
}
holder.config = null
RepeaterService.operatingChannel = config.apChannel
}
}

View File

@@ -1,10 +1,7 @@
package be.mygod.vpnhotspot.manage
import android.annotation.TargetApi
import android.content.ComponentName
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.*
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
@@ -13,6 +10,7 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
@@ -20,34 +18,37 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.LocalOnlyHotspotService
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.RepeaterService
import be.mygod.vpnhotspot.TetheringService
import be.mygod.vpnhotspot.*
import be.mygod.vpnhotspot.databinding.FragmentTetheringBinding
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.net.wifi.WifiApManager
import be.mygod.vpnhotspot.net.wifi.configuration.WifiApDialogFragment
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.isNotGone
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.android.synthetic.main.activity_main.*
import timber.log.Timber
import java.lang.IllegalArgumentException
import java.lang.reflect.InvocationTargetException
import java.net.NetworkInterface
import java.net.SocketException
class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClickListener {
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
companion object {
const val START_REPEATER = 4
const val START_LOCAL_ONLY_HOTSPOT = 1
const val REPEATER_EDIT_CONFIGURATION = 2
const val REPEATER_WPS = 3
const val CONFIGURE_REPEATER = 2
const val CONFIGURE_AP = 4
}
inner class ManagerAdapter : ListAdapter<Manager, RecyclerView.ViewHolder>(Manager) {
internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) }
private val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) }
internal val localOnlyHotspotManager by lazy @TargetApi(26) { LocalOnlyHotspotManager(this@TetheringFragment) }
private val tetherManagers by lazy @TargetApi(24) {
listOf(TetherManager.Wifi(this@TetheringFragment),
TetherManager.Usb(this@TetheringFragment),
@@ -108,13 +109,47 @@ class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClic
item.isNotGone = canMonitor.isNotEmpty()
item.subMenu.apply {
clear()
canMonitor.sorted().forEach { add(it).setOnMenuItemClickListener(this@TetheringFragment) }
for (iface in canMonitor.sorted()) add(iface).setOnMenuItemClickListener {
ContextCompat.startForegroundService(requireContext(), Intent(context, TetheringService::class.java)
.putExtra(TetheringService.EXTRA_ADD_INTERFACE_MONITOR, iface))
true
}
}
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
ContextCompat.startForegroundService(requireContext(), Intent(context, TetheringService::class.java)
.putExtra(TetheringService.EXTRA_ADD_INTERFACE_MONITOR, item?.title ?: return false))
return true
return when (item?.itemId) {
R.id.configuration -> item.subMenu.run {
findItem(R.id.configuration_repeater).isNotGone = RepeaterService.supported
findItem(R.id.configuration_temp_hotspot).isNotGone =
adapter.localOnlyHotspotManager.binder?.configuration != null
true
}
R.id.configuration_repeater -> {
WifiApDialogFragment().withArg(WifiApDialogFragment.Arg(
adapter.repeaterManager.configuration ?: return false,
p2pMode = true
)).show(this, CONFIGURE_REPEATER)
true
}
R.id.configuration_temp_hotspot -> {
WifiApDialogFragment().withArg(WifiApDialogFragment.Arg(
adapter.localOnlyHotspotManager.binder?.configuration ?: return false,
readOnly = true
)).show(this, 0) // read-only, no callback needed
true
}
R.id.configuration_ap -> try {
WifiApDialogFragment().withArg(WifiApDialogFragment.Arg(
WifiApManager.configuration
)).show(this, CONFIGURE_AP)
true
} catch (e: InvocationTargetException) {
if (e.targetException !is SecurityException) Timber.w(e)
SmartSnackbar.make(e.targetException).show()
false
}
else -> false
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -125,13 +160,19 @@ class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClic
binding.interfaces.adapter = adapter
adapter.update(emptyList(), emptyList(), emptyList())
ServiceForegroundConnector(this, this, TetheringService::class)
requireActivity().toolbar.inflateMenu(R.menu.toolbar_tethering)
requireActivity().toolbar.apply {
inflateMenu(R.menu.toolbar_tethering)
setOnMenuItemClickListener(this@TetheringFragment)
}
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
requireActivity().toolbar.menu.clear()
requireActivity().toolbar.apply {
menu.clear()
setOnMenuItemClickListener(null)
}
}
override fun onResume() {
@@ -139,10 +180,20 @@ class TetheringFragment : Fragment(), ServiceConnection, MenuItem.OnMenuItemClic
if (Build.VERSION.SDK_INT >= 27) ManageBar.Data.notifyChange()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = when (requestCode) {
REPEATER_WPS -> adapter.repeaterManager.onWpsResult(resultCode, data)
REPEATER_EDIT_CONFIGURATION -> adapter.repeaterManager.onEditResult(resultCode, data)
else -> super.onActivityResult(requestCode, resultCode, data)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val configuration by lazy { AlertDialogFragment.getRet<WifiApDialogFragment.Arg>(data!!).configuration }
when (requestCode) {
REPEATER_WPS -> adapter.repeaterManager.onWpsResult(resultCode, data)
CONFIGURE_REPEATER -> if (resultCode == DialogInterface.BUTTON_POSITIVE) {
adapter.repeaterManager.updateConfiguration(configuration)
}
CONFIGURE_AP -> if (resultCode == DialogInterface.BUTTON_POSITIVE) try {
WifiApManager.configuration = configuration
} catch (e: IllegalArgumentException) {
SmartSnackbar.make(R.string.configuration_rejected).show()
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {