├── .github ├── dependabot.yml └── workflows │ ├── build-apk.yml │ └── build-pr-apk.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── libs │ ├── BaiduLBS_Android.jar │ ├── arm64-v8a │ │ ├── libBaiduMapSDK_base_v7_6_2.so │ │ ├── libBaiduMapSDK_map_v7_6_2.so │ │ ├── libc++_shared.so │ │ ├── liblocSDK8b.so │ │ └── libtiny_magic.so │ └── x86_64 │ │ ├── libBaiduMapSDK_base_v7_6_2.so │ │ ├── libBaiduMapSDK_map_v7_6_2.so │ │ ├── libc++_shared.so │ │ ├── liblocSDK8b.so │ │ └── libtiny_magic.so ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── moe │ │ └── fuqiuluo │ │ └── portal │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── moe │ │ │ └── fuqiuluo │ │ │ └── portal │ │ │ ├── MainActivity.kt │ │ │ ├── Portal.kt │ │ │ ├── android │ │ │ ├── Bugly.kt │ │ │ ├── coro │ │ │ │ ├── CoroutineController.kt │ │ │ │ └── CoroutineRouteMock.kt │ │ │ ├── permission │ │ │ │ └── RequestPermissions.kt │ │ │ ├── rom │ │ │ │ └── RomUtils.kt │ │ │ ├── root │ │ │ │ └── ShellUtils.kt │ │ │ ├── widget │ │ │ │ ├── DeveloperView.kt │ │ │ │ ├── RockerView.kt │ │ │ │ └── SatelliteRadar.kt │ │ │ └── window │ │ │ │ └── OverlayUtils.kt │ │ │ ├── bdmap │ │ │ ├── BDMap.kt │ │ │ └── Poi.kt │ │ │ ├── ext │ │ │ ├── Loc.kt │ │ │ ├── Oveylay.kt │ │ │ └── Perfs.kt │ │ │ ├── service │ │ │ └── MockServiceHelper.kt │ │ │ └── ui │ │ │ ├── gnss │ │ │ └── GnssMockFragment.kt │ │ │ ├── home │ │ │ └── HomeFragment.kt │ │ │ ├── mock │ │ │ ├── HistoricalLocation.kt │ │ │ ├── HistoricalLocationAdapter.kt │ │ │ ├── HistoricalRoute.kt │ │ │ ├── HistoricalRouteAdapter.kt │ │ │ ├── MockFragment.kt │ │ │ ├── Rocker.kt │ │ │ ├── RouteEditFragment.kt │ │ │ └── RouteMockFragment.kt │ │ │ ├── notification │ │ │ └── NotificationUtils.java │ │ │ ├── settings │ │ │ └── SettingsFragment.kt │ │ │ └── viewmodel │ │ │ ├── BaiduMapViewModel.kt │ │ │ ├── HomeViewModel.kt │ │ │ ├── MockServiceViewModel.kt │ │ │ ├── MockViewModel.kt │ │ │ └── SettingsViewModel.kt │ └── res │ │ ├── drawable │ │ ├── ani_card.jpeg │ │ ├── baseline_add_location_24.xml │ │ ├── baseline_add_location_alt_24.xml │ │ ├── baseline_adjust_24.xml │ │ ├── baseline_arrow_back_24.xml │ │ ├── baseline_close_24.xml │ │ ├── baseline_complete_24.xml │ │ ├── baseline_control_camera_24.xml │ │ ├── baseline_edit_location_alt_24.xml │ │ ├── baseline_info_outline_24.xml │ │ ├── baseline_keyboard_arrow_down_24.xml │ │ ├── baseline_keyboard_arrow_up_24.xml │ │ ├── baseline_label_important_24.xml │ │ ├── baseline_location_on_24.xml │ │ ├── baseline_location_pin_24.xml │ │ ├── baseline_lock_24.xml │ │ ├── baseline_lock_open_24.xml │ │ ├── baseline_manual_24.xml │ │ ├── baseline_my_location_24.xml │ │ ├── baseline_play_24.xml │ │ ├── baseline_robot_24.xml │ │ ├── baseline_rollback_24.xml │ │ ├── baseline_route_24.xml │ │ ├── baseline_satellite_alt_24.xml │ │ ├── baseline_save_24.xml │ │ ├── baseline_search_24.xml │ │ ├── baseline_settings_24.xml │ │ ├── baseline_settings_suggest_24.xml │ │ ├── baseline_stop_24.xml │ │ ├── baseline_unlock_24.xml │ │ ├── baseline_workspaces_filled_24.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_menu_camera.xml │ │ ├── ic_menu_gallery.xml │ │ ├── ic_menu_slideshow.xml │ │ ├── icon_my_location.png │ │ ├── icon_selected_location_16.png │ │ ├── icon_selected_location_200.png │ │ ├── idle_state_selector.xml │ │ ├── ripple_effect.xml │ │ ├── rounded_play_arrow_24.xml │ │ ├── rounded_play_disabled_24.xml │ │ ├── sharp_location_off_24.xml │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_no_permission.xml │ │ ├── app_bar_main.xml │ │ ├── content_main.xml │ │ ├── dialog_add_location.xml │ │ ├── dialog_add_route.xml │ │ ├── dialog_coordinates.xml │ │ ├── dialog_input.xml │ │ ├── fragment_gnss_mock.xml │ │ ├── fragment_home.xml │ │ ├── fragment_mock.xml │ │ ├── fragment_route_edit.xml │ │ ├── fragment_route_mock.xml │ │ ├── fragment_settings.xml │ │ ├── layout_history_location_item.xml │ │ ├── layout_loc_detail.xml │ │ ├── layout_rocker.xml │ │ ├── layout_route_item.xml │ │ ├── layout_search_poi_item.xml │ │ └── nav_header_main.xml │ │ ├── menu │ │ ├── activity_main_drawer.xml │ │ └── main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_adaptive_back.png │ │ └── ic_launcher_adaptive_fore.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_adaptive_back.png │ │ └── ic_launcher_adaptive_fore.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_adaptive_back.png │ │ └── ic_launcher_adaptive_fore.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_adaptive_back.png │ │ └── ic_launcher_adaptive_fore.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_adaptive_back.png │ │ └── ic_launcher_adaptive_fore.png │ │ ├── navigation │ │ └── mobile_navigation.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-w1240dp │ │ └── dimens.xml │ │ ├── values-w600dp │ │ └── dimens.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── fragment_settings_scene.xml │ └── test │ └── java │ └── moe │ └── fuqiuluo │ └── portal │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img └── qrcodemini.png ├── nmea ├── .gitignore ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── moe │ │ └── microbios │ │ └── nmea │ │ ├── NMEA.kt │ │ └── NmeaValue.kt │ └── test │ └── java │ └── Test1.kt ├── refs ├── 10.0.0 │ └── README.md ├── 11.0.0 │ └── README.md ├── 12.0.0 │ └── README.md ├── 13.0.0 │ └── README.md ├── 14.0.0 │ └── README.md ├── 15.0.0 │ └── README.md ├── 7.0.0_r1 │ ├── IGnssStatusListener.aidl │ ├── ILocationManager.aidl │ └── README.md ├── 7.1.1_r6 │ ├── ILocationManager.aidl │ └── README.md ├── 8.0.0_r4 │ ├── ILocationManager.aidl │ └── README.md ├── 9.0.0_r3 │ ├── ILocationManager.aidl │ └── README.md └── README.md ├── settings.gradle.kts ├── system-api ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── moe │ │ └── fuqiuluo │ │ └── hardcoder │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── android │ │ └── location │ │ └── LastLocationRequest.java │ └── test │ └── java │ └── moe │ └── fuqiuluo │ └── hardcoder │ └── ExampleUnitTest.kt └── xposed ├── .gitignore ├── README.md ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── moe │ └── fuqiuluo │ └── xposed │ └── ExampleInstrumentedTest.kt ├── main ├── AndroidManifest.xml ├── assets │ └── xposed_init ├── cpp │ ├── CMakeLists.txt │ ├── dobby_hook.h │ ├── elf_util.cpp │ ├── elf_util.h │ ├── logging.h │ ├── main.cpp │ ├── sensor_hook.cpp │ └── sensor_hook.h └── java │ └── moe │ └── fuqiuluo │ ├── dobby │ └── Dobby.kt │ └── xposed │ ├── BaseDivineService.kt │ ├── BaseLocationHook.kt │ ├── FakeLocation.kt │ ├── RemoteCommandHandler.kt │ ├── hooks │ ├── BasicLocationHook.kt │ ├── LocationManagerHook.kt │ ├── LocationServiceHook.kt │ ├── blindhook │ │ ├── BlindHook.kt │ │ └── BlindHookLocation.kt │ ├── fused │ │ ├── AndroidFusedLocationProviderHook.kt │ │ └── ThirdPartyLocationHook.kt │ ├── gnss │ │ └── GnssHook.kt │ ├── miui │ │ ├── MiuiBlurLocationProviderHook.kt │ │ └── MiuiLocationManagerHook.kt │ ├── nmea │ │ └── LocationNMEAHook.kt │ ├── oplus │ │ └── OplusLocationHook.kt │ ├── provider │ │ └── LocationProviderManagerHook.kt │ ├── sensor │ │ └── SystemSensorManagerHook.kt │ ├── telephony │ │ ├── BaseTelephonyHook.kt │ │ ├── TelephonyHook.kt │ │ └── miui │ │ │ └── MiuiTelephonyManagerHook.kt │ └── wlan │ │ └── WlanHook.kt │ └── utils │ ├── BinderUtils.kt │ ├── FakeLoc.kt │ ├── Logger.kt │ └── Xposed.kt └── test └── java └── moe └── fuqiuluo └── xposed └── ExampleUnitTest.kt /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | time: "21:00" 8 | target-branch: master 9 | groups: 10 | maven-dependencies: 11 | patterns: 12 | - "*" -------------------------------------------------------------------------------- /.github/workflows/build-apk.yml: -------------------------------------------------------------------------------- 1 | name: Build Apks 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | paths-ignore: 8 | - '**.md' 9 | - '**.txt' 10 | - '.github/**' 11 | - '.idea/**' 12 | - '!.github/workflows/**' 13 | 14 | jobs: 15 | build: 16 | name: Build Portal 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: recursive 24 | 25 | - name: Setup Java 21 26 | uses: gmitch215/setup-java@6d2c5e1f82f180ae79f799f0ed6e3e5efb4e664d 27 | with: 28 | distribution: jetbrains 29 | java-version: 21 30 | 31 | - name: Cache Gradle Dependencies 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | ~/.gradle/caches 36 | ~/.gradle/wrapper 37 | !~/.gradle/caches/build-cache-* 38 | key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }} 39 | restore-keys: gradle-deps 40 | 41 | - name: Cache Gradle Build 42 | uses: actions/cache@v4 43 | with: 44 | path: | 45 | ~/.gradle/caches/build-cache-* 46 | ~/.gradle/buildOutputCleanup/cache.properties 47 | key: gradle-builds-core-${{ github.sha }} 48 | restore-keys: gradle-builds 49 | 50 | - name: Setup google-services.json 51 | run: | 52 | echo "${{ secrets.GOOGLE_SERVICES_JSON }}" | base64 -d > app/google-services.json 53 | 54 | - name: Build with Gradle 55 | run: | 56 | echo ${{ secrets.SIGN_KEYSTORE_BASE64 }} | base64 -d > keystore.jks 57 | chmod +x ./gradlew 58 | ./gradlew :app:assembleRelease --build-cache --parallel --warning-mode all --stacktrace 59 | echo "APK_FILE_ALL=$(find app/build/outputs/apk/app/release -name '*.apk')" >> $GITHUB_ENV 60 | echo "APK_FILE_ARM64=$(find app/build/outputs/apk/arm64/release -name '*.apk')" >> $GITHUB_ENV 61 | echo "APK_FILE_X86_64=$(find app/build/outputs/apk/x64/release -name '*.apk')" >> $GITHUB_ENV 62 | env: 63 | KEYSTORE_PATH: "../keystore.jks" 64 | KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }} 65 | KEY_ALIAS: ${{ secrets.SIGN_ALIAS }} 66 | KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }} 67 | 68 | - name: Set Portal Version 69 | run: | 70 | version_name_all=$(basename -s .apk "${{ env.APK_FILE_ALL }}") 71 | version_name_arm64=$(basename -s .apk "${{ env.APK_FILE_ARM64 }}") 72 | version_name_x86_64=$(basename -s .apk "${{ env.APK_FILE_X86_64 }}") 73 | echo "PORTAL_VERSION_ALL=$version_name_all" >> $GITHUB_ENV 74 | echo "PORTAL_VERSION_ARM64=$version_name_arm64" >> $GITHUB_ENV 75 | echo "PORTAL_VERSION_x86_64=$version_name_x86_64" >> $GITHUB_ENV 76 | 77 | - name: Show Artifacts SHA256 78 | run: | 79 | echo "### Build Success :rocket:" >> $GITHUB_STEP_SUMMARY 80 | echo "|ABI|SHA256|" >> $GITHUB_STEP_SUMMARY 81 | echo "|:--------:|:----------|" >> $GITHUB_STEP_SUMMARY 82 | all=($(sha256sum "${{ env.APK_FILE_ALL }}")) 83 | echo "|all|$all" >> $GITHUB_STEP_SUMMARY 84 | arm64=($(sha256sum "${{ env.APK_FILE_ARM64 }}")) 85 | echo "|arm64|$arm64" >> $GITHUB_STEP_SUMMARY 86 | x86_64=($(sha256sum "${{ env.APK_FILE_X86_64 }}")) 87 | echo "|x86_64|$x86_64" >> $GITHUB_STEP_SUMMARY 88 | 89 | - name: Upload ALL APK RELEASE 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: "${{ env.PORTAL_VERSION_ALL }}" 93 | path: "${{ env.APK_FILE_ALL }}" 94 | 95 | - name: Upload ARM64 APK RELEASE 96 | uses: actions/upload-artifact@v4 97 | with: 98 | name: "${{ env.PORTAL_VERSION_ARM64 }}" 99 | path: "${{ env.APK_FILE_ARM64 }}" 100 | 101 | - name: Upload X86_64 APK RELEASE 102 | uses: actions/upload-artifact@v4 103 | with: 104 | name: "${{ env.PORTAL_VERSION_x86_64 }}" 105 | path: "${{ env.APK_FILE_X86_64 }}" 106 | -------------------------------------------------------------------------------- /.github/workflows/build-pr-apk.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Check 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | paths-ignore: 7 | - '**.md' 8 | - '**.txt' 9 | - '.gitignore' 10 | - '.github/**' 11 | - '.idea/**' 12 | - '!.github/workflows/**' 13 | - 14 | jobs: 15 | build: 16 | name: Build Portal 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: recursive 24 | 25 | - name: Setup Java 21 26 | uses: gmitch215/setup-java@6d2c5e1f82f180ae79f799f0ed6e3e5efb4e664d 27 | with: 28 | distribution: jetbrains 29 | java-version: 21 30 | 31 | - name: Cache Gradle Dependencies 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | ~/.gradle/caches 36 | ~/.gradle/wrapper 37 | !~/.gradle/caches/build-cache-* 38 | key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }} 39 | restore-keys: gradle-deps 40 | 41 | - name: Cache Gradle Build 42 | uses: actions/cache@v4 43 | with: 44 | path: | 45 | ~/.gradle/caches/build-cache-* 46 | ~/.gradle/buildOutputCleanup/cache.properties 47 | key: gradle-builds-core-${{ github.sha }} 48 | restore-keys: gradle-builds 49 | 50 | - name: Build with Gradle 51 | run: | 52 | echo ${{ secrets.SIGN_KEYSTORE_BASE64 }} | base64 -d > keystore.jks 53 | chmod +x ./gradlew 54 | ./gradlew :app:assembleRelease --build-cache --parallel --warning-mode all --stacktrace 55 | echo "APK_FILE_ALL=$(find app/build/outputs/apk/app/release -name '*.apk')" >> $GITHUB_ENV 56 | echo "APK_FILE_ARM64=$(find app/build/outputs/apk/arm64/release -name '*.apk')" >> $GITHUB_ENV 57 | echo "APK_FILE_X86_64=$(find app/build/outputs/apk/x64/release -name '*.apk')" >> $GITHUB_ENV 58 | env: 59 | KEYSTORE_PATH: "../keystore.jks" 60 | KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }} 61 | KEY_ALIAS: ${{ secrets.SIGN_ALIAS }} 62 | KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }} 63 | 64 | - name: Set Portal Version 65 | run: | 66 | version_name_all=$(basename -s .apk "${{ env.APK_FILE_ALL }}") 67 | version_name_arm64=$(basename -s .apk "${{ env.APK_FILE_ARM64 }}") 68 | version_name_x86_64=$(basename -s .apk "${{ env.APK_FILE_X86_64 }}") 69 | echo "PORTAL_VERSION_ALL=$version_name_all" >> $GITHUB_ENV 70 | echo "PORTAL_VERSION_ARM64=$version_name_arm64" >> $GITHUB_ENV 71 | echo "PORTAL_VERSION_x86_64=$version_name_x86_64" >> $GITHUB_ENV 72 | 73 | - name: Show Artifacts SHA256 74 | run: | 75 | echo "### Build Success :rocket:" >> $GITHUB_STEP_SUMMARY 76 | echo "|ABI|SHA256|" >> $GITHUB_STEP_SUMMARY 77 | echo "|:--------:|:----------|" >> $GITHUB_STEP_SUMMARY 78 | all=($(sha256sum "${{ env.APK_FILE_ALL }}")) 79 | echo "|all|$all" >> $GITHUB_STEP_SUMMARY 80 | arm64=($(sha256sum "${{ env.APK_FILE_ARM64 }}")) 81 | echo "|arm64|$arm64" >> $GITHUB_STEP_SUMMARY 82 | x86_64=($(sha256sum "${{ env.APK_FILE_X86_64 }}")) 83 | echo "|x86_64|$x86_64" >> $GITHUB_STEP_SUMMARY 84 | 85 | - name: Upload ALL APK RELEASE 86 | uses: actions/upload-artifact@v4 87 | with: 88 | name: "${{ env.PORTAL_VERSION_ALL }}" 89 | path: "${{ env.APK_FILE_ALL }}" 90 | 91 | - name: Upload ARM64 APK RELEASE 92 | uses: actions/upload-artifact@v4 93 | with: 94 | name: "${{ env.PORTAL_VERSION_ARM64 }}" 95 | path: "${{ env.APK_FILE_ARM64 }}" 96 | 97 | - name: Upload X86_64 APK RELEASE 98 | uses: actions/upload-artifact@v4 99 | with: 100 | name: "${{ env.PORTAL_VERSION_x86_64 }}" 101 | path: "${{ env.APK_FILE_X86_64 }}" 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | apk-keys 17 | app/google-services.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Portal 2 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffuqiuluo%2FPortal.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ffuqiuluo%2FPortal?ref=badge_shield) 3 | 4 | 5 | 秋夜长,殊未央,月明白露澄清光,层城绮阁遥相望。 6 | 7 | QQ交流群:599533037(搜索不到请扫描下方二维码) 8 | Telegram: https://t.me/portal_fuqiuluo 9 | 10 | ![QRCODE](img/qrcodemini.png) 11 | 12 | The virtual positioning module based on LSPosed only provides Hook system services to achieve virtual positioning, and cannot be integrated into the APP. 13 | 14 | The purpose of this application is to help developers debug the simulation tool of the location information program, and the application will automatically create features once it is installed and launched。 15 | 16 | > [!note] 17 | > 18 | > 中文地区特供: 19 | > 20 | > 1. 本项目遵循[Apache-2.0 license],仅限于非商业用途的学习、研究目的,禁止用于任何违法行为。 21 | > 2. 未经书面授权,禁止修改、反向工程、重新包装或分发本项目的名称、代码及衍生作品。 22 | > 3. 使用者需承诺遵守相关法律法规,因滥用导致的后果由行为人自行承担,与本项目开发者无关。 23 | > 4. 开发者保留对违反本协议的行为追究法律责任的权利。 24 | 25 | # Warning 26 | 27 | - 一旦有任何人使用**Portal**进行任何违法行为,请立即收集证据举报。 28 | - 禁止以违法目的使用**Portal**/分发**Portal**,否则后果自负。 任何企业/组织/个人不得以任何形式使用**Portal**进行违法行为,否则后果自负。 29 | - 如有企业/组织/个人因为**Portal**导致的任何法律纠纷,**Portal**开发者概不负责。 30 | - 若有企业/组织/个人因为**Portal**导致出现任何损失,业务中断,**Portal**将最大程度协助您的调查。 31 | - **Portal**开发者保留对**Portal**的最终解释权。 32 | 33 | ## How to detect **Portal**? 34 | 35 | - **Portal** will create a notification when it is running, and you can check the notification to see if **Portal** is running. 36 | - **Portal** will add extra to the `Location`, you can check it to see if **Portal** is running. 37 | 38 | ```kotlin 39 | if (location.extras == null) { 40 | location.extras = Bundle() 41 | } 42 | location.extras?.putBoolean("portal.enable", true) 43 | location.extras?.putBoolean("is_mock", true) 44 | ``` 45 | 46 | # Features 47 | 48 | - [x] **Portal** will create a notification when it is running. 49 | - [x] **Portal** will add extra to the `Location`. 50 | - [x] **Portal** will mock in any case. 51 | - [ ] **Portal** will mock the gps status. 52 | - [ ] **Portal** will mock the cell info. 53 | - [ ] **Portal** will mock the wifi info. 54 | - [x] **Portal** will mock the sensor info. 55 | - [x] **Portal** can move position by rocker. 56 | - [x] **Portal** can set the speed in the settings. 57 | - [x] **Portal** can set the altitude in the settings. 58 | - [x] **Portal** can set the accuracy in the settings. 59 | - [x] **Portal** will change the bearing when moving. 60 | 61 | # Thanks 62 | 63 | - [GoGoGo](https://github.com/ZCShou/GoGoGo) 64 | - [Baidu Map SDK](https://lbsyun.baidu.com/faq/api?title=androidsdk) 65 | 66 | 67 | ## License 68 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffuqiuluo%2FPortal.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Ffuqiuluo%2FPortal?ref=badge_large) 69 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/libs/BaiduLBS_Android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/BaiduLBS_Android.jar -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libBaiduMapSDK_base_v7_6_2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/arm64-v8a/libBaiduMapSDK_base_v7_6_2.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libBaiduMapSDK_map_v7_6_2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/arm64-v8a/libBaiduMapSDK_map_v7_6_2.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libc++_shared.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/arm64-v8a/libc++_shared.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/liblocSDK8b.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/arm64-v8a/liblocSDK8b.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libtiny_magic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/arm64-v8a/libtiny_magic.so -------------------------------------------------------------------------------- /app/libs/x86_64/libBaiduMapSDK_base_v7_6_2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/x86_64/libBaiduMapSDK_base_v7_6_2.so -------------------------------------------------------------------------------- /app/libs/x86_64/libBaiduMapSDK_map_v7_6_2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/x86_64/libBaiduMapSDK_map_v7_6_2.so -------------------------------------------------------------------------------- /app/libs/x86_64/libc++_shared.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/x86_64/libc++_shared.so -------------------------------------------------------------------------------- /app/libs/x86_64/liblocSDK8b.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/x86_64/liblocSDK8b.so -------------------------------------------------------------------------------- /app/libs/x86_64/libtiny_magic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/libs/x86_64/libtiny_magic.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -keep class com.baidu.** {*;} 23 | -keep class vi.com.** {*;} 24 | -keep class com.baidu.vi.** {*;} 25 | -dontwarn com.baidu.** 26 | 27 | -keepclassmembers class com.dd.StrokeGradientDrawable { 28 | public void setStrokeColor(int); 29 | } 30 | 31 | -dontwarn com.tencent.bugly.** 32 | -keep public class com.tencent.bugly.**{*;} -------------------------------------------------------------------------------- /app/src/androidTest/java/moe/fuqiuluo/portal/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("moe.fuqiuluo.portal", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/Portal.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal 2 | 3 | import android.app.Application 4 | import com.baidu.location.LocationClient 5 | import com.baidu.mapapi.CoordType 6 | import com.baidu.mapapi.SDKInitializer 7 | import com.tencent.bugly.crashreport.CrashReport 8 | import moe.fuqiuluo.portal.android.Bugly 9 | 10 | class Portal: Application() { 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | SDKInitializer.setAgreePrivacy(this, true) 16 | LocationClient.setAgreePrivacy(true) 17 | 18 | SDKInitializer.initialize(this) 19 | SDKInitializer.setCoordType(DEFAULT_COORD_TYPE) 20 | 21 | CrashReport.initCrashReport(applicationContext) 22 | 23 | CrashReport.setUserId(applicationContext, Bugly.getUniqueDeviceId(applicationContext)) 24 | CrashReport.setDeviceId(applicationContext, Bugly.getUniqueDeviceId(applicationContext)) 25 | CrashReport.setDeviceModel(applicationContext, Bugly.getDeviceModel()) 26 | CrashReport.setCollectPrivacyInfo(applicationContext, true) 27 | 28 | //CrashReport.setAllThreadStackEnable(applicationContext, true, true) 29 | } 30 | 31 | companion object { 32 | val DEFAULT_COORD_TYPE = CoordType.GCJ02 33 | const val DEFAULT_COORD_STR = "GCJ02" 34 | 35 | //val DEFAULT_COORD_TYPE = CoordType.BD09LL 36 | //const val DEFAULT_COORD_STR = "bd09ll" 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/Bugly.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.os.Build 6 | import android.provider.Settings 7 | import android.telephony.TelephonyManager 8 | import java.io.File 9 | import java.security.MessageDigest 10 | import java.util.UUID 11 | 12 | object Bugly { 13 | fun getDeviceModel(): String { 14 | return Build.MODEL 15 | } 16 | 17 | /** 18 | * 获取设备唯一标识符 19 | * 20 | * 该方法综合了多种获取设备ID的策略,按优先级顺序尝试: 21 | * 1. ANDROID_ID (适用于大多数设备) 22 | * 2. IMEI (仅适用于Android Q/10以下版本,需要权限) 23 | * 3. MAC地址 (Android M/6.0以下版本可获取实际MAC地址,以上版本可能返回固定值) 24 | * 4. 自定义生成的UUID (如果以上方法都失败,则生成并持久化一个UUID) 25 | * 26 | * @param context 应用上下文 27 | * @return 设备唯一标识符的MD5哈希值 28 | */ 29 | @SuppressLint("HardwareIds", "MissingPermission") 30 | fun getUniqueDeviceId(context: Context): String { 31 | var deviceId = "" 32 | 33 | val androidId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) 34 | if (!androidId.isNullOrEmpty() && androidId != "9774d56d682e549c") { // 忽略已知的错误值 35 | deviceId = androidId 36 | } 37 | 38 | if (deviceId.isEmpty() && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { 39 | try { 40 | val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager 41 | @Suppress("DEPRECATION") 42 | val imei = telephonyManager.deviceId 43 | if (!imei.isNullOrEmpty()) { 44 | deviceId = imei 45 | } 46 | } catch (e: Exception) { 47 | 48 | } 49 | } 50 | 51 | if (deviceId.isEmpty()) { 52 | deviceId = getOrCreatePersistentUUID(context) 53 | } 54 | 55 | return hashMD5(deviceId) 56 | } 57 | 58 | /** 59 | * 获取或创建持久化的UUID 60 | * 将UUID存储在应用私有目录中,确保卸载后重装会重新生成 61 | */ 62 | private fun getOrCreatePersistentUUID(context: Context): String { 63 | val file = File(context.filesDir, "device_uuid") 64 | 65 | // 检查文件是否存在,如果存在则读取 66 | if (file.exists()) { 67 | try { 68 | return file.readText() 69 | } catch (e: Exception) { 70 | } 71 | } 72 | 73 | val uuid = UUID.randomUUID().toString() 74 | try { 75 | file.writeText(uuid) 76 | } catch (e: Exception) { 77 | } 78 | 79 | return uuid 80 | } 81 | 82 | /** 83 | * 将字符串转换为MD5哈希值 84 | */ 85 | private fun hashMD5(input: String): String { 86 | try { 87 | val md = MessageDigest.getInstance("MD5") 88 | val digest = md.digest(input.toByteArray()) 89 | return digest.joinToString("") { "%02x".format(it) } 90 | } catch (e: Exception) { 91 | return input 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineController.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.coro 2 | 3 | import kotlinx.coroutines.channels.Channel 4 | 5 | class CoroutineController { 6 | private val controlChannel = Channel(Channel.UNLIMITED) 7 | var isPaused = false 8 | 9 | suspend fun controlledCoroutine() { 10 | checkControl() 11 | } 12 | 13 | private suspend fun checkControl() { 14 | controlChannel.tryReceive().getOrNull()?.let { 15 | when (it) { 16 | ControlCommand.Pause -> { 17 | isPaused = true 18 | while (controlChannel.receive() != ControlCommand.Resume) { 19 | // do nothing 20 | } 21 | isPaused = false 22 | } 23 | ControlCommand.Resume -> {} 24 | } 25 | } 26 | } 27 | 28 | fun pause() { 29 | controlChannel.trySend(ControlCommand.Pause) 30 | } 31 | 32 | fun resume() { 33 | controlChannel.trySend(ControlCommand.Resume) 34 | } 35 | } 36 | 37 | enum class ControlCommand { 38 | Pause, 39 | Resume 40 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/coro/CoroutineRouteMock.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.coro 2 | 3 | import kotlinx.coroutines.channels.Channel 4 | 5 | class CoroutineRouteMock { 6 | private val routeMockChannel = Channel(Channel.UNLIMITED) 7 | var isPaused = false 8 | 9 | suspend fun routeMockCoroutine() { 10 | checkRouteMockStatus() 11 | } 12 | 13 | private suspend fun checkRouteMockStatus() { 14 | routeMockChannel.tryReceive().getOrNull()?.let { 15 | when (it) { 16 | RouteMockCommand.Pause -> { 17 | isPaused = true 18 | while (routeMockChannel.receive() != RouteMockCommand.Resume) { 19 | // do nothing 20 | } 21 | isPaused = false 22 | } 23 | RouteMockCommand.Resume -> {} 24 | } 25 | } 26 | } 27 | 28 | fun pause() { 29 | routeMockChannel.trySend(RouteMockCommand.Pause) 30 | } 31 | 32 | fun resume() { 33 | routeMockChannel.trySend(RouteMockCommand.Resume) 34 | } 35 | } 36 | 37 | enum class RouteMockCommand { 38 | Pause, 39 | Resume 40 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/permission/RequestPermissions.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.permission 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import kotlinx.coroutines.channels.Channel 5 | import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions 6 | import android.content.Context 7 | import android.content.pm.PackageManager 8 | 9 | // https://medium.com/@wind.orca.pe/handling-android-runtime-permissions-with-coroutines-and-suspend-functions-5b4aa4e74ee5 10 | 11 | class RequestPermissions( 12 | activity: AppCompatActivity 13 | ) { 14 | private val activityResultLauncher = with(activity) { 15 | registerForActivityResult(RequestMultiplePermissions()) { result -> 16 | val m = result.mapValues { (key, value) -> 17 | if (value) { 18 | PermissionChecker.State.Granted 19 | } else { 20 | PermissionChecker.State.Denied(shouldShowRequestPermissionRationale(key)) 21 | } 22 | } 23 | 24 | channel.trySend(PermissionChecker.Result(m)) 25 | } 26 | } 27 | 28 | private val channel = Channel(1) 29 | 30 | suspend fun request(permissions: Set): PermissionChecker.Result { 31 | activityResultLauncher.launch(permissions.toTypedArray()) 32 | 33 | return channel.receive() 34 | } 35 | 36 | } 37 | 38 | interface PermissionChecker { 39 | class Result(m: Map) : Map by HashMap(m) { 40 | operator fun component1(): Set = granted() 41 | operator fun component2(): Set = denied() 42 | 43 | private fun denied() = filterValues(State::isDenied).keys 44 | private fun granted() = filterValues(State::isGranted).keys 45 | } 46 | 47 | sealed interface State { 48 | val shouldShowRequestPermissionRationale: Boolean 49 | 50 | fun isGranted() = this is Granted 51 | fun isDenied() = this is Denied 52 | 53 | data object Granted : State { 54 | override val shouldShowRequestPermissionRationale: Boolean = false 55 | } 56 | 57 | data class Denied( 58 | override val shouldShowRequestPermissionRationale: Boolean 59 | ) : State 60 | } 61 | 62 | fun Context.checkSelfMultiplePermissions(permissions: Array): Boolean { 63 | return permissions.all { 64 | checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED 65 | } 66 | } 67 | 68 | fun Context.checkSelfSinglePermission(permission: String): Boolean { 69 | return checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/rom/RomUtils.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.rom 2 | 3 | import java.io.BufferedReader 4 | import java.io.IOException 5 | import java.io.InputStreamReader 6 | 7 | object RomUtils { 8 | fun isMiui(): Boolean { 9 | return !getProp("ro.miui.ui.version.name").isNullOrBlank() 10 | } 11 | 12 | fun isEmui(): Boolean { 13 | return getProp("ro.build.version.emui") != null 14 | } 15 | 16 | private fun getProp(name: String): String? { 17 | return try { 18 | val p = Runtime.getRuntime().exec("getprop $name") 19 | val `in` = BufferedReader(InputStreamReader(p.inputStream)) 20 | val value = `in`.readLine() 21 | `in`.close() 22 | value 23 | } catch (e: IOException) { 24 | null 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/root/ShellUtils.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.root 2 | 3 | object ShellUtils { 4 | fun hasRoot(): Boolean { 5 | val runtime = Runtime.getRuntime() 6 | try { 7 | val process = runtime.exec("su") 8 | process.outputStream.write("exit\n".toByteArray()) 9 | process.outputStream.flush() 10 | process.waitFor() 11 | return process.exitValue() == 0 12 | } catch (e: Exception) { 13 | return false 14 | } 15 | } 16 | 17 | fun setEnforceMode(enabled: Boolean) { 18 | val runtime = Runtime.getRuntime() 19 | try { 20 | val process = runtime.exec("su") 21 | process.outputStream.write("setenforce ${if (enabled) "1" else "0"}\n".toByteArray()) 22 | process.outputStream.write("exit\n".toByteArray()) 23 | process.outputStream.flush() 24 | process.waitFor() 25 | } catch (e: Exception) { 26 | e.printStackTrace() 27 | } 28 | } 29 | 30 | fun executeCommand(command: String): String { 31 | val runtime = Runtime.getRuntime() 32 | try { 33 | val process = runtime.exec("su") 34 | process.outputStream.write("$command\n".toByteArray()) 35 | process.outputStream.write("exit\n".toByteArray()) 36 | process.outputStream.flush() 37 | process.waitFor() 38 | if (process.exitValue() != 0) { 39 | return process.errorStream.bufferedReader().readText() 40 | } 41 | return process.inputStream.bufferedReader().readText() 42 | } catch (e: Exception) { 43 | e.printStackTrace() 44 | return "" 45 | } 46 | } 47 | 48 | fun executeCommandToBytes(command: String): ByteArray { 49 | val runtime = Runtime.getRuntime() 50 | try { 51 | val process = runtime.exec("su") 52 | process.outputStream.write("$command\n".toByteArray()) 53 | process.outputStream.write("exit\n".toByteArray()) 54 | process.outputStream.flush() 55 | process.waitFor() 56 | if (process.exitValue() != 0) { 57 | return process.errorStream.use { it.readBytes() } 58 | } 59 | return process.inputStream.use { it.readBytes() } 60 | } catch (e: Exception) { 61 | e.printStackTrace() 62 | return ByteArray(0) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/widget/DeveloperView.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("OVERRIDE_DEPRECATION") 2 | package moe.fuqiuluo.portal.android.widget 3 | 4 | import android.content.Context 5 | import android.graphics.Rect 6 | import android.util.AttributeSet 7 | import android.util.Log 8 | import android.view.CollapsibleActionView 9 | import androidx.appcompat.widget.LinearLayoutCompat 10 | 11 | class DeveloperView @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0 15 | ) : LinearLayoutCompat(context, attrs, defStyleAttr), CollapsibleActionView { 16 | 17 | override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean { 18 | if (!isFocusable) return false 19 | return super.requestFocus(direction, previouslyFocusedRect) 20 | } 21 | 22 | override fun onActionViewExpanded() { 23 | visibility = VISIBLE 24 | invalidate() 25 | requestLayout() 26 | Log.d("DeveloperView", "onActionViewExpanded") 27 | } 28 | 29 | override fun onActionViewCollapsed() { 30 | visibility = GONE 31 | invalidate() 32 | requestLayout() 33 | Log.d("DeveloperView", "onActionViewCollapsed") 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/android/window/OverlayUtils.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.android.window 2 | 3 | import android.app.AppOpsManager 4 | import android.content.Context 5 | import android.os.Build 6 | import android.os.Process 7 | import android.provider.Settings 8 | import android.util.Log 9 | 10 | object OverlayUtils { 11 | fun hasOverlayPermissions(context: Context): Boolean { 12 | kotlin.runCatching { 13 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 14 | val appOpsMgr = context.getSystemService(Context.APP_OPS_SERVICE) as? AppOpsManager 15 | ?: return false 16 | val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 17 | appOpsMgr.unsafeCheckOpNoThrow("android:system_alert_window", Process.myUid(), context.packageName) 18 | } else { 19 | appOpsMgr.checkOpNoThrow("android:system_alert_window", Process.myUid(), context.packageName) 20 | } 21 | return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED 22 | } else { 23 | return Settings.canDrawOverlays(context) 24 | } 25 | }.onFailure { 26 | Log.e("OverlayUtils", "hasOverlayPermissions: ", it) 27 | } 28 | return false 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/bdmap/BDMap.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.bdmap 2 | 3 | import android.util.Log 4 | import com.baidu.mapapi.map.BaiduMap 5 | import com.baidu.mapapi.map.BitmapDescriptorFactory 6 | import com.baidu.mapapi.map.MyLocationConfiguration 7 | import com.baidu.mapapi.search.sug.SuggestionResult 8 | import moe.fuqiuluo.portal.ext.Loc4j 9 | 10 | fun SuggestionResult.toPoi( 11 | currentLocation: Pair? = null 12 | ) = this.allSuggestions 13 | .filter { it.key != null && it.pt != null } 14 | .map { 15 | val gcj02Lat = it.pt.latitude 16 | val gcj02Lon = it.pt.longitude 17 | val (lat, lon) = Loc4j.gcj2wgs(gcj02Lat, gcj02Lon) 18 | if (currentLocation != null) { 19 | Log.d("toPoi", "currentLocation: $currentLocation, lat: $lat, lon: $lon") 20 | Poi( 21 | name = it.key, 22 | address = it.city + " " + it.district, 23 | longitude = lon, 24 | latitude = lat, 25 | tag = it.tag, 26 | ).also { 27 | val distance = it.distanceTo(currentLocation.first, currentLocation.second).toInt() 28 | if (distance < 1000) { 29 | it.address = "${distance}m ${it.address}" 30 | } else { 31 | it.address = "${(distance / 1000.0).toString().take(4)}km ${it.address}" 32 | } 33 | } 34 | } else { 35 | Poi( 36 | name = it.key, 37 | address = it.city + " " + it.district, 38 | longitude = lon, 39 | latitude = lat, 40 | tag = it.tag, 41 | ) 42 | } 43 | } 44 | 45 | fun BaiduMap.setMapConfig(mode: MyLocationConfiguration.LocationMode, resourceId: Int?) { 46 | setMyLocationConfiguration(MyLocationConfiguration(mode, true, resourceId?.let { BitmapDescriptorFactory.fromResource(it) })) 47 | } 48 | 49 | fun BaiduMap.locateMe() { 50 | setMyLocationConfiguration(MyLocationConfiguration(MyLocationConfiguration.LocationMode.FOLLOWING, true, null)) 51 | setMyLocationConfiguration(MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, null)) 52 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/bdmap/Poi.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.bdmap 2 | 3 | import kotlin.math.atan2 4 | import kotlin.math.cos 5 | import kotlin.math.sin 6 | import kotlin.math.sqrt 7 | 8 | data class Poi( 9 | val name: String, 10 | var address: String, 11 | val longitude: Double, 12 | val latitude: Double, 13 | 14 | val tag: String, 15 | ) { 16 | companion object { 17 | const val KEY_NAME = "name" 18 | const val KEY_ADDRESS = "address" 19 | const val KEY_LONGITUDE = "longitude" 20 | const val KEY_LATITUDE = "latitude" 21 | const val KEY_LONGITUDE_RAW = "longitude_raw" 22 | const val KEY_LATITUDE_RAW = "latitude_raw" 23 | const val KEY_TAG = "tag" 24 | } 25 | 26 | fun toMap(): Map { 27 | return mapOf( 28 | KEY_NAME to name, 29 | KEY_ADDRESS to address, 30 | KEY_LONGITUDE to longitude.toString().take(5), 31 | KEY_LATITUDE to latitude.toString().take(5), 32 | KEY_TAG to tag, 33 | KEY_LONGITUDE_RAW to longitude.toString(), 34 | KEY_LATITUDE_RAW to latitude.toString(), 35 | ) 36 | } 37 | 38 | /** 39 | * Calculate the distance between two points on the Earth's surface. 40 | * @param other The other point. 41 | * @return The distance in meters. 42 | */ 43 | fun distanceTo(other: Poi): Double { 44 | val earthRadius = 6371000.0 45 | val dLat = Math.toRadians(other.latitude - latitude) 46 | val dLng = Math.toRadians(other.longitude - longitude) 47 | val a = sin(dLat / 2) * sin(dLat / 2) + 48 | cos(Math.toRadians(latitude)) * cos(Math.toRadians(other.latitude)) * 49 | sin(dLng / 2) * sin(dLng / 2) 50 | val c = 2 * atan2(sqrt(a), sqrt(1 - a)) 51 | return earthRadius * c 52 | } 53 | 54 | fun distanceTo(lat: Double, lng: Double): Double { 55 | val earthRadius = 6371000.0 56 | val dLat = Math.toRadians(lat - latitude) 57 | val dLng = Math.toRadians(lng - longitude) 58 | val a = sin(dLat / 2) * sin(dLat / 2) + 59 | cos(Math.toRadians(latitude)) * cos(Math.toRadians(lat)) * 60 | sin(dLng / 2) * sin(dLng / 2) 61 | val c = 2 * atan2(sqrt(a), sqrt(1 - a)) 62 | return earthRadius * c 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ext/Loc.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ext 2 | 3 | import com.baidu.location.BDLocation 4 | import com.baidu.location.Jni 5 | import com.baidu.mapapi.model.LatLng 6 | 7 | val LatLng.wgs84: Pair 8 | get() = Loc4j.gcj2wgs(latitude, longitude) 9 | 10 | val BDLocation.wgs84: Pair 11 | get() = Loc4j.gcj2wgs(latitude, longitude) 12 | 13 | val Pair.gcj02: LatLng 14 | get() = Loc4j.wgs2gcj(first, second).let { LatLng(it.first, it.second) } 15 | 16 | object Loc4j { 17 | fun gcj2wgs(lat: Double, lon: Double): Pair { 18 | return Jni.coorEncrypt(lon, lat, "gcj2wgs").let { it[1] to it[0] } 19 | } 20 | 21 | fun wgs2gcj(lat: Double, lon: Double): Pair { 22 | return Jni.coorEncrypt(lon, lat, "gps2gcj").let { it[1] to it[0] } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ext/Oveylay.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ext 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.os.Build 6 | import android.provider.Settings 7 | 8 | fun Context.drawOverOtherAppsEnabled(): Boolean { 9 | return Settings.canDrawOverlays(this) 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/mock/HistoricalLocation.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.mock 2 | 3 | import java.math.BigDecimal 4 | 5 | data class HistoricalLocation( 6 | val name: String, 7 | val address: String, 8 | val lat: Double, 9 | val lon: Double 10 | ) { 11 | companion object { 12 | // Format: "name","address","lat","lon" 13 | fun fromString(str: String): HistoricalLocation { 14 | // CSV parser supporting commas inside quoted fields 15 | val fields = mutableListOf() 16 | var currentField = StringBuilder() 17 | var inQuotes = false 18 | 19 | var i = 0 20 | while (i < str.length) { 21 | val char = str[i] 22 | when { 23 | char == '"' && (i + 1 >= str.length || str[i + 1] != '"') -> { 24 | // Toggle quote state 25 | inQuotes = !inQuotes 26 | } 27 | char == '"' && i + 1 < str.length && str[i + 1] == '"' -> { 28 | // Handle escaped quotes ("") 29 | currentField.append('"') 30 | // Skip next quote 31 | i++ 32 | } 33 | char == ',' && !inQuotes -> { 34 | // Comma as separator 35 | fields.add(currentField.toString().trim()) 36 | currentField = StringBuilder() 37 | } 38 | else -> { 39 | // Regular character 40 | currentField.append(char) 41 | } 42 | } 43 | i++ 44 | } 45 | 46 | // Add the last field 47 | fields.add(currentField.toString().trim()) 48 | 49 | if (fields.size != 4) { 50 | throw IllegalArgumentException("Invalid format. Expected 4 fields but got ${fields.size}: $str") 51 | } 52 | 53 | return HistoricalLocation( 54 | name = fields[0].trim('"'), 55 | address = fields[1].trim('"'), 56 | lat = fields[2].trim('"').toDouble(), 57 | lon = fields[3].trim('"').toDouble() 58 | ) 59 | } 60 | } 61 | 62 | override fun toString(): String { 63 | val plainLat = BigDecimal(lat).toPlainString() 64 | val plainLon = BigDecimal(lon).toPlainString() 65 | 66 | // Quote fields containing commas 67 | val quotedName = if (name.contains(",")) "\"$name\"" else name 68 | val quotedAddress = if (address.contains(",")) "\"$address\"" else address 69 | 70 | return "$quotedName,$quotedAddress,$plainLat,$plainLon" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/mock/HistoricalRoute.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.mock 2 | 3 | data class HistoricalRoute( 4 | val name: String, 5 | val route: List> 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/mock/HistoricalRouteAdapter.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.mock 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.google.android.material.textview.MaterialTextView 9 | import moe.fuqiuluo.portal.R 10 | 11 | class HistoricalRouteAdapter( 12 | private val dataSet: MutableList, 13 | private val onItemClicked: (HistoricalRoute, isLongClicked: Boolean) -> Unit 14 | ) : RecyclerView.Adapter() { 15 | 16 | class ViewHolder( 17 | val root: View 18 | ) : RecyclerView.ViewHolder(root) { 19 | val name: MaterialTextView = root.findViewById(R.id.name) 20 | val route: MaterialTextView = root.findViewById(R.id.desc) 21 | } 22 | 23 | operator fun get(position: Int): HistoricalRoute { 24 | return dataSet[position] 25 | } 26 | 27 | fun removeItem(position: Int): HistoricalRoute { 28 | val removed = dataSet.removeAt(position) 29 | notifyItemRemoved(position) 30 | notifyItemRangeChanged(position, itemCount - position) 31 | return removed 32 | } 33 | 34 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 35 | val view = LayoutInflater.from(parent.context) 36 | .inflate(R.layout.layout_route_item, parent, false) 37 | 38 | return ViewHolder(view) 39 | } 40 | 41 | override fun getItemCount(): Int { 42 | return dataSet.size 43 | } 44 | 45 | @SuppressLint("SetTextI18n") 46 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 47 | holder.name.text = dataSet[position].name 48 | holder.route.text = dataSet[position].route.toString() 49 | holder.root.setOnClickListener { 50 | onItemClicked(dataSet[position], false) 51 | } 52 | holder.root.setOnLongClickListener { 53 | onItemClicked(dataSet[position], true) 54 | true 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/notification/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.notification; 2 | 3 | 4 | import android.app.Notification; 5 | import android.app.NotificationChannel; 6 | import android.app.NotificationManager; 7 | import android.content.Context; 8 | import android.content.ContextWrapper; 9 | import android.graphics.Color; 10 | import android.os.Build; 11 | 12 | import androidx.annotation.RequiresApi; 13 | 14 | public class NotificationUtils extends ContextWrapper { 15 | 16 | private NotificationManager mManager; 17 | public static final String ANDROID_CHANNEL_ID = "moe.fuqiuluo.portal"; 18 | public static final String ANDROID_CHANNEL_NAME = "Portal Location"; 19 | 20 | @RequiresApi(api = Build.VERSION_CODES.O) 21 | public NotificationUtils(Context base) { 22 | super(base); 23 | createChannels(); 24 | } 25 | 26 | @RequiresApi(api = Build.VERSION_CODES.O) 27 | public void createChannels() { 28 | // create android channel 29 | NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID, 30 | ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT); 31 | // Sets whether notifications posted to this channel should display notification lights 32 | androidChannel.enableLights(true); 33 | // Sets whether notification posted to this channel should vibrate. 34 | androidChannel.enableVibration(true); 35 | // Sets the notification light color for notifications posted to this channel 36 | androidChannel.setLightColor(Color.GREEN); 37 | // Sets whether notifications posted to this channel appear on the lockscreen or not 38 | androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); 39 | 40 | getManager().createNotificationChannel(androidChannel); 41 | } 42 | 43 | private NotificationManager getManager() { 44 | if (mManager == null) { 45 | mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 46 | } 47 | return mManager; 48 | } 49 | 50 | @RequiresApi(api = Build.VERSION_CODES.O) 51 | public Notification.Builder getAndroidChannelNotification(String title, String body) { 52 | return new Notification.Builder(getApplicationContext(), ANDROID_CHANNEL_ID) 53 | .setContentTitle(title) 54 | .setContentText(body) 55 | .setSmallIcon(android.R.drawable.stat_notify_more) 56 | .setAutoCancel(true); 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/BaiduMapViewModel.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.viewmodel 2 | 3 | import android.app.Notification 4 | import androidx.lifecycle.ViewModel 5 | import com.baidu.location.LocationClient 6 | import com.baidu.mapapi.map.BaiduMap 7 | import com.baidu.mapapi.map.BitmapDescriptor 8 | import com.baidu.mapapi.map.BitmapDescriptorFactory 9 | import com.baidu.mapapi.map.MyLocationConfiguration 10 | import moe.fuqiuluo.portal.R 11 | import com.baidu.mapapi.search.geocode.GeoCoder 12 | import moe.fuqiuluo.portal.bdmap.setMapConfig 13 | 14 | class BaiduMapViewModel: ViewModel() { 15 | var isExists = false 16 | lateinit var baiduMap: BaiduMap 17 | lateinit var mLocationClient: LocationClient 18 | 19 | /** 20 | * Current location 21 | * WGS84 22 | */ 23 | var currentLocation: Pair? = null 24 | 25 | var markName: String? = null 26 | 27 | /** 28 | * Marked location 29 | * WGS84 30 | * first => latitude 31 | * second => longitude 32 | */ 33 | var markedLoc: Pair? = null 34 | var showDetailView = false 35 | 36 | /* Notification */ 37 | var mNotification: Notification? = null 38 | 39 | /** 40 | * 2024.10.10: Cancels the default follow perspective 41 | */ 42 | var perspectiveState = MyLocationConfiguration.LocationMode.NORMAL 43 | set(value) { 44 | field = value 45 | baiduMap.setMapConfig(value, null) 46 | } 47 | 48 | val mMapIndicator: BitmapDescriptor? by lazy { 49 | BitmapDescriptorFactory.fromResource(R.drawable.icon_selected_location_16) 50 | } 51 | 52 | var mGeoCoder: GeoCoder? = null 53 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class HomeViewModel : ViewModel() { 6 | /* Fab */ 7 | var mFabOpened = false 8 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/MockViewModel.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class MockViewModel: ViewModel() { 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/java/moe/fuqiuluo/portal/ui/viewmodel/SettingsViewModel.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal.ui.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | class SettingsViewModel : ViewModel() { 8 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ani_card.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/drawable/ani_card.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_add_location_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_add_location_alt_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_adjust_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_close_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_complete_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_control_camera_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_edit_location_alt_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_info_outline_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_keyboard_arrow_down_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_keyboard_arrow_up_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_label_important_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_location_on_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_location_pin_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_manual_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_my_location_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_play_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_robot_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_rollback_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_route_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_satellite_alt_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_save_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_suggest_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_stop_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_unlock_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_workspaces_filled_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_my_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/drawable/icon_my_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_selected_location_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/drawable/icon_selected_location_16.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_selected_location_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/drawable/icon_selected_location_200.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/idle_state_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple_effect.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_play_disabled_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sharp_location_off_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_no_permission.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 19 | 20 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 38 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_add_location.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | 52 | 53 | 62 | 63 | 64 | 65 | 72 | 73 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_add_route.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | 52 | 53 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_coordinates.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 23 | 24 | 31 | 32 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 23 | 24 | 30 | 31 | 36 | 37 | 38 | 46 | 47 | 51 | 60 | 65 | 66 | 75 | 76 | 85 | 86 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_route_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 23 | 24 | 30 | 31 | 36 | 37 | 38 | 42 | 51 | 56 | 57 | 66 | 67 | 76 | 77 | 86 | 87 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_history_location_item.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | 25 | 26 | 27 | 28 | 36 | 37 | 47 | 48 | 55 | 56 | 60 | 61 | 69 | 70 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_loc_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 22 | 23 | 29 | 30 | 35 | 45 | 57 | 58 | 59 | 60 | 66 | 67 | 71 | 72 | 82 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_route_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | 26 | 27 | 34 | 35 | 45 | 46 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_search_poi_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 24 | 25 | 29 | 34 | 35 | 42 | 43 | 50 | 51 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 22 | 23 | 31 | 32 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/menu/activity_main_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 11 | 15 | 19 | 23 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | 32 | 37 | 38 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | Portal 4 | ナビゲーションドロワーを開く 5 | ナビゲーションドロワーを閉じる 6 | Portal 7 | 学習とコミュニケーションのみのため、3 時間以内に削除してください 8 | ナビゲーションヘッダー 9 | 設定 10 | 検索 11 | 情報 12 | ホーム 13 | 位置情報モック 14 | ルートモック 15 | GNSS モック 16 | ルートモック編集 17 | 設定 18 | 許可をしてくれますか? 19 | カメラに従う 20 | ルートを作成 21 | 通常マップ 22 | 衛星マップ 23 | リバースジオコーディング、経度と緯度に基づいて住所情報を取得します 24 | 経度と緯度 25 | ルートの経度と緯度 26 | アドレス 27 | 緯度を入力してください 28 | 経度を入力してください 29 | ターゲットの場所 30 | ルートを実行 31 | 場所を選択してください 32 | ルートを選択してください 33 | 不明なアドレス 34 | 緯度と経度: 35 | 0.00, 0.00 36 | シュミレーションを開始 37 | ジョイスティックで移動 38 | 位置情報の履歴 39 | ルートの履歴 40 | 位置情報にジャンプ 41 | 位置情報を保存 42 | 位置情報を追加 43 | 名前 44 | 私のモナリザ 45 | ジョイスティックの速度 46 | 1 ミリ秒 47 | SELinux 48 | SELinux を有効化するかどうか 49 | 測位高度 50 | 模擬測位高度 51 | シュミレーションされた移動速度 52 | 位置決めの精度 53 | シュミレートされた位置決めジッターの範囲 54 | デバッグモード 55 | デバッグログを印刷します 56 | 現在地の取得を許可する (新機能) 57 | GetCurrentLocation の呼び出しを許可します 58 | ロケーションリスナーの登録を許可 59 | アプリがロケーションリスナーの登録をできるようにします 60 | ファューズドポジショニングを無効化 61 | ファューズドポジショニングを無効化します 62 | ネットワークを劣化させる 63 | ネットワークを CDMA にダウングレードします 64 | センサーハイジャッキング 65 | シュミレートされたリズム (改善予定) 66 | ルートを表示 67 | 位置報告の間隔 68 | 位置を報告する頻度 (ミリ秒単位) 69 | 100 ミリ秒 70 | GNSS モック 71 | 衛星の情報 72 | 0 G 73 | 利用可能な衛星の数 74 | 使用中の衛星の数 75 | -------------------------------------------------------------------------------- /app/src/main/res/values-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-w1240dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 200dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values-w600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android 5 | com.android.phone 6 | com.android.location.fused 7 | com.xiaomi.location.fused 8 | com.oplus.location 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #D81B60 4 | 5 | #FFFFFFFF 6 | #FFFFFFFF 7 | #696969 8 | #757575 9 | #757575 10 | 11 | #F44336 12 | #4CAF50 13 | #9E9E9E 14 | #2196f3 15 | #FFC107 16 | 17 | #dedede 18 | 19 | 20 | @android:color/black 21 | #00B0FF 22 | 23 | #5F5F5F 24 | #FFFFFF 25 | #373737 26 | #494949 27 | #373737 28 | #FFFFFF 29 | #FFFFFF 30 | #0D0D0D 31 | #5F5F5F 32 | #A0A0A0 33 | 34 | #5F5F5F 35 | #494949 36 | #373737 37 | #5F5F5F 38 | #5F5F5F 39 | #373737 40 | #373737 41 | #373737 42 | #040404 43 | 44 | #bb5F5F5F 45 | #aaFFFFFF 46 | 47 | #BCBCBC 48 | 49 | #FFBB86FC 50 | #FF6200EE 51 | #FF3700B3 52 | #FF03DAC5 53 | #FF018786 54 | #FF000000 55 | #FFFFFFFF 56 | #F44336 57 | #8E24AA 58 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 8dp 6 | 176dp 7 | 16dp 8 | 8dp 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Portal 3 | Open navigation drawer 4 | Close navigation drawer 5 | Portal 6 | 仅提供学习与交流,请3小时内删除 7 | Navigation header 8 | Settings 9 | 搜索 10 | 信息 11 | 12 | Home 13 | Location Mock 14 | Route Mock 15 | Gnss Mock 16 | Route Mock Edit 17 | Setting 18 | 能否给我一个权限呢? 19 | 镜头跟随 20 | 创建路线 21 | 普通图 22 | 卫星图 23 | 逆地理编码,根据经纬度获取地址信息 24 | 经纬度 25 | 路线经纬度 26 | 地址 27 | 请输入纬度 28 | 请输入经度 29 | 目标位置 30 | 实行路线 31 | 请选择一个位置 32 | 请选择一个路线 33 | 未知地址 34 | 经纬度: 35 | 0.00, 0.00 36 | 启动模拟 37 | 移动摇杆 38 | 历史位置 39 | 历史路线 40 | 跳转位置 41 | 保存位置 42 | 添加位置 43 | 名称 44 | 独属于我的蒙娜丽莎 45 | 摇杆速度 46 | 1m/s 47 | SELinux 48 | 是否开启SELinux 49 | 定位海拔 50 | 模拟定位的海拔高度 51 | 模拟移动的移动速度 52 | 定位精度 53 | 模拟定位抖动的范围 54 | 调试模式 55 | 是否打印调试日志 56 | 允许获取当前位置(新) 57 | 是否允许调用GetCurrentLocation 58 | 允许注册位置监听器 59 | 是否允许应用注册位置监听器 60 | 禁用融合定位 61 | 是否禁用融合定位 62 | 网络降级 63 | 将网络降级为CDMA 64 | 传感器劫持 65 | 模拟步频(待完善) 66 | 显示路线 67 | 位置上报间隔 68 | 上报位置的频率单位毫秒(ms) 69 | 100ms 70 | Gnss Mock 71 | 星图信息 72 | 0 G 73 | 可用卫星数量 74 | 在用卫星数量 75 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fragment_settings_scene.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 12 | 13 | 14 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/test/java/moe/fuqiuluo/portal/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.portal 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.android.application) apply false 4 | alias(libs.plugins.kotlin.android) apply false 5 | alias(libs.plugins.android.library) apply false 6 | alias(libs.plugins.jetbrains.kotlin.jvm) apply false 7 | alias(libs.plugins.kotlin.serialization) apply false 8 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.7.1" 3 | circularProgressButtonVersion = "1.4" 4 | firebaseBom = "33.9.0" 5 | systemRefine = "4.4.0" 6 | xposed = "82" 7 | hiddenapibypass = "4.3" 8 | kotlin = "2.0.21" 9 | coreKtx = "1.15.0" 10 | junit = "4.13.2" 11 | junitVersion = "1.2.1" 12 | espressoCore = "3.6.1" 13 | appcompat = "1.7.0" 14 | material = "1.12.0" 15 | constraintlayout = "2.2.0" 16 | lifecycleLivedataKtx = "2.8.7" 17 | lifecycleViewmodelKtx = "2.8.7" 18 | navigationFragmentKtx = "2.8.3" 19 | navigationUiKtx = "2.8.3" 20 | playServicesLocation = "21.3.0" 21 | lifecycleProcess = "2.8.7" 22 | androidbrowserhelper = "2.5.0" 23 | jetbrainsKotlinJvm = "2.0.21" 24 | 25 | [libraries] 26 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 27 | dmytrodanylyk-circular-progress-button = { module = "com.github.dmytrodanylyk:circular-progress-button", version.ref = "circularProgressButtonVersion" } 28 | firebase-analytics = { module = "com.google.firebase:firebase-analytics" } 29 | firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } 30 | firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } 31 | refine-annotation-core = { group = "dev.rikka.tools.refine", name = "annotation", version.ref = "systemRefine" } 32 | refine-annotation-processor = { group = "dev.rikka.tools.refine", name = "annotation-processor", version.ref = "systemRefine" } 33 | refine-runtime = { group = "dev.rikka.tools.refine", name = "runtime", version.ref = "systemRefine" } 34 | xposed-api = { group = "de.robv.android.xposed", name = "api", version.ref = "xposed" } 35 | junit = { group = "junit", name = "junit", version.ref = "junit" } 36 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } 37 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } 38 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 39 | material = { group = "com.google.android.material", name = "material", version.ref = "material" } 40 | androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } 41 | androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" } 42 | androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } 43 | androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } 44 | androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" } 45 | play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" } 46 | androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycleProcess" } 47 | androidbrowserhelper = { group = "com.google.androidbrowserhelper", name = "androidbrowserhelper", version.ref = "androidbrowserhelper" } 48 | okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version = "4.12.0" } 49 | fastjson = { module = "com.alibaba.fastjson2:fastjson2-kotlin", version = "2.0.56" } 50 | kotlin-reflect = {module = "org.jetbrains.kotlin:kotlin-reflect", version = "1.9.23"} 51 | kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.2" } 52 | bugly = { module = "com.tencent.bugly:crashreport", version = "4.1.9.3" } 53 | geotools = {module = "net.sf.geographiclib:GeographicLib-Java", version = "2.0"} 54 | 55 | [plugins] 56 | android-application = { id = "com.android.application", version.ref = "agp" } 57 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 58 | android-library = { id = "com.android.library", version.ref = "agp" } 59 | jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" } 60 | kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "1.4.30" } 61 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 19 02:12:31 HKT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /img/qrcodemini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/img/qrcodemini.png -------------------------------------------------------------------------------- /nmea/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /nmea/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | alias(libs.plugins.jetbrains.kotlin.jvm) 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | 11 | toolchain { 12 | languageVersion.set(JavaLanguageVersion.of(17)) 13 | } 14 | } -------------------------------------------------------------------------------- /nmea/src/test/java/Test1.kt: -------------------------------------------------------------------------------- 1 | package moe.microbios.nmea 2 | 3 | import java.math.BigDecimal 4 | import kotlin.math.absoluteValue 5 | 6 | val data = setOf( 7 | "\$GPGSA,A,1,,,,,,,,,,,,,,,,*32", 8 | "\$GPVTG,,T,,M,,N,,K,N*2C", 9 | "\$GPDTM,,,,,,,,*4A", 10 | "\$GPRMC,,V,,,,,,,,,,N,V*29", 11 | "\$GPGNS,,,,,,N,,,,,,,V*79", 12 | "\$GPGGA,,,,,,0,,,,,,,,*66", 13 | "\$GPGSV,3,1,09,03,29,281,,04,18,319,,16,60,239,,26,73,355,,1*60", 14 | "\$GPGSV,3,2,09,27,18,180,,28,39,060,,29,15,049,,31,55,010,,1*6E", 15 | "\$GPGSV,3,3,09,32,29,137,,1*52", 16 | "\$GLGSV,2,1,08,14,43,011,,16,07,248,,15,46,288,,05,38,326,,1*72", 17 | "\$GPGGA,,,,,,0,,,,,,,,*66", 18 | "\$GPGNS,,,,,,N,,,,,,,V*79", 19 | "\$GQGSV,1,1,03,02,64,078,,03,12,142,,04,44,162,,1*5F", 20 | ) 21 | 22 | val HUNDRED = BigDecimal(100) 23 | val SIXTY = BigDecimal(60) 24 | 25 | fun main() { 26 | data.forEach { 27 | val nmea = NMEA.valueOf(it) 28 | if (nmea.toNmeaString() != it) { 29 | println("Your value: ${nmea.toNmeaString()}") 30 | println("Original value: $it") 31 | throw IllegalArgumentException("NMEA value is not equal to original value: $it") 32 | } 33 | } 34 | println("All tests passed") 35 | 36 | 37 | val lat = 28.139908 38 | val lon = 113.8939 39 | val degree = lat.toInt() 40 | val minute = (lat - degree) * 60 41 | val latitude = degree + minute / 100 42 | 43 | val degree2 = lon.toInt() 44 | val minute2 = (lon - degree2) * 60 45 | val longitude = degree2 + minute2 / 100 46 | 47 | println("Latitude: $latitude") 48 | println("Longitude: $longitude") 49 | } -------------------------------------------------------------------------------- /refs/10.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/11.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android11-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/12.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android12-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/13.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android13-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/14.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android14-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/15.0.0/README.md: -------------------------------------------------------------------------------- 1 | 2 | - [ILocationManager](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android15-release/location/java/android/location/ILocationManager.aidl) -------------------------------------------------------------------------------- /refs/7.0.0_r1/IGnssStatusListener.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.location; 18 | 19 | import android.location.Location; 20 | 21 | /** 22 | * {@hide} 23 | */ 24 | oneway interface IGnssStatusListener 25 | { 26 | void onGnssStarted(); 27 | void onGnssStopped(); 28 | void onFirstFix(int ttff); 29 | void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] cn0s, 30 | in float[] elevations, in float[] azimuths); 31 | // android 11 32 | // void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] cn0s, 33 | // in float[] elevations, in float[] azimuths, 34 | // in float[] carrierFreqs, in float[] basebandCn0s); 35 | void onNmeaReceived(long timestamp, String nmea); 36 | } 37 | -------------------------------------------------------------------------------- /refs/7.0.0_r1/ILocationManager.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.location; 18 | 19 | import android.app.PendingIntent; 20 | import android.location.Address; 21 | import android.location.Criteria; 22 | import android.location.GeocoderParams; 23 | import android.location.Geofence; 24 | import android.location.IGnssMeasurementsListener; 25 | import android.location.IGnssStatusListener; 26 | import android.location.IGnssNavigationMessageListener; 27 | import android.location.ILocationListener; 28 | import android.location.Location; 29 | import android.location.LocationRequest; 30 | import android.os.Bundle; 31 | 32 | import com.android.internal.location.ProviderProperties; 33 | 34 | /** 35 | * System private API for talking with the location service. 36 | * 37 | * @hide 38 | */ 39 | interface ILocationManager 40 | { 41 | void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, 42 | in PendingIntent intent, String packageName); 43 | void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName); 44 | 45 | void requestGeofence(in LocationRequest request, in Geofence geofence, 46 | in PendingIntent intent, String packageName); 47 | void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName); 48 | 49 | Location getLastLocation(in LocationRequest request, String packageName); 50 | 51 | boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName); 52 | void unregisterGnssStatusCallback(IGnssStatusListener callback); 53 | 54 | boolean geocoderIsPresent(); 55 | String getFromLocation(double latitude, double longitude, int maxResults, 56 | in GeocoderParams params, out List
addrs); 57 | String getFromLocationName(String locationName, 58 | double lowerLeftLatitude, double lowerLeftLongitude, 59 | double upperRightLatitude, double upperRightLongitude, int maxResults, 60 | in GeocoderParams params, out List
addrs); 61 | 62 | boolean sendNiResponse(int notifId, int userResponse); 63 | 64 | boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName); 65 | void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); 66 | 67 | boolean addGnssNavigationMessageListener( 68 | in IGnssNavigationMessageListener listener, 69 | in String packageName); 70 | void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); 71 | 72 | int getGnssYearOfHardware(); 73 | 74 | // --- deprecated --- 75 | List getAllProviders(); 76 | List getProviders(in Criteria criteria, boolean enabledOnly); 77 | String getBestProvider(in Criteria criteria, boolean enabledOnly); 78 | boolean providerMeetsCriteria(String provider, in Criteria criteria); 79 | ProviderProperties getProviderProperties(String provider); 80 | String getNetworkProviderPackage(); 81 | boolean isProviderEnabled(String provider); 82 | 83 | void addTestProvider(String name, in ProviderProperties properties, String opPackageName); 84 | void removeTestProvider(String provider, String opPackageName); 85 | void setTestProviderLocation(String provider, in Location loc, String opPackageName); 86 | void clearTestProviderLocation(String provider, String opPackageName); 87 | void setTestProviderEnabled(String provider, boolean enabled, String opPackageName); 88 | void clearTestProviderEnabled(String provider, String opPackageName); 89 | void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime, 90 | String opPackageName); 91 | void clearTestProviderStatus(String provider, String opPackageName); 92 | 93 | boolean sendExtraCommand(String provider, String command, inout Bundle extras); 94 | 95 | // --- internal --- 96 | 97 | // Used by location providers to tell the location manager when it has a new location. 98 | // Passive is true if the location is coming from the passive provider, in which case 99 | // it need not be shared with other providers. 100 | void reportLocation(in Location location, boolean passive); 101 | 102 | // for reporting callback completion 103 | void locationCallbackFinished(ILocationListener listener); 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /refs/7.0.0_r1/README.md: -------------------------------------------------------------------------------- 1 | # Android 7.0.0 R1 APIS 2 | 3 | - http://androidxref.com/7.0.0_r1/xref/frameworks/base/location/java/android/location/ -------------------------------------------------------------------------------- /refs/7.1.1_r6/README.md: -------------------------------------------------------------------------------- 1 | # Android 7.1.1 R6 2 | 3 | - http://androidxref.com/7.1.1_r6/xref/frameworks/base/location/java/android/location -------------------------------------------------------------------------------- /refs/8.0.0_r4/README.md: -------------------------------------------------------------------------------- 1 | # Android 8.0.0 R4 2 | 3 | - http://androidxref.com/8.0.0_r4/xref/frameworks/base/location/java/android/location -------------------------------------------------------------------------------- /refs/9.0.0_r3/README.md: -------------------------------------------------------------------------------- 1 | # Android 8.0.0 R3 2 | 3 | - http://androidxref.com/9.0.0_r3/xref/frameworks/base/location/java/android/location -------------------------------------------------------------------------------- /refs/README.md: -------------------------------------------------------------------------------- 1 | # REFS 2 | 3 | This project hooks some Android system things, 4 | and you need to refer to the implementation of the source code of each version of Android. 5 | Write this thing to facilitate the maintenance of interfaces that are compatible with different versions. -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | 15 | dependencyResolutionManagement { 16 | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) 17 | repositories { 18 | maven ( url = "https://maven.aliyun.com/nexus/content/repositories/google") 19 | maven ( url = "https://maven.aliyun.com/nexus/content/groups/public/" ) 20 | maven ( url = "https://maven.aliyun.com/nexus/content/repositories/jcenter") 21 | google() 22 | mavenCentral() 23 | maven (url = "https://maven.pkg.jetbrains.space/public/p/ktor/eap") 24 | maven (url = "https://dl.bintray.com/kotlin/kotlin-eap" ) 25 | maven (url = "https://api.xposed.info/" ) 26 | maven (url = "https://jitpack.io" ) 27 | } 28 | } 29 | 30 | buildscript { 31 | repositories { 32 | mavenCentral() 33 | maven { 34 | url = uri("https://storage.googleapis.com/r8-releases/raw") 35 | } 36 | } 37 | dependencies {} 38 | } 39 | 40 | rootProject.name = "Portal" 41 | include(":app") 42 | include(":xposed") 43 | include(":system-api") 44 | include(":nmea") 45 | -------------------------------------------------------------------------------- /system-api/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /system-api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.library) 3 | alias(libs.plugins.kotlin.android) 4 | } 5 | 6 | android { 7 | namespace = "moe.fuqiuluo.hardcoder" 8 | compileSdk = 35 9 | 10 | defaultConfig { 11 | minSdk = 24 12 | 13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles("consumer-rules.pro") 15 | } 16 | 17 | buildTypes { 18 | release { 19 | isMinifyEnabled = false 20 | proguardFiles( 21 | getDefaultProguardFile("proguard-android-optimize.txt"), 22 | "proguard-rules.pro" 23 | ) 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility = JavaVersion.VERSION_17 28 | targetCompatibility = JavaVersion.VERSION_17 29 | } 30 | kotlinOptions { 31 | jvmTarget = "17" 32 | } 33 | } 34 | 35 | dependencies { 36 | testImplementation(libs.junit) 37 | androidTestImplementation(libs.androidx.junit) 38 | androidTestImplementation(libs.androidx.espresso.core) 39 | } -------------------------------------------------------------------------------- /system-api/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/system-api/consumer-rules.pro -------------------------------------------------------------------------------- /system-api/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /system-api/src/androidTest/java/moe/fuqiuluo/hardcoder/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.hardcoder 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("moe.fuqiuluo.hardcoder.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /system-api/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /system-api/src/main/java/android/location/LastLocationRequest.java: -------------------------------------------------------------------------------- 1 | package android.location; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class LastLocationRequest implements Parcelable { 7 | protected LastLocationRequest(Parcel in) { 8 | } 9 | 10 | public static final Creator CREATOR = new Creator() { 11 | @Override 12 | public LastLocationRequest createFromParcel(Parcel in) { 13 | return new LastLocationRequest(in); 14 | } 15 | 16 | @Override 17 | public LastLocationRequest[] newArray(int size) { 18 | return new LastLocationRequest[size]; 19 | } 20 | }; 21 | 22 | /** 23 | * Describe the kinds of special objects contained in this Parcelable 24 | * instance's marshaled representation. For example, if the object will 25 | * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)}, 26 | * the return value of this method must include the 27 | * {@link #CONTENTS_FILE_DESCRIPTOR} bit. 28 | * 29 | * @return a bitmask indicating the set of special object types marshaled 30 | * by this Parcelable object instance. 31 | */ 32 | @Override 33 | public int describeContents() { 34 | return 0; 35 | } 36 | 37 | /** 38 | * Flatten this object in to a Parcel. 39 | * 40 | * @param dest The Parcel in which the object should be written. 41 | * @param flags Additional flags about how the object should be written. 42 | * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. 43 | */ 44 | @Override 45 | public void writeToParcel(Parcel dest, int flags) { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /system-api/src/test/java/moe/fuqiuluo/hardcoder/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.hardcoder 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /xposed/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /xposed/README.md: -------------------------------------------------------------------------------- 1 | # Xposed Module 2 | 3 | XPosed module implementation 4 | 5 | # Warning 6 | 7 | This project is only for learning and communication, 8 | and shall not be used for commercial purposes or illegal operations, 9 | otherwise you will be responsible for the consequences. 10 | 11 | # Thanks 12 | 13 | - [FuckLocation](https://github.com/Mikotwa/FuckLocation/blob/main/app/src/main/java/fuck/location/xposed/location/LocationHookerAfterS.kt) -------------------------------------------------------------------------------- /xposed/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.library) 3 | alias(libs.plugins.kotlin.android) 4 | } 5 | 6 | android { 7 | namespace = "moe.fuqiuluo.xposed" 8 | compileSdk = 35 9 | ndkVersion = "26.1.10909125" 10 | 11 | defaultConfig { 12 | minSdk = 24 13 | 14 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles("consumer-rules.pro") 16 | externalNativeBuild { 17 | cmake { 18 | cppFlags += "" 19 | } 20 | } 21 | ndk { 22 | abiFilters.addAll(arrayOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")) 23 | } 24 | } 25 | 26 | buildTypes { 27 | release { 28 | isMinifyEnabled = false 29 | proguardFiles( 30 | getDefaultProguardFile("proguard-android-optimize.txt"), 31 | "proguard-rules.pro" 32 | ) 33 | } 34 | } 35 | buildFeatures { 36 | prefab = true 37 | } 38 | compileOptions { 39 | sourceCompatibility = JavaVersion.VERSION_17 40 | targetCompatibility = JavaVersion.VERSION_17 41 | } 42 | kotlinOptions { 43 | jvmTarget = "17" 44 | } 45 | externalNativeBuild { 46 | cmake { 47 | path = file("src/main/cpp/CMakeLists.txt") 48 | version = "3.22.1" 49 | } 50 | } 51 | } 52 | 53 | dependencies { 54 | compileOnly(libs.xposed.api) 55 | compileOnly(project(":system-api")) 56 | implementation(project(":nmea")) 57 | 58 | // Just Test? 59 | //noinspection GradleDynamicVersion 60 | //implementation("org.lsposed.lsplant:lsplant-standalone:+") 61 | //noinspection UseTomlInstead 62 | implementation("io.github.vvb2060.ndk:dobby:1.2") 63 | 64 | testImplementation(libs.junit) 65 | androidTestImplementation(libs.androidx.junit) 66 | androidTestImplementation(libs.androidx.espresso.core) 67 | } -------------------------------------------------------------------------------- /xposed/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuqiuluo/Portal/f44e510c48d128c4c181d5a9b181339361bf2bfe/xposed/consumer-rules.pro -------------------------------------------------------------------------------- /xposed/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /xposed/src/androidTest/java/moe/fuqiuluo/xposed/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("moe.fuqiuluo.xposed.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /xposed/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xposed/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | moe.fuqiuluo.xposed.FakeLocation -------------------------------------------------------------------------------- /xposed/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.1) 2 | 3 | project("Portal") 4 | 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | # MumuEmulator crash? if enable x86_64 8 | 9 | #find_package(lsplant REQUIRED CONFIG) 10 | find_package(dobby REQUIRED CONFIG) 11 | 12 | add_library(portal SHARED 13 | main.cpp 14 | elf_util.cpp 15 | sensor_hook.cpp) 16 | 17 | target_link_libraries(portal android log) 18 | target_link_libraries(portal dobby::dobby) 19 | #target_link_libraries(${CMAKE_PROJECT_NAME} lsplant::lsplant) 20 | -------------------------------------------------------------------------------- /xposed/src/main/cpp/dobby_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTAL_DOBBY_HOOK_H 2 | #define PORTAL_DOBBY_HOOK_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define uintval(p) reinterpret_cast(p) 9 | #define ptr(p) (reinterpret_cast(p)) 10 | #define align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) 11 | #define align_down(x, n) ((x) & -(n)) 12 | #define page_size getpagesize() 13 | #define page_align(n) align_up(static_cast(n), page_size) 14 | #define ptr_align(x) ptr(align_down(reinterpret_cast(x), page_size)) 15 | #define make_rwx(p, n) ::mprotect(ptr_align(p), \ 16 | page_align(uintval(p) + (n)) != page_align(uintval(p)) \ 17 | ? page_align(n) + page_size : page_align(n), \ 18 | PROT_READ | PROT_WRITE | PROT_EXEC) 19 | 20 | void* InlineHook(void* target, void* hooker) { 21 | make_rwx(target, page_size); 22 | void* origin_call; 23 | if (DobbyHook(target, hooker, &origin_call) == RS_SUCCESS) { 24 | return origin_call; 25 | } else { 26 | return nullptr; 27 | } 28 | } 29 | 30 | #endif //PORTAL_DOBBY_HOOK_H 31 | -------------------------------------------------------------------------------- /xposed/src/main/cpp/elf_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LSPosed. 3 | * 4 | * LSPosed is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * LSPosed is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with LSPosed. If not, see . 16 | * 17 | * Copyright (C) 2019 Swift Gan 18 | * Copyright (C) 2021 LSPosed Contributors 19 | */ 20 | #ifndef SANDHOOK_ELF_UTIL_H 21 | #define SANDHOOK_ELF_UTIL_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define SHT_GNU_HASH 0x6ffffff6 29 | 30 | namespace SandHook { 31 | class ElfImg { 32 | public: 33 | ElfImg(std::string_view elf); 34 | 35 | template 36 | requires(std::is_pointer_v) 37 | constexpr const T getSymbolAddress(std::string_view name) const { 38 | auto offset = getSymbolOffset(name, gnuHash(name), elfHash(name)); 39 | if (offset > 0 && base != nullptr) { 40 | return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); 41 | } 42 | return nullptr; 43 | } 44 | 45 | template 46 | requires(std::is_pointer_v) 47 | constexpr const T getSymbolAddressByPrefix(std::string_view prefix) const { 48 | auto offset = prefixLookup(prefix); 49 | if (offset > 0 && base != nullptr) { 50 | return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); 51 | } 52 | return nullptr; 53 | } 54 | 55 | template 56 | inline constexpr auto offsetOf(ElfW(Ehdr) *head, ElfW(Off) off) const { 57 | return reinterpret_cast, T, T *>>(reinterpret_cast(head) + off); 58 | } 59 | 60 | constexpr uint32_t elfHash(std::string_view name) const { 61 | uint32_t h = 0, g; 62 | for (unsigned char p: name) { 63 | h = (h << 4) + p; 64 | g = h & 0xf0000000; 65 | h ^= g; 66 | h ^= g >> 24; 67 | } 68 | return h; 69 | } 70 | 71 | constexpr uint32_t gnuHash(std::string_view name) const { 72 | uint32_t h = 5381; 73 | for (unsigned char p: name) { 74 | h += (h << 5) + p; 75 | } 76 | return h; 77 | } 78 | 79 | constexpr inline bool contains(std::string_view a, std::string_view b) const { 80 | return a.find(b) != std::string_view::npos; 81 | } 82 | 83 | bool isValid() const { 84 | return base != nullptr; 85 | } 86 | 87 | const std::string name() const { 88 | return elf; 89 | } 90 | 91 | ~ElfImg(); 92 | private: 93 | ElfW(Addr) getSymbolOffset(std::string_view name, uint32_t gnuHash, uint32_t elfHash) const; 94 | ElfW(Addr) elfLookup(std::string_view name, uint32_t hash) const; 95 | ElfW(Addr) gnuLookup(std::string_view name, uint32_t hash) const; 96 | ElfW(Addr) linearLookup(std::string_view name) const; 97 | ElfW(Addr) prefixLookup(std::string_view prefix) const; 98 | 99 | void initLinearMap() const; 100 | void initModuleBase(); 101 | 102 | std::string elf; 103 | void *base = nullptr; 104 | 105 | off_t size = 0; 106 | off_t bias = -4396; 107 | 108 | ElfW(Ehdr) *header = nullptr; 109 | ElfW(Shdr) *sectionHeader = nullptr; 110 | ElfW(Shdr) *strtab = nullptr; 111 | ElfW(Shdr) *dynsym = nullptr; 112 | 113 | ElfW(Sym) *symtabStart = nullptr; 114 | ElfW(Sym) *dynsymStart = nullptr; 115 | ElfW(Sym) *strtabStart = nullptr; 116 | 117 | ElfW(Off) symtabCount = 0; 118 | ElfW(Off) symstrOffsetForSymtab = 0; 119 | 120 | uint32_t nbucket_{}; 121 | uint32_t *bucket_ = nullptr; 122 | uint32_t *chain_ = nullptr; 123 | 124 | uint32_t gnu_nbucket_{}; 125 | uint32_t gnu_symndx_{}; 126 | uint32_t gnu_bloom_size_; 127 | uint32_t gnu_shift2_; 128 | uintptr_t *gnu_bloom_filter_; 129 | uint32_t *gnu_bucket_; 130 | uint32_t *gnu_chain_; 131 | 132 | mutable std::map symtabs_; 133 | }; 134 | } 135 | 136 | #endif //SANDHOOK_ELF_UTIL_H -------------------------------------------------------------------------------- /xposed/src/main/cpp/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOGGING_H 2 | #define _LOGGING_H 3 | 4 | #include 5 | 6 | #ifndef LOG_TAG 7 | #define LOG_TAG "LSPosed-Bridge" 8 | #endif 9 | 10 | #ifdef LOG_DISABLED 11 | #define LOGD(...) 12 | #define LOGV(...) 13 | #define LOGI(...) 14 | #define LOGW(...) 15 | #define LOGE(...) 16 | #else 17 | #ifndef NDEBUG 18 | #define LOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "[Portal][DEBUG] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) 19 | #define LOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "[Portal][VERBOSE] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) 20 | #else 21 | #define LOGD(...) 22 | #define LOGV(...) 23 | #endif 24 | #define LOGW(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "[Portal][ERROR] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) 25 | #define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "[Portal][INFO] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) 26 | #define LOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "[Portal][ERROR] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) 27 | #endif 28 | 29 | #endif // _LOGGING_H -------------------------------------------------------------------------------- /xposed/src/main/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "sensor_hook.h" 7 | 8 | bool enableSensorHook = false; 9 | 10 | JNIEXPORT jint JNICALL 11 | JNI_OnLoad(JavaVM* vm, void* reserved) { 12 | JNIEnv* env; 13 | if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) { 14 | return JNI_ERR; 15 | } 16 | 17 | doSensorHook(); 18 | 19 | return JNI_VERSION_1_6; 20 | } 21 | 22 | extern "C" 23 | JNIEXPORT void JNICALL 24 | Java_moe_fuqiuluo_dobby_Dobby_setStatus(JNIEnv *env, jobject thiz, jboolean status) { 25 | enableSensorHook = status; 26 | } -------------------------------------------------------------------------------- /xposed/src/main/cpp/sensor_hook.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by fuqiuluo on 2024/10/15. 3 | // 4 | #include 5 | #include 6 | #include "sensor_hook.h" 7 | #include "logging.h" 8 | #include "elf_util.h" 9 | #include "dobby_hook.h" 10 | 11 | #define LIBSF_PATH "/system/lib64/libsensorservice.so" 12 | 13 | extern bool enableSensorHook; 14 | 15 | // _ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventm 16 | OriginalSensorEventQueueWriteType OriginalSensorEventQueueWrite = nullptr; 17 | 18 | OriginalConvertToSensorEventType OriginalConvertToSensorEvent = nullptr; 19 | 20 | int64_t SensorEventQueueWrite(void *tube, void *events, int64_t numEvents) { 21 | if (enableSensorHook) { 22 | LOGD("SensorEventQueueWrite called"); 23 | } 24 | return OriginalSensorEventQueueWrite(tube, events, numEvents); 25 | } 26 | 27 | void ConvertToSensorEvent(void *src, void *dst) { 28 | if (enableSensorHook) { 29 | auto a = *(int32_t *)((char*)src + 4); 30 | auto b = *(int32_t *)((char*)src + 8); 31 | auto c = *(int64_t *)((char*)src + 16); 32 | 33 | *(int64_t *)((char*)dst + 16) = 0LL; 34 | *(int32_t *)((char*)dst + 24) = 0; 35 | *(int64_t *)((char*)dst) = c; 36 | *(int32_t *)((char*)dst + 8) = a; 37 | *(int32_t *)((char*)dst + 12) = b; 38 | *(int8_t *)((char*)dst + 28) = b; 39 | 40 | if (b == 18) { 41 | *(float *)((char*)dst + 16) = -1.0; 42 | } else if (b == 19) { 43 | *(int64_t *)((char*)dst + 16) = -1; 44 | } else { 45 | *(float *)((char*)dst + 16) = -1.0; 46 | *(float *)((char*)dst + 24) = -1.0; 47 | *(int8_t *)((char*)dst + 28) = *(int8_t *)((char*)src + 36); 48 | } 49 | } else { 50 | OriginalConvertToSensorEvent(src, dst); 51 | } 52 | 53 | if (enableSensorHook) { 54 | LOGD("ConvertToSensorEvent called"); 55 | } 56 | } 57 | 58 | void doSensorHook() { 59 | SandHook::ElfImg sensorService(LIBSF_PATH); 60 | 61 | if (!sensorService.isValid()) { 62 | LOGE("failed to load libsensorservice"); 63 | return; 64 | } 65 | 66 | auto sensorWrite = sensorService.getSymbolAddress("_ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventm"); 67 | if (sensorWrite == nullptr) { 68 | sensorWrite = sensorService.getSymbolAddress("_ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventj"); 69 | } 70 | 71 | auto convertToSensorEvent = sensorService.getSymbolAddress("_ZN7android8hardware7sensors4V1_014implementation20convertToSensorEventERKNS2_5EventEP15sensors_event_t"); 72 | 73 | LOGD("Dobby SensorEventQueue::write found at %p", sensorWrite); 74 | LOGD("Dobby convertToSensorEvent found at %p", convertToSensorEvent); 75 | 76 | if (sensorWrite != nullptr) { 77 | OriginalSensorEventQueueWrite = (OriginalSensorEventQueueWriteType)InlineHook(sensorWrite, (void *)SensorEventQueueWrite); 78 | } 79 | 80 | if (convertToSensorEvent != nullptr) { 81 | OriginalConvertToSensorEvent = (OriginalConvertToSensorEventType)InlineHook(convertToSensorEvent, (void *)ConvertToSensorEvent); 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /xposed/src/main/cpp/sensor_hook.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by fuqiuluo on 2024/10/15. 3 | // 4 | 5 | #ifndef PORTAL_SENSOR_HOOK_H 6 | #define PORTAL_SENSOR_HOOK_H 7 | 8 | #include "android/sensor.h" 9 | 10 | // ssize_t SensorEventQueue::write(const sp& tube, 11 | // ASensorEvent const* events, size_t numEvents) 12 | typedef int64_t (*OriginalSensorEventQueueWriteType)(void*, void*, int64_t); 13 | 14 | // void convertToSensorEvent(const Event &src, sensors_event_t *dst); 15 | typedef void (*OriginalConvertToSensorEventType)(void*, void*); 16 | 17 | void doSensorHook(); 18 | 19 | #endif //PORTAL_SENSOR_HOOK_H 20 | -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/dobby/Dobby.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.dobby 2 | 3 | object Dobby { 4 | 5 | external fun setStatus(status: Boolean) 6 | 7 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/blindhook/BlindHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.blindhook 2 | 3 | import android.location.Location 4 | import de.robv.android.xposed.XC_MethodHook 5 | import moe.fuqiuluo.xposed.utils.FakeLoc 6 | import moe.fuqiuluo.xposed.utils.Logger 7 | import moe.fuqiuluo.xposed.utils.onceHook 8 | import java.lang.reflect.Member 9 | 10 | object BlindHook { 11 | operator fun invoke(clazz: Class<*>, classLoader: ClassLoader, handler: (Member, T?) -> T?): Int { 12 | var count = 0 13 | clazz.declaredMethods.forEach { 14 | if (it.returnType == Location::class.java) { 15 | if (FakeLoc.enableDebugLog) { 16 | Logger.debug("BlindHookV2 ${it.name}: ${it.parameterTypes.joinToString()}") 17 | } 18 | 19 | it.onceHook(BlindHookForRETLocation(false, false, handler)) 20 | count++ 21 | return@forEach 22 | } 23 | 24 | it.parameterTypes.forEachIndexed { index, type -> 25 | if (type == Location::class.java) { 26 | if (FakeLoc.enableDebugLog) { 27 | Logger.debug("BlindHookV2 ${it.name}: ${it.parameterTypes.joinToString()}") 28 | } 29 | 30 | it.onceHook(BlindHookForLocation(index, false, false, handler)) 31 | count++ 32 | return@forEach 33 | } 34 | } 35 | } 36 | return count 37 | } 38 | 39 | @Suppress("UNCHECKED_CAST") 40 | private class BlindHookForLocation( 41 | val index: Int, 42 | val isList: Boolean, 43 | val isArray: Boolean, 44 | val handler: (Member, T?) -> T? 45 | ): XC_MethodHook() { 46 | override fun beforeHookedMethod(param: MethodHookParam) { 47 | val data = param.args[index] as? T ?: return 48 | 49 | param.args[index] = handler(param.method, data) 50 | } 51 | } 52 | 53 | @Suppress("UNCHECKED_CAST") 54 | private class BlindHookForRETLocation( 55 | val isList: Boolean, 56 | val isArray: Boolean, 57 | val handler: (Member, T?) -> T? 58 | ): XC_MethodHook() { 59 | override fun afterHookedMethod(param: MethodHookParam) { 60 | val data = param.result as? T ?: return 61 | 62 | param.result = handler(param.method, data) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/blindhook/BlindHookLocation.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.blindhook 2 | 3 | import android.location.Location 4 | import de.robv.android.xposed.XposedBridge 5 | import moe.fuqiuluo.xposed.BaseLocationHook 6 | import moe.fuqiuluo.xposed.utils.FakeLoc 7 | import moe.fuqiuluo.xposed.utils.Logger 8 | 9 | object BlindHookLocation: BaseLocationHook() { 10 | operator fun invoke(clazz: Class<*>, classLoader: ClassLoader): Int { 11 | return BlindHook(clazz, classLoader) { method, location: Location? -> 12 | if (location == null || !FakeLoc.enable) return@BlindHook location 13 | 14 | val newLoc = injectLocation(location) 15 | 16 | if (FakeLoc.enableDebugLog) { 17 | Logger.debug("${method.name} injected: $newLoc") 18 | } 19 | 20 | newLoc 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/fused/AndroidFusedLocationProviderHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.fused 2 | 3 | import android.location.Location 4 | import moe.fuqiuluo.xposed.BaseLocationHook 5 | import moe.fuqiuluo.xposed.hooks.blindhook.BlindHookLocation 6 | import moe.fuqiuluo.xposed.utils.FakeLoc 7 | import moe.fuqiuluo.xposed.utils.Logger 8 | import moe.fuqiuluo.xposed.utils.hookMethodAfter 9 | import moe.fuqiuluo.xposed.utils.toClass 10 | 11 | object AndroidFusedLocationProviderHook: BaseLocationHook() { 12 | operator fun invoke(classLoader: ClassLoader) { 13 | val cFusedLocationProvider = "com.android.location.fused.FusedLocationProvider".toClass(classLoader) 14 | if (cFusedLocationProvider == null) { 15 | Logger.warn("Failed to find FusedLocationProvider") 16 | return 17 | } 18 | 19 | if(!initDivineService("AndroidFusedLocationProvider")) { 20 | Logger.error("Failed to init DivineService in AndroidFusedLocationProvider") 21 | return 22 | } 23 | 24 | cFusedLocationProvider.hookMethodAfter("chooseBestLocation", Location::class.java, Location::class.java) { 25 | if (result == null) return@hookMethodAfter 26 | 27 | if (FakeLoc.enable) { 28 | result = injectLocation(result as Location) 29 | } 30 | } 31 | 32 | // cFusedLocationProvider.hookMethodBefore("reportBestLocationLocked") { 33 | // 34 | // } 35 | 36 | val cChildLocationListener = "com.android.location.fused.FusedLocationProvider\$ChildLocationListener".toClass(classLoader) 37 | if (cChildLocationListener == null) { 38 | Logger.warn("Failed to find ChildLocationListener") 39 | return 40 | } 41 | 42 | BlindHookLocation(cChildLocationListener, classLoader) 43 | } 44 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/miui/MiuiBlurLocationProviderHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.miui 2 | 3 | import android.os.Build 4 | import android.telephony.CellIdentity 5 | import android.telephony.CellIdentityCdma 6 | import android.telephony.CellInfo 7 | import android.telephony.CellInfoCdma 8 | import android.telephony.CellSignalStrengthCdma 9 | import de.robv.android.xposed.XC_MethodHook 10 | import de.robv.android.xposed.XposedHelpers 11 | import moe.fuqiuluo.xposed.BaseLocationHook 12 | import moe.fuqiuluo.xposed.hooks.blindhook.BlindHookLocation 13 | import moe.fuqiuluo.xposed.utils.BinderUtils 14 | import moe.fuqiuluo.xposed.utils.FakeLoc 15 | import moe.fuqiuluo.xposed.utils.beforeHook 16 | import moe.fuqiuluo.xposed.utils.onceHookAllMethod 17 | import moe.fuqiuluo.xposed.utils.onceHookMethodBefore 18 | 19 | object MiuiBlurLocationProviderHook: BaseLocationHook() { 20 | operator fun invoke(classLoader: ClassLoader) { 21 | val cMiuiBlurLocationManagerImpl = XposedHelpers.findClassIfExists("com.android.server.location.MiuiBlurLocationManagerImpl", classLoader) 22 | if (cMiuiBlurLocationManagerImpl != null) { 23 | BlindHookLocation(cMiuiBlurLocationManagerImpl, classLoader) 24 | 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 26 | val hooker: XC_MethodHook.MethodHookParam.() -> Unit = { 27 | if (FakeLoc.enable && !BinderUtils.isSystemAppsCall()) { 28 | result = CellIdentityCdma::class.java.getConstructor( 29 | Int::class.java, 30 | Int::class.java, 31 | Int::class.java, 32 | Int::class.java, 33 | Int::class.java, 34 | String::class.java, 35 | String::class.java 36 | ).newInstance( 37 | Int.MAX_VALUE, 38 | Int.MAX_VALUE, 39 | Int.MAX_VALUE, 40 | (FakeLoc.latitude * 14400.0).toInt(), 41 | (FakeLoc.longitude * 14400.0).toInt(), 42 | null, null 43 | ) 44 | } 45 | } 46 | if(cMiuiBlurLocationManagerImpl.onceHookMethodBefore("getBlurryCellLocation", CellIdentity::class.java) { hooker() } == null) { 47 | cMiuiBlurLocationManagerImpl.onceHookMethodBefore("getBlurryCellLocation", 48 | CellIdentity::class.java, Int::class.java, String::class.java) { hooker() } 49 | } 50 | } 51 | 52 | cMiuiBlurLocationManagerImpl.onceHookAllMethod("getBlurryCellInfos", beforeHook { 53 | if (FakeLoc.enable && !BinderUtils.isSystemAppsCall()) { 54 | val cellInfos = arrayListOf() 55 | val cellInfo = kotlin.runCatching { 56 | CellInfoCdma::class.java.getConstructor().newInstance().also { 57 | XposedHelpers.callMethod(it, "setRegistered", true) 58 | XposedHelpers.callMethod(it, "setTimeStamp", System.nanoTime()) 59 | XposedHelpers.callMethod(it, "setCellConnectionStatus", 0) 60 | } 61 | }.getOrElse { 62 | CellInfoCdma::class.java.getConstructor( 63 | Int::class.java, 64 | Boolean::class.java, 65 | Long::class.java, 66 | CellIdentityCdma::class.java, 67 | CellSignalStrengthCdma::class.java 68 | ).newInstance(0, true, System.nanoTime(), CellIdentityCdma::class.java.newInstance(), CellSignalStrengthCdma::class.java.newInstance()) 69 | } 70 | cellInfos.add(cellInfo) 71 | 72 | result = cellInfos 73 | } 74 | }) 75 | } 76 | } 77 | 78 | 79 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/miui/MiuiLocationManagerHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.miui 2 | 3 | import moe.fuqiuluo.xposed.BaseLocationHook 4 | 5 | object MiuiLocationManagerHook: BaseLocationHook() { 6 | operator fun invoke(classLoader: ClassLoader) { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/nmea/LocationNMEAHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.nmea 2 | 3 | import de.robv.android.xposed.XC_MethodHook 4 | import de.robv.android.xposed.XposedBridge 5 | import de.robv.android.xposed.XposedHelpers 6 | import moe.fuqiuluo.xposed.BaseLocationHook 7 | import moe.fuqiuluo.xposed.utils.FakeLoc 8 | import moe.fuqiuluo.xposed.utils.Logger 9 | import moe.fuqiuluo.xposed.utils.onceHookAllMethod 10 | import moe.fuqiuluo.xposed.utils.onceHookBefore 11 | import moe.fuqiuluo.xposed.utils.onceHookMethodBefore 12 | import java.util.Collections 13 | 14 | object LocationNMEAHook: BaseLocationHook() { 15 | operator fun invoke(classILocationManager: Class<*>) { 16 | hookGnssNmea(classILocationManager) 17 | 18 | val doNothingMethod = object: XC_MethodHook() { 19 | override fun beforeHookedMethod(param: MethodHookParam?) { 20 | if (param == null || param.args.isEmpty()) return 21 | 22 | if (FakeLoc.enableDebugLog) { 23 | Logger.debug("doNothingMethod: ${param.method.name}") 24 | } 25 | 26 | if (FakeLoc.enable && !FakeLoc.enableNMEA) { 27 | if (FakeLoc.enableDebugLog) { 28 | Logger.debug("${param.method.name}: disable") 29 | } 30 | param.result = null 31 | } 32 | } 33 | } 34 | 35 | XposedBridge.hookAllMethods(classILocationManager, "addGnssMeasurementsListener", doNothingMethod) 36 | XposedBridge.hookAllMethods(classILocationManager, "removeGnssMeasurementsListener", doNothingMethod) 37 | XposedBridge.hookAllMethods(classILocationManager, "addGnssNavigationMessageListener", doNothingMethod) 38 | XposedBridge.hookAllMethods(classILocationManager, "removeGnssNavigationMessageListener", doNothingMethod) 39 | XposedBridge.hookAllMethods(classILocationManager, "addGnssAntennaInfoListener", doNothingMethod) 40 | XposedBridge.hookAllMethods(classILocationManager, "removeGnssAntennaInfoListener", doNothingMethod) 41 | } 42 | 43 | private fun hookGnssNmea(classILocationManager: Class<*>) { 44 | val hookedGnssCallback = Collections.synchronizedSet(HashSet()) 45 | val unhooks = classILocationManager.declaredMethods.filter { 46 | it.name == "registerGnssNmeaCallback" && it.parameterTypes.size > 1 47 | }.map { method -> 48 | method.onceHookBefore { 49 | val cIGnssNmeaCallback = (args[0] ?: return@onceHookBefore).javaClass 50 | 51 | if (hookedGnssCallback.contains(cIGnssNmeaCallback.name)) return@onceHookBefore 52 | hookedGnssCallback.add(cIGnssNmeaCallback.name) 53 | 54 | if (FakeLoc.enableDebugLog) { 55 | Logger.debug("registerGnssNmeaCallback: $cIGnssNmeaCallback") 56 | } 57 | 58 | cIGnssNmeaCallback.onceHookMethodBefore("onNmeaReceived", Long::class.java, String::class.java) { 59 | if (FakeLoc.enableNMEA && !FakeLoc.enableAGPS) { 60 | result = null // disable 61 | return@onceHookMethodBefore 62 | } 63 | 64 | val nmea = args[1] as? String ?: return@onceHookMethodBefore 65 | args[1] = injectNMEA(nmea) ?: nmea 66 | } 67 | } 68 | } 69 | 70 | if (FakeLoc.enableDebugLog) { 71 | Logger.debug("found ${unhooks.size} registerGnssNmeaCallback") 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/oplus/OplusLocationHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.oplus 2 | 3 | import android.location.LocationListener 4 | import android.os.Bundle 5 | import de.robv.android.xposed.XposedHelpers 6 | import moe.fuqiuluo.xposed.BaseLocationHook 7 | import moe.fuqiuluo.xposed.hooks.blindhook.BlindHookLocation 8 | import moe.fuqiuluo.xposed.hooks.blindhook.BlindHookLocation.invoke 9 | import moe.fuqiuluo.xposed.hooks.fused.ThirdPartyLocationHook 10 | import moe.fuqiuluo.xposed.utils.FakeLoc 11 | import moe.fuqiuluo.xposed.utils.Logger 12 | import moe.fuqiuluo.xposed.utils.hookMethodAfter 13 | import moe.fuqiuluo.xposed.utils.onceHookMethodBefore 14 | import moe.fuqiuluo.xposed.utils.toClass 15 | import java.lang.reflect.Modifier 16 | 17 | object OplusLocationHook: BaseLocationHook() { 18 | operator fun invoke(classLoader: ClassLoader) { 19 | ThirdPartyLocationHook(classLoader) 20 | } 21 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/telephony/BaseTelephonyHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.telephony 2 | 3 | import moe.fuqiuluo.xposed.BaseDivineService 4 | 5 | abstract class BaseTelephonyHook: BaseDivineService() { 6 | companion object { 7 | // val hookGetCellLocation = object: XC_MethodHook() { 8 | // override fun afterHookedMethod(param: MethodHookParam?) { 9 | // if (param == null || param.result == null) return 10 | // 11 | // if (!FakeLocationConfig.enable) { 12 | // return 13 | // } 14 | // 15 | // if (FakeLocationConfig.DEBUG) { 16 | // println("[Portal] ${param.method.name}: injected!") 17 | // } 18 | // 19 | // if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || param.result.javaClass.name == "android.os.Bundle") { 20 | // param.result = Bundle().apply { 21 | // putInt("cid", Int.MAX_VALUE) 22 | // putInt("lac", Int.MAX_VALUE) 23 | // putInt("psc", Int.MAX_VALUE) 24 | // putInt("baseStationLatitude", (FakeLocationConfig.latitude * 14400.0).toInt()) 25 | // putInt("baseStationLongitude", (FakeLocationConfig.longitude * 14400.0).toInt()) 26 | // putBoolean("empty", false) 27 | // putBoolean("emptyParcel", false) 28 | // putInt("mFlags", 1536) 29 | // putBoolean("parcelled", false) 30 | // putInt("baseStationId", Int.MAX_VALUE) 31 | // putInt("systemId", Int.MAX_VALUE) 32 | // putInt("networkId", Int.MAX_VALUE) 33 | // putInt("size", 0) 34 | // } 35 | // } else { 36 | // param.result = null 37 | // } 38 | // } 39 | // } 40 | // 41 | // val hookGetNeighboringCellInfoList = object: XC_MethodHook() { 42 | // override fun afterHookedMethod(param: MethodHookParam?) { 43 | // if (param == null || param.result == null) return 44 | // 45 | // if (FakeLocationConfig.enable) { 46 | // if (FakeLocationConfig.DEBUG) { 47 | // println("[Portal] ${param.method.name}: injected!") 48 | // } 49 | // param.result = emptyList() 50 | // } 51 | // } 52 | // } 53 | } 54 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/hooks/telephony/miui/MiuiTelephonyManagerHook.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.hooks.telephony.miui 2 | 3 | import moe.fuqiuluo.xposed.hooks.telephony.BaseTelephonyHook 4 | 5 | object MiuiTelephonyManagerHook: BaseTelephonyHook() { 6 | operator fun invoke(classLoader: ClassLoader) { 7 | // val cMiuiTelephonyManager = XposedHelpers.findClassIfExists("com.miui.internal.telephony.BaseTelephonyManagerAndroidImpl", classLoader) 8 | // val cTelephonyManagerEx = XposedHelpers.findClassIfExists("miui.telephony.TelephonyManagerEx", classLoader) 9 | // 10 | // if (FakeLocationConfig.DEBUG) { 11 | // println("[Portal] MiuiTelephonyManager: $cMiuiTelephonyManager") 12 | // println("[Portal] MiuiTelephonyManagerEx: $cTelephonyManagerEx") 13 | // } 14 | // 15 | // cMiuiTelephonyManager?.let { clazz -> 16 | // println("[Portal] found " + clazz.declaredMethods.mapNotNull { 17 | // if (it.returnType == CellLocation::class.java) { 18 | // XposedBridge.hookMethod(it, hookGetCellLocation) 19 | // } else null 20 | // }.size + " methods to hook in MiuiTelephonyManager") 21 | // } 22 | // 23 | // cTelephonyManagerEx?.let { clazz -> 24 | // println("[Portal] found " + clazz.declaredMethods.mapNotNull { 25 | // if (it.returnType == CellLocation::class.java) { 26 | // XposedBridge.hookMethod(it, hookGetCellLocation) 27 | // } else null 28 | // }.size + " methods to hook in MiuiTelephonyManagerEx") 29 | // 30 | // var sizeGetNeighboringCellInfoMethod = XposedBridge.hookAllMethods(clazz, "getNeighboringCellInfo", hookGetNeighboringCellInfoList).size 31 | // sizeGetNeighboringCellInfoMethod += XposedBridge.hookAllMethods(clazz, "getNeighboringCellInfoForSlot", hookGetNeighboringCellInfoList).size 32 | // sizeGetNeighboringCellInfoMethod += XposedBridge.hookAllMethods(clazz, "getNeighboringCellInfoForSubscription", hookGetNeighboringCellInfoList).size 33 | // println("[Portal] found $sizeGetNeighboringCellInfoMethod methods to hook in MiuiTelephonyManagerEx") 34 | // } 35 | } 36 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/utils/BinderUtils.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("LocalVariableName", "PrivateApi") 2 | package moe.fuqiuluo.xposed.utils 3 | 4 | import android.content.Context 5 | import android.os.Binder 6 | import android.os.Build 7 | import de.robv.android.xposed.XposedBridge 8 | 9 | 10 | object BinderUtils { 11 | private fun getActivityContext(): Context? { 12 | // public static ActivityManagerService self() 13 | // frameworks/base/services/java/com/android/server/am/ActivityManagerService.java 14 | try { 15 | val cam = Class.forName("com.android.server.am.ActivityManagerService") 16 | val am = cam.getMethod("self").invoke(null) ?: return null 17 | val mContext = cam.getDeclaredField("mContext") 18 | mContext.isAccessible = true 19 | return mContext.get(am) as? Context 20 | } catch (e: Throwable) { 21 | return null 22 | } 23 | } 24 | 25 | fun getSystemContext(): Context? { 26 | try { 27 | val cActivityThread = Class.forName("android.app.ActivityThread") 28 | val activityThread = cActivityThread.getMethod("currentActivityThread") 29 | .invoke(null) ?: return null 30 | return (cActivityThread.getMethod("getSystemContext").invoke(activityThread) as? Context) ?: getActivityContext() 31 | } catch (e: Throwable) { 32 | e.printStackTrace() 33 | } 34 | return null 35 | } 36 | 37 | fun getUidPackageNames(context: Context = getSystemContext()!!, uid: Int = getCallerUid()): Array? { 38 | val packageManager = context.packageManager 39 | return packageManager.getPackagesForUid(uid) 40 | } 41 | 42 | fun getCallerUid(): Int { 43 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 44 | kotlin.runCatching { Binder.getCallingUidOrThrow() }.getOrNull() ?: -1 45 | } else { 46 | Binder.getCallingUid() 47 | } 48 | } 49 | 50 | /** 51 | * Check whether the `PortalService` is started properly 52 | */ 53 | fun isLocationProviderEnabled(uid: Int): Boolean { 54 | val packageNames = getUidPackageNames(uid = uid) 55 | if (uid > 10000 && packageNames?.any { 56 | !it.contains("moe.fuqiuluo.portal") 57 | } == false) { 58 | return true 59 | } 60 | Logger.warn("Someone try to find Portal: uid = $uid, packageName = ${packageNames?.joinToString()}") 61 | return uid < 10000 62 | } 63 | 64 | fun isSystemPackages(packageNames: String): Boolean { 65 | if (packageNames.contains("com.xiaomi.location.fused") || 66 | packageNames.contains("com.xiaomi.metoknlp") || 67 | //packageNames.contains("com.android.phone") || 68 | packageNames.contains("com.android.location.fused") 69 | ) { 70 | return false 71 | } 72 | return packageNames.contains("com.android") || 73 | packageNames.contains("com.miui") || 74 | packageNames.contains("com.xiaomi") || 75 | packageNames.contains("com.oplus") || 76 | packageNames.contains("com.coloros") || 77 | packageNames.contains("com.heytap") || 78 | packageNames.contains("android.framework") || 79 | packageNames.contains("com.qualcomm") || 80 | packageNames.contains("com.google.android.permissioncontroller") 81 | } 82 | 83 | fun isSystemAppsCall(uid: Int = getCallerUid()): Boolean { 84 | if (uid > 10000) { 85 | val packageNames = kotlin.runCatching { getUidPackageNames(uid = uid)?.joinToString() } 86 | .getOrNull() ?: return true 87 | return isSystemPackages(packageNames) 88 | } 89 | return true 90 | } 91 | } -------------------------------------------------------------------------------- /xposed/src/main/java/moe/fuqiuluo/xposed/utils/Logger.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed.utils 2 | 3 | import de.robv.android.xposed.XposedBridge 4 | 5 | object Logger { 6 | private fun isEnableLog(): Boolean { 7 | return FakeLoc.enableLog 8 | } 9 | 10 | fun info(msg: String) { 11 | if (isEnableLog()) { 12 | XposedBridge.log("[Portal] $msg") 13 | } 14 | } 15 | 16 | fun info(msg: String, throwable: Throwable) { 17 | if (isEnableLog()) { 18 | XposedBridge.log("[Portal] $msg: ${throwable.stackTraceToString()}") 19 | } 20 | } 21 | 22 | fun debug(msg: String) { 23 | XposedBridge.log("[Portal][DEBUG] $msg") 24 | } 25 | 26 | fun debug(msg: String, throwable: Throwable) { 27 | XposedBridge.log("[Portal][DEBUG] $msg: ${throwable.stackTraceToString()}") 28 | } 29 | 30 | fun error(msg: String) { 31 | XposedBridge.log("[Portal][ERROR] $msg") 32 | } 33 | 34 | fun error(msg: String, throwable: Throwable) { 35 | XposedBridge.log("[Portal][ERROR] $msg: ${throwable.stackTraceToString()}") 36 | } 37 | 38 | fun warn(msg: String) { 39 | XposedBridge.log("[Portal][WARN] $msg") 40 | } 41 | 42 | fun warn(msg: String, throwable: Throwable) { 43 | XposedBridge.log("[Portal][WARN] $msg: ${throwable.stackTraceToString()}") 44 | } 45 | } -------------------------------------------------------------------------------- /xposed/src/test/java/moe/fuqiuluo/xposed/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package moe.fuqiuluo.xposed 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } --------------------------------------------------------------------------------