blob: ee9c777226f79263a0733426185da8a7be0a62c6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.weex.extend.module.location;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.bridge.SimpleJSCallback;
import com.taobao.weex.utils.WXLogUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class DefaultLocation implements ILocatable {
private static final String TAG = "DefaultLocation";
private static final int TIME_OUT_WHAT = 0x11;
private Map<String, WXLocationListener> mRegisterSucCallbacks = new HashMap<>();
private List<WXLocationListener> mWXLocationListeners = new ArrayList<>();
private WXSDKInstance mWXSDKInstance;
private LocationManager mLocationManager;
private static final int GPS_TIMEOUT = 15000;
private int MIN_TIME = 20000;
private int MIN_DISTANCE = 5;
public DefaultLocation(WXSDKInstance instance) {
mWXSDKInstance = instance;
}
@Override
public void setWXSDKInstance(WXSDKInstance WXSDKInstance) {
mWXSDKInstance = WXSDKInstance;
}
@Override
public void getCurrentPosition(final String successCallback, final String errorCallback, final String params) {
WXLogUtils.d(TAG, "into--[getCurrentPosition] successCallback:" + successCallback + " \nerrorCallback:" + errorCallback + " \nparams:" + params);
if (!TextUtils.isEmpty(params)) {
try {
JSONObject jsObj = new JSONObject(params);
boolean enableHighAccuracy = jsObj.optBoolean("enableHighAccuracy");
boolean enableAddress = jsObj.optBoolean("address");
WXLocationListener listener = findLocation(null, successCallback, errorCallback, enableHighAccuracy, enableAddress);
if (listener != null) {
mWXLocationListeners.add(listener);
}
return;
} catch (JSONException e) {
WXLogUtils.e(TAG, e);
}
}
Map<String, Object> options = new HashMap<>();
options.put(ERROR_CODE, ErrorCode.PARAMS_ERROR);
options.put(ERROR_MSG, ErrorMsg.PARAMS_ERROR);
if (mWXSDKInstance != null) {
new SimpleJSCallback(mWXSDKInstance.getInstanceId(), errorCallback).invoke(options);
}
}
private WXLocationListener findLocation(String watchId, String sucCallback, String errorCallback, boolean enableHighAccuracy, boolean enableAddress) {
WXLogUtils.d(TAG, "into--[findLocation] mWatchId:" + watchId + "\nsuccessCallback:" + sucCallback + "\nerrorCallback:" + errorCallback + "\nenableHighAccuracy:" + enableHighAccuracy + "\nmEnableAddress:" + enableAddress);
if (mLocationManager == null) {
mLocationManager = (LocationManager) mWXSDKInstance.getContext().getSystemService(Context.LOCATION_SERVICE);
}
Criteria criteria = new Criteria();
if (enableHighAccuracy) {
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
}
//String provider = locationManager.getBestProvider(criteria, false);
if (ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
WXLocationListener WXLocationListener = new WXLocationListener(mLocationManager, mWXSDKInstance, watchId, sucCallback, errorCallback, enableAddress);
if (mLocationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER)) {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME, MIN_DISTANCE, WXLocationListener);
}
if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME, MIN_DISTANCE, WXLocationListener);
}
return WXLocationListener;
} else {
Map<String, Object> options = new HashMap<>();
options.put(ERROR_CODE, ErrorCode.NO_PERMISSION_ERROR);
options.put(ERROR_MSG, ErrorMsg.NO_PERMISSION_ERROR);
if (mWXSDKInstance != null) {
new SimpleJSCallback(mWXSDKInstance.getInstanceId(), errorCallback).invoke(options);
}
}
return null;
}
@Override
public void watchPosition(final String successCallback, final String errorCallback, final String params) {
if (WXEnvironment.isApkDebugable()){
WXLogUtils.d("into--[watchPosition] successCallback:" + successCallback + " errorCallback:" + errorCallback + "\nparams:" + params);
}
if (!TextUtils.isEmpty(params)) {
try {
JSONObject jsObj = new JSONObject(params);
boolean enableHighAccuracy = jsObj.optBoolean("enableHighAccuracy");
boolean enableAddress = jsObj.optBoolean("address");
String id = UUID.randomUUID().toString();
WXLocationListener listener = findLocation(id, successCallback, errorCallback, enableHighAccuracy, enableAddress);
if (listener != null) {
mRegisterSucCallbacks.put(id, listener);
}
return;
} catch (JSONException e) {
WXLogUtils.e(TAG, e);
}
}
Map<String, Object> options = new HashMap<>();
options.put(ERROR_CODE, ErrorCode.PARAMS_ERROR);
options.put(ERROR_MSG, ErrorMsg.PARAMS_ERROR);
if (mWXSDKInstance != null) {
new SimpleJSCallback(mWXSDKInstance.getInstanceId(), errorCallback).invoke(options);
}
}
@Override
public void clearWatch(String watchId) {
WXLogUtils.d("into--[clearWatch] mWatchId:" + watchId);
if (mWXSDKInstance == null || mWXSDKInstance.isDestroy() || mLocationManager == null) {
return;
}
if (ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
WXLocationListener listener = mRegisterSucCallbacks.get(watchId);
if (listener != null) {
listener.destroy();
mLocationManager.removeUpdates(listener);
}
mRegisterSucCallbacks.remove(watchId);
}
}
@Override
public void destroy() {
WXLogUtils.d("into--[destroy]");
if (mWXSDKInstance == null || mWXSDKInstance.isDestroy() || mLocationManager == null) {
return;
}
if (mWXLocationListeners != null && mWXLocationListeners.size() > 0) {
if (ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
for (WXLocationListener listener : mWXLocationListeners) {
if (listener != null) {
listener.destroy();
mLocationManager.removeUpdates(listener);
}
}
mWXLocationListeners.clear();
}
}
if (mRegisterSucCallbacks != null && mRegisterSucCallbacks.size() > 0) {
Collection<WXLocationListener> values = mRegisterSucCallbacks.values();
if (ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mWXSDKInstance.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
for (WXLocationListener listener : values) {
listener.destroy();
mLocationManager.removeUpdates(listener);
}
mRegisterSucCallbacks.clear();
}
}
}
static class WXLocationListener implements LocationListener, Handler.Callback {
private Context mContext = null;
private String mWatchId;
private SimpleJSCallback mSucCallback = null;
private SimpleJSCallback mErrorCallback = null;
private boolean mEnableAddress;
private Handler mHandler;
private LocationManager mLocationManager;
private WXLocationListener(LocationManager locationManager, WXSDKInstance instance, String watchId, String sucCallback, String errorCallback, boolean enableAddress) {
this.mWatchId = watchId;
if (instance != null) {
this.mSucCallback = new SimpleJSCallback(instance.getInstanceId(), sucCallback);
this.mErrorCallback = new SimpleJSCallback(instance.getInstanceId(), errorCallback);
mContext = instance.getContext();
}
this.mEnableAddress = enableAddress;
mHandler = new Handler(this);
mLocationManager = locationManager;
mHandler.sendEmptyMessageDelayed(TIME_OUT_WHAT, GPS_TIMEOUT);
}
@Override
public void onLocationChanged(Location location) {
mHandler.removeMessages(TIME_OUT_WHAT);
WXLogUtils.d(TAG, "into--[onLocationChanged] location:" + location == null ? "Location is NULL!" : location.toString());
if (location != null) {
Map<String, Object> result = new HashMap<>();
Map<String, Object> coords = new HashMap<>();
double longitude = location.getLongitude();
double latitude = location.getLatitude();
coords.put("longitude", longitude);
coords.put("latitude", latitude);
coords.put("altitude", location.getAltitude());
coords.put("accuracy", location.getAccuracy());
coords.put("heading", location.getBearing());
coords.put("speed", location.getSpeed());
result.put(COORDS, coords);
// 行政区域
if (mEnableAddress) {
Address addr = getAddress(latitude, longitude);
Map<String, Object> address = new HashMap<>();
if (addr != null) {
address.put("country", addr.getCountryName());
address.put("province", addr.getAdminArea());
address.put("city", addr.getLocality());
address.put("cityCode", addr.getPostalCode());
address.put("area", addr.getSubLocality());
address.put("road", addr.getThoroughfare());
// 其中getAddressLine(0)表示国家,getAddressLine(1)表示精确到某个区,getAddressLine(2)表示精确到具体的街
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 2; i++) {
if (!TextUtils.isEmpty(addr.getAddressLine(i))) { //防止null导致的拼接错误
sb.append(addr.getAddressLine(i));
}
}
address.put("addressLine", sb.toString());
}
result.put(ADDRESS, address);
}
result.put(ERROR_CODE, ErrorCode.SUCCESS);
result.put(ERROR_MSG, ErrorMsg.SUCCESS);
if (!TextUtils.isEmpty(mWatchId)) {
result.put(WATCH_ID, mWatchId);
}
if (mSucCallback != null) {
boolean isKeepAlive = TextUtils.isEmpty(mWatchId) ? false : true;
if (isKeepAlive) {
mSucCallback.invokeAndKeepAlive(result);
} else {
mSucCallback.invoke(result);
}
}
} else {
Map<String, Object> options = new HashMap<>();
options.put(ERROR_CODE, ErrorCode.LOCATION_ERROR);
options.put(ERROR_MSG, ErrorMsg.LOCATION_ERROR);
if (!TextUtils.isEmpty(mWatchId)) {
options.put(WATCH_ID, mWatchId);
}
if (mErrorCallback != null) {
boolean isKeepAlive = TextUtils.isEmpty(mWatchId) ? false : true;
if (isKeepAlive) {
mErrorCallback.invokeAndKeepAlive(options);
} else {
mErrorCallback.invoke(options);
}
}
}
if (TextUtils.isEmpty(mWatchId) && mContext != null) {
if (mLocationManager != null && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
destroy();
mLocationManager.removeUpdates(this);
}
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
WXLogUtils.i(TAG, "into--[onStatusChanged] provider:" + provider + " status:" + status);
}
@Override
public void onProviderEnabled(String provider) {
WXLogUtils.i(TAG, "into--[onProviderEnabled] provider:" + provider);
}
@Override
public void onProviderDisabled(String provider) {
WXLogUtils.i(TAG, "into--[onProviderDisabled] provider:" + provider);
}
@Override
public boolean handleMessage(Message msg) {
if (msg.what == TIME_OUT_WHAT) {
WXLogUtils.d(TAG, "into--[handleMessage] Location Time Out!");
if (mContext == null || mLocationManager == null) {
return false;
}
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager.removeUpdates(this);
}
Map<String, Object> options = new HashMap<>();
options.put(ERROR_CODE, ErrorCode.LOCATION_TIME_OUT);
options.put(ERROR_MSG, ErrorMsg.LOCATION_TIME_OUT);
if (!TextUtils.isEmpty(mWatchId)) {
options.put(WATCH_ID, mWatchId);
}
if (mErrorCallback != null) {
mErrorCallback.invoke(options);
}
return true;
}
return false;
}
/**
* get address info
*/
private Address getAddress(double latitude, double longitude) {
WXLogUtils.d(TAG, "into--[getAddress] latitude:" + latitude + " longitude:" + longitude);
try {
if (mContext == null) {
return null;
}
Geocoder gc = new Geocoder(mContext);
List<Address> list = gc.getFromLocation(latitude, longitude, 1);
if (list != null && list.size() > 0) {
return list.get(0);
}
} catch (Exception e) {
WXLogUtils.e(TAG, e);
}
return null;
}
public void destroy() {
if (mHandler != null) {
mHandler.removeMessages(TIME_OUT_WHAT);
mContext = null;
}
}
}
}