SmartSnackbars

This commit is contained in:
Mygod
2018-08-08 16:03:30 +08:00
parent 7ad1a51832
commit 15d838893c
13 changed files with 105 additions and 47 deletions

View File

@@ -9,8 +9,6 @@ import android.net.wifi.WifiManager
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import be.mygod.vpnhotspot.util.DeviceStorageApp import be.mygod.vpnhotspot.util.DeviceStorageApp
import be.mygod.vpnhotspot.util.Event0 import be.mygod.vpnhotspot.util.Event0
@@ -55,6 +53,4 @@ class App : Application() {
val masquerade: Boolean get() = pref.getBoolean(KEY_MASQUERADE, true) val masquerade: Boolean get() = pref.getBoolean(KEY_MASQUERADE, true)
val cleanRoutings = Event0() val cleanRoutings = Event0()
fun toast(@StringRes resId: Int) = handler.post { Toast.makeText(this, resId, Toast.LENGTH_SHORT).show() }
} }

View File

@@ -3,7 +3,6 @@ package be.mygod.vpnhotspot
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.manage.LocalOnlyHotspotManager import be.mygod.vpnhotspot.manage.LocalOnlyHotspotManager
@@ -11,6 +10,7 @@ import be.mygod.vpnhotspot.net.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.debugLog import be.mygod.vpnhotspot.util.debugLog
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
@RequiresApi(26) @RequiresApi(26)
@@ -89,7 +89,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() {
getString(R.string.tethering_temp_hotspot_failure_tethering_disallowed) getString(R.string.tethering_temp_hotspot_failure_tethering_disallowed)
else -> getString(R.string.failure_reason_unknown, reason) else -> getString(R.string.failure_reason_unknown, reason)
}) })
Toast.makeText(this@LocalOnlyHotspotService, message, Toast.LENGTH_SHORT).show() SmartSnackbar.make(message).show()
startFailure(if (reason == WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE) null else startFailure(if (reason == WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE) null else
StartFailure(message)) StartFailure(message))
} }
@@ -98,7 +98,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() {
e.printStackTrace() e.printStackTrace()
startFailure(e) startFailure(e)
} catch (e: SecurityException) { } catch (e: SecurityException) {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() SmartSnackbar.make(e.localizedMessage).show()
e.printStackTrace() e.printStackTrace()
startFailure(e) startFailure(e)
} }

View File

@@ -1,9 +1,9 @@
package be.mygod.vpnhotspot package be.mygod.vpnhotspot
import android.widget.Toast
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.Routing
import be.mygod.vpnhotspot.net.UpstreamMonitor import be.mygod.vpnhotspot.net.UpstreamMonitor
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.net.InetAddress import java.net.InetAddress
import java.net.InterfaceAddress import java.net.InterfaceAddress
@@ -48,9 +48,9 @@ class LocalOnlyInterfaceManager(val downstream: String) : UpstreamMonitor.Callba
routing.ipForward() // local only interfaces need not enable ip_forward routing.ipForward() // local only interfaces need not enable ip_forward
.rule().forward(strict) .rule().forward(strict)
if (app.masquerade) routing.masquerade(strict) if (app.masquerade) routing.masquerade(strict)
if (!routing.dnsRedirect(dns).start()) app.toast(R.string.noisy_su_failure) if (!routing.dnsRedirect(dns).start()) SmartSnackbar.make(R.string.noisy_su_failure).show()
} catch (e: SocketException) { } catch (e: SocketException) {
Toast.makeText(app, e.message, Toast.LENGTH_SHORT).show() SmartSnackbar.make(e.localizedMessage).show()
Crashlytics.logException(e) Crashlytics.logException(e)
routing = null routing = null
} }

View File

@@ -18,12 +18,17 @@ import be.mygod.vpnhotspot.client.ClientsFragment
import be.mygod.vpnhotspot.databinding.ActivityMainBinding import be.mygod.vpnhotspot.databinding.ActivityMainBinding
import be.mygod.vpnhotspot.manage.TetheringFragment import be.mygod.vpnhotspot.manage.TetheringFragment
import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.google.android.material.bottomnavigation.BottomNavigationMenuView import com.google.android.material.bottomnavigation.BottomNavigationMenuView
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import q.rorbin.badgeview.QBadgeView import q.rorbin.badgeview.QBadgeView
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener, ServiceConnection { class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener, ServiceConnection {
companion object {
var current: MainActivity? = null
}
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private lateinit var badge: QBadgeView private lateinit var badge: QBadgeView
private var clients: ClientMonitorService.Binder? = null private var clients: ClientMonitorService.Binder? = null
@@ -33,7 +38,6 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
.build() .build()
} }
fun launchUrl(url: Uri) = customTabsIntent.launchUrl(this, url) fun launchUrl(url: Uri) = customTabsIntent.launchUrl(this, url)
fun snackbar(text: CharSequence = "") = Snackbar.make(binding.fragmentHolder, text, Snackbar.LENGTH_LONG)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -47,6 +51,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
badge.setGravityOffset(16f, 0f, true) badge.setGravityOffset(16f, 0f, true)
ServiceForegroundConnector(this, this, ClientMonitorService::class) ServiceForegroundConnector(this, this, ClientMonitorService::class)
SmartSnackbar.Register(binding.fragmentHolder)
} }
override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) { override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) {

View File

@@ -11,7 +11,6 @@ import android.net.wifi.p2p.WifiP2pInfo
import android.net.wifi.p2p.WifiP2pManager import android.net.wifi.p2p.WifiP2pManager
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
@@ -22,6 +21,7 @@ import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupI
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps
import be.mygod.vpnhotspot.util.* import be.mygod.vpnhotspot.util.*
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@@ -50,11 +50,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
WpsInfo.KEYPAD WpsInfo.KEYPAD
} }
}, object : WifiP2pManager.ActionListener { }, object : WifiP2pManager.ActionListener {
override fun onSuccess() = Toast.makeText(this@RepeaterService, override fun onSuccess() = SmartSnackbar.make(
if (pin == null) R.string.repeater_wps_success_pbc else R.string.repeater_wps_success_keypad, if (pin == null) R.string.repeater_wps_success_pbc else R.string.repeater_wps_success_keypad)
Toast.LENGTH_SHORT).show() .shortToast().show()
override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService, override fun onFailure(reason: Int) = SmartSnackbar.make(
formatReason(R.string.repeater_wps_failure, reason), Toast.LENGTH_SHORT).show() formatReason(R.string.repeater_wps_failure, reason)).show()
}) })
} }
@@ -64,10 +64,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
fun resetCredentials() = (groups + group).filterNotNull().forEach { fun resetCredentials() = (groups + group).filterNotNull().forEach {
p2pManager.deletePersistentGroup(channel, it.netId, object : WifiP2pManager.ActionListener { p2pManager.deletePersistentGroup(channel, it.netId, object : WifiP2pManager.ActionListener {
override fun onSuccess() = Toast.makeText(this@RepeaterService, override fun onSuccess() = SmartSnackbar.make(R.string.repeater_reset_credentials_success)
R.string.repeater_reset_credentials_success, Toast.LENGTH_SHORT).show() .shortToast().show()
override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService, override fun onFailure(reason: Int) = SmartSnackbar.make(
formatReason(R.string.repeater_reset_credentials_failure, reason), Toast.LENGTH_SHORT).show() formatReason(R.string.repeater_reset_credentials_failure, reason)).show()
}) })
} }
@@ -81,7 +81,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
} catch (e: ReflectiveOperationException) { } catch (e: ReflectiveOperationException) {
e.printStackTrace() e.printStackTrace()
Crashlytics.logException(e) Crashlytics.logException(e)
Toast.makeText(this@RepeaterService, e.message, Toast.LENGTH_LONG).show() SmartSnackbar.make(e.localizedMessage).show()
} }
} }
} }
@@ -115,7 +115,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
binder.statusChanged() binder.statusChanged()
} }
private fun formatReason(@StringRes resId: Int, reason: Int): String? { private fun formatReason(@StringRes resId: Int, reason: Int): String {
val result = getString(resId, when (reason) { val result = getString(resId, when (reason) {
WifiP2pManager.ERROR -> getString(R.string.repeater_failure_reason_error) WifiP2pManager.ERROR -> getString(R.string.repeater_failure_reason_error)
WifiP2pManager.P2P_UNSUPPORTED -> getString(R.string.repeater_failure_reason_p2p_unsupported) WifiP2pManager.P2P_UNSUPPORTED -> getString(R.string.repeater_failure_reason_p2p_unsupported)
@@ -147,14 +147,13 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
p2pManager.setWifiP2pChannels(channel, 0, oc, object : WifiP2pManager.ActionListener { p2pManager.setWifiP2pChannels(channel, 0, oc, object : WifiP2pManager.ActionListener {
override fun onSuccess() { } override fun onSuccess() { }
override fun onFailure(reason: Int) { override fun onFailure(reason: Int) {
Toast.makeText(this@RepeaterService, formatReason(R.string.repeater_set_oc_failure, reason), SmartSnackbar.make(formatReason(R.string.repeater_set_oc_failure, reason)).show()
Toast.LENGTH_SHORT).show()
} }
}) })
} catch (e: InvocationTargetException) { } catch (e: InvocationTargetException) {
if (oc != 0) { if (oc != 0) {
val message = getString(R.string.repeater_set_oc_failure, e.message) val message = getString(R.string.repeater_set_oc_failure, e.message)
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() SmartSnackbar.make(message).show()
Crashlytics.logException(Failure(message)) Crashlytics.logException(Failure(message))
} }
e.printStackTrace() e.printStackTrace()
@@ -195,9 +194,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener {
override fun onSuccess() = doStart() override fun onSuccess() = doStart()
override fun onFailure(reason: Int) { override fun onFailure(reason: Int) {
Toast.makeText(this@RepeaterService, SmartSnackbar.make(formatReason(R.string.repeater_remove_old_group_failure, reason))
formatReason(R.string.repeater_remove_old_group_failure, reason), .show()
Toast.LENGTH_SHORT).show()
} }
}) })
} }
@@ -237,8 +235,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
status = Status.ACTIVE status = Status.ACTIVE
showNotification(group) showNotification(group)
} }
private fun startFailure(msg: CharSequence?, group: WifiP2pGroup? = null) { private fun startFailure(msg: CharSequence, group: WifiP2pGroup? = null) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() SmartSnackbar.make(msg).show()
showNotification() showNotification()
if (group != null) removeGroup() else clean() if (group != null) removeGroup() else clean()
} }
@@ -251,8 +249,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
override fun onSuccess() = clean() override fun onSuccess() = clean()
override fun onFailure(reason: Int) { override fun onFailure(reason: Int) {
if (reason == WifiP2pManager.BUSY) clean() else { // assuming it's already gone if (reason == WifiP2pManager.BUSY) clean() else { // assuming it's already gone
Toast.makeText(this@RepeaterService, SmartSnackbar.make(formatReason(R.string.repeater_remove_group_failure, reason)).show()
formatReason(R.string.repeater_remove_group_failure, reason), Toast.LENGTH_SHORT).show()
status = Status.ACTIVE status = Status.ACTIVE
} }
} }

View File

@@ -15,6 +15,7 @@ import be.mygod.vpnhotspot.net.UpstreamMonitor
import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat
import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore
import be.mygod.vpnhotspot.util.loggerSuStream import be.mygod.vpnhotspot.util.loggerSuStream
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.takisoft.preferencex.PreferenceFragmentCompat import com.takisoft.preferencex.PreferenceFragmentCompat
import java.io.File import java.io.File
@@ -27,7 +28,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref) preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref)
addPreferencesFromResource(R.xml.pref_settings) addPreferencesFromResource(R.xml.pref_settings)
val mainActivity = activity as MainActivity
val boot = findPreference("service.repeater.startOnBoot") as SwitchPreference val boot = findPreference("service.repeater.startOnBoot") as SwitchPreference
boot.setOnPreferenceChangeListener { _, value -> boot.setOnPreferenceChangeListener { _, value ->
BootReceiver.enabled = value as Boolean BootReceiver.enabled = value as Boolean
@@ -35,7 +35,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
} }
boot.isChecked = BootReceiver.enabled boot.isChecked = BootReceiver.enabled
findPreference("service.clean").setOnPreferenceClickListener { findPreference("service.clean").setOnPreferenceClickListener {
if (Routing.clean() == null) mainActivity.snackbar().setText(R.string.root_unavailable).show() if (Routing.clean() == null) SmartSnackbar.make(R.string.root_unavailable).show()
else app.cleanRoutings() else app.cleanRoutings()
true true
} }
@@ -96,7 +96,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
true true
} }
findPreference("misc.source").setOnPreferenceClickListener { findPreference("misc.source").setOnPreferenceClickListener {
mainActivity.launchUrl("https://github.com/Mygod/VPNHotspot".toUri()) (activity as MainActivity).launchUrl("https://github.com/Mygod/VPNHotspot".toUri())
true true
} }
findPreference("misc.donate").setOnPreferenceClickListener { findPreference("misc.donate").setOnPreferenceClickListener {

View File

@@ -9,6 +9,7 @@ import be.mygod.vpnhotspot.net.Routing
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.UpstreamMonitor import be.mygod.vpnhotspot.net.UpstreamMonitor
import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.net.InetAddress import java.net.InetAddress
import java.net.SocketException import java.net.SocketException
@@ -32,7 +33,7 @@ class TetheringService : IpNeighbourMonitoringService(), UpstreamMonitor.Callbac
private var receiverRegistered = false private var receiverRegistered = false
private val receiver = broadcastReceiver { _, intent -> private val receiver = broadcastReceiver { _, intent ->
synchronized(routings) { synchronized(routings) {
for (iface in routings.keys - TetheringManager.getTetheredIfaces(intent.extras)) for (iface in routings.keys - TetheringManager.getTetheredIfaces(intent.extras!!))
routings.remove(iface)?.stop() routings.remove(iface)?.stop()
updateRoutingsLocked() updateRoutingsLocked()
} }
@@ -60,7 +61,7 @@ class TetheringService : IpNeighbourMonitoringService(), UpstreamMonitor.Callbac
routings.remove(downstream) routings.remove(downstream)
failed = true failed = true
} }
if (failed) app.toast(R.string.noisy_su_failure) if (failed) SmartSnackbar.make(R.string.noisy_su_failure).show()
} else if (!receiverRegistered) { } else if (!receiverRegistered) {
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
app.cleanRoutings[this] = { app.cleanRoutings[this] = {

View File

@@ -22,6 +22,7 @@ import be.mygod.vpnhotspot.net.wifi.P2pSupplicantConfiguration
import be.mygod.vpnhotspot.net.wifi.WifiP2pDialogFragment import be.mygod.vpnhotspot.net.wifi.WifiP2pDialogFragment
import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.formatAddresses import be.mygod.vpnhotspot.util.formatAddresses
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.net.NetworkInterface import java.net.NetworkInterface
import java.net.SocketException import java.net.SocketException
@@ -100,14 +101,13 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
val binder = binder val binder = binder
val group = binder?.service?.group val group = binder?.service?.group
val ssid = group?.networkName val ssid = group?.networkName
val mainActivity = parent.activity as MainActivity
if (ssid != null) { if (ssid != null) {
val wifi = WifiConfiguration() val wifi = WifiConfiguration()
val conf = P2pSupplicantConfiguration() val conf = P2pSupplicantConfiguration()
wifi.SSID = ssid wifi.SSID = ssid
wifi.preSharedKey = group.passphrase wifi.preSharedKey = group.passphrase
if (wifi.preSharedKey == null) { if (wifi.preSharedKey == null) {
wifi.preSharedKey = conf.readPsk { mainActivity.snackbar(it.message.toString()).show() } wifi.preSharedKey = conf.readPsk { SmartSnackbar.make(it.message.toString()).show() }
} }
if (wifi.preSharedKey != null) { if (wifi.preSharedKey != null) {
WifiP2pDialogFragment().apply { WifiP2pDialogFragment().apply {
@@ -118,7 +118,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
return return
} }
} }
mainActivity.snackbar().setText(R.string.repeater_configure_failure).show() SmartSnackbar.make(R.string.repeater_configure_failure).show()
} }
} }

View File

@@ -23,6 +23,7 @@ import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.net.wifi.WifiApManager
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@@ -92,7 +93,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
override fun onTetheringStarted() = data.notifyChange() override fun onTetheringStarted() = data.notifyChange()
override fun onTetheringFailed() = override fun onTetheringFailed() =
(parent.activity as MainActivity).snackbar().setText(R.string.tethering_manage_failed).show() SmartSnackbar.make(R.string.tethering_manage_failed).show()
override fun bindTo(viewHolder: RecyclerView.ViewHolder) { override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
(viewHolder as ViewHolder).manager = this (viewHolder as ViewHolder).manager = this

View File

@@ -4,6 +4,7 @@ import android.util.Log
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.util.thread import be.mygod.vpnhotspot.util.thread
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.io.IOException import java.io.IOException
import java.io.InterruptedIOException import java.io.InterruptedIOException
@@ -67,7 +68,7 @@ abstract class IpMonitor : Runnable {
if (err.isNotBlank()) { if (err.isNotBlank()) {
Crashlytics.log(Log.ERROR, javaClass.simpleName, err) Crashlytics.log(Log.ERROR, javaClass.simpleName, err)
Crashlytics.logException(FlushFailure()) Crashlytics.logException(FlushFailure())
app.toast(R.string.noisy_su_failure) SmartSnackbar.make(R.string.noisy_su_failure).show()
} }
} }
process.inputStream.bufferedReader().useLines(this::processLines) process.inputStream.bufferedReader().useLines(this::processLines)

View File

@@ -12,9 +12,9 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.MainActivity
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.manage.TetheringFragment import be.mygod.vpnhotspot.manage.TetheringFragment
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import java.nio.charset.Charset import java.nio.charset.Charset
@@ -90,8 +90,8 @@ class WifiP2pDialogFragment : DialogFragment(), TextWatcher, DialogInterface.OnC
app.handler.postDelayed((targetFragment as TetheringFragment).adapter.repeaterManager app.handler.postDelayed((targetFragment as TetheringFragment).adapter.repeaterManager
.binder!!::requestGroupUpdate, 1000) .binder!!::requestGroupUpdate, 1000)
} }
false -> (activity as MainActivity).snackbar().setText(R.string.noisy_su_failure).show() false -> SmartSnackbar.make(R.string.noisy_su_failure).show()
null -> (activity as MainActivity).snackbar().setText(R.string.root_unavailable).show() null -> SmartSnackbar.make(R.string.root_unavailable).show()
} }
DialogInterface.BUTTON_NEUTRAL -> DialogInterface.BUTTON_NEUTRAL ->
(targetFragment as TetheringFragment).adapter.repeaterManager.binder!!.resetCredentials() (targetFragment as TetheringFragment).adapter.repeaterManager.binder!!.resetCredentials()

View File

@@ -10,6 +10,7 @@ import androidx.databinding.BindingAdapter
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.BuildConfig import be.mygod.vpnhotspot.BuildConfig
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import java.net.NetworkInterface import java.net.NetworkInterface
import java.net.SocketException import java.net.SocketException
@@ -57,7 +58,7 @@ fun thread(name: String? = null, start: Boolean = true, isDaemon: Boolean = fals
contextClassLoader: ClassLoader? = null, priority: Int = -1, block: () -> Unit): Thread { contextClassLoader: ClassLoader? = null, priority: Int = -1, block: () -> Unit): Thread {
val thread = kotlin.concurrent.thread(false, isDaemon, contextClassLoader, name, priority, block) val thread = kotlin.concurrent.thread(false, isDaemon, contextClassLoader, name, priority, block)
thread.setUncaughtExceptionHandler { _, e -> thread.setUncaughtExceptionHandler { _, e ->
app.toast(R.string.noisy_su_failure) SmartSnackbar.make(R.string.noisy_su_failure).show()
Crashlytics.logException(e) Crashlytics.logException(e)
} }
if (start) thread.start() if (start) thread.start()

View File

@@ -0,0 +1,56 @@
package be.mygod.vpnhotspot.widget
import android.annotation.SuppressLint
import android.view.View
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import be.mygod.vpnhotspot.App.Companion.app
import com.google.android.material.snackbar.Snackbar
sealed class SmartSnackbar {
companion object {
@SuppressLint("StaticFieldLeak")
private var holder: View? = null
fun make(@StringRes text: Int): SmartSnackbar = make(app.getText(text))
fun make(text: CharSequence = ""): SmartSnackbar {
val holder = holder
return if (holder == null) ToastWrapper(Toast.makeText(app, text, Toast.LENGTH_LONG)) else
SnackbarWrapper(Snackbar.make(holder, text, Snackbar.LENGTH_LONG))
}
}
class Register(private val view: View) : LifecycleObserver {
init {
(view.context as LifecycleOwner).lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
holder = view
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
if (holder === view) holder = null
}
}
abstract fun show()
open fun shortToast() = this
}
private class SnackbarWrapper(private val snackbar: Snackbar) : SmartSnackbar() {
override fun show() = snackbar.show()
}
private class ToastWrapper(private val toast: Toast) : SmartSnackbar() {
override fun show() {
app.handler.post(toast::show)
}
override fun shortToast() = apply { toast.duration = Toast.LENGTH_SHORT }
}