diff --git a/app/build.gradle b/app/build.gradle
index cdfbf9a..ce067eb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,6 +22,9 @@ android {
// App启动页厂商名称
buildConfigField "String", "APP_VENDOR", "\"${project.properties.appVendor}\""
+ // App图标
+ buildConfigField "String", "APP_ICON", "\"${project.properties.appIconKey}\""
+
// AndroidManifest.xml占位符
manifestPlaceholders = [
// App名称
@@ -52,12 +55,14 @@ android {
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"")
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"")
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"")
+ resValue("string", "app_name2", "\"${project.properties.appName}\"")
}
debug {
signingConfig signingConfigs.release
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"")
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"")
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"")
+ resValue("string", "app_name2", "\"${project.properties.appName}\"")
}
}
compileOptions {
@@ -78,4 +83,9 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.Justson.AgentWeb:agentweb-core:v4.1.9-androidx' // (必选)
implementation 'com.github.Justson.AgentWeb:agentweb-filechooser:v4.1.9-androidx'
-}
\ No newline at end of file
+ implementation 'androidx.preference:preference:1.1.1'
+ implementation 'cn.hutool:hutool-all:5.5.4'
+
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ //compile 'com.amap.api:location:latest.integration'
+}
diff --git a/app/libs/AMap_Location_V6.4.9_20241226.jar b/app/libs/AMap_Location_V6.4.9_20241226.jar
new file mode 100644
index 0000000..648692e
Binary files /dev/null and b/app/libs/AMap_Location_V6.4.9_20241226.jar differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e3487cd..54bf096 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,16 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/nsgk/ruralWeb/location/NSAMapLocation.java b/app/src/main/java/com/nsgk/ruralWeb/location/NSAMapLocation.java
new file mode 100644
index 0000000..9e4af21
--- /dev/null
+++ b/app/src/main/java/com/nsgk/ruralWeb/location/NSAMapLocation.java
@@ -0,0 +1,451 @@
+package com.nsgk.ruralWeb.location;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+import com.amap.api.location.CoordinateConverter;
+import com.amap.api.location.DPoint;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+// 主线程调用, 无线程安全
+public class NSAMapLocation implements AMapLocationListener
+{
+ // 定位场景: 默认无场景
+ public static final int FLAG_PURPOSE_SIGNIN = 1; // 签到
+ public static final int FLAG_PURPOSE_TRANSPORT = 1 << 1; // 出行
+ public static final int FLAG_PURPOSE_SPORT = 1 << 2; // 运动
+
+ // 定位模式
+ public static final int FLAG_MODE_HIGH = 1 << 3; // 高精度模式
+ public static final int FLAG_MODE_SAVING = 1 << 4; // 低功耗模式
+ public static final int FLAG_MODE_SENSORS = 1 << 5; // 仅设备模式
+
+ // 获取一次定位结果: 默认为false
+ public static final int FLAG_ONCE = 1 << 6; // true
+
+ // 获取最近3s内精度最高的一次定位结果: 默认为false
+ public static final int FLAG_ONCE_LATEST = 1 << 7; // true
+
+ // 设置是否返回地址信息: 默认返回地址信息
+ public static final int FLAG_NO_NEED_ADDRESS = 1 << 8; // false
+
+ // 设置是否允许模拟位置: 默认为true
+ public static final int FLAG_MOCK_DISABLE = 1 << 9; // false
+
+ // 关闭缓存机制: 默认缓存
+ public static final int FLAG_CACHE_DISABLE = 1 << 10; // false
+
+ // 线程
+ public static final int FLAG_BUILTIN_THREAD = 1 << 11; // 使用内建线程
+ public static final int FLAG_CUSTOM_THREAD = 1 << 12; // 使用外部线程
+ public static final int FLAG_MAIN_THREAD = 1 << 13; // 使用主线程
+
+ public static final int DEFAULT_FLAG = FLAG_MODE_HIGH | FLAG_NO_NEED_ADDRESS;
+
+
+ public static final int OPTION_INTERVAL = 1; // 定位间隔(毫秒) 默认1000
+ public static final int OPTION_HTTP_TIMEOUT = 2; // 定位间隔(毫秒) 默认20000
+
+ private static final int DEFAULT_INTERVAL = 1000;
+ private static final int DEFAULT_HTTP_TIMEOUT = 20000;
+
+ private static final String ID_TAG = NSSystemLocation.class.getName();
+
+ private final Object m_lock = new Object();
+
+ //声明AMapLocationClient类对象
+ public AMapLocationClient mLocationClient = null;
+ //声明定位回调监听器
+ public AMapLocationListener mLocationListener = this;
+ //声明AMapLocationClientOption对象
+ public AMapLocationClientOption mLocationOption = null;
+ private final Context m_context;
+ private int m_flag = DEFAULT_FLAG;
+ private boolean m_isPreInit = false;
+ private NSLocationInfo m_lastLocation = null;
+ private HandlerThread m_thread = null;
+ private Looper m_customLooper = null;
+ private AtomicInteger m_readOnce = new AtomicInteger(-1);
+ private Map m_options = new HashMap<>();
+
+ public NSAMapLocation(Context context)
+ {
+ m_context = context;
+ }
+
+ public boolean Init(int flag)
+ {
+ if(IsInitialized())
+ {
+ Log.e(ID_TAG, "定位已初始化");
+ return true;
+ }
+
+ try
+ {
+ m_readOnce.set(-1);
+ m_flag = flag < 0 ? DEFAULT_FLAG : flag;
+
+ // 在构造AMapLocationClient 之前必须进行合规检查,设置接口之前保证隐私政策合规
+ PreInit();
+
+ Context applicationContext = m_context.getApplicationContext();
+ //初始化定位
+ if(HasFlag(FLAG_BUILTIN_THREAD))
+ {
+ m_thread = new HandlerThread("AMap内建定位");
+ m_thread.start();
+ mLocationClient = new AMapLocationClient(m_thread.getLooper(), applicationContext);
+ Log.d(ID_TAG, "定位使用内建线程: " + m_thread.getId());
+ }
+ else if(HasFlag(FLAG_CUSTOM_THREAD))
+ {
+ if(null == m_customLooper)
+ {
+ throw new RuntimeException("请先传入线程Looper");
+ }
+ mLocationClient = new AMapLocationClient(m_customLooper, applicationContext);
+ Log.d(ID_TAG, "定位使用用户指定线程: " + m_customLooper.getThread().getId());
+ }
+ else if(HasFlag(FLAG_MAIN_THREAD))
+ {
+ Looper mainLooper = Looper.getMainLooper();
+ mLocationClient = new AMapLocationClient(mainLooper, applicationContext);
+ Log.d(ID_TAG, "定位使用主线程: " + mainLooper.getThread().getId());
+ }
+ else
+ {
+ mLocationClient = new AMapLocationClient(applicationContext);
+ Log.d(ID_TAG, "定位使用当前线程: " + Thread.currentThread().getId());
+ }
+
+ //设置定位回调监听
+ mLocationClient.setLocationListener(mLocationListener);
+
+ InitOptions();
+
+ mLocationClient.setLocationOption(mLocationOption);
+
+ //V6.4.9版本起 client设置逆地理回调
+/* mLocationClient.setReGeoLocationCallback(new IReGeoLocationCallback()
+ {
+ @Override
+ public void onReGeoLocation(AMapLocation reGeoLocation)
+ {
+ Log.i(ID_TAG, reGeoLocation.toStr());
+ }
+ });*/
+
+ Log.i(ID_TAG, "定位初始化完成");
+ return true;
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ mLocationClient = null;
+ return false;
+ }
+ }
+
+ public void SetThread(Looper looper)
+ {
+ CheckInitialization(false);
+ m_customLooper = looper;
+ }
+
+ public void SetThread(HandlerThread thread)
+ {
+ CheckInitialization(false);
+ m_customLooper = thread.getLooper();
+ }
+
+ public boolean IsRunning()
+ {
+ return IsInitialized() && mLocationClient.isStarted();
+ }
+
+ public void Start()
+ {
+ Run(-1);
+ }
+
+ public void Stop()
+ {
+ CheckInitialization(true);
+
+ //设置场景模式后最好调用一次stop,再调用start以保证场景模式生效
+ mLocationClient.stopLocation();
+
+ Log.i(ID_TAG, "定位停止");
+ }
+
+ public void Shutdown()
+ {
+ if(!IsInitialized())
+ {
+ Log.e(ID_TAG, "请先初始化");
+ return;
+ }
+
+ Stop();
+
+ mLocationClient.onDestroy();
+ mLocationClient = null;
+ if(null != m_thread)
+ {
+ m_thread.quit();
+ m_thread = null;
+ Log.d(ID_TAG, "内建线程结束");
+ }
+
+ Log.i(ID_TAG, "定位销毁");
+ }
+
+ public void PreInit()
+ {
+ if(m_isPreInit)
+ return;
+ Context applicationContext = m_context.getApplicationContext();
+ /** 设置包含隐私政策,并展示用户授权弹窗 必须在AmapLocationClient实例化之前调用
+ *
+ * @param context
+ * @param isContains: 是隐私权政策是否包含高德开平隐私权政策 true是包含
+ * @param isShow: 隐私权政策是否弹窗展示告知用户 true是展示
+ * @since 5.6.0
+ */
+ AMapLocationClient.updatePrivacyShow(applicationContext, true, true);
+ /**
+ * 设置是否同意用户授权政策 必须在AmapLocationClient实例化之前调用
+ * @param context
+ * @param isAgree:隐私权政策是否取得用户同意 true是用户同意
+ *
+ * @since 5.6.0
+ */
+ AMapLocationClient.updatePrivacyAgree(applicationContext, true);
+ Log.i(ID_TAG, "定位预初始化");
+ m_isPreInit = true;
+ }
+
+ public boolean IsInitialized()
+ {
+ return null != mLocationClient;
+ }
+
+ private void CheckInitialization(boolean inited)
+ {
+ if(IsInitialized() != inited)
+ {
+ String msg = inited ? "定位未初始化" : "定位已初始化";
+ Log.e(ID_TAG, msg);
+ throw new RuntimeException(msg);
+ }
+ }
+
+ private boolean HasFlag(int f)
+ {
+ return (m_flag & f) != 0;
+ }
+
+ public void SetOption(int type, Object value)
+ {
+ CheckInitialization(false);
+ m_options.put(type, value);
+ }
+
+ @SuppressLint("unchecked")
+ private T GetOption(int type, T def)
+ {
+ Object o = m_options.get(type);
+ if(null == o)
+ return def;
+ return (T) o;
+ }
+
+ private void InitOptions()
+ {
+ //初始化AMapLocationClientOption对象
+ mLocationOption = new AMapLocationClientOption();
+
+ /**
+ * 设置定位场景,目前支持三种场景(签到、出行、运动,默认无场景)
+ */
+ if(HasFlag(FLAG_PURPOSE_SIGNIN))
+ mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.SignIn);
+ else if(HasFlag(FLAG_PURPOSE_TRANSPORT))
+ mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Transport);
+ else if(HasFlag(FLAG_PURPOSE_SPORT))
+ mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);
+
+ //设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。
+ if(HasFlag(FLAG_MODE_HIGH))
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ //设置定位模式为AMapLocationMode.Battery_Saving,低功耗模式。
+ else if(HasFlag(FLAG_MODE_SAVING))
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Battery_Saving);
+ //设置定位模式为AMapLocationMode.Device_Sensors,仅设备模式。
+ else if(HasFlag(FLAG_MODE_SENSORS))
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);
+
+ //获取一次定位结果:
+ //该方法默认为false。
+ if(HasFlag(FLAG_ONCE))
+ mLocationOption.setOnceLocation(true);
+
+ //获取最近3s内精度最高的一次定位结果:
+ //设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。
+ if(HasFlag(FLAG_ONCE_LATEST))
+ mLocationOption.setOnceLocationLatest(true);
+
+ //设置定位间隔,单位毫秒,默认为2000ms,最低1000ms。
+ int interval = GetOption(OPTION_INTERVAL, DEFAULT_INTERVAL);
+ mLocationOption.setInterval(interval);
+
+ //设置是否返回地址信息(默认返回地址信息)
+ if(HasFlag(FLAG_NO_NEED_ADDRESS))
+ mLocationOption.setNeedAddress(false);
+
+ //设置是否允许模拟位置,默认为true,允许模拟位置
+ if(HasFlag(FLAG_MOCK_DISABLE))
+ mLocationOption.setMockEnable(false);
+
+ //单位是毫秒,默认30000毫秒,建议超时时间不要低于8000毫秒。
+ int timeout = GetOption(OPTION_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT);
+ mLocationOption.setHttpTimeOut(timeout);
+
+ //关闭缓存机制
+ if(HasFlag(FLAG_CACHE_DISABLE))
+ mLocationOption.setLocationCacheEnable(false);
+ }
+
+ private void Run(int num)
+ {
+ CheckInitialization(true);
+
+ m_readOnce.set(num);
+
+ //设置场景模式后最好调用一次stop,再调用start以保证场景模式生效
+ mLocationClient.stopLocation();
+ mLocationClient.startLocation();
+
+ Log.i(ID_TAG, "定位开始");
+ }
+
+ /*
+ 在调用线程中执行
+ */
+ @Override
+ public void onLocationChanged(AMapLocation amapLocation)
+ {
+ Log.d(ID_TAG, "onLocationChanged线程: " + Thread.currentThread().getId());
+ System.err.println(amapLocation);
+ SetLastLocation(amapLocation);
+ if(null != amapLocation)
+ {
+ if(m_readOnce.get() > 0)
+ {
+ m_readOnce.decrementAndGet();
+ if(m_readOnce.get() == 0)
+ {
+ synchronized(m_lock)
+ {
+ m_lock.notifyAll();
+ }
+ }
+ }
+ }
+ }
+
+ private double[] Convert(double longitude, double latitude)
+ {
+ try
+ {
+ //初始化坐标转换类
+ CoordinateConverter converter = new CoordinateConverter(m_context.getApplicationContext());
+ converter.from(CoordinateConverter.CoordType.GPS);
+ //设置需要转换的坐标
+ converter.coord(new DPoint(latitude, longitude));
+ //转换成高德坐标
+ DPoint destPoint = converter.convert();
+ double dx = destPoint.getLongitude() - longitude;
+ double dy = destPoint.getLatitude() - latitude;
+ longitude = longitude - dx;
+ latitude = latitude - dy;
+ return new double[]{
+ longitude, latitude
+ };
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void SetLastLocation(AMapLocation amapLocation)
+ {
+ if(null != amapLocation)
+ {
+ double longitude = amapLocation.getLongitude();
+ double latitude = amapLocation.getLatitude();
+ double[] coord = Convert(longitude, latitude);
+ longitude = coord[0];
+ latitude = coord[1];
+ m_lastLocation = new NSLocationInfo(amapLocation.getProvider(), longitude, latitude);
+ }
+ else
+ {
+ m_lastLocation = null;
+ }
+ }
+
+ /**
+ * 同步调用, 会锁住线程
+ */
+ public NSLocationInfo Read(int count)
+ {
+ CheckInitialization(true);
+/* if(IsRunning())
+ {
+ throw new RuntimeException("请先停止定位");
+ }*/
+
+ Log.d(ID_TAG, "定位线程: " + Thread.currentThread().getId());
+ synchronized(m_lock)
+ {
+ try
+ {
+ CleanLastLocation();
+ Run(count);
+ m_lock.wait();
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ Stop();
+ }
+ }
+ return m_lastLocation;
+ }
+
+ public NSLocationInfo GetLastLocation()
+ {
+ return m_lastLocation;
+ }
+
+ public void CleanLastLocation()
+ {
+ m_lastLocation = null;
+ }
+}