├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── debug.keystore
├── libs
│ └── XposedBridgeApi-54.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── acgmiao
│ │ └── dev
│ │ └── fuckrunning
│ │ └── ExampleInstrumentedTest.java
│ ├── debug
│ └── res
│ │ └── values
│ │ └── google_maps_api.xml
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── xposed_init
│ ├── java
│ │ └── com
│ │ │ └── acgmiao
│ │ │ └── dev
│ │ │ └── fuckrunning
│ │ │ ├── MainHook.java
│ │ │ ├── MyLocationApplication.java
│ │ │ ├── activity
│ │ │ ├── MainActivity.java
│ │ │ └── MapsActivity.java
│ │ │ └── util
│ │ │ ├── CoordTrans.java
│ │ │ ├── Distance.java
│ │ │ ├── PermissionUtils.java
│ │ │ └── Rectangle.java
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_maps.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── release
│ └── res
│ │ └── values
│ │ └── google_maps_api.xml
│ └── test
│ └── java
│ └── com
│ └── acgmiao
│ └── dev
│ └── fuckrunning
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /gradle.properties
5 | /.idea
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FuckRunning
2 |
3 | More infomation please visit this [article](https://acgmiao.com/post/fuckrunning/).
4 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "26.0.2"
6 | defaultConfig {
7 | applicationId "com.acgmiao.dev.fuckrunning"
8 | minSdkVersion 23
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.1"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 |
15 | signingConfigs {
16 | debug {
17 | storeFile file("./debug.keystore")
18 | }
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | debug {
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation 'com.google.android.gms:play-services-maps:11.8.0'
33 | compileOnly fileTree(include: ['*.jar'], dir: 'libs')
34 | compileOnly files('libs/XposedBridgeApi-54.jar')
35 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
36 | exclude group: 'com.android.support', module: 'support-annotations'
37 | })
38 | implementation 'com.android.support:appcompat-v7:25.4.0'
39 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
40 | testImplementation 'junit:junit:4.12'
41 | }
42 |
--------------------------------------------------------------------------------
/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/debug.keystore
--------------------------------------------------------------------------------
/app/libs/XposedBridgeApi-54.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/libs/XposedBridgeApi-54.jar
--------------------------------------------------------------------------------
/app/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 /Users/Wenxin/Library/Android/sdk/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/acgmiao/dev/fuckrunning/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning;
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("com.acgmiao.dev.fuckrunning", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/debug/res/values/google_maps_api.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 | AIzaSyDWjlbBqHGUilN0u0XrY149ZjawzWEK95U
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
13 |
14 |
17 |
18 |
26 |
29 |
32 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
55 |
58 |
59 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | com.acgmiao.dev.fuckrunning.MainHook
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/MainHook.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorEvent;
6 | import android.util.SparseArray;
7 |
8 | import java.lang.reflect.Field;
9 |
10 | import de.robv.android.xposed.IXposedHookLoadPackage;
11 | import de.robv.android.xposed.XC_MethodHook;
12 | import de.robv.android.xposed.XposedBridge;
13 | import de.robv.android.xposed.XposedHelpers;
14 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
15 |
16 | public class MainHook implements IXposedHookLoadPackage {
17 | private static float count = 0;
18 | private static final int max = 100000;
19 |
20 | @Override
21 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
22 | //测试是否成功生效
23 | if (loadPackageParam.packageName.equals("com.acgmiao.dev.fuckrunning")) {
24 | XposedBridge.log("找到测试应用");
25 | Class clazz = loadPackageParam.classLoader.loadClass("com.acgmiao.dev.fuckrunning.activity.MainActivity");
26 | XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() {
27 | @Override
28 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
29 | super.beforeHookedMethod(param);
30 | }
31 |
32 | @Override
33 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
34 | super.afterHookedMethod(param);
35 | param.setResult("你已被劫持");
36 | }
37 | });
38 | }
39 | //开始拦截目标应用
40 | if (loadPackageParam.packageName.equals("com.zjwh.android_wh_physicalfitness")) {
41 | XposedBridge.log("找到目标应用");
42 | XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "getNewAppInstance", Context.class, new XC_MethodHook() {
43 | @Override
44 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
45 | super.afterHookedMethod(param);
46 | XposedBridge.log("找到防反编译壳");
47 | Context context = (Context) param.args[0];
48 | ClassLoader classLoader = context.getClassLoader();
49 | XposedHelpers.findAndHookMethod("com.zjwh.android_wh_physicalfitness.emulator.c", classLoader, "a", new XC_MethodHook() {
50 | @Override
51 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
52 | super.beforeHookedMethod(param);
53 | }
54 |
55 | @Override
56 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
57 | super.afterHookedMethod(param);
58 | XposedBridge.log("成功劫持检测方法a()");
59 | param.setResult(false);
60 | }
61 | });
62 | XposedHelpers.findAndHookMethod("com.zjwh.android_wh_physicalfitness.emulator.c", classLoader, "b", new XC_MethodHook() {
63 | @Override
64 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
65 | super.beforeHookedMethod(param);
66 | }
67 |
68 | @Override
69 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
70 | super.afterHookedMethod(param);
71 | XposedBridge.log("成功劫持检测方法b()");
72 | param.setResult(false);
73 | }
74 | });
75 | XposedHelpers.findAndHookMethod("com.zjwh.android_wh_physicalfitness.emulator.c", classLoader, "c", Context.class, new XC_MethodHook() {
76 | @Override
77 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
78 | super.beforeHookedMethod(param);
79 | }
80 |
81 | @Override
82 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
83 | super.afterHookedMethod(param);
84 | XposedBridge.log("成功劫持检测方法c(Context)");
85 | param.setResult(false);
86 | }
87 | });
88 | //劫持传感器
89 | final Class> sensorEL = XposedHelpers.findClass("android.hardware.SystemSensorManager$SensorEventQueue", classLoader);
90 | XposedBridge.hookAllMethods(sensorEL, "dispatchSensorEvent", new XC_MethodHook() {
91 | @Override
92 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
93 | int handle = (Integer) param.args[0];
94 | Field field = param.thisObject.getClass().getDeclaredField("mSensorsEvents");
95 | field.setAccessible(true);
96 | Sensor ss = ((SparseArray) field.get(param.thisObject)).get(handle).sensor;
97 | if (ss == null) {
98 | XposedBridge.log("未劫持到传感器");
99 | return;
100 | }
101 | if (ss.getType() == Sensor.TYPE_ACCELEROMETER) {
102 | count += 1;
103 | //步频100算法
104 | if (count % 3 == 0) {
105 | ((float[]) param.args[1])[0] = ((float[]) param.args[1])[0] * 100;
106 | ((float[]) param.args[1])[1] += (float) -10;
107 | } else if (count % 2 == 0) {
108 | ((float[]) param.args[1])[0] = ((float[]) param.args[1])[0] * 1000;
109 | ((float[]) param.args[1])[2] += (float) -20;
110 | ((float[]) param.args[1])[1] += (float) -5;
111 | } else {
112 | ((float[]) param.args[1])[0] = ((float[]) param.args[1])[0] * 10;
113 | ((float[]) param.args[1])[2] += (float) 20;
114 | ((float[]) param.args[1])[1] += (float) -15;
115 | }
116 | XposedBridge.log("传感器类型:" + ss.getType() + "加速度传感器,数据" + ((float[]) param.args[1])[0] + "," + ((float[]) param.args[1])[1] + "," + ((float[]) param.args[1])[2]);
117 | }
118 | if (ss.getType() == Sensor.TYPE_STEP_COUNTER || ss.getType() == Sensor.TYPE_STEP_DETECTOR) {
119 | if (10000 * count <= max) {
120 | ((float[]) param.args[1])[0] = ((float[]) param.args[1])[0] + 10000 * count;
121 | count += 1;
122 | } else {
123 | count = 0;
124 | }
125 | XposedBridge.log("传感器类型:" + ss.getType() + "计步器,数据" + ((float[]) param.args[1])[0]);
126 | }
127 | }
128 | });
129 | }
130 | });
131 | }
132 | }
133 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/MyLocationApplication.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning;
2 |
3 | import android.app.Application;
4 |
5 |
6 | public class MyLocationApplication extends Application {
7 |
8 | private static MyLocationApplication mApplication;
9 |
10 | @Override
11 | public void onCreate() {
12 | mApplication = this;
13 | super.onCreate();
14 | }
15 |
16 | public static MyLocationApplication getApplication() {
17 | return mApplication;
18 | }
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.Toast;
9 |
10 | import com.acgmiao.dev.fuckrunning.R;
11 |
12 |
13 | public class MainActivity extends AppCompatActivity {
14 |
15 | @Override
16 | public void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_main);
19 | Button testbutton = (Button) findViewById(R.id.testButton);
20 | testbutton.setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View v) {
23 | Toast.makeText(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show();
24 | }
25 | });
26 | Button mapbutton = (Button) findViewById(R.id.mapButton);
27 | mapbutton.setOnClickListener(new View.OnClickListener() {
28 | @Override
29 | public void onClick(View v) {
30 | Intent intent = new Intent(MainActivity.this, MapsActivity.class);
31 | startActivity(intent);
32 | }
33 | });
34 | }
35 |
36 | private String toastMessage() {
37 | return "我未被劫持";
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/activity/MapsActivity.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.activity;
2 |
3 | import android.Manifest;
4 | import android.annotation.SuppressLint;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.content.pm.PackageManager;
10 | import android.location.Location;
11 | import android.location.LocationListener;
12 | import android.location.LocationManager;
13 | import android.os.Bundle;
14 | import android.support.annotation.NonNull;
15 | import android.support.v4.app.ActivityCompat;
16 | import android.support.v4.content.ContextCompat;
17 | import android.support.v7.app.AppCompatActivity;
18 | import android.util.Log;
19 | import android.widget.Toast;
20 |
21 | import com.acgmiao.dev.fuckrunning.R;
22 | import com.acgmiao.dev.fuckrunning.util.PermissionUtils;
23 | import com.google.android.gms.maps.CameraUpdateFactory;
24 | import com.google.android.gms.maps.GoogleMap;
25 | import com.google.android.gms.maps.LocationSource;
26 | import com.google.android.gms.maps.OnMapReadyCallback;
27 | import com.google.android.gms.maps.SupportMapFragment;
28 | import com.google.android.gms.maps.model.LatLng;
29 | import com.google.android.gms.maps.model.Marker;
30 | import com.google.android.gms.maps.model.MarkerOptions;
31 |
32 | import java.util.List;
33 |
34 | import static com.acgmiao.dev.fuckrunning.util.CoordTrans.GPS2GCJ;
35 |
36 | public class MapsActivity extends AppCompatActivity implements
37 | OnMapReadyCallback,
38 | GoogleMap.OnMyLocationButtonClickListener,
39 | GoogleMap.OnMyLocationClickListener,
40 | GoogleMap.OnMapClickListener,
41 | GoogleMap.OnMapLongClickListener,
42 | ActivityCompat.OnRequestPermissionsResultCallback,
43 | LocationListener {
44 |
45 | private static final long LOCATION_REFRESH_TIME_INTERVAL = 1000;
46 |
47 | private static final float LOCATION_MIN_DISTANCE = 1.0f;
48 |
49 | private static final long BAD_ACCURACY_UPDATE_TIME = 2 * 60 * 1000;
50 |
51 | private GoogleMap mMap;
52 |
53 | private LocationManager mLocationManager;
54 |
55 | private boolean mIsAddListener;
56 |
57 | private boolean mIsPermissionGranted;
58 |
59 | private BroadcastReceiver broadcastReceiver;
60 |
61 | private boolean mIsResume;
62 |
63 | private Location mLastKnownLocation;
64 |
65 | private long mLastUpdateLocationTime;
66 |
67 | private LocationSource.OnLocationChangedListener myLocationListener = null;
68 |
69 | private int MARKER_NUMBER_CONTER = 0;
70 |
71 | private Marker[] mMakerArray = new Marker[5];
72 |
73 | @Override
74 | protected void onCreate(Bundle savedInstanceState) {
75 | super.onCreate(savedInstanceState);
76 | setContentView(R.layout.activity_maps);
77 | broadcastReceiver = new BroadcastReceiver() {
78 | @Override
79 | public void onReceive(Context context, Intent intent) {
80 | if (intent.getAction().equalsIgnoreCase(LocationManager.PROVIDERS_CHANGED_ACTION)) {
81 | if (mIsPermissionGranted && mIsResume) {
82 | boolean isGPSOn = isGPSOn();
83 | if (isGPSOn) {
84 | startGetLocation();
85 | } else {
86 | stopGetLocation();
87 | }
88 | }
89 | }
90 | }
91 | };
92 | SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
93 | .findFragmentById(R.id.map);
94 | mapFragment.getMapAsync(this);
95 | }
96 |
97 | @Override
98 | protected void onResume() {
99 | super.onResume();
100 | mIsResume = true;
101 | registerBroadcastReceiver();
102 | if (mIsPermissionGranted) {
103 | startGetLocation();
104 | } else {
105 | if (checkLocationPermission()) {
106 | mIsPermissionGranted = true;
107 | startGetLocation();
108 | }
109 | }
110 | }
111 |
112 | @Override
113 | protected void onPause() {
114 | super.onPause();
115 | mIsResume = true;
116 | unregisterBroadcastReceiver();
117 | if (mIsPermissionGranted) {
118 | stopGetLocation();
119 | }
120 | }
121 |
122 | @Override
123 | public void onMapReady(GoogleMap googleMap) {
124 | mMap = googleMap;
125 | boolean isGotLocationPermission = checkLocationPermission();
126 | if (isGotLocationPermission) {
127 | // goto next method
128 | mIsPermissionGranted = true;
129 | startGetLocation();
130 | } else {
131 | // need to get location permission
132 | requestLocationPermission();
133 | }
134 |
135 | mMap.setOnMyLocationButtonClickListener(this);
136 | mMap.setOnMyLocationClickListener(this);
137 | mMap.setOnMapClickListener(this);
138 | mMap.setOnMapLongClickListener(this);
139 | }
140 |
141 | @Override
142 | public void onMapClick(LatLng point) {
143 | addMarker(point);
144 | Toast.makeText(this, "tapped, point=" + point, Toast.LENGTH_SHORT).show();
145 | }
146 |
147 | @Override
148 | public void onMapLongClick(LatLng point) {
149 | Toast.makeText(this, "long pressed, point=" + point, Toast.LENGTH_SHORT).show();
150 | }
151 |
152 | public void addMarker(LatLng point) {
153 | if(MARKER_NUMBER_CONTER<5) {//最大5个点
154 | mMakerArray[MARKER_NUMBER_CONTER++] = mMap.addMarker(new MarkerOptions()
155 | .position(point)
156 | .draggable(true)
157 | .alpha(0.7f)
158 | .title(String.valueOf(MARKER_NUMBER_CONTER)));
159 | } else {
160 | Toast.makeText(this, "数组越界,不加Marker", Toast.LENGTH_SHORT).show();
161 | }
162 | }
163 |
164 |
165 | private void registerBroadcastReceiver() {
166 | IntentFilter intentFilter = new IntentFilter();
167 | intentFilter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
168 | registerReceiver(broadcastReceiver, intentFilter);
169 | }
170 |
171 | private void unregisterBroadcastReceiver() {
172 | unregisterReceiver(broadcastReceiver);
173 | }
174 |
175 | private void initLocationManager() {
176 | if (mLocationManager == null) {
177 | mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
178 | }
179 | }
180 |
181 | @SuppressLint("MissingPermission")
182 | private void startGetLocation() {
183 | initLocationManager();
184 | if (!mIsAddListener) {
185 | List providers = mLocationManager.getProviders(true);
186 | for (int i = 0; i < providers.size(); i++) {
187 | String provider = providers.get(i);
188 | Log.e("test", "startGetLocation:" + provider);
189 | mLocationManager.requestLocationUpdates(
190 | provider, LOCATION_REFRESH_TIME_INTERVAL, LOCATION_MIN_DISTANCE, this);
191 | }
192 | mIsAddListener = true;
193 | }
194 | }
195 |
196 | private void stopGetLocation() {
197 | initLocationManager();
198 | if (mIsAddListener) {
199 | Log.e("test", "stopGetLocation");
200 | mLocationManager.removeUpdates(this);
201 | mIsAddListener = false;
202 | //mMap.setLocationSource(this);
203 | }
204 | }
205 |
206 | @SuppressLint("MissingPermission")
207 | @Override
208 | public void onLocationChanged(Location location) {
209 | long nowTime = System.currentTimeMillis();
210 | boolean isUpdateMap = false;
211 | double lot = location.getLongitude();
212 | double lat = location.getLatitude();
213 | LatLng mgLatLng = GPS2GCJ(new LatLng(lat, lot));
214 | location.setLatitude(mgLatLng.latitude);
215 | location.setLongitude(mgLatLng.longitude);
216 | mMap.setLocationSource(new MyLocationSource());
217 | if (mLastKnownLocation == null) {//第一次定到位
218 | mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mgLatLng, 15));
219 | }
220 | if (mLastKnownLocation == null || location.getAccuracy() <= mLastKnownLocation.getAccuracy()) {
221 | mLastKnownLocation = location;
222 | mLastUpdateLocationTime = nowTime;
223 | if (!mMap.isMyLocationEnabled()) {
224 | mMap.setMyLocationEnabled(true);
225 | }
226 | isUpdateMap = true;
227 | }
228 | // force update location
229 | if(mLastUpdateLocationTime > 0 && nowTime - mLastUpdateLocationTime >= BAD_ACCURACY_UPDATE_TIME) {
230 | if(location.getAccuracy() >= mLastKnownLocation.getAccuracy()) {
231 | mLastKnownLocation = location;
232 | mLastUpdateLocationTime = nowTime;
233 | isUpdateMap = true;
234 | }
235 | }
236 | if(isUpdateMap){
237 | myLocationListener.onLocationChanged(mLastKnownLocation);
238 | Log.e("test","onLocationChanged Performed");
239 | }
240 | Log.e("test", "onLocationChanged:" + location.toString() + ",Provider:" + location.getProvider());
241 | }
242 |
243 | @Override
244 | public void onStatusChanged(String s, int i, Bundle bundle) {
245 | Log.e("test", "onStatusChanged:" + s + "," + i);
246 | }
247 |
248 | @Override
249 | public void onProviderEnabled(String s) {
250 | Log.e("test", "onProviderEnabled:" + s);
251 | }
252 |
253 | @Override
254 | public void onProviderDisabled(String s) {
255 | Log.e("test", "onProviderDisabled:" + s);
256 | }
257 |
258 | private boolean isGPSOn() {
259 | initLocationManager();
260 | return mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
261 | }
262 |
263 | private boolean checkLocationPermission() {
264 | boolean locationPermission = false;
265 | int permissionStatus = ContextCompat.
266 | checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION);
267 | if (permissionStatus == PackageManager.PERMISSION_GRANTED) {
268 | locationPermission = true;
269 | }
270 | return locationPermission;
271 | }
272 |
273 | private void requestLocationPermission() {
274 | PermissionUtils.requestPermission(this,
275 | PermissionUtils.LOCATION_PERMISSION_REQUEST_CODE,
276 | android.Manifest.permission.ACCESS_FINE_LOCATION, false);
277 | }
278 |
279 | @Override
280 | public boolean onMyLocationButtonClick() {
281 | Toast.makeText(this, "MyLocationApplication button clicked", Toast.LENGTH_SHORT).show();
282 | // Return false so that we don't consume the event and the default behavior still occurs
283 | return false;
284 | }
285 |
286 | @Override
287 | public void onMyLocationClick(@NonNull Location location) {
288 | Toast.makeText(this, "Current location:\n" + location, Toast.LENGTH_LONG).show();
289 | }
290 |
291 | @Override
292 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
293 | @NonNull int[] grantResults) {
294 |
295 | if (requestCode != PermissionUtils.LOCATION_PERMISSION_REQUEST_CODE) {
296 | return;
297 | }
298 | if (PermissionUtils.isPermissionGranted(permissions, grantResults,
299 | Manifest.permission.ACCESS_FINE_LOCATION)) {
300 | mIsPermissionGranted = true;
301 | initLocationManager();
302 | startGetLocation();
303 | } else {
304 | mIsPermissionGranted = false;
305 | Log.e("test", "not get location permission");
306 | }
307 | }
308 |
309 | @Override
310 | protected void onResumeFragments() {
311 | super.onResumeFragments();
312 | }
313 |
314 | // private void showMissingPermissionError() {
315 | // PermissionUtils.PermissionDeniedDialog.newInstance(false).show(getSupportFragmentManager(), "dialog");
316 | // }
317 |
318 | private class MyLocationSource implements LocationSource {
319 | @Override
320 | public void activate(OnLocationChangedListener listener) {
321 | myLocationListener = listener;
322 | }
323 |
324 | @Override
325 | public void deactivate() {
326 | myLocationListener = null;
327 | }
328 | }
329 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/util/CoordTrans.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.util;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | import static com.acgmiao.dev.fuckrunning.util.Rectangle.IsInsideChina;
6 |
7 | public class CoordTrans {
8 |
9 | private static final double pi = 3.1415926535897932384626;
10 | private static final double a = 6378245.0;
11 | private static final double ee = 0.00669342162296594323;
12 |
13 | public static LatLng GPS2GCJ(LatLng location) {
14 | double lat = location.latitude;
15 | double lon = location.longitude;
16 | if (!IsInsideChina(location)) {
17 | return new LatLng(lat, lon);
18 | }
19 | double dLat = transformLat(lon - 105.0, lat - 35.0);
20 | double dLon = transformLon(lon - 105.0, lat - 35.0);
21 | double radLat = lat / 180.0 * pi;
22 | double magic = Math.sin(radLat);
23 | magic = 1 - ee * magic * magic;
24 | double sqrtMagic = Math.sqrt(magic);
25 | dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
26 | dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
27 | double mgLat = lat + dLat;
28 | double mgLon = lon + dLon;
29 | return new LatLng(mgLat, mgLon);
30 | }
31 |
32 | public static LatLng GCJ2GPS(LatLng location) {
33 | double lat = location.latitude;
34 | double lon = location.longitude;
35 | if (!IsInsideChina(location)) {
36 | return new LatLng(lat, lon);
37 | }
38 | double dLat = transformLat(lon - 105.0, lat - 35.0);
39 | double dLon = transformLon(lon - 105.0, lat - 35.0);
40 | double radLat = lat / 180.0 * pi;
41 | double magic = Math.sin(radLat);
42 | magic = 1 - ee * magic * magic;
43 | double sqrtMagic = Math.sqrt(magic);
44 | dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
45 | dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
46 | double mgLat = lat + dLat;
47 | double mgLon = lon + dLon;
48 | double longitude = lon * 2 - mgLon;
49 | double latitude = lat * 2 - mgLat;
50 | return new LatLng(latitude, longitude);
51 | }
52 |
53 | private static double transformLat(double x, double y) {
54 | double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
55 | + 0.2 * Math.sqrt(Math.abs(x));
56 | ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
57 | ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
58 | ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
59 | return ret;
60 | }
61 |
62 | private static double transformLon(double x, double y) {
63 | double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
64 | * Math.sqrt(Math.abs(x));
65 | ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
66 | ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
67 | ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
68 | * pi)) * 2.0 / 3.0;
69 | return ret;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/util/Distance.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.util;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | public class Distance {
6 |
7 | private static final double EARTH_RADIUS = 6378137;
8 |
9 | //根据两点坐标,计算两点间距离,单位为m
10 | public static double GetDistance(LatLng location1, LatLng location2) {
11 | double lat1 = location1.latitude;
12 | double lng1 = location1.longitude;
13 | double lat2 = location2.latitude;
14 | double lng2 = location2.longitude;
15 | double radLat1 = rad(lat1);
16 | double radLat2 = rad(lat2);
17 | double a = radLat1 - radLat2;
18 | double b = rad(lng1) - rad(lng2);
19 | double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
20 | Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
21 | s = s * EARTH_RADIUS;
22 | s = Math.round(s * 10000) / 10000;
23 | return s;
24 | }
25 |
26 | private static double rad(double d) {
27 | return d * Math.PI / 180.0;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/util/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.util;
2 |
3 | import android.Manifest;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.content.DialogInterface;
7 | import android.content.pm.PackageManager;
8 | import android.os.Bundle;
9 | import android.support.v4.app.ActivityCompat;
10 | import android.support.v4.app.DialogFragment;
11 | import android.support.v7.app.AppCompatActivity;
12 | import android.widget.Toast;
13 |
14 | import com.acgmiao.dev.fuckrunning.R;
15 |
16 | public abstract class PermissionUtils {
17 |
18 | public static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
19 |
20 | /**
21 | * Requests the fine location permission. If a rationale with an additional explanation should
22 | * be shown to the user, displays a dialog that triggers the request.
23 | */
24 | public static void requestPermission(AppCompatActivity activity, int requestId,
25 | String permission, boolean finishActivity) {
26 | if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
27 | // Display a dialog with rationale.
28 | PermissionUtils.RationaleDialog.newInstance(requestId, finishActivity)
29 | .show(activity.getSupportFragmentManager(), "dialog");
30 | } else {
31 | // Location permission has not been granted yet, request it.
32 | ActivityCompat.requestPermissions(activity, new String[]{permission}, requestId);
33 |
34 | }
35 | }
36 |
37 | /**
38 | * Checks if the result contains a {@link PackageManager#PERMISSION_GRANTED} result for a
39 | * permission from a runtime permissions request.
40 | *
41 | * @see android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback
42 | */
43 | public static boolean isPermissionGranted(String[] grantPermissions, int[] grantResults,
44 | String permission) {
45 | for (int i = 0; i < grantPermissions.length; i++) {
46 | if (permission.equals(grantPermissions[i])) {
47 | return grantResults[i] == PackageManager.PERMISSION_GRANTED;
48 | }
49 | }
50 | return false;
51 | }
52 |
53 | /**
54 | * A dialog that displays a permission denied message.
55 | */
56 | public static class PermissionDeniedDialog extends DialogFragment {
57 |
58 | private static final String ARGUMENT_FINISH_ACTIVITY = "finish";
59 |
60 | private boolean mFinishActivity = false;
61 |
62 | /**
63 | * Creates a new instance of this dialog and optionally finishes the calling Activity
64 | * when the 'Ok' button is clicked.
65 | */
66 | public static PermissionDeniedDialog newInstance(boolean finishActivity) {
67 | Bundle arguments = new Bundle();
68 | arguments.putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity);
69 |
70 | PermissionDeniedDialog dialog = new PermissionDeniedDialog();
71 | dialog.setArguments(arguments);
72 | return dialog;
73 | }
74 |
75 | @Override
76 | public Dialog onCreateDialog(Bundle savedInstanceState) {
77 | mFinishActivity = getArguments().getBoolean(ARGUMENT_FINISH_ACTIVITY);
78 |
79 | return new AlertDialog.Builder(getActivity())
80 | .setMessage(R.string.location_permission_denied)
81 | .setPositiveButton(android.R.string.ok, null)
82 | .create();
83 | }
84 |
85 | @Override
86 | public void onDismiss(DialogInterface dialog) {
87 | super.onDismiss(dialog);
88 | if (mFinishActivity) {
89 | Toast.makeText(getActivity(),
90 | "不给权限就自杀",
91 | Toast.LENGTH_SHORT)
92 | .show();
93 | getActivity().finish();
94 | }
95 | }
96 | }
97 |
98 | /**
99 | * A dialog that explains the use of the location permission and requests the necessary
100 | * permission.
101 | *
102 | * The activity should implement
103 | * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
104 | * to handle permit or denial of this permission request.
105 | */
106 | public static class RationaleDialog extends DialogFragment {
107 |
108 | private static final String ARGUMENT_PERMISSION_REQUEST_CODE = "requestCode";
109 |
110 | private static final String ARGUMENT_FINISH_ACTIVITY = "finish";
111 |
112 | private boolean mFinishActivity = false;
113 |
114 | /**
115 | * Creates a new instance of a dialog displaying the rationale for the use of the location
116 | * permission.
117 | *
118 | * The permission is requested after clicking 'ok'.
119 | *
120 | * @param requestCode Id of the request that is used to request the permission. It is
121 | * returned to the
122 | * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}.
123 | * @param finishActivity Whether the calling Activity should be finished if the dialog is
124 | * cancelled.
125 | */
126 | public static RationaleDialog newInstance(int requestCode, boolean finishActivity) {
127 | Bundle arguments = new Bundle();
128 | arguments.putInt(ARGUMENT_PERMISSION_REQUEST_CODE, requestCode);
129 | arguments.putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity);
130 | RationaleDialog dialog = new RationaleDialog();
131 | dialog.setArguments(arguments);
132 | return dialog;
133 | }
134 |
135 | @Override
136 | public Dialog onCreateDialog(Bundle savedInstanceState) {
137 | Bundle arguments = getArguments();
138 | final int requestCode = arguments.getInt(ARGUMENT_PERMISSION_REQUEST_CODE);
139 | mFinishActivity = arguments.getBoolean(ARGUMENT_FINISH_ACTIVITY);
140 |
141 | return new AlertDialog.Builder(getActivity())
142 | .setMessage(R.string.permission_rationale_location)
143 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
144 | @Override
145 | public void onClick(DialogInterface dialog, int which) {
146 | // After click on Ok, request the permission.
147 | ActivityCompat.requestPermissions(getActivity(),
148 | new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
149 | requestCode);
150 | // Do not finish the Activity while requesting permission.
151 | mFinishActivity = false;
152 | }
153 | })
154 | .setNegativeButton(android.R.string.cancel, null)
155 | .create();
156 | }
157 |
158 | @Override
159 | public void onDismiss(DialogInterface dialog) {
160 | super.onDismiss(dialog);
161 | if (mFinishActivity) {
162 | Toast.makeText(getActivity(),
163 | "不给权限就自杀",
164 | Toast.LENGTH_SHORT)
165 | .show();
166 | getActivity().finish();
167 | }
168 | }
169 | }
170 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/acgmiao/dev/fuckrunning/util/Rectangle.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning.util;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | public class Rectangle {
6 | private double West;
7 | private double North;
8 | private double East;
9 | private double South;
10 |
11 | private Rectangle(double latitude1, double longitude1, double latitude2, double longitude2) {
12 | this.West = Math.min(longitude1, longitude2);
13 | this.North = Math.max(latitude1, latitude2);
14 | this.East = Math.max(longitude1, longitude2);
15 | this.South = Math.min(latitude1, latitude2);
16 | }
17 |
18 | private static Rectangle[] region = new Rectangle[]{
19 | new Rectangle(49.220400, 79.446200, 42.889900, 96.330000),
20 | new Rectangle(54.141500, 109.687200, 39.374200, 135.000200),
21 | new Rectangle(42.889900, 73.124600, 29.529700, 124.143255),
22 | new Rectangle(29.529700, 82.968400, 26.718600, 97.035200),
23 | new Rectangle(29.529700, 97.025300, 20.414096, 124.367395),
24 | new Rectangle(20.414096, 107.975793, 17.871542, 111.744104),
25 |
26 | };
27 |
28 | private static Rectangle[] exclude = new Rectangle[]{
29 | new Rectangle(25.398623, 119.921265, 21.785006, 122.497559),
30 | new Rectangle(22.284000, 101.865200, 20.098800, 106.665000),
31 | new Rectangle(21.542200, 106.452500, 20.487800, 108.051000),
32 | new Rectangle(55.817500, 109.032300, 50.325700, 119.127000),
33 | new Rectangle(55.817500, 127.456800, 49.557400, 137.022700),
34 | new Rectangle(44.892200, 131.266200, 42.569200, 137.022700),
35 | };
36 |
37 | public static boolean IsInsideChina(LatLng location) {
38 | for (int i = 0; i < region.length; i++) {
39 | if (InRectangle(region[i], location)) {
40 | for (int j = 0; j < exclude.length; j++) {
41 | if (InRectangle(exclude[j], location)) {
42 | return false;
43 | }
44 | }
45 | return true;
46 | }
47 | }
48 | return false;
49 | }
50 |
51 | private static boolean InRectangle(Rectangle rect, LatLng location) {
52 | return rect.West <= location.longitude
53 | && rect.East >= location.longitude
54 | && rect.North >= location.latitude
55 | && rect.South <= location.latitude;
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_maps.xml:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | FuckRunning
3 | Map
4 |
5 |
6 | 应用需要使用定位服务来获取您当前的位置信息。
7 | 应用需要开启定位权限来获取您当前的位置。\n如果您选择了\“不再询问\”,请通过 设置 > 应用 > \FuckRunning\ 修改。
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/release/res/values/google_maps_api.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 | YOUR_KEY_HERE
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/com/acgmiao/dev/fuckrunning/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.acgmiao.dev.fuckrunning;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.1'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LIznzn/FuckRunning/2229321911bd8b665f8673de384182c9f016c2f9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Oct 26 21:53:37 HKT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------