├── LICENSE ├── README.md ├── ShotScreen ├── app_index.gif ├── code_design.png ├── location_result.png ├── location_xianggang.png └── location_xinjiang.png ├── virtuallocation-release.apk ├── virtuallocation ├── .gitignore ├── build.gradle ├── jar │ └── XposedBridgeApi-54.jar ├── libs │ └── BaiduLBS_Android.jar ├── proguard-rules.pro ├── src │ ├── androidTest │ │ └── java │ │ │ └── top │ │ │ └── littlerich │ │ │ └── virtuallocation │ │ │ └── ExampleInstrumentedTest.java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ └── xposed_init │ │ ├── java │ │ │ └── top │ │ │ │ └── littlerich │ │ │ │ └── virtuallocation │ │ │ │ ├── XPosedPlugin.java │ │ │ │ ├── activity │ │ │ │ ├── AboutActivity.java │ │ │ │ ├── AppsActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── PreciseLocationActivity.java │ │ │ │ ├── adapter │ │ │ │ └── AppAdapter.java │ │ │ │ ├── base │ │ │ │ ├── BaseActivity.java │ │ │ │ └── BaseDialog.java │ │ │ │ ├── common │ │ │ │ ├── AppApplication.java │ │ │ │ ├── Common.java │ │ │ │ └── Config.java │ │ │ │ ├── listener │ │ │ │ ├── AsyncLocationResultListener.java │ │ │ │ ├── GeoCoderListener.java │ │ │ │ ├── MapClickListener.java │ │ │ │ └── MarkerDragListener.java │ │ │ │ ├── model │ │ │ │ ├── Gps.java │ │ │ │ └── MyAppInfo.java │ │ │ │ ├── presenter │ │ │ │ ├── HookApis.java │ │ │ │ ├── JellyInterpolator.java │ │ │ │ └── SdkHookManager.java │ │ │ │ ├── util │ │ │ │ ├── ApkTool.java │ │ │ │ ├── LocationUtil.java │ │ │ │ ├── SharePreferenceUtil.java │ │ │ │ └── XposedUtil.java │ │ │ │ └── view │ │ │ │ ├── CircleWaveView.java │ │ │ │ ├── CustomProgressDialog.java │ │ │ │ └── TopBanner.java │ │ ├── jniLibs │ │ │ └── armeabi │ │ │ │ ├── libBaiduMapSDK_v3_2_0_15.so │ │ │ │ └── liblocSDK5.so │ │ └── res │ │ │ ├── anim │ │ │ └── spinloaing.xml │ │ │ ├── drawable │ │ │ ├── bg_begin_location.xml │ │ │ ├── bg_botton_shadow.xml │ │ │ ├── bg_notice.xml │ │ │ ├── bg_progressbar.xml │ │ │ └── bg_with_round.xml │ │ │ ├── layout │ │ │ ├── activity_about.xml │ │ │ ├── activity_apps.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_precise_location.xml │ │ │ ├── dialog_customprogress.xml │ │ │ ├── item_app_info.xml │ │ │ ├── layout_drawer.xml │ │ │ └── layout_schema.xml │ │ │ ├── menu │ │ │ └── menu_main.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_current_location.png │ │ │ ├── ic_drawer_add.png │ │ │ ├── ic_drawer_me.png │ │ │ ├── ic_expand.png │ │ │ ├── ic_filter.png │ │ │ ├── ic_launcher_round.png │ │ │ ├── ic_reback.png │ │ │ ├── ic_right.png │ │ │ ├── ic_stop_location.png │ │ │ ├── ic_topbar_search.png │ │ │ └── ic_virtual_addr.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher_round.png │ │ │ └── icon_gcoding.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── attrs_topbar.xml │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── test │ │ └── java │ │ └── top │ │ └── littlerich │ │ └── virtuallocation │ │ └── ExampleUnitTest.java └── virtuallocation.iml └── 环境配置工具 └── Xposed框架2.6.1.apk /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

VirtualLocation(UI仿共享单车OFO)

4 |

博客主页

5 |

对Android程序进行虚拟定位,可修改微信、QQ、陌陌以及一些打卡APP等软件,随意切换手机所处位置!(喜欢的给一个star, 有帮助的给一个fork, 欢迎Star和Fork ^_^)

6 |

下载 话不多说,试玩应用先。

7 | 8 |

效果预览

9 |

主页

10 |
11 | 12 | Alt text 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ---------- 27 | 28 | 29 |

微信虚拟定位演示

30 | 31 | 1、打开本程序,选择好要穿越的地点,确认穿越即可!
32 | Alt textAlt text
33 | 34 | 2、再打开微信,这里演示在朋友圈发位置状态,如下:
35 | Alt text 36 | 37 | ---------- 38 | 39 |

钉钉虚拟定位打卡演示

40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ---------- 67 | 68 | 69 |

原理

70 |

本程序有两种方式可以实现虚拟定位:

71 |
    72 |
  1. 通过ADB模拟定位功能
  2. 73 |
  3. 通过Hook修改获取经纬度API的值 (必需安装Xposed以及ROOT)
  4. 74 |
75 | 76 | 程序代码设计流程图如下:
77 | Alt text 78 | 79 |

第一种方式主要是是通过ADB模拟定位功能,再开启线程,不断的更新LocationManager的经纬度值,即可是实现定位模拟定位

80 | 81 | ```java 82 | mMockThread = new Thread(new Runnable() { 83 | @Override 84 | public void run() { 85 | while (true) { 86 | try { 87 | Thread.sleep(500); 88 | if (!hasAddTestProvider) { 89 | Log.d("xqf", "针对Android6.0+系统,需要单独把程序调加到ADB模拟定位服务中"); 90 | continue; 91 | } 92 | setLocation(LocationUtil.mLatitude, LocationUtil.mLongitude); 93 | Log.d("xqf", "setLocation240=latitude:" + mLatitude + "?longitude:" + mLongitude); 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | }); 100 | mMockThread.start(); 101 | ``` 102 | 103 |

第二种方式主要是采用Hook修改系统API。目前很多程序都调用了isFromMockProvider方法来检测用户是否打开了ADB模拟定位功能,所以我又采用了Hook方式,就不怕不能虚拟定位了,具体如何Hook,可以看我的一篇博客:基于Xposed框架Hook定位功能来破解QQ的LBS红包

104 | 105 | ---------- 106 | 107 |

测试

108 |

在Android测试机 分别是 魅蓝2、华为、SCL-TL00、Vivo xs1、红米note运行正常

109 | 110 |

版本

111 |

v1.0

112 |

完成了通过ADB模拟定位功能来虚拟定位,但是新版的一些程序都做了ADB模拟定位检测,所以现在很多新版本程序都不行了

113 |

v1.1

114 |

完善了通过Hook修改虚拟定位API,提高程序的兼容性和可行性,但同时也必须Android设备要ROOT

115 | 116 |

issue

117 |

如果程序运行有什么问题,可以先查看Issues中的问题回答,这样我就不用重复回答大家的问题了,之前太多人加QQ问问题,工作有点忙实在是来不及一一回复(注:Issues在本网页顶部菜单栏上)

118 |

Email:bugbye@163.com

119 |

License

120 |
Copyright 2016 xuqingfu
121 | 
122 | Licensed under the Apache License, Version 2.0 (the "License");
123 | you may not use this file except in compliance with the License.
124 | You may obtain a copy of the License at
125 | 
126 |     http://www.apache.org/licenses/LICENSE-2.0
127 | 
128 | Unless required by applicable law or agreed to in writing, software
129 | distributed under the License is distributed on an "AS IS" BASIS,
130 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131 | See the License for the specific language governing permissions and
132 | limitations under the License.
133 | 
134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /ShotScreen/app_index.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/ShotScreen/app_index.gif -------------------------------------------------------------------------------- /ShotScreen/code_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/ShotScreen/code_design.png -------------------------------------------------------------------------------- /ShotScreen/location_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/ShotScreen/location_result.png -------------------------------------------------------------------------------- /ShotScreen/location_xianggang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/ShotScreen/location_xianggang.png -------------------------------------------------------------------------------- /ShotScreen/location_xinjiang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/ShotScreen/location_xinjiang.png -------------------------------------------------------------------------------- /virtuallocation-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/virtuallocation-release.apk -------------------------------------------------------------------------------- /virtuallocation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /virtuallocation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "top.littlerich.virtuallocation" 8 | minSdkVersion 18 9 | targetSdkVersion 19 10 | versionCode 20170508 11 | versionName "v1.0.252" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | provided files('jar/XposedBridgeApi-54.jar') 28 | compile files('libs/BaiduLBS_Android.jar') 29 | // jniLibs.srcDir 'src/main/jniLibs' 30 | compile 'com.android.support:appcompat-v7:25.3.1' 31 | compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8' 32 | compile 'com.android.support:cardview-v7:25.3.1' 33 | compile 'com.android.support:recyclerview-v7:25.3.1' 34 | testCompile 'junit:junit:4.12' 35 | compile 'com.android.support:design:25.3.1' 36 | } 37 | -------------------------------------------------------------------------------- /virtuallocation/jar/XposedBridgeApi-54.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/virtuallocation/jar/XposedBridgeApi-54.jar -------------------------------------------------------------------------------- /virtuallocation/libs/BaiduLBS_Android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/virtuallocation/libs/BaiduLBS_Android.jar -------------------------------------------------------------------------------- /virtuallocation/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\DevSoft\Android\AndroidSDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | 27 | -keep class com.baidu.** {*;} 28 | -keep class vi.com.** {*;} 29 | -dontwarn com.baidu.** 30 | -------------------------------------------------------------------------------- /virtuallocation/src/androidTest/java/top/littlerich/virtuallocation/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("top.littlerich.virtuallocation", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /virtuallocation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 41 | 44 | 45 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /virtuallocation/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | top.littlerich.virtuallocation.XPosedPlugin -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/XPosedPlugin.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import de.robv.android.xposed.IXposedHookLoadPackage; 7 | import de.robv.android.xposed.XC_MethodHook; 8 | import de.robv.android.xposed.XposedHelpers; 9 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 10 | import top.littlerich.virtuallocation.presenter.HookApis; 11 | import top.littlerich.virtuallocation.presenter.SdkHookManager; 12 | import top.littlerich.virtuallocation.util.XposedUtil; 13 | 14 | /** 15 | * Created by xuqingfu on 2017/4/15. 16 | */ 17 | 18 | public class XPosedPlugin implements IXposedHookLoadPackage { 19 | 20 | private static final String TAG = "silence"; 21 | 22 | @Override 23 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 24 | 25 | if (!XposedUtil.isNeedHook(loadPackageParam.packageName)) { //过滤程序 26 | return; 27 | } 28 | Log.i(TAG, "加载Hook程序:" + loadPackageParam.packageName); 29 | 30 | SdkHookManager.findMethodIsFromMockProvider(); 31 | 32 | SdkHookManager.findMethodGetInt(loadPackageParam); 33 | 34 | XposedHelpers.findAndHookMethod("android.app.Application", loadPackageParam.classLoader, "attach", Context.class, new XC_MethodHook() { 35 | @Override 36 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { 37 | super.afterHookedMethod(param); 38 | Log.v(TAG, "启动程序:" + param.thisObject.toString()); 39 | ClassLoader appClassLoader = ((Context)param.args[0]).getClassLoader(); 40 | try { 41 | HookApis.findMethodAmapLongitudeAndLatitude(appClassLoader); 42 | 43 | } 44 | catch(Exception e) { 45 | Log.e(TAG, "Hook发生异常:" + e.toString()); 46 | } 47 | } 48 | }); 49 | 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/activity/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.view.Window; 9 | 10 | import top.littlerich.virtuallocation.R; 11 | import top.littlerich.virtuallocation.view.TopBanner; 12 | 13 | public class AboutActivity extends AppCompatActivity { 14 | 15 | TopBanner mTopbanner; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | requestWindowFeature(Window.FEATURE_NO_TITLE); 21 | setContentView(R.layout.activity_about); 22 | initViews(); 23 | bindListener(); 24 | } 25 | 26 | private void initViews() { 27 | mTopbanner = (TopBanner) findViewById(R.id.topbanner); 28 | } 29 | 30 | private void bindListener() { 31 | mTopbanner.setTopBannerListener(new TopBanner.OnTopBannerListener() { 32 | @Override 33 | public void leftClick(View v) { 34 | finish(); 35 | } 36 | 37 | @Override 38 | public void rightClick(View v) { 39 | 40 | } 41 | }); 42 | } 43 | 44 | public static void openActivity(Context conetxt){ 45 | Intent intent = new Intent(conetxt, AboutActivity.class); 46 | conetxt.startActivity(intent); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/activity/AppsActivity.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | import android.widget.ProgressBar; 12 | import android.widget.Toast; 13 | 14 | import java.util.List; 15 | 16 | import top.littlerich.virtuallocation.R; 17 | import top.littlerich.virtuallocation.adapter.AppAdapter; 18 | import top.littlerich.virtuallocation.model.MyAppInfo; 19 | import top.littlerich.virtuallocation.util.ApkTool; 20 | import top.littlerich.virtuallocation.view.TopBanner; 21 | 22 | public class AppsActivity extends AppCompatActivity { 23 | 24 | private RecyclerView mRecyclerView; 25 | private LinearLayoutManager mLayoutManager; 26 | AppAdapter mAppAdapter; 27 | public Handler mHandler = new Handler(); 28 | private ProgressBar mProgressBar; 29 | private TopBanner mTopbanner; 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_apps); 35 | 36 | initView(); 37 | bindListener(); 38 | 39 | //创建默认的线性LayoutManager 40 | mLayoutManager = new LinearLayoutManager(this); 41 | mRecyclerView.setLayoutManager(mLayoutManager); 42 | //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 43 | mRecyclerView.setHasFixedSize(true); 44 | //创建并设置Adapter 45 | mAppAdapter = new AppAdapter(AppsActivity.this); 46 | mRecyclerView.setAdapter(mAppAdapter); 47 | initAppList(false); 48 | } 49 | 50 | private void bindListener() { 51 | mTopbanner.setTopBannerListener(new TopBanner.OnTopBannerListener() { 52 | @Override 53 | public void leftClick(View v) { 54 | finish(); 55 | } 56 | 57 | @Override 58 | public void rightClick(View v) { 59 | if (mProgressBar.isShown()) { 60 | Toast.makeText(AppsActivity.this, "数据未加载完成,无法过滤!", Toast.LENGTH_SHORT).show(); 61 | } else { 62 | mProgressBar.setVisibility(View.VISIBLE); 63 | initAppList(true); 64 | } 65 | } 66 | }); 67 | } 68 | 69 | private void initView() { 70 | mTopbanner = (TopBanner) findViewById(R.id.topbanner); 71 | mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); 72 | mProgressBar = (ProgressBar) findViewById(R.id.pb_progressbar); 73 | mProgressBar.setVisibility(View.VISIBLE); 74 | } 75 | 76 | private void initAppList(final boolean isFilter) { 77 | new Thread() { 78 | @Override 79 | public void run() { 80 | super.run(); 81 | //扫描得到APP列表 82 | final List appInfos = ApkTool.scanLocalInstallAppList(AppsActivity.this.getPackageManager(), isFilter); 83 | mHandler.post(new Runnable() { 84 | @Override 85 | public void run() { 86 | mAppAdapter.setData(appInfos); 87 | runOnUiThread(new Runnable() { 88 | @Override 89 | public void run() { 90 | mProgressBar.setVisibility(View.GONE); 91 | } 92 | }); 93 | } 94 | }); 95 | } 96 | }.start(); 97 | } 98 | 99 | public static void openActivity(Context context) { 100 | Intent intent = new Intent(context, AppsActivity.class); 101 | context.startActivity(intent); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.activity; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.DialogInterface; 5 | import android.location.LocationManager; 6 | import android.os.Handler; 7 | import android.support.v4.widget.DrawerLayout; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import android.view.animation.Animation; 11 | import android.view.animation.AnimationUtils; 12 | import android.view.animation.LinearInterpolator; 13 | import android.widget.Button; 14 | import android.widget.ImageView; 15 | import android.widget.TextView; 16 | import android.widget.Toast; 17 | 18 | import com.baidu.location.LocationClient; 19 | import com.baidu.location.LocationClientOption; 20 | import com.baidu.mapapi.map.BaiduMap; 21 | import com.baidu.mapapi.map.BitmapDescriptor; 22 | import com.baidu.mapapi.map.BitmapDescriptorFactory; 23 | import com.baidu.mapapi.map.MapStatusUpdate; 24 | import com.baidu.mapapi.map.MapStatusUpdateFactory; 25 | import com.baidu.mapapi.map.MapView; 26 | import com.baidu.mapapi.map.Marker; 27 | import com.baidu.mapapi.map.MarkerOptions; 28 | import com.baidu.mapapi.map.MyLocationConfiguration; 29 | import com.baidu.mapapi.map.OverlayOptions; 30 | import com.baidu.mapapi.model.LatLng; 31 | import com.baidu.mapapi.search.geocode.GeoCoder; 32 | import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption; 33 | 34 | import top.littlerich.virtuallocation.R; 35 | import top.littlerich.virtuallocation.base.BaseActivity; 36 | import top.littlerich.virtuallocation.common.AppApplication; 37 | import top.littlerich.virtuallocation.listener.AsyncLocationResultListener; 38 | import top.littlerich.virtuallocation.listener.GeoCoderListener; 39 | import top.littlerich.virtuallocation.listener.MapClickListener; 40 | import top.littlerich.virtuallocation.listener.MarkerDragListener; 41 | import top.littlerich.virtuallocation.util.LocationUtil; 42 | import top.littlerich.virtuallocation.view.TopBanner; 43 | 44 | import static top.littlerich.virtuallocation.util.LocationUtil.hasAddTestProvider; 45 | 46 | /** 47 | * Created by xuqingfu on 2017/4/15. 48 | */ 49 | public class MainActivity extends BaseActivity implements View.OnClickListener{ 50 | 51 | private String mMockProviderName = LocationManager.GPS_PROVIDER; 52 | private Button bt_Ok; 53 | private LocationManager locationManager; 54 | public static double latitude = 25.2358842413, longitude = 119.2035484314; 55 | 56 | private Thread thread;// 需要一个线程一直刷新 57 | private Boolean RUN = true; 58 | private TextView tv_location; 59 | boolean isFirstLoc = true;// 是否首次定位 60 | // 百度定位相关 61 | private LocationClient mLocClient; 62 | private MyLocationConfiguration.LocationMode mCurrentMode;// 定位模式 63 | private BitmapDescriptor mCurrentMarker;// 定位图标 64 | private MapView mMapView; 65 | private static BaiduMap mBaiduMap; 66 | // 初始化全局 bitmap 信息,不用时及时 recycle 67 | private BitmapDescriptor bd = BitmapDescriptorFactory.fromResource(R.mipmap.icon_gcoding); 68 | private static Marker mMarker; 69 | private static LatLng curLatlng; 70 | private static GeoCoder mSearch; 71 | public static double myGpslatitude, myGpslongitude; 72 | DrawerLayout mDrawerLayout; 73 | TopBanner mTopbanner; 74 | private TextView mAboutAuthor; 75 | private ImageView mCurrentLocation; 76 | private Animation mOperatingAnim; 77 | private TextView mPreciseLocation; 78 | private TextView mAddProcess; 79 | private ImageView mStopMock; 80 | 81 | @Override 82 | protected Object getContentViewId() { 83 | return R.layout.layout_schema; 84 | } 85 | 86 | @Override 87 | protected void IniView() { 88 | bt_Ok = (Button) findViewById(R.id.bt_Ok); 89 | tv_location = (TextView) findViewById(R.id.tv_location); 90 | mDrawerLayout = (DrawerLayout) findViewById(R.id.dl_left); 91 | mTopbanner = (TopBanner) findViewById(R.id.topbanner); 92 | mAboutAuthor = (TextView) findViewById(R.id.tv_about_me); 93 | mCurrentLocation = (ImageView)findViewById(R.id.iv_location); 94 | mStopMock = (ImageView)findViewById(R.id.iv_stop_location); 95 | mPreciseLocation = (TextView)findViewById(R.id.tv_precise); 96 | mAddProcess = (TextView) findViewById(R.id.tv_add_app); 97 | //加载旋转动画 98 | mOperatingAnim = AnimationUtils.loadAnimation(this, R.anim.spinloaing); 99 | LinearInterpolator lin = new LinearInterpolator(); 100 | 101 | mOperatingAnim.setInterpolator(lin); 102 | // 地图初始化 103 | mMapView = (MapView) findViewById(R.id.bmapView); 104 | mBaiduMap = mMapView.getMap(); 105 | // 开启定位图层 106 | mBaiduMap.setMyLocationEnabled(true); 107 | //隐藏地图比例尺 108 | mMapView.showScaleControl(false); 109 | //关闭缩放放大控件 110 | mMapView.showZoomControls(false); 111 | mMapView.removeViewAt(1); 112 | // 定位初始化 113 | mLocClient = new LocationClient(this); 114 | } 115 | 116 | @Override 117 | protected void IniLister() { 118 | bt_Ok.setOnClickListener(this); 119 | mLocClient.registerLocationListener(new AsyncLocationResultListener(mMapView, isFirstLoc)); 120 | mBaiduMap.setOnMapClickListener(new MapClickListener(bt_Ok)); 121 | mBaiduMap.setOnMarkerDragListener(new MarkerDragListener()); 122 | 123 | // 初始化搜索模块,注册事件监听 124 | mSearch = GeoCoder.newInstance(); 125 | mSearch.setOnGetGeoCodeResultListener(new GeoCoderListener(MainActivity.this, tv_location)); 126 | mTopbanner.setTopBannerListener(new TopBanner.OnTopBannerListener() { 127 | @Override 128 | public void leftClick(View v) { 129 | if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)){ 130 | mDrawerLayout.closeDrawer(Gravity.LEFT); 131 | }else { 132 | mDrawerLayout.openDrawer(Gravity.LEFT); 133 | } 134 | } 135 | 136 | @Override 137 | public void rightClick(View v) { 138 | 139 | } 140 | }); 141 | mAboutAuthor.setOnClickListener(new View.OnClickListener() { 142 | @Override 143 | public void onClick(View v) { 144 | AboutActivity.openActivity(MainActivity.this); 145 | } 146 | }); 147 | mCurrentLocation.setOnClickListener(new View.OnClickListener() { 148 | @Override 149 | public void onClick(View v) { 150 | LatLng ll = new LatLng(myGpslatitude, myGpslongitude); 151 | setCurrentMapLatLng(ll); 152 | } 153 | }); 154 | mBaiduMap.setOnMapLoadedCallback(new BaiduMap.OnMapLoadedCallback() { 155 | @Override 156 | public void onMapLoaded() { 157 | new Handler().postDelayed(new Runnable(){ 158 | 159 | public void run() { 160 | mCurrentLocation.clearAnimation(); 161 | } 162 | 163 | }, 1000 * 6); 164 | } 165 | }); 166 | mPreciseLocation.setOnClickListener(new View.OnClickListener() { 167 | @Override 168 | public void onClick(View v) { 169 | PreciseLocationActivity.openActivity(MainActivity.this); 170 | } 171 | }); 172 | mAddProcess.setOnClickListener(new View.OnClickListener() { 173 | @Override 174 | public void onClick(View v) { 175 | AppsActivity.openActivity(MainActivity.this); 176 | } 177 | }); 178 | mStopMock.setOnClickListener(new View.OnClickListener() { 179 | @Override 180 | public void onClick(View v) { 181 | LocationUtil.stopMockLocation(); 182 | Toast.makeText(MainActivity.this, "虚拟定位已暂停!", Toast.LENGTH_SHORT).show(); 183 | bt_Ok.setText("立即穿越"); 184 | } 185 | }); 186 | 187 | } 188 | 189 | @Override 190 | protected void IniData() { 191 | inilocation(); 192 | iniMap(); 193 | 194 | if (mOperatingAnim != null) { 195 | mCurrentLocation.startAnimation(mOperatingAnim); 196 | } 197 | 198 | } 199 | 200 | /** 201 | * inilocation 初始化 位置模拟 202 | */ 203 | private void inilocation() { 204 | try { 205 | if (LocationUtil.initLocation(MainActivity.this)) { 206 | LocationUtil.initLocationManager(); 207 | } else {//未开启ADB模拟定位功能,则只能选择HOOK技术 208 | 209 | } 210 | } catch (Exception e) { 211 | Toast.makeText(MainActivity.this, "虚拟定位功能未打开!", Toast.LENGTH_SHORT).show(); 212 | e.printStackTrace(); 213 | } 214 | } 215 | 216 | /** 217 | * iniMap 初始化地图 218 | */ 219 | private void iniMap() { 220 | LocationClientOption option = new LocationClientOption(); 221 | option.setOpenGps(true);// 打开gps 222 | option.setCoorType("bd09ll"); // 设置坐标类型 223 | option.setScanSpan(3000); 224 | mCurrentMode = MyLocationConfiguration.LocationMode.NORMAL; 225 | 226 | // 缩放 227 | MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(14.0f); 228 | mBaiduMap.setMapStatus(msu); 229 | 230 | mBaiduMap.setMyLocationConfigeration(new MyLocationConfiguration(mCurrentMode, true, mCurrentMarker)); 231 | mLocClient.setLocOption(option); 232 | mLocClient.start(); 233 | initOverlay(); 234 | 235 | // 开启线程,一直修改GPS坐标 236 | LocationUtil.startLocaton(); 237 | } 238 | 239 | /** 240 | * initOverlay 设置覆盖物,这里就是地图上那个点 241 | */ 242 | private void initOverlay() { 243 | LatLng ll = new LatLng(AppApplication.mMockGps.mLatitude, AppApplication.mMockGps.mLongitude); 244 | OverlayOptions oo = new MarkerOptions().position(ll).icon(bd).zIndex(9) 245 | .draggable(true); 246 | mMarker = (Marker) (mBaiduMap.addOverlay(oo)); 247 | } 248 | 249 | /** 250 | * setCurrentMapLatLng 设置当前坐标 251 | */ 252 | public static void setCurrentMapLatLng(LatLng arg0) { 253 | curLatlng = arg0; 254 | mMarker.setPosition(arg0); 255 | 256 | // 设置地图中心点为这是位置 257 | LatLng ll = new LatLng(arg0.latitude, arg0.longitude); 258 | MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(ll); 259 | mBaiduMap.animateMapStatus(u); 260 | 261 | // 根据经纬度坐标 找到实地信息,会在接口onGetReverseGeoCodeResult中呈现结果 262 | mSearch.reverseGeoCode(new ReverseGeoCodeOption().location(arg0)); 263 | } 264 | 265 | @Override 266 | protected void thisFinish() { 267 | AlertDialog.Builder build = new AlertDialog.Builder(this); 268 | build.setTitle("提示"); 269 | build.setMessage("退出后,将不再提供定位服务,继续退出吗?"); 270 | build.setPositiveButton("确认", new DialogInterface.OnClickListener() { 271 | 272 | @Override 273 | public void onClick(DialogInterface dialog, int which) { 274 | finish(); 275 | } 276 | }); 277 | build.setNeutralButton("最小化", new DialogInterface.OnClickListener() { 278 | 279 | @Override 280 | public void onClick(DialogInterface dialog, int which) { 281 | moveTaskToBack(true); 282 | } 283 | }); 284 | build.setNegativeButton("取消", null); 285 | build.show(); 286 | } 287 | 288 | @Override 289 | protected void onResume() { 290 | mMapView.onResume(); 291 | super.onResume(); 292 | } 293 | 294 | @Override 295 | protected void onPause() { 296 | mMapView.onPause(); 297 | if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(Gravity.LEFT)){ 298 | mDrawerLayout.closeDrawer(Gravity.LEFT); 299 | } 300 | super.onPause(); 301 | } 302 | 303 | @Override 304 | public void onBackPressed() { 305 | thisFinish(); 306 | } 307 | 308 | @Override 309 | protected void onDestroy() { 310 | RUN = false; 311 | thread = null; 312 | //清除旋转加载动画 313 | mCurrentLocation.clearAnimation(); 314 | 315 | // 退出时销毁定位 316 | mLocClient.stop(); 317 | // 关闭定位图层 318 | mBaiduMap.setMyLocationEnabled(false); 319 | mMapView.onDestroy(); 320 | mMapView = null; 321 | bd.recycle(); 322 | mSearch.destroy(); 323 | super.onDestroy(); 324 | } 325 | 326 | @Override 327 | public void onClick(View v) { 328 | switch (v.getId()) { 329 | case R.id.bt_Ok: 330 | latitude = curLatlng.latitude; 331 | longitude = curLatlng.longitude; 332 | try { 333 | if (!hasAddTestProvider){ 334 | LocationUtil.initLocation(MainActivity.this); 335 | LocationUtil.initLocationManager(); 336 | } 337 | LocationUtil.setLongitudeAndLatitude(curLatlng.longitude, curLatlng.latitude); 338 | AppApplication.mMockGps.mLatitude = curLatlng.latitude; 339 | AppApplication.mMockGps.mLongitude = curLatlng.longitude; 340 | bt_Ok.setText("穿越完成"); 341 | } catch (Exception e) { 342 | e.printStackTrace(); 343 | } 344 | break; 345 | } 346 | } 347 | 348 | } -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/activity/PreciseLocationActivity.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.activity; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ObjectAnimator; 6 | import android.animation.PropertyValuesHolder; 7 | import android.animation.ValueAnimator; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Bundle; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.support.v7.widget.AppCompatButton; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.animation.AccelerateDecelerateInterpolator; 16 | import android.widget.EditText; 17 | import android.widget.ProgressBar; 18 | import android.widget.TextView; 19 | import android.widget.Toast; 20 | 21 | import top.littlerich.virtuallocation.R; 22 | import top.littlerich.virtuallocation.presenter.JellyInterpolator; 23 | import top.littlerich.virtuallocation.util.LocationUtil; 24 | import top.littlerich.virtuallocation.view.TopBanner; 25 | 26 | public class PreciseLocationActivity extends AppCompatActivity { 27 | 28 | private EditText mLongitudeValue; 29 | private EditText mLatitudeValue; 30 | private AppCompatButton mBeginLocation; 31 | private TopBanner mTopbanner; 32 | private ProgressBar mPbLocating; 33 | private TextView mTip; 34 | float width; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_precise_location); 40 | initViews(); 41 | bindListener(); 42 | } 43 | 44 | private void bindListener() { 45 | mBeginLocation.setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | try { 49 | LocationUtil.setLongitudeAndLatitude(Double.valueOf(mLongitudeValue.getText().toString()), 50 | Double.valueOf(mLatitudeValue.getText().toString())); 51 | width = mBeginLocation.getMeasuredWidth(); 52 | float height = mBeginLocation.getMeasuredHeight(); 53 | inputAnimator(mBeginLocation, width, height); 54 | } catch (NumberFormatException e) { 55 | Toast.makeText(PreciseLocationActivity.this, "请输入正确的经纬度!", Toast.LENGTH_SHORT).show(); 56 | } 57 | } 58 | }); 59 | mTopbanner.setTopBannerListener(new TopBanner.OnTopBannerListener() { 60 | @Override 61 | public void leftClick(View v) { 62 | finish(); 63 | } 64 | 65 | @Override 66 | public void rightClick(View v) { 67 | 68 | } 69 | }); 70 | mPbLocating.setOnClickListener(new View.OnClickListener() { 71 | @Override 72 | public void onClick(View v) { 73 | showTip("精确定位", View.GONE); 74 | mPbLocating.setVisibility(View.GONE); 75 | mBeginLocation.setVisibility(View.VISIBLE); 76 | repaintView(mBeginLocation); 77 | } 78 | }); 79 | } 80 | 81 | /** 82 | * 使组件变回原有形状 83 | * @param view 84 | */ 85 | private void repaintView(final View view){ 86 | AnimatorSet set = new AnimatorSet(); 87 | 88 | ValueAnimator animator = ValueAnimator.ofFloat(width, 0); 89 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 90 | 91 | @Override 92 | public void onAnimationUpdate(ValueAnimator animation) { 93 | float value = (Float) animation.getAnimatedValue(); 94 | ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mBeginLocation 95 | .getLayoutParams(); 96 | params.leftMargin = (int) value; 97 | params.rightMargin = (int) value; 98 | view.setLayoutParams(params); 99 | } 100 | }); 101 | 102 | ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, 103 | "scaleX", 0.5f, 1f); 104 | set.setDuration(1000); 105 | set.setInterpolator(new AccelerateDecelerateInterpolator()); 106 | set.playTogether(animator, animator2); 107 | set.start(); 108 | } 109 | 110 | private void initViews() { 111 | mLatitudeValue = (EditText)findViewById(R.id.et_latitude); 112 | mLongitudeValue = (EditText)findViewById(R.id.et_longitude); 113 | mBeginLocation = (AppCompatButton)findViewById(R.id.btn_precise_location); 114 | mTopbanner = (TopBanner) findViewById(R.id.topbanner); 115 | mPbLocating = (ProgressBar)findViewById(R.id.pb_locating); 116 | mTip = (TextView)findViewById(R.id.tv_tip); 117 | } 118 | 119 | 120 | private void inputAnimator(final View view, float w, float h) { 121 | 122 | AnimatorSet set = new AnimatorSet(); 123 | 124 | ValueAnimator animator = ValueAnimator.ofFloat(0, w); 125 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 126 | 127 | @Override 128 | public void onAnimationUpdate(ValueAnimator animation) { 129 | float value = (Float) animation.getAnimatedValue(); 130 | ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mBeginLocation 131 | .getLayoutParams(); 132 | params.leftMargin = (int) value; 133 | params.rightMargin = (int) value; 134 | view.setLayoutParams(params); 135 | } 136 | }); 137 | 138 | ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, 139 | "scaleX", 1f, 0.5f); 140 | set.setDuration(1000); 141 | set.setInterpolator(new AccelerateDecelerateInterpolator()); 142 | set.playTogether(animator, animator2); 143 | set.start(); 144 | set.addListener(new Animator.AnimatorListener() { 145 | 146 | @Override 147 | public void onAnimationStart(Animator animation) { 148 | 149 | } 150 | 151 | @Override 152 | public void onAnimationRepeat(Animator animation) { 153 | // TODO Auto-generated method stub 154 | 155 | } 156 | 157 | @Override 158 | public void onAnimationEnd(Animator animation) { 159 | showTip("穿越完成", View.VISIBLE); 160 | mBeginLocation.setVisibility(View.GONE); 161 | mPbLocating.setVisibility(View.VISIBLE); 162 | progressAnimator(mPbLocating); 163 | } 164 | 165 | @Override 166 | public void onAnimationCancel(Animator animation) { 167 | // TODO Auto-generated method stub 168 | 169 | } 170 | }); 171 | 172 | } 173 | 174 | private void showTip(String title, int isVisible) { 175 | mTopbanner.setTitleText(title); 176 | mTip.setVisibility(isVisible); 177 | } 178 | 179 | private void progressAnimator(final View view) { 180 | PropertyValuesHolder animator = PropertyValuesHolder.ofFloat("scaleX", 181 | 0.5f, 1f); 182 | PropertyValuesHolder animator2 = PropertyValuesHolder.ofFloat("scaleY", 183 | 0.5f, 1f); 184 | ObjectAnimator animator3 = ObjectAnimator.ofPropertyValuesHolder(view, 185 | animator, animator2); 186 | animator3.setDuration(1000); 187 | animator3.setInterpolator(new JellyInterpolator()); 188 | animator3.start(); 189 | } 190 | 191 | public static void openActivity(Context context){ 192 | Intent intent = new Intent(context, PreciseLocationActivity.class); 193 | context.startActivity(intent); 194 | } 195 | 196 | @Override 197 | protected void onDestroy() { 198 | super.onDestroy(); 199 | mPbLocating.clearAnimation(); 200 | mBeginLocation.clearAnimation(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/adapter/AppAdapter.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import top.littlerich.virtuallocation.R; 15 | import top.littlerich.virtuallocation.model.MyAppInfo; 16 | 17 | /** 18 | * Created by xuqingfu on 2017/4/24. 19 | */ 20 | 21 | public class AppAdapter extends RecyclerView.Adapter { 22 | 23 | List mListData = new ArrayList(); 24 | private Context mContext; 25 | 26 | public AppAdapter(Context context) { 27 | mContext = context; 28 | } 29 | 30 | @Override 31 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 32 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_app_info, parent, false); 33 | ViewHolder vh = new ViewHolder(view); 34 | return vh; 35 | } 36 | 37 | @Override 38 | public void onBindViewHolder(ViewHolder holder, int position) { 39 | MyAppInfo myAppInfo = mListData.get(position); 40 | holder.iv_app_icon.setImageDrawable(myAppInfo.getImage()); 41 | holder.tx_app_name.setText(myAppInfo.getAppName()); 42 | holder.tx_app_pkg.setText(myAppInfo.getPkgName()); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return mListData != null ? mListData.size() : 0; 48 | } 49 | 50 | public void setData(List myAppInfos) { 51 | this.mListData = myAppInfos; 52 | notifyDataSetChanged(); 53 | } 54 | 55 | /* 56 | 57 | 58 | 59 | 60 | @Override 61 | public View getView(int position, View convertView, ViewGroup parent) { 62 | ViewHolder mViewHolder; 63 | MyAppInfo myAppInfo = mListData.get(position); 64 | if (convertView == null) { 65 | mViewHolder = new ViewHolder(); 66 | convertView = LayoutInflater.from(mContext).inflate(R.layout.item_app_info, null); 67 | mViewHolder.iv_app_icon = (ImageView) convertView.findViewById(R.id.iv_app_icon); 68 | mViewHolder.tx_app_name = (TextView) convertView.findViewById(R.id.tv_app_name); 69 | convertView.setTag(mViewHolder); 70 | } else { 71 | mViewHolder = (ViewHolder) convertView.getTag(); 72 | } 73 | mViewHolder.iv_app_icon.setImageDrawable(myAppInfo.getImage()); 74 | mViewHolder.tx_app_name.setText(myAppInfo.getAppName()); 75 | return convertView; 76 | }*/ 77 | 78 | public static class ViewHolder extends RecyclerView.ViewHolder { 79 | 80 | ImageView iv_app_icon; 81 | TextView tx_app_name; 82 | TextView tx_app_pkg; 83 | 84 | public ViewHolder(View itemView) { 85 | super(itemView); 86 | iv_app_icon = (ImageView) itemView.findViewById(R.id.iv_app_icon); 87 | tx_app_name = (TextView) itemView.findViewById(R.id.tv_app_name); 88 | tx_app_pkg = (TextView)itemView.findViewById(R.id.tv_app_pkg); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.base; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.Toolbar; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.Window; 9 | 10 | import top.littlerich.virtuallocation.view.CustomProgressDialog; 11 | 12 | 13 | /** 14 | * Created by xuqingfu on 2017/4/15. 15 | */ 16 | public abstract class BaseActivity extends Activity { 17 | protected View mView; 18 | protected CustomProgressDialog progressDialog; 19 | protected Toolbar toolbar; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | requestWindowFeature(Window.FEATURE_NO_TITLE); 25 | Object object = getContentViewId(); 26 | if (object instanceof Integer) { 27 | mView = LayoutInflater.from(this).inflate((Integer) object, null); 28 | } else if (object instanceof View) { 29 | mView = (View) object; 30 | } 31 | setContentView(mView); 32 | // toolbar = (Toolbar) findViewById(R.id.toolbar); 33 | IniView(); 34 | progressDialog = new CustomProgressDialog(this); 35 | progressDialog.setCancelable(true); 36 | progressDialog.setCanceledOnTouchOutside(false); 37 | IniLister(); 38 | IniData(); 39 | } 40 | 41 | /** 42 | * @Title: getContentViewId 43 | * @Description: 布局文件Id 44 | */ 45 | protected abstract Object getContentViewId(); 46 | 47 | /** 48 | * @Title: IniView 49 | * @Description: 初始化View 50 | */ 51 | protected abstract void IniView(); 52 | 53 | /** 54 | * @Title: IniLister 55 | * @Description: 初始化接口 56 | */ 57 | protected abstract void IniLister(); 58 | 59 | /** 60 | * @Title: IniData 61 | * @Description: 初始化数据 62 | */ 63 | protected abstract void IniData(); 64 | 65 | /** 66 | * thisFinish 当前关闭 67 | */ 68 | protected abstract void thisFinish(); 69 | 70 | @Override 71 | public void onBackPressed() { 72 | thisFinish(); 73 | } 74 | 75 | /** 76 | * showProgressDialog 显示等待框 77 | * 78 | * @param text 显示文字 79 | */ 80 | public void showProgressDialog(String text) { 81 | if (progressDialog != null) { 82 | progressDialog.show(); 83 | progressDialog.setMessage(text); 84 | } 85 | } 86 | 87 | /** 88 | * cancelProgressDialog 取消等待框 89 | */ 90 | public void cancelProgressDialog() { 91 | if (progressDialog != null && progressDialog.isShowing()) { 92 | progressDialog.dismiss(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/base/BaseDialog.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.base; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | 9 | import top.littlerich.virtuallocation.R; 10 | 11 | 12 | /** 13 | * Created by xuqingfu on 2017/4/15. 14 | */ 15 | public abstract class BaseDialog extends Dialog { 16 | protected View mView; 17 | protected Context mContext; 18 | 19 | public BaseDialog(Context context) { 20 | this(context, R.style.clashDialog); 21 | } 22 | 23 | public BaseDialog(Context context, int theme) { 24 | super(context, theme); 25 | this.mContext = context; 26 | } 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | LayoutInflater mLayoutInflater = LayoutInflater.from(mContext); 32 | mView = mLayoutInflater.inflate(getContentViewId(), null); 33 | this.setContentView(mView); 34 | InitView(); 35 | IniListener(); 36 | InitData(); 37 | } 38 | 39 | /** 40 | * 初始化view变量,用于显示 41 | */ 42 | protected abstract int getContentViewId(); 43 | 44 | /** 45 | * 初始化所有控件 46 | */ 47 | protected abstract void InitView(); 48 | 49 | /** 50 | * 设置事件 51 | */ 52 | protected abstract void IniListener(); 53 | 54 | /** 55 | * 初始化所有数据 56 | */ 57 | protected abstract void InitData(); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/common/AppApplication.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.common; 2 | 3 | import android.app.Application; 4 | 5 | import com.baidu.mapapi.SDKInitializer; 6 | 7 | import top.littlerich.virtuallocation.model.Gps; 8 | 9 | /** 10 | * Created by xuqingfu on 2017/4/15. 11 | */ 12 | public class AppApplication extends Application { 13 | 14 | /**全局GPS*/ 15 | public static Gps mMockGps; 16 | 17 | static { 18 | mMockGps = Config.COMPANY; 19 | } 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | // 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext 25 | SDKInitializer.initialize(this); 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/common/Common.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.common; 2 | 3 | /** 4 | * Created by xuqingfu on 2017/5/31. 5 | */ 6 | 7 | public class Common { 8 | public static final String SP_FILTER_APP_PKG_NAME = "hook_app_name"; 9 | } 10 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/common/Config.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.common; 2 | 3 | import top.littlerich.virtuallocation.model.Gps; 4 | 5 | /** 6 | * Created by xuqingfu on 2017/5/26. 7 | */ 8 | 9 | public class Config { 10 | 11 | public static final Gps SWUN = new Gps(30.6386505171,104.0492391586); 12 | public static final Gps CAPITAL = new Gps(39.9127646676,116.3859844208); 13 | public static final Gps COMPANY = new Gps(30.5819505065,104.0539652109); 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/listener/AsyncLocationResultListener.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.listener; 2 | 3 | import com.baidu.location.BDLocation; 4 | import com.baidu.location.BDLocationListener; 5 | import com.baidu.mapapi.map.MapView; 6 | import com.baidu.mapapi.model.LatLng; 7 | 8 | import top.littlerich.virtuallocation.activity.MainActivity; 9 | 10 | import static top.littlerich.virtuallocation.activity.MainActivity.myGpslatitude; 11 | import static top.littlerich.virtuallocation.activity.MainActivity.myGpslongitude; 12 | import static top.littlerich.virtuallocation.activity.MainActivity.setCurrentMapLatLng; 13 | 14 | /** 15 | * 定位结果回调 16 | * API: http://lbsyun.baidu.com/index.php?title=android-locsdk/guide/getloc 17 | * Created by xuqingfu on 2017/5/3. 18 | */ 19 | 20 | public class AsyncLocationResultListener implements BDLocationListener { 21 | 22 | private MapView mMapView; 23 | boolean isFirstLoc = true;// 是否首次定位 24 | //private double myGpslatitude, myGpslongitude; 25 | 26 | public AsyncLocationResultListener(MapView mapView, boolean isFirstLoc) { 27 | mMapView = mapView; 28 | this.isFirstLoc = isFirstLoc; 29 | //this.myGpslatitude = myGpslatitude; 30 | // this.myGpslongitude = myGpslongitude; 31 | } 32 | 33 | @Override 34 | public void onReceiveLocation(BDLocation location) { 35 | // map view 销毁后不在处理新接收的位置 36 | if (location == null || mMapView == null) { 37 | return; 38 | } 39 | 40 | if (isFirstLoc) { 41 | isFirstLoc = false; 42 | MainActivity.myGpslatitude = location.getLatitude(); 43 | MainActivity.myGpslongitude = location.getLongitude(); 44 | LatLng ll = new LatLng(myGpslatitude, myGpslongitude); 45 | setCurrentMapLatLng(ll); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/listener/GeoCoderListener.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.listener; 2 | 3 | import android.content.Context; 4 | import android.widget.TextView; 5 | import android.widget.Toast; 6 | 7 | import com.baidu.mapapi.search.core.SearchResult; 8 | import com.baidu.mapapi.search.geocode.GeoCodeResult; 9 | import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener; 10 | import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult; 11 | 12 | /** 13 | * 地理编码:1、正向地图编码 2、反向地图编码 14 | * Created by xuqingfu on 2017/5/4. 15 | */ 16 | 17 | public class GeoCoderListener implements OnGetGeoCoderResultListener { 18 | 19 | private Context mContext; 20 | private TextView mLocationTip; 21 | 22 | public GeoCoderListener(Context context, TextView locationTip) { 23 | mContext = context; 24 | mLocationTip = locationTip; 25 | } 26 | 27 | /** 28 | * 搜索(根据实地信息-->经纬坐标) 29 | * @param geoCodeResult 30 | */ 31 | @Override 32 | public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) { 33 | 34 | } 35 | 36 | /** 37 | * 搜索(根据坐标-->实地信息) 38 | * @param result 39 | */ 40 | @Override 41 | public void onGetReverseGeoCodeResult(ReverseGeoCodeResult result) { 42 | if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) { 43 | Toast.makeText(mContext, "抱歉,未能找到结果", Toast.LENGTH_LONG).show(); 44 | return; 45 | } 46 | mLocationTip.setText(String.format("伪造位置:%s", result.getAddress())); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/listener/MapClickListener.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.listener; 2 | 3 | import android.widget.Button; 4 | 5 | import com.baidu.mapapi.map.BaiduMap; 6 | import com.baidu.mapapi.map.MapPoi; 7 | import com.baidu.mapapi.model.LatLng; 8 | 9 | import static top.littlerich.virtuallocation.activity.MainActivity.setCurrentMapLatLng; 10 | 11 | /** 12 | * 地图单击事件监听接口 13 | * Created by xuqingfu on 2017/5/4. 14 | */ 15 | 16 | public class MapClickListener implements BaiduMap.OnMapClickListener { 17 | 18 | private Button mLocOk; 19 | 20 | public MapClickListener(Button locOk) { 21 | mLocOk = locOk; 22 | } 23 | 24 | /** 25 | * 地图单击事件 26 | * @param latLng 点击的地理坐标 27 | */ 28 | @Override 29 | public void onMapClick(LatLng latLng) { 30 | setCurrentMapLatLng(latLng); 31 | if ("穿越完成".equals(mLocOk.getText().toString())) { 32 | mLocOk.setText("修改位置"); 33 | } 34 | } 35 | 36 | /** 37 | * 地图内 Poi 单击事件 38 | * @param mapPoi 点击的 poi 信息 39 | * @return 40 | */ 41 | @Override 42 | public boolean onMapPoiClick(MapPoi mapPoi) { 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/listener/MarkerDragListener.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.listener; 2 | 3 | import com.baidu.mapapi.map.BaiduMap; 4 | import com.baidu.mapapi.map.Marker; 5 | 6 | import static top.littlerich.virtuallocation.activity.MainActivity.setCurrentMapLatLng; 7 | 8 | /** 9 | * Created by xuqingfu on 2017/5/4. 10 | */ 11 | 12 | public class MarkerDragListener implements BaiduMap.OnMarkerDragListener { 13 | /** 14 | * 地图上标记拖动 15 | * @param marker 16 | */ 17 | @Override 18 | public void onMarkerDrag(Marker marker) { 19 | 20 | } 21 | 22 | /** 23 | * 地图上标记拖动结束 24 | * @param marker 25 | */ 26 | @Override 27 | public void onMarkerDragEnd(Marker marker) { 28 | setCurrentMapLatLng(marker.getPosition()); 29 | } 30 | 31 | /** 32 | * 地图上标记拖动开始 33 | * @param marker 34 | */ 35 | @Override 36 | public void onMarkerDragStart(Marker marker) { 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/model/Gps.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.model; 2 | 3 | /** 4 | * Created by xuqingfu on 2017/5/26. 5 | */ 6 | 7 | public class Gps { 8 | public double mLatitude; 9 | public double mLongitude; 10 | 11 | public Gps(double latitude, double longitude) { 12 | mLatitude = latitude; 13 | mLongitude = longitude; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/model/MyAppInfo.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.model; 2 | 3 | import android.graphics.drawable.Drawable; 4 | /** 5 | * Created by xuqingfu on 17/4/24. 6 | */ 7 | public class MyAppInfo { 8 | private Drawable image; 9 | private String appName; 10 | private String pkgName; 11 | 12 | public MyAppInfo(Drawable image, String appName, String pkgName) { 13 | this.image = image; 14 | this.appName = appName; 15 | this.pkgName = pkgName; 16 | } 17 | public MyAppInfo() { 18 | 19 | } 20 | 21 | public Drawable getImage() { 22 | return image; 23 | } 24 | 25 | public void setImage(Drawable image) { 26 | this.image = image; 27 | } 28 | 29 | public String getAppName() { 30 | return appName; 31 | } 32 | 33 | public void setAppName(String appName) { 34 | this.appName = appName; 35 | } 36 | 37 | public String getPkgName() { 38 | return pkgName; 39 | } 40 | 41 | public void setPkgName(String pkgName) { 42 | this.pkgName = pkgName; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/presenter/HookApis.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.presenter; 2 | 3 | import android.util.Log; 4 | 5 | import de.robv.android.xposed.XC_MethodHook; 6 | import de.robv.android.xposed.XposedHelpers; 7 | import top.littlerich.virtuallocation.common.AppApplication; 8 | 9 | /** 10 | * Created by xuqingfu on 2017/5/26. 11 | */ 12 | 13 | public class HookApis { 14 | 15 | private static final String TAG = "silence"; 16 | 17 | /** 18 | * 修改高德地图的经纬度值 19 | * @param 20 | * @throws ClassNotFoundException 21 | */ 22 | public static void findMethodAmapLongitudeAndLatitude(ClassLoader classLoader) { 23 | try { 24 | Class aMapLocationClazz = classLoader.loadClass("com.amap.api.location.AMapLocation"); 25 | XposedHelpers.findAndHookMethod(aMapLocationClazz, "getLongitude", new Object[]{new XC_MethodHook() { 26 | protected void beforeHookedMethod(MethodHookParam arg3) throws Throwable { 27 | arg3.setResult(Double.valueOf(AppApplication.mMockGps.mLongitude)); 28 | Log.d(TAG, "修改高德地图的经纬度值:" + AppApplication.mMockGps.mLongitude + ":" + AppApplication.mMockGps.mLatitude); 29 | } 30 | }}); 31 | 32 | XposedHelpers.findAndHookMethod(aMapLocationClazz, "getLatitude", new Object[]{new XC_MethodHook() { 33 | protected void beforeHookedMethod(MethodHookParam arg3) throws Throwable { 34 | arg3.setResult(Double.valueOf(AppApplication.mMockGps.mLatitude)); 35 | } 36 | }}); 37 | } catch (Exception e) { 38 | Log.i(TAG, "该程序无高德地图模块,无法HOOK"); 39 | } 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/presenter/JellyInterpolator.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.presenter; 2 | 3 | import android.view.animation.LinearInterpolator; 4 | 5 | public class JellyInterpolator extends LinearInterpolator { 6 | private float factor; 7 | 8 | public JellyInterpolator() { 9 | this.factor = 0.15f; 10 | } 11 | 12 | @Override 13 | public float getInterpolation(float input) { 14 | return (float) (Math.pow(2, -10 * input) 15 | * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) + 1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/presenter/SdkHookManager.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.presenter; 2 | 3 | import android.content.ContentResolver; 4 | import android.location.Location; 5 | import android.provider.Settings; 6 | 7 | import de.robv.android.xposed.XC_MethodHook; 8 | import de.robv.android.xposed.XC_MethodReplacement; 9 | import de.robv.android.xposed.XposedHelpers; 10 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 11 | 12 | import static android.provider.Settings.Secure.LOCATION_MODE_SENSORS_ONLY; 13 | 14 | /** 15 | * Created by xuqingfu on 2017/5/31. 16 | */ 17 | 18 | public class SdkHookManager { 19 | 20 | /** 21 | * 判断是否开启允许ADB模拟定位功能 22 | */ 23 | public static void findMethodIsFromMockProvider(){ 24 | XposedHelpers.findAndHookMethod(Location.class, "isFromMockProvider", new XC_MethodHook() { 25 | @Override 26 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 27 | super.afterHookedMethod(param); 28 | param.setResult(false); 29 | } 30 | }); 31 | } 32 | 33 | /** 34 | * 劫持判断是否使用虚拟定位 35 | * @param loadPackageParam 36 | */ 37 | public static void findMethodGetInt(XC_LoadPackage.LoadPackageParam loadPackageParam){ 38 | // boolean isOpen = Settings.Secure.getInt(getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0; 39 | XposedHelpers.findAndHookMethod("android.provider.Settings$Secure", loadPackageParam.classLoader, "getInt", ContentResolver.class, String.class, int.class, new XC_MethodHook() { 40 | 41 | /** 42 | * 设置返回值类型: 43 | * 1、gpsEnabled && networkEnabled:LOCATION_MODE_HIGH_ACCURACY 44 | * 2、gpsEnabled:LOCATION_MODE_SENSORS_ONLY 45 | * 3、networkEnabled:LOCATION_MODE_BATTERY_SAVING 46 | * 4、disable:LOCATION_MODE_OFF 47 | */ 48 | @Override 49 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 50 | super.afterHookedMethod(param); 51 | param.setResult(LOCATION_MODE_SENSORS_ONLY); 52 | } 53 | }); 54 | 55 | XposedHelpers.findAndHookMethod(Settings.Secure.class, "getInt", ContentResolver.class, String.class, int.class, new XC_MethodReplacement() { 56 | @Override 57 | protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable { 58 | return LOCATION_MODE_SENSORS_ONLY; 59 | } 60 | }); 61 | } 62 | 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/util/ApkTool.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.util; 2 | 3 | import android.content.pm.ApplicationInfo; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.util.Log; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import top.littlerich.virtuallocation.model.MyAppInfo; 12 | 13 | /** 14 | * 扫描本地安装的应用,工具类 15 | *

16 | * Created by xuqingfu on 2017/4/24. 17 | */ 18 | public class ApkTool { 19 | static String TAG = "ApkTool"; 20 | public static List mLocalInstallApps = null; 21 | 22 | public static List scanLocalInstallAppList(PackageManager packageManager, boolean isFilter) { 23 | List myAppInfos = new ArrayList(); 24 | try { 25 | List packageInfos = packageManager.getInstalledPackages(0); 26 | for (int i = 0; i < packageInfos.size(); i++) { 27 | PackageInfo packageInfo = packageInfos.get(i); 28 | //过滤掉系统app 29 | if (isFilter && (ApplicationInfo.FLAG_SYSTEM & packageInfo.applicationInfo.flags) != 0) { 30 | continue; 31 | } 32 | MyAppInfo myAppInfo = new MyAppInfo(); 33 | myAppInfo.setPkgName(packageInfo.applicationInfo.loadLabel(packageManager).toString()); 34 | 35 | myAppInfo.setAppName(packageInfo.packageName); 36 | if (packageInfo.applicationInfo.loadIcon(packageManager) == null) { 37 | continue; 38 | } 39 | myAppInfo.setImage(packageInfo.applicationInfo.loadIcon(packageManager)); 40 | myAppInfos.add(myAppInfo); 41 | } 42 | } catch (Exception e) { 43 | Log.e(TAG, "===============获取应用包信息失败"); 44 | } 45 | return myAppInfos; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/util/LocationUtil.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.util; 2 | 3 | import android.content.Context; 4 | import android.location.Criteria; 5 | import android.location.Location; 6 | import android.location.LocationListener; 7 | import android.location.LocationManager; 8 | import android.location.LocationProvider; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.os.SystemClock; 12 | import android.provider.Settings; 13 | import android.util.Log; 14 | 15 | /** 16 | * Created by xuqingfu on 2017/4/24. 17 | */ 18 | 19 | public class LocationUtil { 20 | 21 | private static final String TAG = "silence"; 22 | 23 | private static Double mLatitude = 30.6363334898; 24 | private static Double mLongitude = 104.0486168861; 25 | 26 | private static LocationManager locationManager; 27 | private static boolean canMockPosition; 28 | /** 29 | * 判断在Android6.0+上是否将本程序添加到ADB模拟定位中 30 | */ 31 | public static boolean hasAddTestProvider = false; 32 | private static Thread mMockThread; 33 | 34 | 35 | /** 36 | * 初始化模拟定位,并检测是否开启ADB模拟定位 37 | * @param context 38 | * @return 39 | */ 40 | public static boolean initLocation(Context context) { 41 | locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 42 | canMockPosition = (Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0) || Build.VERSION.SDK_INT > 22; 43 | Log.d(TAG, "hasAddTestProvider:" + canMockPosition); 44 | return canMockPosition; 45 | } 46 | 47 | /** 48 | * 配置LocationManger参数 49 | */ 50 | public static void initLocationManager() throws Exception{ 51 | if (canMockPosition && !hasAddTestProvider) { 52 | try { 53 | String providerStr = LocationManager.GPS_PROVIDER; 54 | LocationProvider provider = locationManager.getProvider(providerStr); 55 | if (provider != null) { 56 | locationManager.addTestProvider( 57 | provider.getName() 58 | , provider.requiresNetwork() 59 | , provider.requiresSatellite() 60 | , provider.requiresCell() 61 | , provider.hasMonetaryCost() 62 | , provider.supportsAltitude() 63 | , provider.supportsSpeed() 64 | , provider.supportsBearing() 65 | , provider.getPowerRequirement() 66 | , provider.getAccuracy()); 67 | } else { 68 | locationManager.addTestProvider( 69 | providerStr 70 | , true, true, false, false, true, true, true 71 | , Criteria.POWER_HIGH 72 | , Criteria.ACCURACY_FINE); 73 | } 74 | locationManager.setTestProviderEnabled(providerStr, true); 75 | locationManager.requestLocationUpdates(providerStr, 0, 0, new LocationStatuListener()); 76 | locationManager.setTestProviderStatus(providerStr, LocationProvider.AVAILABLE, null, System.currentTimeMillis()); 77 | Log.i(TAG,"already open GPS!"); 78 | // 模拟位置可用 79 | hasAddTestProvider = true; 80 | Log.d(TAG, "hasAddTestProvider:" + hasAddTestProvider); 81 | canMockPosition = true; 82 | } catch (Exception e) { 83 | canMockPosition = false; 84 | Log.d(TAG, "初始化异常:" + e); 85 | throw e; 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * 开启虚拟定位线程 92 | */ 93 | public static void startLocaton(){ 94 | if (mMockThread == null) { 95 | mMockThread = new Thread(new Runnable() { 96 | @Override 97 | public void run() { 98 | while (true) { 99 | try { 100 | Thread.sleep(500); 101 | if (!hasAddTestProvider) { 102 | Log.d("xqf", "定位服务未打开"); 103 | continue; 104 | } 105 | setLocation(LocationUtil.mLatitude, LocationUtil.mLongitude); 106 | Log.d(TAG, "setLocation240=latitude:" + mLatitude + "?longitude:" + mLongitude); 107 | } catch (InterruptedException e) { 108 | e.printStackTrace(); 109 | } catch (Exception e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | } 114 | }); 115 | mMockThread.start(); 116 | } 117 | } 118 | 119 | /** 120 | * GPS定位需要不停的刷新经纬度值 121 | */ 122 | private static void setLocation(double latitude, double longitude) throws Exception{ 123 | try { 124 | String providerStr = LocationManager.GPS_PROVIDER; 125 | Location mockLocation = new Location(providerStr); 126 | mockLocation.setLatitude(latitude); 127 | mockLocation.setLongitude(longitude); 128 | mockLocation.setAltitude(0); // 高程(米) 129 | mockLocation.setBearing(0); // 方向(度) 130 | mockLocation.setSpeed(0); //速度(米/秒) 131 | mockLocation.setAccuracy(2); // 精度(米) 132 | mockLocation.setTime(System.currentTimeMillis()); // 本地时间 133 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 134 | //api 16以上的需要加上这一句才能模拟定位 , 也就是targetSdkVersion > 16 135 | mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 136 | } 137 | locationManager.setTestProviderLocation(providerStr, mockLocation); 138 | } catch (Exception e) { 139 | // 防止用户在软件运行过程中关闭模拟位置或选择其他应用 140 | stopMockLocation(); 141 | throw e; 142 | } 143 | } 144 | 145 | public static void stopMockLocation() { 146 | if (hasAddTestProvider) { 147 | try { 148 | locationManager.removeTestProvider(LocationManager.GPS_PROVIDER); 149 | } catch (Exception ex) { 150 | // 若未成功addTestProvider,或者系统模拟位置已关闭则必然会出错 151 | } 152 | hasAddTestProvider = false; 153 | } 154 | } 155 | 156 | /** 157 | * 设置地理经纬度值 158 | * @param mLongitude 159 | * @param mLatitude 160 | */ 161 | public static void setLongitudeAndLatitude(Double mLongitude, Double mLatitude) { 162 | LocationUtil.mLatitude = mLatitude; 163 | LocationUtil.mLongitude = mLongitude; 164 | } 165 | 166 | /** 167 | * 监听Location经纬度值的修改状态 168 | */ 169 | private static class LocationStatuListener implements LocationListener { 170 | @Override 171 | public void onLocationChanged(Location location) { 172 | double lat = location.getLatitude(); 173 | double lng = location.getLongitude(); 174 | Log.i(TAG, String.format("location: x=%s y=%s", lat, lng)); 175 | } 176 | 177 | @Override 178 | public void onStatusChanged(String provider, int status, Bundle extras) { 179 | 180 | } 181 | 182 | @Override 183 | public void onProviderEnabled(String provider) { 184 | 185 | } 186 | 187 | @Override 188 | public void onProviderDisabled(String provider) { 189 | 190 | } 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/util/SharePreferenceUtil.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import top.littlerich.virtuallocation.common.Common; 7 | 8 | import static android.content.Context.MODE_PRIVATE; 9 | 10 | /** 11 | * Created by xuqingfu on 2017/3/14. 12 | */ 13 | 14 | public class SharePreferenceUtil { 15 | 16 | private static final String SP_FILENAME = "config"; 17 | 18 | public static void saveData(Context context, String userName, String psd) { 19 | SharedPreferences sp = context.getSharedPreferences(SP_FILENAME, MODE_PRIVATE); 20 | SharedPreferences.Editor editor = sp.edit(); 21 | editor.putString(Common.SP_FILTER_APP_PKG_NAME, userName); 22 | editor.commit(); 23 | } 24 | 25 | public static boolean loadData(Context context) { 26 | SharedPreferences sp = context.getSharedPreferences(SP_FILENAME, MODE_PRIVATE); 27 | // Common.USER_NAME = sp.getString(Common.SP_FILTER_APP_PKG_NAME, "").toString(); 28 | /*if (TextUtils.isEmpty(Common.USER_NAME) || TextUtils.isEmpty(Common.USER_PWD)) { 29 | return false; 30 | }*/ 31 | return true; 32 | } 33 | 34 | public static void cleanData(Context context){ 35 | saveData(context, "", ""); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/util/XposedUtil.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by xuqingfu on 2017/4/17. 8 | */ 9 | 10 | public class XposedUtil { 11 | 12 | private static List pkgs = new ArrayList<>(); 13 | 14 | static { 15 | pkgs.add("com.tencent.mm"); 16 | pkgs.add("com.tencent.mobileqq"); 17 | pkgs.add("com.alibaba.android.rimet"); 18 | } 19 | 20 | public static void hookAndChange(ClassLoader classLoader){ 21 | 22 | } 23 | 24 | public static boolean isNeedHook(String pkgName){ 25 | return pkgs.contains(pkgName); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/view/CircleWaveView.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.view; 2 | 3 | import android.view.View; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.util.AttributeSet; 9 | 10 | /** 11 | * Created by xuqingfu on 2017/5/2. 12 | */ 13 | 14 | public class CircleWaveView extends View { 15 | private float mWidth; 16 | private float mHeight; 17 | 18 | private float centerX; // 圆心X 19 | private float centerY; // 圆心Y 20 | private float floatRadius; // 变化的半径 21 | private float maxRadius = -1; // 圆半径 22 | private volatile boolean started = false; 23 | private Paint mLinePaint; 24 | private Paint mSolidPaint; 25 | private int waveColor = Color.LTGRAY; // Color.argb(0, 0, 0, 0); //颜色 26 | private int waveInterval = 100; // 圆环的宽度 27 | private boolean centerAlign = true;// 居中 28 | private float bottomMargin = 0;// 底部margin 29 | private boolean fillCircle = true;// 是否填充成实心圆环 30 | 31 | public CircleWaveView(Context context, AttributeSet attrs) { 32 | this(context, attrs, 0); 33 | } 34 | 35 | public CircleWaveView(Context context) { 36 | this(context, null, 0); 37 | } 38 | 39 | public CircleWaveView(Context context, AttributeSet attrs, int defStyle) { 40 | super(context, attrs, defStyle); 41 | initView(); 42 | } 43 | 44 | private void initView() { 45 | mLinePaint = new Paint(); 46 | mSolidPaint = new Paint(); 47 | } 48 | 49 | private void init() { 50 | mWidth = getWidth(); 51 | mHeight = getHeight(); 52 | 53 | mLinePaint.setAntiAlias(true); 54 | mLinePaint.setStrokeWidth(1.0F); 55 | mLinePaint.setStyle(Paint.Style.STROKE); 56 | mLinePaint.setColor(waveColor); 57 | 58 | if (fillCircle) { 59 | mSolidPaint.setStyle(Paint.Style.STROKE); 60 | mSolidPaint.setStrokeWidth(waveInterval); 61 | // 设置背景色的颜色 62 | mSolidPaint.setColor(Color.parseColor("#b37400")); 63 | } 64 | 65 | centerX = mWidth / 2.0F; 66 | if (centerAlign) { 67 | centerY = (mHeight / 2.0F); 68 | 69 | } else { 70 | centerY = mHeight - bottomMargin; 71 | } 72 | 73 | if (mWidth >= mHeight) { 74 | maxRadius = mHeight / 2.0F; 75 | } else { 76 | maxRadius = mWidth / 2.0F; 77 | } 78 | 79 | floatRadius = (maxRadius % waveInterval); 80 | 81 | start(); 82 | } 83 | 84 | public void start() { 85 | if (!started) { 86 | started = true; 87 | new Thread(thread).start(); 88 | } 89 | } 90 | 91 | public void stop() { 92 | // Thread.interrupted(); 93 | started = true; 94 | } 95 | 96 | public void onDetachedFromWindow() { 97 | super.onDetachedFromWindow(); 98 | 99 | stop(); 100 | } 101 | 102 | protected void onDraw(Canvas canvas) { 103 | super.onDraw(canvas); 104 | if (maxRadius <= 0.0F) { 105 | return; 106 | } 107 | float radius = floatRadius % waveInterval; 108 | while (true) { 109 | int alpha = (int) (255.0F * (1.0F - radius / maxRadius)); 110 | if (alpha <= 0) { 111 | break; 112 | } 113 | // 设置背景色 114 | 115 | if (fillCircle) { 116 | mSolidPaint.setAlpha(alpha >> 2); 117 | canvas.drawCircle(centerX, centerY, radius - waveInterval / 2, 118 | mSolidPaint); 119 | } 120 | mLinePaint.setAlpha(alpha); 121 | canvas.drawCircle(centerX, centerY, radius, mLinePaint); 122 | radius += waveInterval; 123 | } 124 | } 125 | 126 | public void onWindowFocusChanged(boolean hasWindowFocus) { 127 | super.onWindowFocusChanged(hasWindowFocus); 128 | if (hasWindowFocus) { 129 | init(); 130 | } else { 131 | stop(); 132 | } 133 | } 134 | //在外面定义一个方法把started设置为true就可以暂停了 135 | private Runnable thread = new Runnable() { 136 | 137 | @Override 138 | public void run() { 139 | // TODO Auto-generated method stub 140 | while (started) { 141 | floatRadius = 4.0F + floatRadius; 142 | if (floatRadius > maxRadius) { 143 | floatRadius = (maxRadius % waveInterval); 144 | postInvalidate(); 145 | } 146 | postInvalidate(); 147 | try { 148 | Thread.sleep(50L); 149 | } catch (InterruptedException localInterruptedException) { 150 | localInterruptedException.printStackTrace(); 151 | } 152 | } 153 | } 154 | }; 155 | 156 | public void setMaxRadius(float maxRadius) { 157 | this.maxRadius = maxRadius; 158 | } 159 | 160 | public void setWaveColor(int waveColor) { 161 | this.waveColor = waveColor; 162 | } 163 | 164 | public void setWaveInterval(int waveInterval) { 165 | this.waveInterval = waveInterval; 166 | } 167 | 168 | public void setCenterAlign(boolean centerAlign) { 169 | this.centerAlign = centerAlign; 170 | } 171 | 172 | } -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/view/CustomProgressDialog.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.view; 2 | 3 | import android.content.Context; 4 | import android.widget.TextView; 5 | 6 | import top.littlerich.virtuallocation.R; 7 | import top.littlerich.virtuallocation.base.BaseDialog; 8 | 9 | /** 10 | * Created by xuqingfu on 2017/4/15. 11 | */ 12 | public class CustomProgressDialog extends BaseDialog { 13 | 14 | private TextView tvMsg; 15 | 16 | public CustomProgressDialog(Context context) { 17 | super(context); 18 | } 19 | 20 | @Override 21 | protected int getContentViewId() { 22 | return R.layout.dialog_customprogress; 23 | } 24 | 25 | @Override 26 | protected void InitView() { 27 | tvMsg = (TextView) mView.findViewById(R.id.id_tv_loadingmsg); 28 | } 29 | 30 | @Override 31 | protected void IniListener() { 32 | // TODO Auto-generated method stub 33 | 34 | } 35 | 36 | @Override 37 | protected void InitData() { 38 | 39 | } 40 | 41 | public void setMessage(String strMessage) { 42 | if (tvMsg != null) { 43 | tvMsg.setText(strMessage); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /virtuallocation/src/main/java/top/littlerich/virtuallocation/view/TopBanner.java: -------------------------------------------------------------------------------- 1 | package top.littlerich.virtuallocation.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.Drawable; 7 | import android.text.TextUtils.TruncateAt; 8 | import android.util.AttributeSet; 9 | import android.util.TypedValue; 10 | import android.view.Gravity; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.LinearLayout; 15 | import android.widget.RelativeLayout; 16 | import android.widget.TextView; 17 | 18 | import top.littlerich.virtuallocation.R; 19 | 20 | 21 | /** 22 | * 自定义Actionbar 23 | */ 24 | public class TopBanner extends RelativeLayout { 25 | // private static int LEFT_BTN_ID = 1; 26 | // private static int TITLE_ID = 2; 27 | // private static int RIGHT_BTN_ID = 3; 28 | 29 | private ImageView leftButton; 30 | private ImageView rightButton; 31 | private TextView title; 32 | private LinearLayout leftLayout; 33 | private LinearLayout rightLayout; 34 | 35 | private OnTopBannerListener listener; 36 | 37 | @SuppressWarnings("deprecation") 38 | public TopBanner(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | String leftButtonText; 41 | float leftButtonTextSize; 42 | Drawable leftButtonBackground; 43 | int leftButtonTextColor; 44 | boolean leftButtonVisiable; 45 | String rightButtonText; 46 | float rightButtonTextSize; 47 | Drawable rightButtonBackground; 48 | int rightButtonTextColor; 49 | boolean rightButtonVisiable; 50 | String titleText; 51 | float titleTextSize; 52 | int titleTextColor; 53 | float rightButtonHeight; 54 | float rightButtonWidth; 55 | float leftButtonHeight; 56 | float leftButtonWidth; 57 | LayoutParams leftButtonParams; 58 | LayoutParams rightButtonParams; 59 | LayoutParams titleButtonParams; 60 | 61 | LayoutParams leftParams; 62 | LayoutParams rightParams; 63 | // 获取属性 64 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.topBanner); 65 | 66 | leftButtonText = typedArray.getString(R.styleable.topBanner_leftButtonText); 67 | leftButtonTextSize = typedArray.getDimension(R.styleable.topBanner_leftButtonTextSize, 10); 68 | leftButtonBackground = typedArray.getDrawable(R.styleable.topBanner_leftButtonBackground); 69 | leftButtonTextColor = typedArray.getColor(R.styleable.topBanner_leftButtonTextColor, Color.BLACK); 70 | leftButtonVisiable = typedArray.getBoolean(R.styleable.topBanner_leftButtonVisiable, true); 71 | rightButtonText = typedArray.getString(R.styleable.topBanner_rightButtonText); 72 | rightButtonTextSize = typedArray.getDimension(R.styleable.topBanner_rightButtonTextSize, 10); 73 | rightButtonBackground = typedArray.getDrawable(R.styleable.topBanner_rightButtonBackground); 74 | rightButtonTextColor = typedArray.getColor(R.styleable.topBanner_rightButtonTextColor, Color.BLACK); 75 | rightButtonVisiable = typedArray.getBoolean(R.styleable.topBanner_rightButtonVisiable, true); 76 | 77 | titleText = typedArray.getString(R.styleable.topBanner_titleText); 78 | titleTextSize = typedArray.getDimension(R.styleable.topBanner_titleTextSize, 20); 79 | titleTextColor = typedArray.getColor(R.styleable.topBanner_titleTextColors, Color.WHITE); 80 | 81 | rightButtonHeight = typedArray.getDimension(R.styleable.topBanner_rightButtonHeight, ViewGroup.LayoutParams.WRAP_CONTENT); 82 | rightButtonWidth = typedArray.getDimension(R.styleable.topBanner_rightButtonWidth, ViewGroup.LayoutParams.WRAP_CONTENT); 83 | leftButtonHeight = typedArray.getDimension(R.styleable.topBanner_leftButtonHeight, ViewGroup.LayoutParams.WRAP_CONTENT); 84 | leftButtonWidth = typedArray.getDimension(R.styleable.topBanner_leftButtonWidth, ViewGroup.LayoutParams.WRAP_CONTENT); 85 | 86 | // 属性重用 87 | typedArray.recycle(); 88 | 89 | leftButton = new ImageView(context); 90 | rightButton = new ImageView(context); 91 | title = new TextView(context); 92 | 93 | leftLayout = new LinearLayout(context); 94 | rightLayout = new LinearLayout(context); 95 | 96 | // leftLayout.setPadding(30, 30, 30, 30); 97 | // rightLayout.setPadding(30, 30, 30, 30); 98 | 99 | leftLayout.setBackgroundColor(0x2723bf); 100 | rightLayout.setBackgroundColor(0x2723bf); 101 | 102 | leftParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams 103 | .WRAP_CONTENT); 104 | rightParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams 105 | .WRAP_CONTENT); 106 | 107 | leftButtonParams = new LayoutParams((int) leftButtonWidth, (int) leftButtonHeight); 108 | rightButtonParams = new LayoutParams((int) rightButtonWidth, (int) rightButtonHeight); 109 | titleButtonParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 110 | 111 | leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); 112 | leftParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);// 上下居中 113 | // lxh 114 | leftParams.setMargins(40, 0, 0, 0);// 左上右下 115 | // leftButton.setPadding(40, 10, 10, 10); 116 | leftButton.setPaddingRelative(40, 10, 10, 10); 117 | 118 | // leftButtonParams.addRule(RelativeLayout.CENTER_VERTICAL, 119 | // RelativeLayout.TRUE); 120 | 121 | rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); 122 | rightParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); 123 | rightParams.setMargins(0, 0, 40, 0);// 左上右下 124 | rightButton.setPaddingRelative(10, 10, 40, 10); 125 | // rightButtonParams.addRule(RelativeLayout.CENTER_VERTICAL, 126 | // RelativeLayout.TRUE); 127 | 128 | titleButtonParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 129 | // titleButtonParams.addRule(RelativeLayout.LEFT_OF, RIGHT_BTN_ID); 130 | // titleButtonParams.addRule(RelativeLayout.RIGHT_OF, LEFT_BTN_ID); 131 | 132 | leftLayout.addView(leftButton, leftButtonParams); 133 | rightLayout.addView(rightButton, rightButtonParams); 134 | 135 | addView(leftLayout, leftParams); 136 | addView(rightLayout, rightParams); 137 | addView(title, titleButtonParams); 138 | 139 | // 设置对应的属性值 140 | // leftButton.setGravity(Gravity.CENTER); 141 | // leftButton.setText(leftButtonText); 142 | // leftButton.setTextColor(leftButtonTextColor); 143 | // leftButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, leftButtonTextSize); 144 | leftButton.setBackgroundDrawable(leftButtonBackground); 145 | setLeftButtonVisibility(leftButtonVisiable); 146 | 147 | // rightButton.setGravity(Gravity.CENTER); 148 | // rightButton.setText(rightButtonText); 149 | // rightButton.setTextColor(rightButtonTextColor); 150 | // rightButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, rightButtonTextSize); 151 | rightButton.setBackgroundDrawable(rightButtonBackground); 152 | setRightButtonVisibility(rightButtonVisiable); 153 | 154 | title.setText(titleText); 155 | title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, titleTextSize); 156 | title.setTextColor(titleTextColor); 157 | title.setEllipsize(TruncateAt.MARQUEE); 158 | title.setGravity(Gravity.CENTER); 159 | title.setSingleLine(true); 160 | 161 | leftLayout.setOnClickListener(new OnClickListener() { 162 | @Override 163 | public void onClick(View v) { 164 | if (null != listener) { 165 | listener.leftClick(v); 166 | } 167 | } 168 | }); 169 | 170 | rightLayout.setOnClickListener(new OnClickListener() { 171 | @Override 172 | public void onClick(View v) { 173 | if (null != listener) { 174 | listener.rightClick(v); 175 | } 176 | } 177 | }); 178 | } 179 | 180 | 181 | public void setTopBannerListener(OnTopBannerListener listener) { 182 | this.listener = listener; 183 | } 184 | 185 | private void setLeftButtonVisibility(Boolean isVisibility) { 186 | leftButton.setVisibility(isVisibility ? View.VISIBLE : View.GONE); 187 | } 188 | 189 | private void setRightButtonVisibility(Boolean isVisibility) { 190 | rightButton.setVisibility(isVisibility ? View.VISIBLE : View.GONE); 191 | } 192 | 193 | public String getTitleText() { 194 | return title.getText().toString().trim(); 195 | } 196 | 197 | public void setTitleText(String titleText) { 198 | title.setText(titleText); 199 | } 200 | 201 | public ImageView getLeftButton() { 202 | return leftButton; 203 | } 204 | 205 | public ImageView getRightButton() { 206 | return rightButton; 207 | } 208 | 209 | public LinearLayout getLeftLayout() { 210 | return leftLayout; 211 | } 212 | 213 | public LinearLayout getRightLayout() { 214 | return rightLayout; 215 | } 216 | 217 | public interface OnTopBannerListener { 218 | void leftClick(View v); 219 | 220 | void rightClick(View v); 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /virtuallocation/src/main/jniLibs/armeabi/libBaiduMapSDK_v3_2_0_15.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/virtuallocation/src/main/jniLibs/armeabi/libBaiduMapSDK_v3_2_0_15.so -------------------------------------------------------------------------------- /virtuallocation/src/main/jniLibs/armeabi/liblocSDK5.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle888/VirtualLocation/7eda42b23c4162d0705b01ecc4f888007bd7b071/virtuallocation/src/main/jniLibs/armeabi/liblocSDK5.so -------------------------------------------------------------------------------- /virtuallocation/src/main/res/anim/spinloaing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/drawable/bg_begin_location.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 15 | 17 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/drawable/bg_botton_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/drawable/bg_notice.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/drawable/bg_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/drawable/bg_with_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 15 | 17 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 24 | 32 | 39 | 46 | 47 | 48 | 55 | 62 | 70 | 71 | 72 | 79 | 86 | 94 | 95 | 96 | 103 | 111 | 112 | 113 | 120 | 128 | 129 | 130 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/layout/activity_apps.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 26 | 31 | 37 | /> 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /virtuallocation/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 37 | 38 | 39 | 40 | 56 | 57 | 58 | 59 | 60 | 71 | 72 | 73 | 79 | 87 | 96 | 97 | 98 | 104 |