diff --git a/app/src/main/java/moe/fuqiuluo/portal/ext/Perfs.kt b/app/src/main/java/moe/fuqiuluo/portal/ext/Perfs.kt index bf2b76a..d11ec69 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ext/Perfs.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ext/Perfs.kt @@ -88,3 +88,55 @@ var Context.needOpenSELinux: Boolean set(value) = sharedPrefs.edit { putBoolean("needOpenSELinux", value) } + +var Context.needDowngradeToCdma: Boolean + get() = sharedPrefs.getBoolean("needDowngradeToCdma", FakeLoc.needDowngradeToCdma) + + set(value) = sharedPrefs.edit { + putBoolean("needDowngradeToCdma", value) + } + +//var Context.updateInterval: Long +// get() = sharedPrefs.getLong("updateInterval", FakeLoc.updateInterval) +// +// set(value) = sharedPrefs.edit { +// putLong("updateInterval", value) +// } +// +//var Context.hideMock: Boolean +// get() = sharedPrefs.getBoolean("hideMock", FakeLoc.hideMock) +// +// set(value) = sharedPrefs.edit { +// putBoolean("hideMock", value) +// } + +var Context.debug: Boolean + get() = sharedPrefs.getBoolean("debug", FakeLoc.enableDebugLog) + + set(value) = sharedPrefs.edit { + putBoolean("debug", value) + } + +var Context.disableGetCurrentLocation: Boolean + get() = sharedPrefs.getBoolean("disableGetCurrentLocation", FakeLoc.disableGetCurrentLocation) + + set(value) = sharedPrefs.edit { + putBoolean("disableGetCurrentLocation", value) + } + +var Context.disableRegitserLocationListener: Boolean + get() = sharedPrefs.getBoolean("disableRegitserLocationListener", FakeLoc.disableRegisterLocationListener) + + set(value) = sharedPrefs.edit { + putBoolean("disableRegitserLocationListener", value) + } + +var Context.disableFusedProvider: Boolean + get() = sharedPrefs.getBoolean("disableFusedProvider", FakeLoc.disableFusedLocation) + + set(value) = sharedPrefs.edit { + putBoolean("disableFusedProvider", value) + } + + + diff --git a/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt b/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt index 10a4c19..dd469ab 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt @@ -7,6 +7,13 @@ import android.os.Build import android.os.Bundle import android.util.Log import moe.fuqiuluo.portal.android.root.ShellUtils +import moe.fuqiuluo.portal.ext.altitude +import moe.fuqiuluo.portal.ext.debug +import moe.fuqiuluo.portal.ext.disableFusedProvider +import moe.fuqiuluo.portal.ext.disableGetCurrentLocation +import moe.fuqiuluo.portal.ext.disableRegitserLocationListener +import moe.fuqiuluo.portal.ext.needDowngradeToCdma +import moe.fuqiuluo.portal.ext.speed import moe.fuqiuluo.xposed.utils.FakeLoc import java.io.File @@ -214,6 +221,33 @@ object MockServiceHelper { return null } + fun putConfig(locationManager: LocationManager, context: Context): Boolean { + if (!::randomKey.isInitialized) { + return false + } + + FakeLoc.altitude = context.altitude + FakeLoc.speed = context.speed + FakeLoc.enableDebugLog = context.debug + FakeLoc.disableGetCurrentLocation = context.disableGetCurrentLocation + FakeLoc.disableRegisterLocationListener = context.disableRegitserLocationListener + FakeLoc.disableFusedLocation = context.disableFusedProvider + FakeLoc.needDowngradeToCdma = context.needDowngradeToCdma + + val rely = Bundle() + rely.putString("command_id", "put_config") + rely.putBoolean("enable", FakeLoc.enable) + rely.putDouble("altitude", FakeLoc.altitude) + rely.putDouble("speed", FakeLoc.speed) + rely.putBoolean("enable_debug_log", FakeLoc.enableDebugLog) + rely.putBoolean("disable_get_current_location", FakeLoc.disableGetCurrentLocation) + rely.putBoolean("disable_register_location_listener", FakeLoc.disableRegisterLocationListener) + rely.putBoolean("disable_fused_location", FakeLoc.disableFusedLocation) + rely.putBoolean("need_downgrade_to_2g", FakeLoc.needDowngradeToCdma) + + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + fun isServiceInit(): Boolean { return ::randomKey.isInitialized } diff --git a/app/src/main/java/moe/fuqiuluo/portal/ui/settings/SettingsFragment.kt b/app/src/main/java/moe/fuqiuluo/portal/ui/settings/SettingsFragment.kt index 725a61f..cd5d414 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ui/settings/SettingsFragment.kt @@ -11,23 +11,37 @@ import android.widget.TextView import android.widget.Toast import androidx.constraintlayout.helper.widget.Carousel import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputEditText +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import moe.fuqiuluo.portal.R import moe.fuqiuluo.portal.databinding.FragmentSettingsBinding import moe.fuqiuluo.portal.ext.accuracy import moe.fuqiuluo.portal.ext.altitude +import moe.fuqiuluo.portal.ext.debug +import moe.fuqiuluo.portal.ext.disableFusedProvider +import moe.fuqiuluo.portal.ext.disableGetCurrentLocation +import moe.fuqiuluo.portal.ext.disableRegitserLocationListener +import moe.fuqiuluo.portal.ext.needDowngradeToCdma import moe.fuqiuluo.portal.ext.needOpenSELinux import moe.fuqiuluo.portal.ext.rawHistoricalLocations import moe.fuqiuluo.portal.ext.speed +import moe.fuqiuluo.portal.service.MockServiceHelper +import moe.fuqiuluo.portal.ui.viewmodel.MockServiceViewModel import moe.fuqiuluo.portal.ui.viewmodel.SettingsViewModel import java.math.BigDecimal +import kotlin.getValue class SettingsFragment : Fragment() { private var _binding: FragmentSettingsBinding? = null private val binding get() = _binding!! + private val mockServiceViewModel by activityViewModels() + @SuppressLint("SetTextI18n") override fun onCreateView( inflater: LayoutInflater, @@ -48,6 +62,7 @@ class SettingsFragment : Fragment() { isChecked: Boolean ) { context.needOpenSELinux = isChecked + showToast(if (isChecked) "已开启SELinux" else "已关闭SELinux") } }) @@ -59,10 +74,10 @@ class SettingsFragment : Fragment() { showDialog("设置海拔高度", binding.altitudeValue.text.toString().let { it.substring(0, it.length - 1) }) { val value = it.toDoubleOrNull() if (value == null || value < 0.0) { - Toast.makeText(context, "海拔高度不合法", Toast.LENGTH_SHORT).show() + showToast("海拔高度不合法") return@showDialog } else if (value > 10000) { - Toast.makeText(context, "海拔高度不能超过10000米", Toast.LENGTH_SHORT).show() + showToast("海拔高度不能超过10000米") return@showDialog } context.altitude = value @@ -74,10 +89,10 @@ class SettingsFragment : Fragment() { showDialog("设置速度", binding.speedValue.text.toString().let { it.substring(0, it.length - 3) }) { val value = it.toDoubleOrNull() if (value == null || value < 0.0) { - Toast.makeText(context, "速度不合法", Toast.LENGTH_SHORT).show() + showToast("速度不合法") return@showDialog } else if (value > 1000) { - Toast.makeText(context, "速度不能超过1000米/秒", Toast.LENGTH_SHORT).show() + showToast("速度不能超过1000米/秒") return@showDialog } context.speed = value @@ -100,13 +115,89 @@ class SettingsFragment : Fragment() { } } + binding.debugSwitch.isChecked = context.debug + binding.debugSwitch.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged( + buttonView: CompoundButton?, + isChecked: Boolean + ) { + context.debug = isChecked + showToast(if (isChecked) "已开启调试模式" else "已关闭调试模式") + updateRemoteConfig() + } + }) + + binding.dgcSwitch.isChecked = !context.disableGetCurrentLocation + binding.dgcSwitch.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged( + buttonView: CompoundButton?, + isChecked: Boolean + ) { + context.disableGetCurrentLocation = !isChecked + showToast(if (!isChecked) "禁止应用使用该方法" else "已允许应用使用该方法") + updateRemoteConfig() + } + }) + + binding.rllSwitch.isChecked = !context.disableRegitserLocationListener + binding.rllSwitch.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged( + buttonView: CompoundButton?, + isChecked: Boolean + ) { + context.disableRegitserLocationListener = !isChecked + showToast(if (!isChecked) "禁止应用使用该方法" else "已允许应用使用该方法") + updateRemoteConfig() + } + }) + + binding.dfusedSwitch.isChecked = context.disableFusedProvider + binding.dfusedSwitch.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged( + buttonView: CompoundButton?, + isChecked: Boolean + ) { + context.disableFusedProvider = isChecked + showToast(if (isChecked) "已禁用FusedProvider" else "已启用FusedProvider") + updateRemoteConfig() + } + }) + + binding.cdmaSwitch.isChecked = context.needDowngradeToCdma + binding.cdmaSwitch.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged( + buttonView: CompoundButton?, + isChecked: Boolean + ) { + context.needDowngradeToCdma = isChecked + showToast(if (isChecked) "已降级为CDMA" else "已取消降级为CDMA") + updateRemoteConfig() + } + }) return root } + private fun showToast(message: String) { + lifecycleScope.launch(Dispatchers.Main) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } + } + + private fun updateRemoteConfig() { + val context = requireContext() + with(mockServiceViewModel) { + if(!MockServiceHelper.putConfig(locationManager!!, context)) { + showToast("更新远程配置失败") + } else { + showToast("同步配置成功") + } + } + } + @SuppressLint("MissingInflatedId") - fun showDialog(titleText: String, valueText: String, handler: (String) -> Unit) { + private fun showDialog(titleText: String, valueText: String, handler: (String) -> Unit) { val inflater = LayoutInflater.from(requireContext()) val dialogView = inflater.inflate(R.layout.dialog_input, null) diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index f99cc62..273ed4e 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -190,6 +190,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c93cc31..199f412 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,4 +43,14 @@ 模拟移动的移动速度 定位精度 模拟定位抖动的范围 + 调试模式 + 是否打印调试日志 + GetCurrentLocation接口 + 是否允许应用使用GetCurrentLocation + RegisterLocationListener接口 + 是否允许应用registerLocationListener + 禁用Fused位置提供器 + 应用使用fused(可能导致模拟失败) + 网络降级 + 将网络降级为CDMA \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt index bf59308..d56a4e5 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt @@ -14,7 +14,7 @@ import kotlin.random.Random object RemoteCommandHandler { private val proxyBinders by lazy { Collections.synchronizedList(arrayListOf()) } - private val needProxyCmd = arrayOf("start", "stop", "set_speed_amp", "set_altitude", "update_location", "move") + private val needProxyCmd = arrayOf("start", "stop", "set_speed_amp", "set_altitude", "set_speed", "update_location", "set_bearing", "move", "put_config") internal val randomKey by lazy { "portal_" + Random.nextDouble() } private var isLoadedLibrary = false @@ -176,6 +176,28 @@ object RemoteCommandHandler { } return true } + "put_config" -> { + val enable = rely.getBoolean("enable", FakeLoc.enable) + val speed = rely.getDouble("speed", FakeLoc.speed) + val altitude = rely.getDouble("altitude", FakeLoc.altitude) + val accuracy = rely.getFloat("accuracy", FakeLoc.accuracy) + val enableDebugLog = rely.getBoolean("enable_debug_log", FakeLoc.enableDebugLog) + val disableGetCurrentLocation = rely.getBoolean("disable_get_current_location", FakeLoc.disableGetCurrentLocation) + val disableRegisterLocationListener = rely.getBoolean("disable_register_location_listener", FakeLoc.disableRegisterLocationListener) + val disableFusedLocation = rely.getBoolean("disable_fused_location", FakeLoc.disableFusedLocation) + val needDowngradeToCdma = rely.getBoolean("need_downgrade_to_2g", FakeLoc.needDowngradeToCdma) + + FakeLoc.enable = enable + FakeLoc.speed = speed + FakeLoc.altitude = altitude + FakeLoc.accuracy = accuracy + FakeLoc.enableDebugLog = enableDebugLog + FakeLoc.disableGetCurrentLocation = disableGetCurrentLocation + FakeLoc.disableRegisterLocationListener = disableRegisterLocationListener + FakeLoc.disableFusedLocation = disableFusedLocation + FakeLoc.needDowngradeToCdma = needDowngradeToCdma + return true + } "sync_config" -> { rely.putBoolean("enable", FakeLoc.enable) rely.putDouble("latitude", FakeLoc.latitude)