Support new permission in Tiramisu

This commit is contained in:
Mygod
2022-07-12 10:18:52 -04:00
parent 77faca0dfb
commit 4e1f7c9c40
11 changed files with 59 additions and 38 deletions

View File

@@ -10,8 +10,9 @@
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 |
| 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 |

View File

@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required since API 31, when targeting API 31 -->
<uses-permission-sdk-23 android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- Required since API 33, when targeting API 33 -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="32"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="32"/>
</manifest>

View File

@@ -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 <reified T> 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 {

View File

@@ -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)

View File

@@ -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<LocalOnlyHotspotService>(context)
override val type get() = VIEW_TYPE_LOCAL_ONLY_HOTSPOT
private val data = Data()

View File

@@ -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 -> { }
}

View File

@@ -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<RepeaterService>(requireContext()) else {
Snackbar.make((activity as MainActivity).binding.fragmentHolder,
R.string.repeater_missing_location_permissions, Snackbar.LENGTH_LONG).show()
}

View File

@@ -38,7 +38,7 @@
<string name="repeater_failure_disconnected">Servizio non disponibile. Riprova dopo</string>
<string name="tethering_temp_hotspot">Hotspot Wi\u2011Fi temporaneo</string>
<string name="tethering_temp_hotspot_location">L\'hotspot temporaneo richiede che la localizzazione sia attiva.</string>
<string name="tethering_location_off">L\'hotspot temporaneo richiede che la localizzazione sia attiva.</string>
<string name="tethering_temp_hotspot_failure">Avvio dell\'hotspot fallito (causa: %s)</string>
<string name="tethering_temp_hotspot_failure_no_channel">nessun canale</string>
<string name="tethering_temp_hotspot_failure_generic">errore generico</string>

View File

@@ -34,7 +34,7 @@
<string name="repeater_location_off_configure">进入设置</string>
<string name="tethering_temp_hotspot">临时 WLAN 热点</string>
<string name="tethering_temp_hotspot_location">使用临时热点需要打开位置服务。</string>
<string name="tethering_location_off">使用此功能需要打开位置服务。</string>
<string name="tethering_temp_hotspot_failure">打开热点失败 (原因:%s)</string>
<string name="tethering_temp_hotspot_failure_no_channel">无频段</string>
<string name="tethering_temp_hotspot_failure_generic">通用错误</string>

View File

@@ -44,7 +44,7 @@
<string name="repeater_location_off_configure">設定</string>
<string name="tethering_temp_hotspot">臨時 Wi\u2011Fi 無線基地台</string>
<string name="tethering_temp_hotspot_location">開啟臨時無線基地台須開啟定位</string>
<string name="tethering_location_off">開啟須開啟定位</string>
<string name="tethering_temp_hotspot_failure">啟動無線基地台失敗 (原因:%s)</string>
<string name="tethering_temp_hotspot_failure_no_channel">沒有頻道</string>
<string name="tethering_temp_hotspot_failure_generic">一般錯誤</string>

View File

@@ -53,7 +53,7 @@
<string name="repeater_location_off_configure">Configure</string>
<string name="tethering_temp_hotspot">Temporary Wi\u2011Fi hotspot</string>
<string name="tethering_temp_hotspot_location">Temporary hotspot requires location to be turned on.</string>
<string name="tethering_location_off">This feature requires location to be turned on.</string>
<string name="tethering_temp_hotspot_failure">Failed to start hotspot (reason: %s)</string>
<string name="tethering_temp_hotspot_failure_no_channel">no channel</string>
<string name="tethering_temp_hotspot_failure_generic">generic error</string>