diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index 1b06c022..2556197d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -9,8 +9,6 @@ import android.net.wifi.WifiManager import android.os.Build import android.os.Handler import android.preference.PreferenceManager -import android.widget.Toast -import androidx.annotation.StringRes import androidx.core.content.getSystemService import be.mygod.vpnhotspot.util.DeviceStorageApp import be.mygod.vpnhotspot.util.Event0 @@ -55,6 +53,4 @@ class App : Application() { val masquerade: Boolean get() = pref.getBoolean(KEY_MASQUERADE, true) val cleanRoutings = Event0() - - fun toast(@StringRes resId: Int) = handler.post { Toast.makeText(this, resId, Toast.LENGTH_SHORT).show() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index 0c956105..67d8154f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -3,7 +3,6 @@ package be.mygod.vpnhotspot import android.content.Intent import android.content.IntentFilter import android.net.wifi.WifiManager -import android.widget.Toast import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.App.Companion.app 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.util.broadcastReceiver import be.mygod.vpnhotspot.util.debugLog +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics @RequiresApi(26) @@ -89,7 +89,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() { getString(R.string.tethering_temp_hotspot_failure_tethering_disallowed) 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(message)) } @@ -98,7 +98,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() { e.printStackTrace() startFailure(e) } catch (e: SecurityException) { - Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() + SmartSnackbar.make(e.localizedMessage).show() e.printStackTrace() startFailure(e) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyInterfaceManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyInterfaceManager.kt index 85fac1a2..52e716ba 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyInterfaceManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyInterfaceManager.kt @@ -1,9 +1,9 @@ package be.mygod.vpnhotspot -import android.widget.Toast import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.UpstreamMonitor +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.net.InetAddress 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 .rule().forward(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) { - Toast.makeText(app, e.message, Toast.LENGTH_SHORT).show() + SmartSnackbar.make(e.localizedMessage).show() Crashlytics.logException(e) routing = null } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt index c663953c..c44c6498 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt @@ -18,12 +18,17 @@ import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.databinding.ActivityMainBinding import be.mygod.vpnhotspot.manage.TetheringFragment 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.BottomNavigationView import com.google.android.material.snackbar.Snackbar import q.rorbin.badgeview.QBadgeView class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener, ServiceConnection { + companion object { + var current: MainActivity? = null + } + private lateinit var binding: ActivityMainBinding private lateinit var badge: QBadgeView private var clients: ClientMonitorService.Binder? = null @@ -33,7 +38,6 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS .build() } 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?) { super.onCreate(savedInstanceState) @@ -47,6 +51,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL badge.setGravityOffset(16f, 0f, true) ServiceForegroundConnector(this, this, ClientMonitorService::class) + SmartSnackbar.Register(binding.fragmentHolder) } override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index e8419bc9..996cd9f6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -11,7 +11,6 @@ import android.net.wifi.p2p.WifiP2pInfo import android.net.wifi.p2p.WifiP2pManager import android.os.Looper import android.util.Log -import android.widget.Toast import androidx.annotation.StringRes import androidx.core.content.getSystemService 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.startWps import be.mygod.vpnhotspot.util.* +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.lang.reflect.InvocationTargetException @@ -50,11 +50,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere WpsInfo.KEYPAD } }, object : WifiP2pManager.ActionListener { - override fun onSuccess() = Toast.makeText(this@RepeaterService, - if (pin == null) R.string.repeater_wps_success_pbc else R.string.repeater_wps_success_keypad, - Toast.LENGTH_SHORT).show() - override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService, - formatReason(R.string.repeater_wps_failure, reason), Toast.LENGTH_SHORT).show() + override fun onSuccess() = SmartSnackbar.make( + if (pin == null) R.string.repeater_wps_success_pbc else R.string.repeater_wps_success_keypad) + .shortToast().show() + override fun onFailure(reason: Int) = SmartSnackbar.make( + formatReason(R.string.repeater_wps_failure, reason)).show() }) } @@ -64,10 +64,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere fun resetCredentials() = (groups + group).filterNotNull().forEach { p2pManager.deletePersistentGroup(channel, it.netId, object : WifiP2pManager.ActionListener { - override fun onSuccess() = Toast.makeText(this@RepeaterService, - R.string.repeater_reset_credentials_success, Toast.LENGTH_SHORT).show() - override fun onFailure(reason: Int) = Toast.makeText(this@RepeaterService, - formatReason(R.string.repeater_reset_credentials_failure, reason), Toast.LENGTH_SHORT).show() + override fun onSuccess() = SmartSnackbar.make(R.string.repeater_reset_credentials_success) + .shortToast().show() + override fun onFailure(reason: Int) = SmartSnackbar.make( + formatReason(R.string.repeater_reset_credentials_failure, reason)).show() }) } @@ -81,7 +81,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere } catch (e: ReflectiveOperationException) { e.printStackTrace() 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() } - private fun formatReason(@StringRes resId: Int, reason: Int): String? { + private fun formatReason(@StringRes resId: Int, reason: Int): String { val result = getString(resId, when (reason) { WifiP2pManager.ERROR -> getString(R.string.repeater_failure_reason_error) 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 { override fun onSuccess() { } override fun onFailure(reason: Int) { - Toast.makeText(this@RepeaterService, formatReason(R.string.repeater_set_oc_failure, reason), - Toast.LENGTH_SHORT).show() + SmartSnackbar.make(formatReason(R.string.repeater_set_oc_failure, reason)).show() } }) } catch (e: InvocationTargetException) { if (oc != 0) { 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)) } e.printStackTrace() @@ -195,9 +194,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener { override fun onSuccess() = doStart() override fun onFailure(reason: Int) { - Toast.makeText(this@RepeaterService, - formatReason(R.string.repeater_remove_old_group_failure, reason), - Toast.LENGTH_SHORT).show() + SmartSnackbar.make(formatReason(R.string.repeater_remove_old_group_failure, reason)) + .show() } }) } @@ -237,8 +235,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere status = Status.ACTIVE showNotification(group) } - private fun startFailure(msg: CharSequence?, group: WifiP2pGroup? = null) { - Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + private fun startFailure(msg: CharSequence, group: WifiP2pGroup? = null) { + SmartSnackbar.make(msg).show() showNotification() if (group != null) removeGroup() else clean() } @@ -251,8 +249,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onSuccess() = clean() override fun onFailure(reason: Int) { if (reason == WifiP2pManager.BUSY) clean() else { // assuming it's already gone - Toast.makeText(this@RepeaterService, - formatReason(R.string.repeater_remove_group_failure, reason), Toast.LENGTH_SHORT).show() + SmartSnackbar.make(formatReason(R.string.repeater_remove_group_failure, reason)).show() status = Status.ACTIVE } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt index 5b51ba0d..582c5d14 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt @@ -15,6 +15,7 @@ import be.mygod.vpnhotspot.net.UpstreamMonitor import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore import be.mygod.vpnhotspot.util.loggerSuStream +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import com.takisoft.preferencex.PreferenceFragmentCompat import java.io.File @@ -27,7 +28,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref) addPreferencesFromResource(R.xml.pref_settings) - val mainActivity = activity as MainActivity val boot = findPreference("service.repeater.startOnBoot") as SwitchPreference boot.setOnPreferenceChangeListener { _, value -> BootReceiver.enabled = value as Boolean @@ -35,7 +35,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { } boot.isChecked = BootReceiver.enabled 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() true } @@ -96,7 +96,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { true } findPreference("misc.source").setOnPreferenceClickListener { - mainActivity.launchUrl("https://github.com/Mygod/VPNHotspot".toUri()) + (activity as MainActivity).launchUrl("https://github.com/Mygod/VPNHotspot".toUri()) true } findPreference("misc.donate").setOnPreferenceClickListener { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt index 950193ad..51584b3d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt @@ -9,6 +9,7 @@ import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.UpstreamMonitor import be.mygod.vpnhotspot.util.broadcastReceiver +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.net.InetAddress import java.net.SocketException @@ -32,7 +33,7 @@ class TetheringService : IpNeighbourMonitoringService(), UpstreamMonitor.Callbac private var receiverRegistered = false private val receiver = broadcastReceiver { _, intent -> synchronized(routings) { - for (iface in routings.keys - TetheringManager.getTetheredIfaces(intent.extras)) + for (iface in routings.keys - TetheringManager.getTetheredIfaces(intent.extras!!)) routings.remove(iface)?.stop() updateRoutingsLocked() } @@ -60,7 +61,7 @@ class TetheringService : IpNeighbourMonitoringService(), UpstreamMonitor.Callbac routings.remove(downstream) failed = true } - if (failed) app.toast(R.string.noisy_su_failure) + if (failed) SmartSnackbar.make(R.string.noisy_su_failure).show() } else if (!receiverRegistered) { registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) app.cleanRoutings[this] = { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 4559571f..9072c591 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -22,6 +22,7 @@ import be.mygod.vpnhotspot.net.wifi.P2pSupplicantConfiguration import be.mygod.vpnhotspot.net.wifi.WifiP2pDialogFragment import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.formatAddresses +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.net.NetworkInterface import java.net.SocketException @@ -100,14 +101,13 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic val binder = binder val group = binder?.service?.group val ssid = group?.networkName - val mainActivity = parent.activity as MainActivity if (ssid != null) { val wifi = WifiConfiguration() val conf = P2pSupplicantConfiguration() wifi.SSID = ssid wifi.preSharedKey = group.passphrase 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) { WifiP2pDialogFragment().apply { @@ -118,7 +118,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic return } } - mainActivity.snackbar().setText(R.string.repeater_configure_failure).show() + SmartSnackbar.make(R.string.repeater_configure_failure).show() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt index d88e9900..53677ca3 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -23,6 +23,7 @@ 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.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.lang.reflect.InvocationTargetException @@ -92,7 +93,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override fun onTetheringStarted() = data.notifyChange() 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) { (viewHolder as ViewHolder).manager = this diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpMonitor.kt index b6c82bc4..19d77f58 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpMonitor.kt @@ -4,6 +4,7 @@ import android.util.Log import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.util.thread +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.io.IOException import java.io.InterruptedIOException @@ -67,7 +68,7 @@ abstract class IpMonitor : Runnable { if (err.isNotBlank()) { Crashlytics.log(Log.ERROR, javaClass.simpleName, err) Crashlytics.logException(FlushFailure()) - app.toast(R.string.noisy_su_failure) + SmartSnackbar.make(R.string.noisy_su_failure).show() } } process.inputStream.bufferedReader().useLines(this::processLines) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pDialogFragment.kt index 52e7fb20..aaca5d83 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pDialogFragment.kt @@ -12,9 +12,9 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import be.mygod.vpnhotspot.App.Companion.app -import be.mygod.vpnhotspot.MainActivity import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.manage.TetheringFragment +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.google.android.material.textfield.TextInputLayout import java.nio.charset.Charset @@ -90,8 +90,8 @@ class WifiP2pDialogFragment : DialogFragment(), TextWatcher, DialogInterface.OnC app.handler.postDelayed((targetFragment as TetheringFragment).adapter.repeaterManager .binder!!::requestGroupUpdate, 1000) } - false -> (activity as MainActivity).snackbar().setText(R.string.noisy_su_failure).show() - null -> (activity as MainActivity).snackbar().setText(R.string.root_unavailable).show() + false -> SmartSnackbar.make(R.string.noisy_su_failure).show() + null -> SmartSnackbar.make(R.string.root_unavailable).show() } DialogInterface.BUTTON_NEUTRAL -> (targetFragment as TetheringFragment).adapter.repeaterManager.binder!!.resetCredentials() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 6ff925da..b97bc70c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -10,6 +10,7 @@ import androidx.databinding.BindingAdapter import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.BuildConfig import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.widget.SmartSnackbar import com.crashlytics.android.Crashlytics import java.net.NetworkInterface 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 { val thread = kotlin.concurrent.thread(false, isDaemon, contextClassLoader, name, priority, block) thread.setUncaughtExceptionHandler { _, e -> - app.toast(R.string.noisy_su_failure) + SmartSnackbar.make(R.string.noisy_su_failure).show() Crashlytics.logException(e) } if (start) thread.start() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/widget/SmartSnackbar.kt b/mobile/src/main/java/be/mygod/vpnhotspot/widget/SmartSnackbar.kt new file mode 100644 index 00000000..d51d4b0d --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/widget/SmartSnackbar.kt @@ -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 } +}