Add manual MAC lookup

This commit is contained in:
Mygod
2019-01-31 17:10:26 +08:00
parent 73d29cba20
commit 509511461c
5 changed files with 32 additions and 9 deletions

View File

@@ -3,7 +3,6 @@ package be.mygod.vpnhotspot
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.app.UiModeManager import android.app.UiModeManager
import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
@@ -16,6 +15,7 @@ import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.util.DeviceStorageApp import be.mygod.vpnhotspot.util.DeviceStorageApp
import be.mygod.vpnhotspot.util.Event0 import be.mygod.vpnhotspot.util.Event0
import be.mygod.vpnhotspot.util.RootSession import be.mygod.vpnhotspot.util.RootSession
import java.util.*
class App : Application() { class App : Application() {
companion object { companion object {
@@ -48,7 +48,12 @@ class App : Application() {
} }
lateinit var deviceStorage: Application lateinit var deviceStorage: Application
val pref: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(deviceStorage) } val english by lazy {
createConfigurationContext(Configuration(resources.configuration).apply {
setLocale(Locale.ENGLISH)
})
}
val pref by lazy { PreferenceManager.getDefaultSharedPreferences(deviceStorage) }
val connectivity by lazy { getSystemService<ConnectivityManager>()!! } val connectivity by lazy { getSystemService<ConnectivityManager>()!! }
val uiMode by lazy { getSystemService<UiModeManager>()!! } val uiMode by lazy { getSystemService<UiModeManager>()!! }
val wifi by lazy { getSystemService<WifiManager>()!! } val wifi by lazy { getSystemService<WifiManager>()!! }

View File

@@ -55,6 +55,7 @@ class ClientsFragment : Fragment(), MainScope by MainScope.Supervisor() {
setTitle(getString(R.string.clients_nickname_title, arg.mac.macToString())) setTitle(getString(R.string.clients_nickname_title, arg.mac.macToString()))
setPositiveButton(android.R.string.ok, listener) setPositiveButton(android.R.string.ok, listener)
setNegativeButton(android.R.string.cancel, null) setNegativeButton(android.R.string.cancel, null)
setNeutralButton(R.string.clients_nickname_set_to_vendor, listener)
} }
override fun onCreateDialog(savedInstanceState: Bundle?) = super.onCreateDialog(savedInstanceState).apply { override fun onCreateDialog(savedInstanceState: Bundle?) = super.onCreateDialog(savedInstanceState).apply {
@@ -63,12 +64,15 @@ class ClientsFragment : Fragment(), MainScope by MainScope.Supervisor() {
} }
override fun onClick(dialog: DialogInterface?, which: Int) { override fun onClick(dialog: DialogInterface?, which: Int) {
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) { when (which) {
DialogInterface.BUTTON_POSITIVE -> GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) {
MacLookup.abort(arg.mac) MacLookup.abort(arg.mac)
AppDatabase.instance.clientRecordDao.upsert(arg.mac) { AppDatabase.instance.clientRecordDao.upsert(arg.mac) {
nickname = this@NicknameDialogFragment.dialog!!.findViewById<EditText>(android.R.id.edit).text nickname = this@NicknameDialogFragment.dialog!!.findViewById<EditText>(android.R.id.edit).text
} }
} }
DialogInterface.BUTTON_NEUTRAL -> MacLookup.perform(arg.mac, true)
}
} }
} }

View File

@@ -1,8 +1,12 @@
package be.mygod.vpnhotspot.client package be.mygod.vpnhotspot.client
import android.content.Context
import androidx.annotation.MainThread import androidx.annotation.MainThread
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.room.macToString import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -18,8 +22,12 @@ import java.net.URL
* This class generates a default nickname for new clients. * This class generates a default nickname for new clients.
*/ */
object MacLookup { object MacLookup {
class UnexpectedError(mac: Long, val error: String) : class UnexpectedError(val mac: Long, val error: String) : JSONException("") {
JSONException("Server returned error for ${mac.macToString()}: $error") private fun formatMessage(context: Context) =
context.getString(R.string.clients_mac_lookup_unexpected_error, mac.macToString(), error)
override val message get() = formatMessage(app.english)
override fun getLocalizedMessage() = formatMessage(app)
}
private val macLookupBusy = mutableMapOf<Long, Pair<HttpURLConnection, Job>>() private val macLookupBusy = mutableMapOf<Long, Pair<HttpURLConnection, Job>>()
private val countryCodeRegex = "[A-Z]{2}".toRegex() // http://en.wikipedia.org/wiki/ISO_3166-1 private val countryCodeRegex = "[A-Z]{2}".toRegex() // http://en.wikipedia.org/wiki/ISO_3166-1
@@ -31,7 +39,7 @@ object MacLookup {
} }
@MainThread @MainThread
fun perform(mac: Long) { fun perform(mac: Long, explicit: Boolean = false) {
abort(mac) abort(mac)
val conn = URL("https://macvendors.co/api/" + mac.macToString()).openConnection() as HttpURLConnection val conn = URL("https://macvendors.co/api/" + mac.macToString()).openConnection() as HttpURLConnection
macLookupBusy[mac] = conn to GlobalScope.launch(Dispatchers.IO) { macLookupBusy[mac] = conn to GlobalScope.launch(Dispatchers.IO) {
@@ -50,11 +58,13 @@ object MacLookup {
} }
} catch (e: IOException) { } catch (e: IOException) {
Timber.d(e) Timber.d(e)
if (explicit) SmartSnackbar.make(e).show()
} catch (e: JSONException) { } catch (e: JSONException) {
if ((e as? UnexpectedError)?.error == "no result") { if ((e as? UnexpectedError)?.error == "no result") {
// no vendor found, we should not retry in the future // no vendor found, we should not retry in the future
AppDatabase.instance.clientRecordDao.upsert(mac) { macLookupPending = false } AppDatabase.instance.clientRecordDao.upsert(mac) { macLookupPending = false }
} else Timber.w(e) } else Timber.w(e)
if (explicit) SmartSnackbar.make(e).show()
} }
} }
} }

View File

@@ -63,7 +63,9 @@
<string name="clients_popup_block_service_inactive">拉黑需要为该接口打开服务。</string> <string name="clients_popup_block_service_inactive">拉黑需要为该接口打开服务。</string>
<string name="clients_popup_unblock">洗白</string> <string name="clients_popup_unblock">洗白</string>
<string name="clients_popup_stats">流量…</string> <string name="clients_popup_stats">流量…</string>
<string name="clients_mac_lookup_unexpected_error">服务器为 %1$s 返回错误:%2$s</string>
<string name="clients_nickname_title">%s 的昵称</string> <string name="clients_nickname_title">%s 的昵称</string>
<string name="clients_nickname_set_to_vendor">← 🏳️‍🌈 厂商</string>
<string name="clients_stats_title">%s 的流量</string> <string name="clients_stats_title">%s 的流量</string>
<plurals name="clients_stats_message_1"> <plurals name="clients_stats_message_1">
<item quantity="other">自 %2$s 以来连接了 %1$s 次</item> <item quantity="other">自 %2$s 以来连接了 %1$s 次</item>

View File

@@ -67,7 +67,9 @@
<string name="clients_popup_block_service_inactive">Turn on service for this interface to block the client.</string> <string name="clients_popup_block_service_inactive">Turn on service for this interface to block the client.</string>
<string name="clients_popup_unblock">Unblock</string> <string name="clients_popup_unblock">Unblock</string>
<string name="clients_popup_stats">Stats…</string> <string name="clients_popup_stats">Stats…</string>
<string name="clients_mac_lookup_unexpected_error">Server returned error for %1$s: %2$s</string>
<string name="clients_nickname_title">Nickname for %s</string> <string name="clients_nickname_title">Nickname for %s</string>
<string name="clients_nickname_set_to_vendor">← 🏳️‍🌈 Vendor</string>
<string name="clients_stats_title">Stats for %s</string> <string name="clients_stats_title">Stats for %s</string>
<plurals name="clients_stats_message_1"> <plurals name="clients_stats_message_1">
<item quantity="one">Connected 1 time since %2$s</item> <item quantity="one">Connected 1 time since %2$s</item>