diff --git a/README.md b/README.md
index 93d14036..45f168d3 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,13 @@
Connecting things to your VPN made simple. Share your VPN connection over hotspot or repeater. (**root required**)
-| Release channel | [GitHub (recommended)](https://github.com/Mygod/VPNHotspot/releases) | [Google Play](https://play.google.com/store/apps/details?id=be.mygod.vpnhotspot) ([beta](https://play.google.com/apps/testing/be.mygod.vpnhotspot)) |
-| --- | :---: | :---: |
-| Monitor connected clients without root | ✓ | Up to Android 10 |
-| Auto update | Email updates via watching releases | ✓ |
-| In-app update channel | GitHub | Google Play |
-| [Sponsor/Donation](https://github.com/sponsors/Mygod) | ✓ | Google Play In-App Purchases only |
+| Release channel | [GitHub (recommended)](https://github.com/Mygod/VPNHotspot/releases) | [Google Play](https://play.google.com/store/apps/details?id=be.mygod.vpnhotspot) ([beta](https://play.google.com/apps/testing/be.mygod.vpnhotspot)) |
+|---------------------------------------------------------|:--------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------:|
+| Monitor connected clients without root | ✓ | Up to Android 10 |
+| Use repeater/temporary hotspot without location enabled | Up to Android 10/9 | Up to Android 10/9 or Android 13+ |
+| Auto update | Email updates via watching releases | ✓ |
+| In-app update channel | GitHub | Google Play |
+| [Sponsor/Donation](https://github.com/sponsors/Mygod) | ✓ | Google Play In-App Purchases only |
This app is useful for:
diff --git a/mobile/src/google/AndroidManifest.xml b/mobile/src/google/AndroidManifest.xml
index 0c4ac9c4..62479015 100644
--- a/mobile/src/google/AndroidManifest.xml
+++ b/mobile/src/google/AndroidManifest.xml
@@ -1,5 +1,12 @@
-
+
+
+
+
+
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt
index 8a74252b..9856e2c9 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt
@@ -2,11 +2,16 @@ package be.mygod.vpnhotspot
import android.annotation.SuppressLint
import android.app.Application
+import android.content.ActivityNotFoundException
import android.content.ClipboardManager
+import android.content.Context
+import android.content.Intent
import android.content.res.Configuration
import android.location.LocationManager
import android.os.Build
+import android.provider.Settings
import android.util.Log
+import android.widget.Toast
import androidx.annotation.Size
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
@@ -19,6 +24,7 @@ import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.root.RootManager
import be.mygod.vpnhotspot.util.DeviceStorageApp
import be.mygod.vpnhotspot.util.Services
+import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.google.firebase.analytics.ktx.ParametersBuilder
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
@@ -104,6 +110,26 @@ class App : Application() {
Firebase.analytics.logEvent(event, builder.bundle)
}
+ /**
+ * LOH also requires location to be turned on. So does p2p for some reason. Source:
+ * https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/53e0284/service/java/com/android/server/wifi/WifiServiceImpl.java#1204
+ * https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/53e0284/service/java/com/android/server/wifi/WifiSettingsStore.java#228
+ */
+ inline fun startServiceWithLocation(context: Context) {
+ if (BuildConfig.TARGET_SDK >= 33 && Build.VERSION.SDK_INT >= 33 || if (Build.VERSION.SDK_INT >= 28) {
+ location?.isLocationEnabled == true
+ } else @Suppress("DEPRECATION") {
+ Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF
+ }) ContextCompat.startForegroundService(context, Intent(context, T::class.java)) else try {
+ context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
+ Toast.makeText(context, R.string.tethering_location_off, Toast.LENGTH_LONG).show()
+ } catch (e: ActivityNotFoundException) {
+ app.logEvent("location_settings") { param("message", e.toString()) }
+ SmartSnackbar.make(R.string.tethering_location_off).show()
+ }
+ }
+
lateinit var deviceStorage: Application
val english by lazy {
createConfigurationContext(Configuration(resources.configuration).apply {
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt
index c689d6f2..8c44a15e 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt
@@ -364,7 +364,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
launch {
val filter = intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
- if (Build.VERSION.SDK_INT >= 30) filter.addAction(LocationManager.MODE_CHANGED_ACTION)
+ if (Build.VERSION.SDK_INT in 30 until 33) filter.addAction(LocationManager.MODE_CHANGED_ACTION)
registerReceiver(receiver, filter)
receiverRegistered = true
val group = p2pManager.requestGroupInfo(channel)
@@ -397,7 +397,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
override fun onSuccess() {
// wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3
// in order for this to happen, we need to make sure that the callbacks are firing
- if (Build.VERSION.SDK_INT >= 30) onLocationModeChanged(app.location?.isLocationEnabled == true)
+ if (Build.VERSION.SDK_INT in 30 until 33) onLocationModeChanged(app.location?.isLocationEnabled == true)
}
}
val channel = channel ?: return listener.onFailure(WifiP2pManager.BUSY)
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt
index 62db344d..03ddb23e 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt
@@ -4,26 +4,26 @@ import android.Manifest
import android.content.*
import android.os.Build
import android.os.IBinder
-import android.provider.Settings
import android.view.View
-import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.App.Companion.app
+import be.mygod.vpnhotspot.BuildConfig
import be.mygod.vpnhotspot.LocalOnlyHotspotService
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.formatAddresses
-import be.mygod.vpnhotspot.widget.SmartSnackbar
import java.net.NetworkInterface
@RequiresApi(26)
class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager(), ServiceConnection {
companion object {
- val permission = if (Build.VERSION.SDK_INT >= 29) {
- Manifest.permission.ACCESS_FINE_LOCATION
- } else Manifest.permission.ACCESS_COARSE_LOCATION
+ val permission = when {
+ BuildConfig.TARGET_SDK >= 33 && Build.VERSION.SDK_INT >= 33 -> Manifest.permission.NEARBY_WIFI_DEVICES
+ Build.VERSION.SDK_INT >= 29 -> Manifest.permission.ACCESS_FINE_LOCATION
+ else -> Manifest.permission.ACCESS_COARSE_LOCATION
+ }
}
class ViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
@@ -55,23 +55,7 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager()
ServiceForegroundConnector(parent, this, LocalOnlyHotspotService::class)
}
- /**
- * LOH also requires location to be turned on. Source:
- * https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/53e0284/service/java/com/android/server/wifi/WifiServiceImpl.java#1204
- * https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/53e0284/service/java/com/android/server/wifi/WifiSettingsStore.java#228
- */
- fun start(context: Context) {
- if (if (Build.VERSION.SDK_INT < 28) @Suppress("DEPRECATION") {
- Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF) == Settings.Secure.LOCATION_MODE_OFF
- } else app.location?.isLocationEnabled != true) try {
- context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
- Toast.makeText(context, R.string.tethering_temp_hotspot_location, Toast.LENGTH_LONG).show()
- } catch (e: ActivityNotFoundException) {
- app.logEvent("location_settings") { param("message", e.toString()) }
- SmartSnackbar.make(R.string.tethering_temp_hotspot_location).show()
- } else context.startForegroundService(Intent(context, LocalOnlyHotspotService::class.java))
- }
+ fun start(context: Context) = app.startServiceWithLocation(context)
override val type get() = VIEW_TYPE_LOCAL_ONLY_HOTSPOT
private val data = Data()
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 82db1e60..faa4f29e 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt
@@ -92,7 +92,9 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
when (binder?.service?.status) {
RepeaterService.Status.IDLE -> if (Build.VERSION.SDK_INT < 29) parent.requireContext().let { context ->
ContextCompat.startForegroundService(context, Intent(context, RepeaterService::class.java))
- } else parent.startRepeater.launch(Manifest.permission.ACCESS_FINE_LOCATION)
+ } else parent.startRepeater.launch(if (BuildConfig.TARGET_SDK >= 33 && Build.VERSION.SDK_INT >= 33) {
+ Manifest.permission.NEARBY_WIFI_DEVICES
+ } else Manifest.permission.ACCESS_FINE_LOCATION)
RepeaterService.Status.ACTIVE -> binder.shutdown()
else -> { }
}
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
index 9f40b2c2..17d0d230 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt
@@ -24,6 +24,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.*
+import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.databinding.FragmentTetheringBinding
import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager
@@ -136,7 +137,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
@RequiresApi(29)
val startRepeater = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
- if (granted) requireActivity().startForegroundService(Intent(activity, RepeaterService::class.java)) else {
+ if (granted) app.startServiceWithLocation(requireContext()) else {
Snackbar.make((activity as MainActivity).binding.fragmentHolder,
R.string.repeater_missing_location_permissions, Snackbar.LENGTH_LONG).show()
}
diff --git a/mobile/src/main/res/values-it/strings.xml b/mobile/src/main/res/values-it/strings.xml
index 223c86c3..64d0c3b7 100644
--- a/mobile/src/main/res/values-it/strings.xml
+++ b/mobile/src/main/res/values-it/strings.xml
@@ -38,7 +38,7 @@
Servizio non disponibile. Riprova dopo
Hotspot Wi\u2011Fi temporaneo
- L\'hotspot temporaneo richiede che la localizzazione sia attiva.
+ L\'hotspot temporaneo richiede che la localizzazione sia attiva.
Avvio dell\'hotspot fallito (causa: %s)
nessun canale
errore generico
diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml
index c17ea4a8..685f7586 100644
--- a/mobile/src/main/res/values-zh-rCN/strings.xml
+++ b/mobile/src/main/res/values-zh-rCN/strings.xml
@@ -34,7 +34,7 @@
进入设置
临时 WLAN 热点
- 使用临时热点需要打开位置服务。
+ 使用此功能需要打开位置服务。
打开热点失败 (原因:%s)
无频段
通用错误
diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml
index b8b29975..225ac273 100644
--- a/mobile/src/main/res/values-zh-rTW/strings.xml
+++ b/mobile/src/main/res/values-zh-rTW/strings.xml
@@ -44,7 +44,7 @@
設定
臨時 Wi\u2011Fi 無線基地台
- 開啟臨時無線基地台須開啟定位
+ 開啟須開啟定位
啟動無線基地台失敗 (原因:%s)
沒有頻道
一般錯誤
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index acad391d..24431a38 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -53,7 +53,7 @@
Configure
Temporary Wi\u2011Fi hotspot
- Temporary hotspot requires location to be turned on.
+ This feature requires location to be turned on.
Failed to start hotspot (reason: %s)
no channel
generic error