From 50361faffe2039acbc8062d15a36c6b923989e8a Mon Sep 17 00:00:00 2001 From: fuqiuluo Date: Tue, 15 Oct 2024 12:25:36 +0800 Subject: [PATCH] Support rocker move --- .../android/coro/CoroutineController.kt | 40 ++++ .../portal/service/MockServiceHelper.kt | 93 ++++++++++ .../fuqiuluo/portal/ui/mock/MockFragment.kt | 21 ++- .../moe/fuqiuluo/portal/ui/mock/Rocker.kt | 5 + .../ui/viewmodel/MockServiceViewModel.kt | 21 +++ app/src/main/res/layout/layout_rocker.xml | 146 +++++++++++---- app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/themes.xml | 6 + .../moe/fuqiuluo/xposed/BaseDivineService.kt | 3 +- .../moe/fuqiuluo/xposed/BaseLocationHook.kt | 4 - .../java/moe/fuqiuluo/xposed/FakeLocation.kt | 9 +- .../fuqiuluo/xposed/RemoteCommandHandler.kt | 31 +++- .../xposed/hooks/LocationServiceHook.kt | 2 +- .../fused/{ => miui}/MiFusedLocationHook.kt | 11 +- .../hooks/sensor/SystemSensorManagerHook.kt | 172 ++++++++++++++++++ .../xposed/hooks/telephony/TelephonyHook.kt | 5 +- .../miui/MiuiTelephonyManagerHook.kt | 2 +- .../java/moe/fuqiuluo/xposed/utils/FakeLoc.kt | 91 ++++----- 18 files changed, 562 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineController.kt rename xposed/src/main/java/moe/fuqiuluo/xposed/hooks/fused/{ => miui}/MiFusedLocationHook.kt (93%) create mode 100644 xposed/src/main/java/moe/fuqiuluo/xposed/hooks/sensor/SystemSensorManagerHook.kt rename xposed/src/main/java/moe/fuqiuluo/xposed/hooks/{ => telephony}/miui/MiuiTelephonyManagerHook.kt (97%) diff --git a/app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineController.kt b/app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineController.kt new file mode 100644 index 0000000..a0b1910 --- /dev/null +++ b/app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineController.kt @@ -0,0 +1,40 @@ +package moe.fuqiuluo.portal.android.coro + +import kotlinx.coroutines.channels.Channel + +class CoroutineController { + private val controlChannel = Channel(Channel.UNLIMITED) + var isPaused = false + + suspend fun controlledCoroutine() { + checkControl() + } + + private suspend fun checkControl() { + controlChannel.tryReceive().getOrNull()?.let { + when (it) { + ControlCommand.Pause -> { + isPaused = true + while (controlChannel.receive() != ControlCommand.Resume) { + // do nothing + } + isPaused = false + } + ControlCommand.Resume -> {} + } + } + } + + fun pause() { + controlChannel.trySend(ControlCommand.Pause) + } + + fun resume() { + controlChannel.trySend(ControlCommand.Resume) + } +} + +enum class ControlCommand { + Pause, + Resume +} \ No newline at end of file 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 adeab19..563f171 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/service/MockServiceHelper.kt @@ -3,6 +3,7 @@ package moe.fuqiuluo.portal.service import android.location.LocationManager import android.os.Bundle import android.util.Log +import moe.fuqiuluo.xposed.utils.FakeLoc object MockServiceHelper { const val PROVIDER_NAME = "portal" @@ -79,6 +80,98 @@ object MockServiceHelper { return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) } + fun setBearing(locationManager: LocationManager, bearing: Double): Boolean { + if (!::randomKey.isInitialized) { + return false + } + val rely = Bundle() + rely.putString("command_id", "set_bearing") + rely.putDouble("bearing", bearing) + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + + fun setSpeed(locationManager: LocationManager, speed: Float): Boolean { + if (!::randomKey.isInitialized) { + return false + } + val rely = Bundle() + rely.putString("command_id", "set_speed") + rely.putFloat("speed", speed) + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + + fun setAltitude(locationManager: LocationManager, altitude: Double): Boolean { + if (!::randomKey.isInitialized) { + return false + } + val rely = Bundle() + rely.putString("command_id", "set_altitude") + rely.putDouble("altitude", altitude) + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + + fun setSpeedAmplitude(locationManager: LocationManager, speedAmplitude: Double): Boolean { + if (!::randomKey.isInitialized) { + return false + } + val rely = Bundle() + rely.putString("command_id", "set_speed_amp") + rely.putDouble("speed_amplitude", speedAmplitude) + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + + fun getSpeed(locationManager: LocationManager): Float? { + if (!::randomKey.isInitialized) { + return null + } + val rely = Bundle() + rely.putString("command_id", "get_speed") + if(locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely)) { + return rely.getFloat("speed") + } + return null + } + + fun getBearing(locationManager: LocationManager): Float? { + if (!::randomKey.isInitialized) { + return null + } + val rely = Bundle() + rely.putString("command_id", "get_bearing") + if(locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely)) { + return rely.getFloat("bearing") + } + return null + } + + fun getAltitude(locationManager: LocationManager): Double? { + if (!::randomKey.isInitialized) { + return null + } + val rely = Bundle() + rely.putString("command_id", "get_altitude") + if(locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely)) { + return rely.getDouble("altitude") + } + return null + } + + fun move(locationManager: LocationManager, distance: Double, bearing: Double): Boolean { + if (!::randomKey.isInitialized) { + return false + } + val rely = Bundle() + rely.putString("command_id", "move") + rely.putDouble("n", distance) + rely.putDouble("bearing", bearing) + + if (FakeLoc.enableDebugLog) { + Log.d("MockServiceHelper", "move: distance=$distance, bearing=$bearing") + } + + return locationManager.sendExtraCommand(PROVIDER_NAME, randomKey, rely) + } + fun setLocation(locationManager: LocationManager, lat: Double, lon: Double): Boolean { return updateLocation(locationManager, lat, lon, "=") } diff --git a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt index 72f3ec8..d0334e8 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt @@ -2,13 +2,9 @@ package moe.fuqiuluo.portal.ui.mock import android.annotation.SuppressLint import android.os.Bundle -import android.util.Log import android.view.LayoutInflater -import android.view.MotionEvent import android.view.View import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT -import android.view.WindowManager import android.widget.CheckedTextView import android.widget.Toast import androidx.core.content.ContextCompat @@ -34,6 +30,7 @@ import moe.fuqiuluo.portal.ext.selectLocation import moe.fuqiuluo.portal.service.MockServiceHelper import moe.fuqiuluo.portal.ui.viewmodel.MockServiceViewModel import moe.fuqiuluo.portal.ui.viewmodel.MockViewModel +import moe.fuqiuluo.xposed.utils.FakeLoc class MockFragment : Fragment() { private var _binding: FragmentMockBinding? = null @@ -98,17 +95,30 @@ class MockFragment : Fragment() { rocker.show() } else { rocker.hide() + rockerCoroutineController.pause() } } } rocker.setRockerListener(object: RockerView.Companion.OnMoveListener { override fun onAngle(angle: Double) { - Log.d("Rocker", "angle: $angle") + MockServiceHelper.setBearing(locationManager!!, angle) + FakeLoc.bearing = angle + FakeLoc.hasBearings = true } override fun onLockChanged(isLocked: Boolean) { + isRockerLocked = isLocked + } + + override fun onFinished() { + if (!isRockerLocked) { + rockerCoroutineController.pause() + } + } + override fun onStarted() { + rockerCoroutineController.resume() } }) } @@ -257,6 +267,7 @@ class MockFragment : Fragment() { binding.rocker.isClickable = false binding.rocker.toggle() mockServiceViewModel.rocker.hide() + mockServiceViewModel.rockerCoroutineController.pause() binding.rocker.isClickable = true } } finally { diff --git a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/Rocker.kt b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/Rocker.kt index 63a5d43..90af6f6 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/Rocker.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/Rocker.kt @@ -11,6 +11,7 @@ import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.WindowManager +import android.widget.Toast import moe.fuqiuluo.portal.R import moe.fuqiuluo.portal.android.widget.RockerView import moe.fuqiuluo.portal.ext.rockerCoords @@ -50,6 +51,10 @@ class Rocker(private val activity: Activity): View.OnTouchListener { layoutParams.y = rockerCoords.second root.setOnTouchListener(this) + + root.findViewById(R.id.expand_menu).setOnClickListener { + Toast.makeText(activity, "暂不支持", Toast.LENGTH_SHORT).show() + } } fun show() { diff --git a/app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/MockServiceViewModel.kt b/app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/MockServiceViewModel.kt index ed91835..c6e7115 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/MockServiceViewModel.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/MockServiceViewModel.kt @@ -2,10 +2,16 @@ package moe.fuqiuluo.portal.ui.viewmodel import android.app.Activity import android.location.LocationManager +import android.util.Log import androidx.lifecycle.ViewModel +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import moe.fuqiuluo.portal.android.coro.CoroutineController import moe.fuqiuluo.portal.service.MockServiceHelper import moe.fuqiuluo.portal.ui.mock.HistoricalLocation import moe.fuqiuluo.portal.ui.mock.Rocker +import moe.fuqiuluo.xposed.utils.FakeLoc class MockServiceViewModel: ViewModel() { lateinit var rocker: Rocker @@ -19,10 +25,25 @@ class MockServiceViewModel: ViewModel() { var selectedLocation: HistoricalLocation? = null + var isRockerLocked = false + val rockerCoroutineController = CoroutineController() + fun initRocker(activity: Activity): Rocker { if (!::rocker.isInitialized) { rocker = Rocker(activity) } + + rockerCoroutineController.pause() + GlobalScope.launch { + do { + delay(1000) + rockerCoroutineController.controlledCoroutine() + + if(!MockServiceHelper.move(locationManager!!, FakeLoc.speed, FakeLoc.bearing)) { + Log.e("MockServiceViewModel", "Failed to move") + } + } while (true) + } return rocker } diff --git a/app/src/main/res/layout/layout_rocker.xml b/app/src/main/res/layout/layout_rocker.xml index 43b92ec..7084311 100644 --- a/app/src/main/res/layout/layout_rocker.xml +++ b/app/src/main/res/layout/layout_rocker.xml @@ -25,47 +25,131 @@ app:cardCornerRadius="12dp" app:cardBackgroundColor="@color/white" app:cardElevation="4dp"> - - + android:orientation="vertical"> - - - + + + + + + + + + + + + + + + + + app:layout_constraintStart_toStartOf="parent"> - + + - + + + + + + + \ 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 f7607d7..c7d8375 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,4 +34,6 @@ 名称 独属于我的蒙娜丽莎 + 摇杆速度 + 1m/s \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 2fd2610..49977b9 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -27,6 +27,12 @@ @style/Portal.SearchViewStyle @drawable/ripple_effect + + @style/Portal.SliderTheme + + +