模擬位置 定位 釘釘打卡 運動軌跡 MD
阿新 • • 發佈:2019-04-12
cloc open ide string etl ons 釘釘 super shu
源碼
Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | [email protected] |
目錄
目錄
模擬位置
源碼
Activity
簡潔版 Activity
Service
IMyBinder
Utils
模擬位置
參考:
允許模擬位置在Android M下的坑
Android手機模擬GPS位置
android 模擬位置信息Location使用示例
參考這個工程
很多文章中說,M以上選擇模擬位置信息應用只能影響當前應用的定位信息,實際測試後發現這個結論是完全錯誤的,這個定位結果是可以影響所有APP的。
源碼
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
Activity
public class MainActivity extends ListActivity implements ServiceConnection, LocationListener {
private Location currentLocation;
private IMyBinder mIBinder;
public TextView textView;
private LocationManager manager;
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private static final String CHHJ = "30.557622,114.337485,0,0";//30.557622,114.337485,0,0
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"bindService",
"unbindService",
"打開GPS設置,關閉位置服務",
"打開開發者模式,請在【選擇模擬位置信息應用】中選擇本應用",
"模擬步行,請打開高德地圖APP查看效果",
"模擬當前位置",
"++++++++++++模擬楚河漢街++++++++++++",
"監控定位狀態",
"獲取當前位置",
"判斷是否開啟了模擬位置功能,在22以後無效",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
textView = new TextView(this);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
getListView().addFooterView(textView);
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //模擬位置服務
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 0);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
bindService(new Intent(this, MockService.class), this, Context.BIND_AUTO_CREATE);
break;
case 1:
if (mIBinder != null) {
unbindService(this);
mIBinder = null;
}
break;
case 2://打開GPS設置
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
break;
case 3://打開開發者模式
startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
break;
case 4://模擬步行
//百度地圖和騰訊地圖都屏蔽了模擬位置,但是如果你的應用中集成了這些地圖SDK,在你不明確禁用的情況下,還是能使用的
if (mIBinder != null) mIBinder.mockMovingLocation();
else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 5://模擬當前位置
if (mIBinder != null) {
if (currentLocation != null) mIBinder.mockFixLocation(currentLocation);
else Toast.makeText(this, "獲取位置失敗", Toast.LENGTH_SHORT).show();
} else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 6://模擬楚河漢街
if (mIBinder != null) {
Location location = new Location(PROVDER_NAME);
String[] datas = CHHJ.split(",");
location.setLatitude(Double.parseDouble(datas[0]));// 維度(度)
location.setLongitude(Double.parseDouble(datas[1]));// 經度(度)
location.setSpeed(Float.parseFloat(datas[2])); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
mIBinder.mockFixLocation(location);
} else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 7://監控定位狀態
requestLocationUpdates();
break;
case 8://獲取當前位置
Location location = Utils.getCurrentLocatioon(this);//經度 114.337675,維度 30.558067
if (location != null) textView.setText("經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + "\n");
currentLocation = location != null ? location : currentLocation;
break;
case 9://判斷用戶是否開啟了模擬位置功能
//在API 22以後已經不存在【允許模擬位置】設置,取而代之的是【選擇模擬位置信息應用】設置,下面的判斷也同樣失效了
int state = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0);
Toast.makeText(this, state != 0 ? "開啟了模擬位置功能" : "未開啟模擬位置功能", Toast.LENGTH_SHORT).show();
break;
}
}
private void requestLocationUpdates() {
if (manager.isProviderEnabled(PROVDER_NAME)) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "沒有權限", Toast.LENGTH_SHORT).show();
return;
}
manager.requestLocationUpdates(PROVDER_NAME, 0, 0, this);
Toast.makeText(this, "已開啟位置監控", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Gps Provider 不可用", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = (IMyBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
//region LocationListener
@Override
public void onLocationChanged(Location location) {
textView.append("【監聽到位置發生改變】經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + "\n");
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.i("bqt", "【onStatusChanged】" + provider + "," + status);
}
@Override
public void onProviderEnabled(String provider) {
Log.i("bqt", "【onProviderEnabled】" + provider);
}
@Override
public void onProviderDisabled(String provider) {
Log.i("bqt", "【onProviderDisabled】" + provider);
}
//endregion
}
簡潔版 Activity
public class MainActivity extends ListActivity implements ServiceConnection {
private IMyBinder mIBinder;
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private static final String CHHJ = "30.557622,114.337485,0,0";//30.557622,114.337485,0,0
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"開啟",
"關閉",
"關閉位置服務",
"選擇模擬位置信息應用",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 0);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
bindService(new Intent(this, MockService.class), this, Context.BIND_AUTO_CREATE);
break;
case 1:
if (mIBinder != null) {
unbindService(this);
mIBinder = null;
}
break;
case 2://打開GPS設置
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
break;
case 3://打開開發者模式
startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
break;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = (IMyBinder) service;
Location location = new Location(PROVDER_NAME);
String[] datas = CHHJ.split(",");
location.setLatitude(Double.parseDouble(datas[0]));// 維度(度)
location.setLongitude(Double.parseDouble(datas[1]));// 經度(度)
location.setSpeed(Float.parseFloat(datas[2])); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
mIBinder.mockFixLocation(location);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
Service
public class MockService extends Service {
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private LocationManager manager;
private ScheduledExecutorService service;//定時修改位置信息
@Override
public void onCreate() {
super.onCreate();
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //位置服務
if (!manager.isProviderEnabled(PROVDER_NAME)) { //Returns the current enabled/disabled status of the given provider.
Log.i("bqt", "【準備啟用Gps Provider】");
//false, false, false, false, true, false, false, 0, 5 或 false, false, false, false, true, true, true, 0, 5
manager.addTestProvider(PROVDER_NAME, true, true, false,
false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
manager.setTestProviderEnabled(PROVDER_NAME, true);
}
}
@Override
public IBinder onBind(Intent intent) {
return new MyMyBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
if (!manager.isProviderEnabled(PROVDER_NAME)) {
manager.clearTestProviderEnabled(LocationManager.GPS_PROVIDER);
manager.removeTestProvider(PROVDER_NAME);//移除GpsMockProvider
}
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
}
private class MyMyBinder extends Binder implements IMyBinder {
@Override
public void mockMovingLocation() {
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
service = Executors.newScheduledThreadPool(2);
ConcurrentLinkedQueue<Location> locations = Utils.getLocations(MockService.this, PROVDER_NAME);
service.scheduleWithFixedDelay(() -> task(locations.poll()), 1, 1, TimeUnit.SECONDS);
}
@Override
public void mockFixLocation(Location location) {
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
service = Executors.newScheduledThreadPool(2);
//如果只在有限的時間內發一次,會導致其他APP獲取不到定位信息,必須持續刷新,即使位置不變
service.scheduleWithFixedDelay(() -> task(location), 1, 1, TimeUnit.SECONDS);
}
private void task(Location location) {
if (location != null) {
location.setTime(System.currentTimeMillis()); //當前時間
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
Log.i("bqt", "【模擬位置】經度 " + location.getLongitude() + ",維度 " + location.getLatitude());
manager.setTestProviderLocation(PROVDER_NAME, location);// 添加並啟動GpsMockProvider
}
}
}
}
IMyBinder
public interface IMyBinder {
void mockMovingLocation();
void mockFixLocation(Location location);
}
Utils
public class Utils {
private static double PI = 3.14159265358979324;//圓周率 GCJ_02_To_WGS_84
public static ConcurrentLinkedQueue<Location> getLocations(Context context, String name) {
ConcurrentLinkedQueue<Location> locations = new ConcurrentLinkedQueue<>();
try {
InputStreamReader reader = new InputStreamReader(context.getResources().getAssets().open("test_tji.csv"));
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while ((line = bufferedReader.readLine()) != null) {
String[] datas = line.split(",");
Location location = Utils.initLocaton(name, datas);
locations.add(location);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return locations;
}
//參考格式【30.232852,120.058218,43,2,20181213115837】
private static Location initLocaton(String name, String[] datas) {
Location location = new Location(name); //the name of the provider that generated this location
float latitude = Float.parseFloat(datas[0]);
float longitude = Float.parseFloat(datas[1]);
double[] transformLocation = Utils.delta(latitude, longitude);
location.setLatitude(transformLocation[0]);// 維度(度)
location.setLongitude(transformLocation[1]);// 經度(度)
location.setSpeed(Float.parseFloat(datas[2]) / 3.6f); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
Bundle bundle = new Bundle();
bundle.putString("info", "來自包青天模擬的位置");
location.setExtras(bundle); //額外的信息
return location;
}
//此方法可以將高德地圖SDK獲取到的GPS經緯度轉換為真實的經緯度,可以用於解決安卓系統使用高德SDK獲取經緯度的轉換問題。
private static double[] delta(double lat, double lon) {
double a = 6378245.0;//克拉索夫斯基橢球參數長半軸a
double ee = 0.00669342162296594323;//克拉索夫斯基橢球參數第一偏心率平方
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
return new double[]{lat - dLat, lon - dLon};
}
//轉換經度
private static double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
//轉換緯度
private static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
public static Location getCurrentLocatioon(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return null;
}
LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); //模擬位置服務
List<String> prodiverlist = manager.getProviders(true);
if (prodiverlist == null || prodiverlist.size() == 0) {
Toast.makeText(context, "沒有可用的位置提供器", Toast.LENGTH_SHORT).show();
return null;
}
Log.i("bqt", "【可用位置服務】" + prodiverlist); //[passive, network, GpsMockProvider, gps]
Location location;
for (String prodiver : prodiverlist) {
location = manager.getLastKnownLocation(prodiver);
if (location != null) { //經度 114.337675,維度 30.558067,速度 0.0,方向 0.0
Log.i("bqt", "【當前位置】類型 " + location.getProvider() + ",經度 " + location.getLongitude()
+ ",維度 " + location.getLatitude() + ",速度 " + location.getSpeed() + ",方向 " + location.getBearing());
return location;
}
}
Toast.makeText(context, "獲取位置失敗", Toast.LENGTH_SHORT).show();
return null;
}
}
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
public class MainActivity extends ListActivity implements ServiceConnection, LocationListener {
private Location currentLocation;
private IMyBinder mIBinder;
public TextView textView;
private LocationManager manager;
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private static final String CHHJ = "30.557622,114.337485,0,0";//30.557622,114.337485,0,0
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"bindService",
"unbindService",
"打開GPS設置,關閉位置服務",
"打開開發者模式,請在【選擇模擬位置信息應用】中選擇本應用",
"模擬步行,請打開高德地圖APP查看效果",
"模擬當前位置",
"++++++++++++模擬楚河漢街++++++++++++",
"監控定位狀態",
"獲取當前位置",
"判斷是否開啟了模擬位置功能,在22以後無效",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
textView = new TextView(this);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
getListView().addFooterView(textView);
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //模擬位置服務
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 0);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
bindService(new Intent(this, MockService.class), this, Context.BIND_AUTO_CREATE);
break;
case 1:
if (mIBinder != null) {
unbindService(this);
mIBinder = null;
}
break;
case 2://打開GPS設置
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
break;
case 3://打開開發者模式
startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
break;
case 4://模擬步行
//百度地圖和騰訊地圖都屏蔽了模擬位置,但是如果你的應用中集成了這些地圖SDK,在你不明確禁用的情況下,還是能使用的
if (mIBinder != null) mIBinder.mockMovingLocation();
else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 5://模擬當前位置
if (mIBinder != null) {
if (currentLocation != null) mIBinder.mockFixLocation(currentLocation);
else Toast.makeText(this, "獲取位置失敗", Toast.LENGTH_SHORT).show();
} else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 6://模擬楚河漢街
if (mIBinder != null) {
Location location = new Location(PROVDER_NAME);
String[] datas = CHHJ.split(",");
location.setLatitude(Double.parseDouble(datas[0]));// 維度(度)
location.setLongitude(Double.parseDouble(datas[1]));// 經度(度)
location.setSpeed(Float.parseFloat(datas[2])); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
mIBinder.mockFixLocation(location);
} else Toast.makeText(this, "服務未開啟", Toast.LENGTH_SHORT).show();
break;
case 7://監控定位狀態
requestLocationUpdates();
break;
case 8://獲取當前位置
Location location = Utils.getCurrentLocatioon(this);//經度 114.337675,維度 30.558067
if (location != null) textView.setText("經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + "\n");
currentLocation = location != null ? location : currentLocation;
break;
case 9://判斷用戶是否開啟了模擬位置功能
//在API 22以後已經不存在【允許模擬位置】設置,取而代之的是【選擇模擬位置信息應用】設置,下面的判斷也同樣失效了
int state = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0);
Toast.makeText(this, state != 0 ? "開啟了模擬位置功能" : "未開啟模擬位置功能", Toast.LENGTH_SHORT).show();
break;
}
}
private void requestLocationUpdates() {
if (manager.isProviderEnabled(PROVDER_NAME)) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "沒有權限", Toast.LENGTH_SHORT).show();
return;
}
manager.requestLocationUpdates(PROVDER_NAME, 0, 0, this);
Toast.makeText(this, "已開啟位置監控", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Gps Provider 不可用", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = (IMyBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
//region LocationListener
@Override
public void onLocationChanged(Location location) {
textView.append("【監聽到位置發生改變】經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + "\n");
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.i("bqt", "【onStatusChanged】" + provider + "," + status);
}
@Override
public void onProviderEnabled(String provider) {
Log.i("bqt", "【onProviderEnabled】" + provider);
}
@Override
public void onProviderDisabled(String provider) {
Log.i("bqt", "【onProviderDisabled】" + provider);
}
//endregion
}
public class MainActivity extends ListActivity implements ServiceConnection {
private IMyBinder mIBinder;
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private static final String CHHJ = "30.557622,114.337485,0,0";//30.557622,114.337485,0,0
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"開啟",
"關閉",
"關閉位置服務",
"選擇模擬位置信息應用",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 0);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
bindService(new Intent(this, MockService.class), this, Context.BIND_AUTO_CREATE);
break;
case 1:
if (mIBinder != null) {
unbindService(this);
mIBinder = null;
}
break;
case 2://打開GPS設置
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
break;
case 3://打開開發者模式
startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
break;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = (IMyBinder) service;
Location location = new Location(PROVDER_NAME);
String[] datas = CHHJ.split(",");
location.setLatitude(Double.parseDouble(datas[0]));// 維度(度)
location.setLongitude(Double.parseDouble(datas[1]));// 經度(度)
location.setSpeed(Float.parseFloat(datas[2])); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
mIBinder.mockFixLocation(location);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
public class MockService extends Service {
private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER;
private LocationManager manager;
private ScheduledExecutorService service;//定時修改位置信息
@Override
public void onCreate() {
super.onCreate();
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //位置服務
if (!manager.isProviderEnabled(PROVDER_NAME)) { //Returns the current enabled/disabled status of the given provider.
Log.i("bqt", "【準備啟用Gps Provider】");
//false, false, false, false, true, false, false, 0, 5 或 false, false, false, false, true, true, true, 0, 5
manager.addTestProvider(PROVDER_NAME, true, true, false,
false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
manager.setTestProviderEnabled(PROVDER_NAME, true);
}
}
@Override
public IBinder onBind(Intent intent) {
return new MyMyBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
if (!manager.isProviderEnabled(PROVDER_NAME)) {
manager.clearTestProviderEnabled(LocationManager.GPS_PROVIDER);
manager.removeTestProvider(PROVDER_NAME);//移除GpsMockProvider
}
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
}
private class MyMyBinder extends Binder implements IMyBinder {
@Override
public void mockMovingLocation() {
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
service = Executors.newScheduledThreadPool(2);
ConcurrentLinkedQueue<Location> locations = Utils.getLocations(MockService.this, PROVDER_NAME);
service.scheduleWithFixedDelay(() -> task(locations.poll()), 1, 1, TimeUnit.SECONDS);
}
@Override
public void mockFixLocation(Location location) {
if (service != null && !service.isShutdown()) {
service.shutdownNow();
}
service = Executors.newScheduledThreadPool(2);
//如果只在有限的時間內發一次,會導致其他APP獲取不到定位信息,必須持續刷新,即使位置不變
service.scheduleWithFixedDelay(() -> task(location), 1, 1, TimeUnit.SECONDS);
}
private void task(Location location) {
if (location != null) {
location.setTime(System.currentTimeMillis()); //當前時間
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
Log.i("bqt", "【模擬位置】經度 " + location.getLongitude() + ",維度 " + location.getLatitude());
manager.setTestProviderLocation(PROVDER_NAME, location);// 添加並啟動GpsMockProvider
}
}
}
}
public interface IMyBinder {
void mockMovingLocation();
void mockFixLocation(Location location);
}
public class Utils {
private static double PI = 3.14159265358979324;//圓周率 GCJ_02_To_WGS_84
public static ConcurrentLinkedQueue<Location> getLocations(Context context, String name) {
ConcurrentLinkedQueue<Location> locations = new ConcurrentLinkedQueue<>();
try {
InputStreamReader reader = new InputStreamReader(context.getResources().getAssets().open("test_tji.csv"));
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while ((line = bufferedReader.readLine()) != null) {
String[] datas = line.split(",");
Location location = Utils.initLocaton(name, datas);
locations.add(location);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return locations;
}
//參考格式【30.232852,120.058218,43,2,20181213115837】
private static Location initLocaton(String name, String[] datas) {
Location location = new Location(name); //the name of the provider that generated this location
float latitude = Float.parseFloat(datas[0]);
float longitude = Float.parseFloat(datas[1]);
double[] transformLocation = Utils.delta(latitude, longitude);
location.setLatitude(transformLocation[0]);// 維度(度)
location.setLongitude(transformLocation[1]);// 經度(度)
location.setSpeed(Float.parseFloat(datas[2]) / 3.6f); //速度(米/秒)
location.setBearing(Float.parseFloat(datas[3]));// 方向(度)
location.setAltitude(30); // 海拔(米)
location.setAccuracy(20f); // 精度(米)
Bundle bundle = new Bundle();
bundle.putString("info", "來自包青天模擬的位置");
location.setExtras(bundle); //額外的信息
return location;
}
//此方法可以將高德地圖SDK獲取到的GPS經緯度轉換為真實的經緯度,可以用於解決安卓系統使用高德SDK獲取經緯度的轉換問題。
private static double[] delta(double lat, double lon) {
double a = 6378245.0;//克拉索夫斯基橢球參數長半軸a
double ee = 0.00669342162296594323;//克拉索夫斯基橢球參數第一偏心率平方
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
return new double[]{lat - dLat, lon - dLon};
}
//轉換經度
private static double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
//轉換緯度
private static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
public static Location getCurrentLocatioon(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return null;
}
LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); //模擬位置服務
List<String> prodiverlist = manager.getProviders(true);
if (prodiverlist == null || prodiverlist.size() == 0) {
Toast.makeText(context, "沒有可用的位置提供器", Toast.LENGTH_SHORT).show();
return null;
}
Log.i("bqt", "【可用位置服務】" + prodiverlist); //[passive, network, GpsMockProvider, gps]
Location location;
for (String prodiver : prodiverlist) {
location = manager.getLastKnownLocation(prodiver);
if (location != null) { //經度 114.337675,維度 30.558067,速度 0.0,方向 0.0
Log.i("bqt", "【當前位置】類型 " + location.getProvider() + ",經度 " + location.getLongitude()
+ ",維度 " + location.getLatitude() + ",速度 " + location.getSpeed() + ",方向 " + location.getBearing());
return location;
}
}
Toast.makeText(context, "獲取位置失敗", Toast.LENGTH_SHORT).show();
return null;
}
}
2019-4-11
附件列表
模擬位置 定位 釘釘打卡 運動軌跡 MD