package be.mygod.vpnhotspot.manage import android.Manifest import android.annotation.TargetApi import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager import android.location.LocationManager import android.os.Build import android.os.IBinder import android.provider.Settings import android.view.View import android.widget.Toast import androidx.core.content.getSystemService import androidx.core.os.bundleOf import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.DebugHelper 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 @TargetApi(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 } class ViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root), View.OnClickListener { init { itemView.setOnClickListener(this) } lateinit var manager: LocalOnlyHotspotManager override fun onClick(view: View) { val binder = manager.binder if (binder?.iface != null) binder.stop() else { val context = manager.parent.requireContext() if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { manager.parent.requestPermissions(arrayOf(permission), TetheringFragment.START_LOCAL_ONLY_HOTSPOT) return } /** * 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 */ 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 context.getSystemService()?.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) { DebugHelper.logEvent("location_settings", bundleOf(Pair("message", e.message))) SmartSnackbar.make(R.string.tethering_temp_hotspot_location).show() } return } context.startForegroundService(Intent(context, LocalOnlyHotspotService::class.java)) } } } private inner class Data : be.mygod.vpnhotspot.manage.Data() { private val lookup: Map get() = parent.ifaceLookup override val icon get() = R.drawable.ic_action_perm_scan_wifi override val title: CharSequence get() = parent.getString(R.string.tethering_temp_hotspot) override val text: CharSequence get() { return lookup[binder?.iface ?: return ""]?.formatAddresses() ?: "" } override val active get() = binder?.iface != null override val selectable get() = active } init { ServiceForegroundConnector(parent, this, LocalOnlyHotspotService::class) } override val type get() = VIEW_TYPE_LOCAL_ONLY_HOTSPOT private val data = Data() internal var binder: LocalOnlyHotspotService.Binder? = null override fun bindTo(viewHolder: RecyclerView.ViewHolder) { viewHolder as ViewHolder viewHolder.binding.data = data viewHolder.manager = this } fun update() = data.notifyChange() override fun onServiceConnected(name: ComponentName?, service: IBinder?) { binder = service as LocalOnlyHotspotService.Binder service.ifaceChanged[this] = { data.notifyChange() } } override fun onServiceDisconnected(name: ComponentName?) { binder?.ifaceChanged?.remove(this) binder = null } }