Autore | SHA1 | Messaggio | Data |
---|---|---|---|
|
a7ad0aef3e | 设置页面 | 1 settimana fa |
|
41e0d7cc0f | 首页 | 1 settimana fa |
|
99654f3554 | 系统实时定位 | 1 settimana fa |
|
25ca6eb217 | 高德定位 | 1 settimana fa |
|
77b717cb63 | 定位 | 4 settimane fa |
@@ -45,4 +45,9 @@ | |||||
* 路径: `/app-keystore.jks` | * 路径: `/app-keystore.jks` | ||||
* 密码: `ns61GK32x%` | * 密码: `ns61GK32x%` | ||||
* Key Alias: `nsgk_rural_web` | * Key Alias: `nsgk_rural_web` | ||||
* Key Password: `ns61GK32x%` | |||||
* Key Password: `ns61GK32x%` | |||||
> 示例 | |||||
```shell | |||||
.\打包-正式.bat http://mxixiaxian.nongshen.net/sunVillage_info/login_code_new 阳光三资 ygcw | |||||
``` |
@@ -22,6 +22,9 @@ android { | |||||
// App启动页厂商名称 | // App启动页厂商名称 | ||||
buildConfigField "String", "APP_VENDOR", "\"${project.properties.appVendor}\"" | buildConfigField "String", "APP_VENDOR", "\"${project.properties.appVendor}\"" | ||||
// App图标 | |||||
buildConfigField "String", "APP_ICON", "\"${project.properties.appIconKey}\"" | |||||
// AndroidManifest.xml占位符 | // AndroidManifest.xml占位符 | ||||
manifestPlaceholders = [ | manifestPlaceholders = [ | ||||
// App名称 | // App名称 | ||||
@@ -52,12 +55,14 @@ android { | |||||
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"") | resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"") | ||||
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"") | resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"") | ||||
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"") | resValue("string", "app_vendor", "\"${project.properties.appVendor}\"") | ||||
resValue("string", "app_name2", "\"${project.properties.appName}\"") | |||||
} | } | ||||
debug { | debug { | ||||
signingConfig signingConfigs.release | signingConfig signingConfigs.release | ||||
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"") | resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"") | ||||
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"") | resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"") | ||||
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"") | resValue("string", "app_vendor", "\"${project.properties.appVendor}\"") | ||||
resValue("string", "app_name2", "\"${project.properties.appName}\"") | |||||
} | } | ||||
} | } | ||||
compileOptions { | compileOptions { | ||||
@@ -78,4 +83,9 @@ dependencies { | |||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' | 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-core:v4.1.9-androidx' // (必选) | ||||
implementation 'com.github.Justson.AgentWeb:agentweb-filechooser:v4.1.9-androidx' | implementation 'com.github.Justson.AgentWeb:agentweb-filechooser:v4.1.9-androidx' | ||||
} | |||||
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' | |||||
} |
@@ -15,6 +15,16 @@ | |||||
<!--AgentWeb 是默认允许定位的 ,如果你需要该功能 , 请在你的 AndroidManifest 文件里面加入如下权限 。--> | <!--AgentWeb 是默认允许定位的 ,如果你需要该功能 , 请在你的 AndroidManifest 文件里面加入如下权限 。--> | ||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||||
<!--用于获取wifi的获取权限,wifi信息会用来进行网络定位--> | |||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> | |||||
<!--用于申请调用A-GPS模块--> | |||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission> | |||||
<!--如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限--> | |||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> | |||||
<!--如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明--> | |||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> | |||||
<!-- android:icon 、 android:roundIcon 是APP显示图标--> | <!-- android:icon 、 android:roundIcon 是APP显示图标--> | ||||
<!-- | <!-- | ||||
APP_NAME占位符: | APP_NAME占位符: | ||||
@@ -36,6 +46,10 @@ | |||||
android:supportsRtl="true" | android:supportsRtl="true" | ||||
android:usesCleartextTraffic="true" | android:usesCleartextTraffic="true" | ||||
android:theme="@style/Theme.Nsgk_rural_web"> | android:theme="@style/Theme.Nsgk_rural_web"> | ||||
<meta-data | |||||
android:name="com.amap.api.v2.apikey" | |||||
android:value="7e6911ad842a6a6f492d27efdbd42f30"/> | |||||
<activity | <activity | ||||
android:name=".WelcomeActivity" | android:name=".WelcomeActivity" | ||||
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode" | android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode" | ||||
@@ -46,9 +60,16 @@ | |||||
<category android:name="android.intent.category.LAUNCHER" /> | <category android:name="android.intent.category.LAUNCHER" /> | ||||
</intent-filter> | </intent-filter> | ||||
<meta-data android:name="android.app.shortcuts" | |||||
android:resource="@xml/shortcuts" /> | |||||
</activity> | </activity> | ||||
<activity | <activity | ||||
android:name=".FullscreenActivity" | android:name=".FullscreenActivity" | ||||
android:label="${APP_NAME}" | |||||
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode" /> | |||||
<activity | |||||
android:name=".SettingsActivity" | |||||
android:label="设置" | |||||
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode" /> | android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode" /> | ||||
<!--开机广播接受者 | <!--开机广播接受者 | ||||
@@ -58,6 +79,7 @@ | |||||
</intent-filter> | </intent-filter> | ||||
</receiver> | </receiver> | ||||
--> | --> | ||||
<service android:name="com.amap.api.location.APSService"></service> | |||||
</application> | </application> | ||||
</manifest> | </manifest> |
@@ -1,34 +0,0 @@ | |||||
package com.nsgk.ruralWeb; | |||||
public class Constants | |||||
{ | |||||
/** | |||||
* App首页链接地址 | |||||
* 可以在 /gradle.properties 里配置 appHomeUrl=链接地址, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappHomeUrl="链接地址", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppHomeUrl() | |||||
{ | |||||
return BuildConfig.APP_HOME_URL; | |||||
} | |||||
/** | |||||
* App启动页版权文本 | |||||
* 可以在 /gradle.properties 里配置 appCopyright=文本, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappCopyright="文本", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppCopyright() | |||||
{ | |||||
return BuildConfig.APP_COMPYRIGHT; | |||||
} | |||||
/** | |||||
* App启动页厂商文本 | |||||
* 可以在 /gradle.properties 里配置 appVendor=文本, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappVendor="文本", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppVendor() | |||||
{ | |||||
return BuildConfig.APP_VENDOR; | |||||
} | |||||
} |
@@ -1,178 +0,0 @@ | |||||
package com.nsgk.ruralWeb; | |||||
import android.app.Activity; | |||||
import android.location.LocationManager; | |||||
import android.Manifest; | |||||
import android.content.Context; | |||||
import android.content.pm.PackageManager; | |||||
import android.location.Location; | |||||
import android.location.LocationManager; | |||||
import android.location.LocationProvider; | |||||
import android.os.Handler; | |||||
import android.util.Log; | |||||
import android.webkit.JavascriptInterface; | |||||
import android.webkit.ValueCallback; | |||||
import android.webkit.WebView; | |||||
import android.widget.Toast; | |||||
import androidx.core.app.ActivityCompat; | |||||
import com.nsgk.ruralWeb.utils.ContextUtils; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
public class EnvWindowObject | |||||
{ | |||||
private static final String ID_TAG = EnvWindowObject.class.getName(); | |||||
private static final String FUSED_PROVIDER = "fused"; | |||||
private final Handler m_handler; | |||||
private final Context m_context; | |||||
private final WebView m_webView; | |||||
// 最近一次获取到的定位坐标, 如果获取定位失败则返回此值. 可能不需要, 因为PASSIVE_PROVIDER就为最近的定位 | |||||
private String lastLocation = null; | |||||
public EnvWindowObject(Context context, Handler handler, WebView webView) | |||||
{ | |||||
m_context = context; | |||||
m_handler = handler; | |||||
m_webView = webView; | |||||
} | |||||
@JavascriptInterface | |||||
public void Toast(final String message) | |||||
{ | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() | |||||
{ | |||||
Toast.makeText(m_context, message, Toast.LENGTH_LONG).show(); | |||||
} | |||||
}); | |||||
} | |||||
@JavascriptInterface | |||||
public void Log(final String message) | |||||
{ | |||||
Log.i(ID_TAG, message); | |||||
} | |||||
@JavascriptInterface | |||||
public String GetLocation(final String type) | |||||
{ | |||||
try | |||||
{ | |||||
if(ActivityCompat.checkSelfPermission(m_context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(m_context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) | |||||
{ | |||||
Activity activity = (Activity) m_context; | |||||
if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) && activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)) // do not ask | |||||
{ | |||||
Toast.makeText(m_context, "请先允许定位服务", Toast.LENGTH_LONG).show(); | |||||
ContextUtils.RequestLocationPermission(activity, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
} | |||||
else | |||||
{ | |||||
activity.requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
} | |||||
return lastLocation; | |||||
} | |||||
LocationManager lm = (LocationManager) m_context.getSystemService(Context.LOCATION_SERVICE); | |||||
List<String> allProviders = lm.getAllProviders(); | |||||
Map<String, LocationInfo> map = new HashMap<>(); | |||||
for(String provider : allProviders) | |||||
{ | |||||
Location lastKnownLocation = lm.getLastKnownLocation(provider); | |||||
if(null == lastKnownLocation) | |||||
continue; | |||||
LocationInfo loc = new LocationInfo(provider, lastKnownLocation.getLongitude(), lastKnownLocation.getLatitude()); | |||||
map.put(provider, loc); | |||||
} | |||||
LocationInfo loc = null; | |||||
if(null != type && !type.isEmpty() && map.containsKey(type)) | |||||
loc = map.get(type); | |||||
else if(map.containsKey(LocationManager.NETWORK_PROVIDER)) | |||||
loc = map.get(LocationManager.NETWORK_PROVIDER); | |||||
else if(map.containsKey(LocationManager.GPS_PROVIDER)) | |||||
loc = map.get(LocationManager.GPS_PROVIDER); | |||||
else if(map.containsKey(LocationManager.PASSIVE_PROVIDER)) | |||||
loc = map.get(LocationManager.GPS_PROVIDER); | |||||
else if(map.containsKey(FUSED_PROVIDER)) | |||||
loc = map.get(FUSED_PROVIDER); | |||||
if(null != loc) | |||||
lastLocation = loc.longitude + "," + loc.latitude; | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return lastLocation; | |||||
} | |||||
protected void RunOnUIThread(Runnable runnable) | |||||
{ | |||||
if(null != m_handler) | |||||
m_handler.post(runnable); | |||||
else if(m_context instanceof Activity) | |||||
((Activity)m_context).runOnUiThread(runnable); | |||||
else | |||||
{ | |||||
Log.e(ID_TAG, "无法在UI线程中执行"); | |||||
} | |||||
} | |||||
protected void CallJSFunc(final String func, Object...args) | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
for(int i = 0; i < args.length; i++) | |||||
{ | |||||
sb.append("'").append(args[i].toString()).append("'"); | |||||
if(i < args.length - 1) | |||||
sb.append(", "); | |||||
} | |||||
final String arg = sb.toString(); | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() | |||||
{ | |||||
String script = "javascript:typeof(" + func + ") == 'function' && " + func + "(" + arg + ");"; | |||||
Log.e(ID_TAG, String.format("调用js函数: 函数(%s), 参数(%s)", func, arg)); | |||||
m_webView.evaluateJavascript(script, new ValueCallback<String>() { | |||||
@Override | |||||
public void onReceiveValue(String value) { | |||||
Log.e(ID_TAG, String.format("js函数 %s 返回: %s", func, value)); | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
protected void CallJSFunc_beforeAndroid4_4(final String func, Object...args) | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
for(int i = 0; i < args.length; i++) | |||||
{ | |||||
sb.append("'").append(args[i].toString()).append("'"); | |||||
if(i < args.length - 1) | |||||
sb.append(", "); | |||||
} | |||||
final String arg = sb.toString(); | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
Log.e(ID_TAG, String.format("调用js函数: 函数(%s), 参数(%s)", func, arg)); | |||||
String script = "javascript:typeof(" + func + ") == 'function' && " + func + "(" + arg + ");"; | |||||
Log.e(ID_TAG, script); | |||||
m_webView.loadUrl(script, null); | |||||
} | |||||
}); | |||||
} | |||||
} |
@@ -3,35 +3,52 @@ package com.nsgk.ruralWeb; | |||||
import android.annotation.SuppressLint; | import android.annotation.SuppressLint; | ||||
import android.content.Intent; | import android.content.Intent; | ||||
import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||
import android.graphics.Bitmap; | |||||
import android.os.Bundle; | import android.os.Bundle; | ||||
import android.os.Handler; | import android.os.Handler; | ||||
import android.util.Log; | import android.util.Log; | ||||
import android.view.KeyEvent; | import android.view.KeyEvent; | ||||
import android.view.ViewGroup; | import android.view.ViewGroup; | ||||
import android.webkit.WebBackForwardList; | |||||
import android.webkit.WebHistoryItem; | |||||
import android.webkit.WebView; | import android.webkit.WebView; | ||||
import android.widget.LinearLayout; | import android.widget.LinearLayout; | ||||
import android.widget.RelativeLayout; | import android.widget.RelativeLayout; | ||||
import android.widget.Toast; | import android.widget.Toast; | ||||
import com.just.agentweb.AgentWeb; | import com.just.agentweb.AgentWeb; | ||||
import com.just.agentweb.AgentWebConfig; | |||||
import com.just.agentweb.DefaultWebClient; | import com.just.agentweb.DefaultWebClient; | ||||
import com.nsgk.ruralWeb.utils.ContextUtils; | |||||
import com.just.agentweb.WebChromeClient; | |||||
import com.just.agentweb.WebViewClient; | |||||
import com.nsgk.ruralWeb.sys.NSConstants; | |||||
import com.nsgk.ruralWeb.sys.NSPreference; | |||||
import com.nsgk.ruralWeb.utils.NSContextUtils; | |||||
import com.nsgk.ruralWeb.utils.NSStr; | |||||
import com.nsgk.ruralWeb.web.NSEnvWindowObject; | |||||
import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||
import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||
import cn.hutool.core.util.StrUtil; | |||||
/** | /** | ||||
* An example full-screen activity that shows and hides the system UI (i.e. | * An example full-screen activity that shows and hides the system UI (i.e. | ||||
* status bar and navigation/system bar) with user interaction. | * status bar and navigation/system bar) with user interaction. | ||||
*/ | */ | ||||
public class FullscreenActivity extends AppCompatActivity { | public class FullscreenActivity extends AppCompatActivity { | ||||
private static final String ID_TAG = FullscreenActivity.class.getName(); | |||||
public static final int PERMISSION_LOCATION_REQUEST_CODE = 0x1001; | public static final int PERMISSION_LOCATION_REQUEST_CODE = 0x1001; | ||||
private AgentWeb mAgentWeb; | private AgentWeb mAgentWeb; | ||||
private Preference preference; | |||||
private NSPreference preference; | |||||
private NSEnvWindowObject envWindowObject; | |||||
private String m_lastUrl; | |||||
private String m_mainUrl; | |||||
@SuppressLint("SetJavaScriptEnabled") | @SuppressLint("SetJavaScriptEnabled") | ||||
@Override | @Override | ||||
@@ -41,24 +58,34 @@ public class FullscreenActivity extends AppCompatActivity { | |||||
setContentView(R.layout.activity_fullscreen); | setContentView(R.layout.activity_fullscreen); | ||||
String appHomeUrl = GetHomeUrl(); | String appHomeUrl = GetHomeUrl(); | ||||
//appHomeUrl = "http://192.168.0.250:85/sunVillage_info/login"; | //appHomeUrl = "http://192.168.0.250:85/sunVillage_info/login"; | ||||
Log.i("NSGK", "App home url: " + appHomeUrl); | |||||
Log.i(ID_TAG, "App home url: " + appHomeUrl); | |||||
// init(); | // init(); | ||||
mAgentWeb = AgentWeb.with(this)// | |||||
.setAgentWebParent((RelativeLayout) findViewById(R.id.ll), -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))//传入AgentWeb的父控件。 | |||||
AgentWeb.PreAgentWeb builder = AgentWeb.with(this)// | |||||
.setAgentWebParent(findViewById(R.id.ll), -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))//传入AgentWeb的父控件。 | |||||
.useDefaultIndicator(-1, 3)//设置进度条颜色与高度,-1为默认值,高度为2,单位为dp。 | .useDefaultIndicator(-1, 3)//设置进度条颜色与高度,-1为默认值,高度为2,单位为dp。 | ||||
/* .setWebChromeClient(new WebChromeClient() { | |||||
.setWebChromeClient(new WebChromeClient() { | |||||
@Override | @Override | ||||
public void onProgressChanged(WebView view, int newProgress) | public void onProgressChanged(WebView view, int newProgress) | ||||
{ | { | ||||
super.onProgressChanged(view, newProgress); | super.onProgressChanged(view, newProgress); | ||||
if(newProgress == 100) | |||||
if(newProgress != 100) | |||||
return; | |||||
String url = view.getUrl(); | |||||
if(IsInDefaultPage()) | |||||
{ | |||||
m_mainUrl = url; | |||||
DumpMainUrl(url); | |||||
Log.i(ID_TAG, "设定主页地址: " + m_mainUrl); | |||||
} | |||||
else if(IsDefaultPage(url)) | |||||
{ | { | ||||
GetPreference().SetString(Preference.LAST_ACCESS_URL, view.getUrl()); | |||||
m_mainUrl = null; | |||||
Log.i(ID_TAG, "清空主页地址"); | |||||
} | } | ||||
m_lastUrl = url; | |||||
} | } | ||||
})*/ | |||||
.setWebViewClient(new com.just.agentweb.WebViewClient() { | |||||
}) | |||||
.setWebViewClient(new WebViewClient() { | |||||
})//WebViewClient , 与 WebView 使用一致 ,但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。 | })//WebViewClient , 与 WebView 使用一致 ,但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。 | ||||
.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) //严格模式 Android 4.2.2 以下会放弃注入对象 ,使用AgentWebView没影响。 | .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) //严格模式 Android 4.2.2 以下会放弃注入对象 ,使用AgentWebView没影响。 | ||||
.setMainFrameErrorView(R.layout.agentweb_error_page, -1) //参数1是错误显示的布局,参数2点击刷新控件ID -1表示点击整个布局都刷新, AgentWeb 3.0.0 加入。 | .setMainFrameErrorView(R.layout.agentweb_error_page, -1) //参数1是错误显示的布局,参数2点击刷新控件ID -1表示点击整个布局都刷新, AgentWeb 3.0.0 加入。 | ||||
@@ -66,10 +93,32 @@ public class FullscreenActivity extends AppCompatActivity { | |||||
.interceptUnkownUrl() //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。 | .interceptUnkownUrl() //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。 | ||||
.createAgentWeb()//创建AgentWeb。 | .createAgentWeb()//创建AgentWeb。 | ||||
.ready()//设置 WebSettings。 | .ready()//设置 WebSettings。 | ||||
.go(appHomeUrl); //WebView载入该url地址的页面并显示。 | |||||
; | |||||
boolean openDef = IsDefaultPage(appHomeUrl); | |||||
if(!openDef) | |||||
{ | |||||
Log.i(ID_TAG, "载入主页: " + appHomeUrl); | |||||
RestoreCookie(); // 重载cookie | |||||
m_mainUrl = appHomeUrl; | |||||
} | |||||
else | |||||
{ | |||||
Log.i(ID_TAG, "载入登录页"); | |||||
GetPreference().Remove(NSPreference.COOKIES); // Remove cookies | |||||
} | |||||
mAgentWeb = builder.go(appHomeUrl); //WebView载入该url地址的页面并显示。 | |||||
int fontScale = GetPreference().GetInt(NSPreference.FONT_SCALE); | |||||
Log.i(ID_TAG, "全局字体缩放: " + fontScale + "%"); | |||||
if(fontScale > 0 && fontScale != 100) | |||||
GetWebView().getSettings().setTextZoom(fontScale); | |||||
// 注入宿主对象 | // 注入宿主对象 | ||||
mAgentWeb.getJsInterfaceHolder().addJavaObject("_Native_object", new EnvWindowObject(this, new Handler(), mAgentWeb.getWebCreator().getWebView())); | |||||
envWindowObject = new NSEnvWindowObject(this, new Handler(), GetWebView()); | |||||
mAgentWeb.getJsInterfaceHolder().addJavaObject("_Native_object", envWindowObject); | |||||
/* 上边url是各个APP项目的入口地址 | /* 上边url是各个APP项目的入口地址 | ||||
事项审批 http://116.255.223.226:82/yinnongLogin 图标 ic_launcher_sxsp 或者 ic_launcher_yhzl | 事项审批 http://116.255.223.226:82/yinnongLogin 图标 ic_launcher_sxsp 或者 ic_launcher_yhzl | ||||
@@ -85,10 +134,15 @@ public class FullscreenActivity extends AppCompatActivity { | |||||
一体机 http://47.98.113.57:81 图标 ic_launcher_njytj | 一体机 http://47.98.113.57:81 图标 ic_launcher_njytj | ||||
*/ | */ | ||||
Log.d(ID_TAG, "UI线程: " + Thread.currentThread().getId()); | |||||
} | } | ||||
@Override | @Override | ||||
protected void onPause() { | protected void onPause() { | ||||
if(IsRecordUrl()) | |||||
{ | |||||
DumpCookie(); | |||||
} | |||||
mAgentWeb.getWebLifeCycle().onPause(); | mAgentWeb.getWebLifeCycle().onPause(); | ||||
super.onPause(); | super.onPause(); | ||||
} | } | ||||
@@ -101,7 +155,14 @@ public class FullscreenActivity extends AppCompatActivity { | |||||
@Override | @Override | ||||
protected void onDestroy() { | protected void onDestroy() { | ||||
if(!IsRecordUrl()) | |||||
{ | |||||
Log.d(ID_TAG, "Remove url and cookie record"); | |||||
GetPreference().Remove(NSPreference.COOKIES); | |||||
GetPreference().Remove(NSPreference.LAST_ACCESS_URL); | |||||
} | |||||
mAgentWeb.getWebLifeCycle().onDestroy(); | mAgentWeb.getWebLifeCycle().onDestroy(); | ||||
envWindowObject.OnDestroy(); | |||||
super.onDestroy(); | super.onDestroy(); | ||||
} | } | ||||
@@ -131,38 +192,102 @@ public class FullscreenActivity extends AppCompatActivity { | |||||
for(int i = 0; i < permissions.length; i++) | for(int i = 0; i < permissions.length; i++) | ||||
{ | { | ||||
boolean b = grantResults[i] == PackageManager.PERMISSION_GRANTED; | boolean b = grantResults[i] == PackageManager.PERMISSION_GRANTED; | ||||
Log.i("NSGK", String.format("请求权限: %s -> %s", permissions[i], b ? "通过" : "拒绝")); | |||||
Log.i(ID_TAG, String.format("请求权限: %s -> %s", permissions[i], b ? "通过" : "拒绝")); | |||||
if(b) | if(b) | ||||
granted++; | granted++; | ||||
} | } | ||||
if (granted < permissions.length) { | if (granted < permissions.length) { | ||||
Toast.makeText(this, "需要定位权限", Toast.LENGTH_LONG).show(); | Toast.makeText(this, "需要定位权限", Toast.LENGTH_LONG).show(); | ||||
ContextUtils.RequestLocationPermission(this, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
NSContextUtils.RequestLocationPermission(this, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private Preference GetPreference() | |||||
@Override | |||||
public void onBackPressed() | |||||
{ | |||||
WebView webView = GetWebView(); | |||||
if (null != webView && !webView.canGoBack() && !IsInDefaultPage()) { | |||||
webView.loadUrl(NSConstants.AppHomeUrl()); | |||||
webView.clearHistory(); | |||||
//History(); | |||||
return; | |||||
} | |||||
super.onBackPressed(); | |||||
} | |||||
private NSPreference GetPreference() | |||||
{ | { | ||||
if(null == preference) | if(null == preference) | ||||
preference = new Preference(this); | |||||
preference = new NSPreference(this); | |||||
return preference; | return preference; | ||||
} | } | ||||
private boolean IsRecordUrl() | |||||
{ | |||||
return GetPreference().GetBool(NSPreference.OPEN_LAST_URL); | |||||
} | |||||
private String GetHomeUrl() | private String GetHomeUrl() | ||||
{ | { | ||||
return Constants.AppHomeUrl(); | |||||
// return GetPreference().GetString(GetPreference().LAST_ACCESS_URL, Constants.AppHomeUrl()); | |||||
String url = null; | |||||
if(IsRecordUrl()) | |||||
url = GetPreference().GetString(NSPreference.LAST_ACCESS_URL, NSConstants.AppHomeUrl()); | |||||
if(StrUtil.isBlank(url)) | |||||
url = NSConstants.AppHomeUrl(); | |||||
return url; | |||||
} | } | ||||
private void DumpLastUrl() | |||||
private WebView GetWebView() | |||||
{ | { | ||||
if(null == mAgentWeb) | |||||
return; | |||||
WebView webView = mAgentWeb.getWebCreator().getWebView(); | |||||
if(null == webView) | |||||
return; | |||||
String url = webView.getUrl(); | |||||
GetPreference().SetString(Preference.LAST_ACCESS_URL, url); | |||||
return mAgentWeb.getWebCreator().getWebView(); | |||||
} | |||||
private void DumpMainUrl(String url) | |||||
{ | |||||
if(StrUtil.isNotEmpty(url)) | |||||
GetPreference().SetString(NSPreference.LAST_ACCESS_URL, url); | |||||
else | |||||
GetPreference().Remove(NSPreference.LAST_ACCESS_URL); | |||||
Log.d(ID_TAG, "Dump URL: " + url); | |||||
} | |||||
private void DumpCookie() | |||||
{ | |||||
String url = NSStr.GetUrlPart(NSConstants.AppHomeUrl()); | |||||
String cookies = AgentWebConfig.getCookiesByUrl(url); | |||||
GetPreference().SetString(NSPreference.COOKIES, cookies); | |||||
Log.d(ID_TAG, "Dump cookie: " + "url=" + url + ", cookie=" + cookies); | |||||
} | |||||
private void RestoreCookie() | |||||
{ | |||||
String cookies = GetPreference().GetString(NSPreference.COOKIES); | |||||
if(StrUtil.isNotBlank(cookies)) | |||||
{ | |||||
String url = NSStr.GetUrlPart(NSConstants.AppHomeUrl()); | |||||
AgentWebConfig.syncCookie(url, cookies); | |||||
Log.d(ID_TAG, "Restore cookie: " + "url=" + url + ", cookie=" + cookies); | |||||
} | |||||
} | |||||
private boolean IsInDefaultPage() | |||||
{ | |||||
return IsDefaultPage(m_lastUrl); | |||||
} | |||||
private boolean IsDefaultPage(String url) | |||||
{ | |||||
return NSConstants.AppHomeUrl().equals(url); | |||||
} | |||||
private void History() | |||||
{ | |||||
WebBackForwardList webBackForwardList = GetWebView().copyBackForwardList(); | |||||
for(int i = 0; i < webBackForwardList.getSize(); i++) | |||||
{ | |||||
WebHistoryItem item = webBackForwardList.getItemAtIndex(i); | |||||
Log.d(ID_TAG, StrUtil.format("{}: {} {}", i, item.getTitle(), item.getUrl())); | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,25 +0,0 @@ | |||||
package com.nsgk.ruralWeb; | |||||
public final class LocationInfo | |||||
{ | |||||
public final String provider; | |||||
public final double longitude; | |||||
public final double latitude; | |||||
public LocationInfo(String provider, double longitude, double latitude) | |||||
{ | |||||
this.provider = provider; | |||||
this.longitude = longitude; | |||||
this.latitude = latitude; | |||||
} | |||||
@Override | |||||
public String toString() | |||||
{ | |||||
return "LocationInfo{" + | |||||
"provider='" + provider + '\'' + | |||||
", latitude=" + latitude + | |||||
", longitude=" + longitude + | |||||
'}'; | |||||
} | |||||
} |
@@ -1,37 +0,0 @@ | |||||
package com.nsgk.ruralWeb; | |||||
import android.content.Context; | |||||
import android.content.SharedPreferences; | |||||
import android.preference.PreferenceManager; | |||||
public final class Preference | |||||
{ | |||||
public static final String LAST_ACCESS_URL = "last_access_url"; | |||||
private final Context context; | |||||
public Preference(Context context) | |||||
{ | |||||
this.context = context; | |||||
} | |||||
private SharedPreferences Read() | |||||
{ | |||||
return PreferenceManager.getDefaultSharedPreferences(context); | |||||
} | |||||
private SharedPreferences.Editor Write() | |||||
{ | |||||
return PreferenceManager.getDefaultSharedPreferences(context).edit(); | |||||
} | |||||
public String GetString(String name, String...def) | |||||
{ | |||||
return Read().getString(name, null != def && def.length > 0 ? def[0] : null); | |||||
} | |||||
public void SetString(String name, String val) | |||||
{ | |||||
Write().putString(name, val).commit(); | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
package com.nsgk.ruralWeb; | |||||
import android.os.Bundle; | |||||
import androidx.appcompat.app.AppCompatActivity; | |||||
import com.nsgk.ruralWeb.ui.SettingsFragment; | |||||
public class SettingsActivity extends AppCompatActivity | |||||
{ | |||||
private static final String ID_TAG = SettingsActivity.class.getName(); | |||||
@Override | |||||
protected void onCreate(Bundle savedInstanceState) | |||||
{ | |||||
super.onCreate(savedInstanceState); | |||||
setTitle(R.string.settings_name); | |||||
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); | |||||
} | |||||
@Override | |||||
protected void onDestroy() { | |||||
super.onDestroy(); | |||||
} | |||||
} |
@@ -14,6 +14,8 @@ import android.widget.TextView; | |||||
import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||
import com.nsgk.ruralWeb.sys.NSConstants; | |||||
public class WelcomeActivity extends AppCompatActivity { | public class WelcomeActivity extends AppCompatActivity { | ||||
private ImageView imageView; | private ImageView imageView; | ||||
private LinearLayout llcenter; | private LinearLayout llcenter; | ||||
@@ -55,14 +57,14 @@ public class WelcomeActivity extends AppCompatActivity { | |||||
llcenter = findViewById(R.id.llcenter); | llcenter = findViewById(R.id.llcenter); | ||||
// 动态设置启动页版权信息 | // 动态设置启动页版权信息 | ||||
String text = GetTrimString(Constants.AppCopyright()); | |||||
String text = GetTrimString(NSConstants.AppCopyright()); | |||||
if(null != text) | if(null != text) | ||||
{ | { | ||||
TextView copyrightText = findViewById(R.id.welcome_text_copyright); | TextView copyrightText = findViewById(R.id.welcome_text_copyright); | ||||
copyrightText.setText(text); | copyrightText.setText(text); | ||||
} | } | ||||
// 动态设置启动页厂商名称 | // 动态设置启动页厂商名称 | ||||
text = GetTrimString(Constants.AppVendor()); | |||||
text = GetTrimString(NSConstants.AppVendor()); | |||||
if(null != text) | if(null != text) | ||||
{ | { | ||||
TextView vendorText = findViewById(R.id.welcome_text_vendor); | TextView vendorText = findViewById(R.id.welcome_text_vendor); | ||||
@@ -0,0 +1,22 @@ | |||||
package com.nsgk.ruralWeb.enums; | |||||
public final class NSEnums | |||||
{ | |||||
public final class LocationScheme | |||||
{ | |||||
public static final String SYSTEM = "system"; | |||||
public static final String GAODE = "gaode"; | |||||
public static final String REALTIME = "realtime"; | |||||
private LocationScheme() {} | |||||
} | |||||
public final class LocationProvider | |||||
{ | |||||
public static final String AUTO = "auto"; | |||||
private LocationProvider() {} | |||||
} | |||||
private NSEnums() {} | |||||
} |
@@ -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<Integer, Object> 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(); | |||||
/** 设置包含隐私政策,并展示用户授权弹窗 <b>必须在AmapLocationClient实例化之前调用</b> | |||||
* | |||||
* @param context | |||||
* @param isContains: 是隐私权政策是否包含高德开平隐私权政策 true是包含 | |||||
* @param isShow: 隐私权政策是否弹窗展示告知用户 true是展示 | |||||
* @since 5.6.0 | |||||
*/ | |||||
AMapLocationClient.updatePrivacyShow(applicationContext, true, true); | |||||
/** | |||||
* 设置是否同意用户授权政策 <b>必须在AmapLocationClient实例化之前调用</b> | |||||
* @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> 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; | |||||
} | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package com.nsgk.ruralWeb.location; | |||||
public final class NSLocationInfo | |||||
{ | |||||
public final String provider; | |||||
public final double longitude; | |||||
public final double latitude; | |||||
public NSLocationInfo(String provider, double longitude, double latitude) | |||||
{ | |||||
this.provider = provider; | |||||
this.longitude = longitude; | |||||
this.latitude = latitude; | |||||
} | |||||
@Override | |||||
public String toString() | |||||
{ | |||||
return "定位信息: " + | |||||
"提供器=" + provider + | |||||
"; 经度=" + longitude + | |||||
"; 纬度=" + latitude | |||||
; | |||||
} | |||||
} |
@@ -0,0 +1,369 @@ | |||||
package com.nsgk.ruralWeb.location; | |||||
import android.annotation.SuppressLint; | |||||
import android.content.Context; | |||||
import android.location.Location; | |||||
import android.location.LocationListener; | |||||
import android.location.LocationManager; | |||||
import android.os.HandlerThread; | |||||
import android.os.Looper; | |||||
import android.util.Log; | |||||
import androidx.annotation.NonNull; | |||||
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.ArrayList; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.concurrent.atomic.AtomicInteger; | |||||
// 主线程调用, 无线程安全 | |||||
public class NSRealtimeLocation implements LocationListener | |||||
{ | |||||
// 定位提供器 | |||||
public static final int FLAG_PROVIDER_NETWORK = 1; // 网络定位 | |||||
public static final int FLAG_PROVIDER_GPS = 1 << 1; // GPS定位 | |||||
// 定位提供器优先级 | |||||
public static final int FLAG_PRIORITY_NETWORK = 1 << 2; // 网络定位优先 | |||||
public static final int FLAG_PRIORITY_GPS = 1 << 3; // GPS定位优先 | |||||
// 线程 | |||||
public static final int FLAG_BUILTIN_THREAD = 1 << 4; // 使用内建线程 | |||||
public static final int FLAG_CUSTOM_THREAD = 1 << 5; // 使用外部线程 | |||||
public static final int FLAG_MAIN_THREAD = 1 << 6; // 使用主线程 | |||||
public static final int DEFAULT_FLAG = FLAG_PROVIDER_NETWORK | FLAG_PROVIDER_GPS | FLAG_PRIORITY_NETWORK; | |||||
public static final int OPTION_MIN_TIME = 1; // 最小定位间隔(毫秒) 默认60000 | |||||
public static final int OPTION_MIN_DISTANCE = 2; // 最小定位距离变化(米) 默认10 | |||||
private static final int DEFAULT_MIN_TIME = 1000; | |||||
private static final int DEFAULT_MIN_DISTANCE = 1; | |||||
private static final String ID_TAG = NSRealtimeLocation.class.getName(); | |||||
private final Object m_lock = new Object(); | |||||
private LocationManager m_locationManager; | |||||
private final Context m_context; | |||||
private int m_flag = DEFAULT_FLAG; | |||||
private NSLocationInfo m_lastLocation = null; | |||||
private HandlerThread m_thread = null; | |||||
private Looper m_customLooper = null; | |||||
private AtomicInteger m_readOnce = new AtomicInteger(-1); | |||||
private Map<Integer, Object> m_options = new HashMap<>(); | |||||
private String m_provider = null; | |||||
private Looper m_looper = null; | |||||
public NSRealtimeLocation(Context context) | |||||
{ | |||||
m_context = context; | |||||
} | |||||
public boolean Init(int flag) | |||||
{ | |||||
if(IsInitialized()) | |||||
{ | |||||
Log.e(ID_TAG, "定位已初始化"); | |||||
return true; | |||||
} | |||||
if(!HasFlag(FLAG_PROVIDER_NETWORK | FLAG_PROVIDER_GPS)) | |||||
{ | |||||
Log.e(ID_TAG, "请设置定位提供器"); | |||||
return false; | |||||
} | |||||
try | |||||
{ | |||||
m_readOnce.set(-1); | |||||
m_flag = flag < 0 ? DEFAULT_FLAG : flag; | |||||
m_locationManager = (LocationManager) m_context.getSystemService(Context.LOCATION_SERVICE); | |||||
//初始化定位 | |||||
if(HasFlag(FLAG_BUILTIN_THREAD)) | |||||
{ | |||||
m_thread = new HandlerThread("系统实时内建定位"); | |||||
m_thread.start(); | |||||
m_looper = m_thread.getLooper(); | |||||
Log.d(ID_TAG, "定位使用内建线程: " + m_thread.getId()); | |||||
} | |||||
else if(HasFlag(FLAG_CUSTOM_THREAD)) | |||||
{ | |||||
if(null == m_customLooper) | |||||
{ | |||||
throw new RuntimeException("请先传入线程Looper"); | |||||
} | |||||
m_looper = m_customLooper; | |||||
Log.d(ID_TAG, "定位使用用户指定线程: " + m_customLooper.getThread().getId()); | |||||
} | |||||
else if(HasFlag(FLAG_MAIN_THREAD)) | |||||
{ | |||||
Looper mainLooper = Looper.getMainLooper(); | |||||
m_looper = mainLooper; | |||||
Log.d(ID_TAG, "定位使用主线程: " + mainLooper.getThread().getId()); | |||||
} | |||||
else | |||||
{ | |||||
m_looper = Looper.myLooper(); | |||||
Log.d(ID_TAG, "定位使用当前线程: " + Thread.currentThread().getId()); | |||||
} | |||||
Log.i(ID_TAG, "定位初始化完成"); | |||||
return true; | |||||
} | |||||
catch(Exception e) | |||||
{ | |||||
e.printStackTrace(); | |||||
m_locationManager = 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() && null != m_provider; | |||||
} | |||||
public void Start() | |||||
{ | |||||
Run(-1); | |||||
} | |||||
public void Stop() | |||||
{ | |||||
CheckInitialization(true); | |||||
m_locationManager.removeUpdates(this); | |||||
m_provider = null; | |||||
Log.i(ID_TAG, "定位停止"); | |||||
} | |||||
public void Shutdown() | |||||
{ | |||||
if(!IsInitialized()) | |||||
{ | |||||
Log.e(ID_TAG, "请先初始化"); | |||||
return; | |||||
} | |||||
Stop(); | |||||
m_locationManager = null; | |||||
if(null != m_thread) | |||||
{ | |||||
m_thread.quit(); | |||||
m_thread = null; | |||||
Log.d(ID_TAG, "内建线程结束"); | |||||
} | |||||
Log.i(ID_TAG, "定位销毁"); | |||||
} | |||||
public boolean IsInitialized() | |||||
{ | |||||
return null != m_locationManager; | |||||
} | |||||
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> T GetOption(int type, T def) | |||||
{ | |||||
Object o = m_options.get(type); | |||||
if(null == o) | |||||
return def; | |||||
return (T) o; | |||||
} | |||||
private void InitOptions() | |||||
{ | |||||
List<String> providers = new ArrayList<>(); | |||||
m_provider = null; | |||||
if(HasFlag(FLAG_PROVIDER_NETWORK)) | |||||
{ | |||||
if(m_locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) | |||||
providers.add(LocationManager.NETWORK_PROVIDER); | |||||
else | |||||
Log.w(ID_TAG, "网络定位提供器未启用"); | |||||
} | |||||
if(HasFlag(FLAG_PROVIDER_GPS)) | |||||
{ | |||||
if(m_locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) | |||||
providers.add(LocationManager.GPS_PROVIDER); | |||||
else | |||||
Log.w(ID_TAG, "GPS定位提供器未启用"); | |||||
} | |||||
if(providers.isEmpty()) | |||||
throw new RuntimeException("无定位提供器"); | |||||
if(HasFlag(FLAG_PRIORITY_NETWORK)) | |||||
{ | |||||
if(providers.contains(LocationManager.NETWORK_PROVIDER)) | |||||
m_provider = LocationManager.NETWORK_PROVIDER; | |||||
else | |||||
m_provider = LocationManager.GPS_PROVIDER; | |||||
} | |||||
else if(HasFlag(FLAG_PRIORITY_GPS)) | |||||
{ | |||||
if(providers.contains(LocationManager.GPS_PROVIDER)) | |||||
m_provider = LocationManager.GPS_PROVIDER; | |||||
else | |||||
m_provider = LocationManager.NETWORK_PROVIDER; | |||||
} | |||||
/* else | |||||
{ | |||||
if(providers.contains(LocationManager.NETWORK_PROVIDER)) | |||||
m_provider = LocationManager.NETWORK_PROVIDER; | |||||
else | |||||
m_provider = LocationManager.GPS_PROVIDER; | |||||
}*/ | |||||
if(null == m_provider) | |||||
throw new RuntimeException("未确定定位提供器"); | |||||
Log.i(ID_TAG, "使用定位器: " + m_provider); | |||||
} | |||||
@SuppressLint("MissingPermission") | |||||
private void Run(int num) | |||||
{ | |||||
CheckInitialization(true); | |||||
Stop(); | |||||
m_readOnce.set(num); | |||||
InitOptions(); | |||||
int minTime = GetOption(OPTION_MIN_TIME, DEFAULT_MIN_TIME); | |||||
int minDistance = GetOption(OPTION_MIN_DISTANCE, DEFAULT_MIN_DISTANCE); | |||||
m_locationManager.requestLocationUpdates(m_provider, minTime, minDistance, this, m_looper); | |||||
Location lastKnownLocation = m_locationManager.getLastKnownLocation(m_provider); | |||||
SetLastLocation(lastKnownLocation); | |||||
Log.i(ID_TAG, "定位开始"); | |||||
} | |||||
private void SetLastLocation(Location location) | |||||
{ | |||||
if(null != location) | |||||
{ | |||||
m_lastLocation = new NSLocationInfo(location.getProvider(), location.getLongitude(), location.getLatitude()); | |||||
} | |||||
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; | |||||
} | |||||
/* | |||||
在调用线程中执行 | |||||
*/ | |||||
@Override | |||||
public void onLocationChanged(@NonNull Location location) | |||||
{ | |||||
Log.d(ID_TAG, "onLocationChanged线程: " + Thread.currentThread().getId()); | |||||
System.err.println(location); | |||||
SetLastLocation(location); | |||||
//if(null != location) | |||||
{ | |||||
if(m_readOnce.get() > 0) | |||||
{ | |||||
m_readOnce.decrementAndGet(); | |||||
if(m_readOnce.get() == 0) | |||||
{ | |||||
synchronized(m_lock) | |||||
{ | |||||
m_lock.notifyAll(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,189 @@ | |||||
package com.nsgk.ruralWeb.location; | |||||
import android.annotation.SuppressLint; | |||||
import android.app.Activity; | |||||
import android.content.Context; | |||||
import android.location.Criteria; | |||||
import android.location.Location; | |||||
import android.location.LocationManager; | |||||
import android.os.Build; | |||||
import android.util.Log; | |||||
import android.widget.Toast; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.concurrent.CompletableFuture; | |||||
public class NSSystemLocation | |||||
{ | |||||
private static final String ID_TAG = NSSystemLocation.class.getName(); | |||||
public static final String FUSED_PROVIDER = "fused"; | |||||
private final Context m_context; | |||||
private NSLocationInfo m_lastLocation = null; | |||||
public NSSystemLocation(Context context) | |||||
{ | |||||
m_context = context; | |||||
} | |||||
public NSLocationInfo GetLocation(String provider) | |||||
{ | |||||
NSLocationInfo loc; | |||||
try | |||||
{ | |||||
loc = GetHighLocation(provider); | |||||
if(null == loc) | |||||
loc = GetLastLocation(provider); | |||||
if(null != loc) | |||||
m_lastLocation = loc; | |||||
else | |||||
ShowToast("定位失败, 请先开启位置服务", Toast.LENGTH_LONG); | |||||
return loc; | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
@SuppressLint("MissingPermission") | |||||
private NSLocationInfo GetLastLocation(final String type) | |||||
{ | |||||
NSLocationInfo loc = null; | |||||
try | |||||
{ | |||||
LocationManager lm = (LocationManager) m_context.getSystemService(Context.LOCATION_SERVICE); | |||||
List<String> allProviders = lm.getAllProviders(); | |||||
Log.i(ID_TAG, "使用最近所有定位提供器: " + allProviders); | |||||
Map<String, NSLocationInfo> map = new HashMap<>(); | |||||
for(String provider : allProviders) | |||||
{ | |||||
Location lastKnownLocation = lm.getLastKnownLocation(provider); | |||||
if(null == lastKnownLocation) | |||||
continue; | |||||
NSLocationInfo l = new NSLocationInfo(provider, lastKnownLocation.getLongitude(), lastKnownLocation.getLatitude()); | |||||
map.put(provider, l); | |||||
} | |||||
Log.i(ID_TAG, "最近定位提供器结果: " + map); | |||||
if(null != type && !type.isEmpty() && map.containsKey(type)) | |||||
loc = map.get(type); | |||||
else if(map.containsKey(LocationManager.GPS_PROVIDER)) | |||||
loc = map.get(LocationManager.GPS_PROVIDER); | |||||
else if(map.containsKey(LocationManager.NETWORK_PROVIDER)) | |||||
loc = map.get(LocationManager.NETWORK_PROVIDER); | |||||
else if(map.containsKey(FUSED_PROVIDER)) | |||||
loc = map.get(FUSED_PROVIDER); | |||||
else if(map.containsKey(LocationManager.PASSIVE_PROVIDER)) | |||||
loc = map.get(LocationManager.PASSIVE_PROVIDER); | |||||
if(null == loc) | |||||
Log.i(ID_TAG, "所有提供器无最近定位"); | |||||
else | |||||
Log.i(ID_TAG, "使用提供器最近定位: " + loc.provider); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return loc; | |||||
} | |||||
@SuppressLint("MissingPermission") | |||||
private NSLocationInfo GetHighLocation(String provider) | |||||
{ | |||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R) | |||||
{ | |||||
return null; | |||||
} | |||||
NSLocationInfo loc = null; | |||||
try | |||||
{ | |||||
LocationManager lm = (LocationManager) m_context.getSystemService(Context.LOCATION_SERVICE); | |||||
if(null != provider) | |||||
{ | |||||
if(LocationManager.GPS_PROVIDER.equals(provider)) | |||||
{ | |||||
if(!lm.isProviderEnabled(provider)) | |||||
{ | |||||
Log.w(ID_TAG, "GPS定位提供器未启用, 尝试网络定位器"); | |||||
provider = LocationManager.NETWORK_PROVIDER; | |||||
if(!lm.isProviderEnabled(provider)) | |||||
{ | |||||
Log.w(ID_TAG, "网络定位提供器也未启用"); | |||||
provider = null; | |||||
} | |||||
} | |||||
} | |||||
else if(LocationManager.NETWORK_PROVIDER.equals(provider)) | |||||
{ | |||||
Log.w(ID_TAG, "网络定位提供器未启用, 尝试GPS定位器"); | |||||
if(!lm.isProviderEnabled(provider)) | |||||
{ | |||||
provider = LocationManager.GPS_PROVIDER; | |||||
if(!lm.isProviderEnabled(provider)) | |||||
{ | |||||
Log.w(ID_TAG, "GPS定位提供器也未启用"); | |||||
provider = null; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
if(null == provider) | |||||
{ | |||||
Criteria criteria = new Criteria(); | |||||
criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度 | |||||
// criteria.setPowerRequirement(Criteria.POWER_HIGH); | |||||
// criteria.setAltitudeRequired(false); | |||||
provider = lm.getBestProvider(criteria, true); | |||||
} | |||||
Log.i(ID_TAG, "使用高精度定位提供器: " + provider); | |||||
if(null == provider) | |||||
{ | |||||
Log.i(ID_TAG, "无法获取高精度定位提供器"); | |||||
return null; | |||||
} | |||||
CompletableFuture<Location> future = new CompletableFuture<>(); | |||||
lm.getCurrentLocation(provider, null, m_context.getMainExecutor(), future::complete); | |||||
Location location = future.get(); | |||||
if(null == location) | |||||
{ | |||||
Log.i(ID_TAG, "无法使用高精度提供器定位"); | |||||
return null; | |||||
} | |||||
loc = new NSLocationInfo(provider, location.getLongitude(), location.getLatitude()); | |||||
Log.i(ID_TAG, "使用高精度提供器获取定位: " + loc); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return loc; | |||||
} | |||||
private void ShowToast(final String message, int duration) | |||||
{ | |||||
((Activity)m_context).runOnUiThread((new Runnable() { | |||||
@Override | |||||
public void run() | |||||
{ | |||||
Toast.makeText(m_context, message, duration).show(); | |||||
} | |||||
})); | |||||
} | |||||
public NSLocationInfo GetLastLocation() | |||||
{ | |||||
return m_lastLocation; | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
package com.nsgk.ruralWeb.sys; | |||||
import android.location.LocationManager; | |||||
import com.nsgk.ruralWeb.BuildConfig; | |||||
import com.nsgk.ruralWeb.enums.NSEnums; | |||||
public final class NSConstants | |||||
{ | |||||
/** | |||||
* App首页链接地址 | |||||
* 可以在 /gradle.properties 里配置 appHomeUrl=链接地址, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappHomeUrl="链接地址", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppHomeUrl() | |||||
{ | |||||
return BuildConfig.APP_HOME_URL; | |||||
} | |||||
/** | |||||
* App启动页版权文本 | |||||
* 可以在 /gradle.properties 里配置 appCopyright=文本, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappCopyright="文本", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppCopyright() | |||||
{ | |||||
return BuildConfig.APP_COMPYRIGHT; | |||||
} | |||||
/** | |||||
* App启动页厂商文本 | |||||
* 可以在 /gradle.properties 里配置 appVendor=文本, 不要携带双引号 | |||||
* 也可以在命令行添加 -PappVendor="文本", 双引号可携带也可不携带 | |||||
*/ | |||||
public static String AppVendor() | |||||
{ | |||||
return BuildConfig.APP_VENDOR; | |||||
} | |||||
public static String AppIcon() | |||||
{ | |||||
return BuildConfig.APP_ICON; | |||||
} | |||||
// 偏好默认值 | |||||
public static final String DEFAULT_LOCATION_SCHEME = NSEnums.LocationScheme.REALTIME; | |||||
public static final int DEFAULT_LOCATION_GAODE_INTERVAL = 1000; | |||||
public static final int DEFAULT_LOCATION_GAODE_READ_COUNT = 1; | |||||
public static final String DEFAULT_LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; | |||||
public static final int DEFAULT_LOCATION_REALTIME_INTERVAL = 1000; | |||||
public static final int DEFAULT_LOCATION_REALTIME_DISTANCE = 1; | |||||
public static final int DEFAULT_LOCATION_REALTIME_READ_COUNT = 1; | |||||
public static final boolean DEFAULT_OPEN_LAST_URL = false; | |||||
public static final int DEFAULT_FONT_SCALE = 100; | |||||
private NSConstants() {} | |||||
} |
@@ -0,0 +1,78 @@ | |||||
package com.nsgk.ruralWeb.sys; | |||||
import android.content.Context; | |||||
import android.content.SharedPreferences; | |||||
import android.preference.PreferenceManager; | |||||
import com.nsgk.ruralWeb.utils.NSStr; | |||||
public final class NSPreference | |||||
{ | |||||
public static final String LOCATION_SCHEME = "location_scheme"; | |||||
public static final String LOCATION_GAODE_INTERVAL = "location_gaode_interval"; | |||||
public static final String LOCATION_GAODE_READ_COUNT = "location_gaode_read_count"; | |||||
public static final String LOCATION_PROVIDER = "location_provider"; | |||||
public static final String LOCATION_REALTIME_INTERVAL = "location_realtime_interval"; | |||||
public static final String LOCATION_REALTIME_DISTANCE = "location_realtime_distance"; | |||||
public static final String LOCATION_REALTIME_READ_COUNT = "location_realtime_read_count"; | |||||
public static final String OPEN_LAST_URL = "open_last_url"; | |||||
public static final String LAST_ACCESS_URL = "last_access_url"; | |||||
public static final String COOKIES = "cookies"; | |||||
public static final String FONT_SCALE = "font_scale"; | |||||
public static final String RESET_SETTINGS = "RESET_SETTINGS"; | |||||
public static final String VERSION = "VERSION"; | |||||
private final Context context; | |||||
public NSPreference(Context context) | |||||
{ | |||||
this.context = context; | |||||
} | |||||
public SharedPreferences Read() | |||||
{ | |||||
return PreferenceManager.getDefaultSharedPreferences(context); | |||||
} | |||||
public SharedPreferences.Editor Write() | |||||
{ | |||||
return PreferenceManager.getDefaultSharedPreferences(context).edit(); | |||||
} | |||||
public String GetString(String name, String... def) | |||||
{ | |||||
return Read().getString(name, null != def && def.length > 0 ? def[0] : null); | |||||
} | |||||
public boolean GetBool(String name, boolean... def) | |||||
{ | |||||
return Read().getBoolean(name, null != def && def.length > 0 ? def[0] : false); | |||||
} | |||||
public void Remove(String name) | |||||
{ | |||||
Write().remove(name).commit(); | |||||
} | |||||
public void SetString(String name, String val) | |||||
{ | |||||
Write().putString(name, val).commit(); | |||||
} | |||||
public void SetInt(String name, int val) | |||||
{ | |||||
Write().putInt(name, val).commit(); | |||||
} | |||||
public int GetInt(String name, int... def) | |||||
{ | |||||
return Read().getInt(name, null != def && def.length > 0 ? def[0] : 0); | |||||
} | |||||
public int GetIntFromString(String name, int defVal) | |||||
{ | |||||
String str = Read().getString(name, ""); | |||||
return NSStr.parseInt_s(str, defVal); | |||||
} | |||||
} |
@@ -0,0 +1,309 @@ | |||||
package com.nsgk.ruralWeb.ui; | |||||
import android.content.Context; | |||||
import android.content.DialogInterface; | |||||
import android.content.SharedPreferences; | |||||
import android.os.Bundle; | |||||
import android.text.Html; | |||||
import android.widget.Toast; | |||||
import androidx.appcompat.app.AlertDialog; | |||||
import androidx.preference.Preference; | |||||
import androidx.preference.PreferenceFragmentCompat; | |||||
import androidx.preference.PreferenceManager; | |||||
import androidx.preference.SeekBarPreference; | |||||
import com.nsgk.ruralWeb.BuildConfig; | |||||
import com.nsgk.ruralWeb.R; | |||||
import com.nsgk.ruralWeb.sys.NSConstants; | |||||
import com.nsgk.ruralWeb.sys.NSPreference; | |||||
import com.nsgk.ruralWeb.utils.NSContextUtils; | |||||
import com.nsgk.ruralWeb.utils.NSMisc; | |||||
import cn.hutool.core.util.NumberUtil; | |||||
import cn.hutool.core.util.StrUtil; | |||||
public class SettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener | |||||
{ | |||||
private static final String ID_TAG = SettingsFragment.class.getName(); | |||||
@Override | |||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { | |||||
addPreferencesFromResource(R.xml.settings_preference); | |||||
Preference preference; | |||||
final Context context = getContext(); | |||||
preference = findPreference(NSPreference.LOCATION_SCHEME); | |||||
preference.setDefaultValue(NSConstants.DEFAULT_LOCATION_SCHEME); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_GAODE_INTERVAL); | |||||
preference.setDefaultValue("" + NSConstants.DEFAULT_LOCATION_GAODE_INTERVAL); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_GAODE_READ_COUNT); | |||||
preference.setDefaultValue("" + NSConstants.DEFAULT_LOCATION_GAODE_READ_COUNT); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.OPEN_LAST_URL); | |||||
preference.setDefaultValue(NSConstants.DEFAULT_OPEN_LAST_URL); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_PROVIDER); | |||||
preference.setDefaultValue(NSConstants.DEFAULT_LOCATION_PROVIDER); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_INTERVAL); | |||||
preference.setDefaultValue("" + NSConstants.DEFAULT_LOCATION_REALTIME_INTERVAL); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_DISTANCE); | |||||
preference.setDefaultValue("" + NSConstants.DEFAULT_LOCATION_REALTIME_DISTANCE); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_READ_COUNT); | |||||
preference.setDefaultValue("" + NSConstants.DEFAULT_LOCATION_REALTIME_READ_COUNT); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.FONT_SCALE); | |||||
preference.setDefaultValue(NSConstants.DEFAULT_FONT_SCALE); | |||||
preference.setOnPreferenceChangeListener(this); | |||||
preference = findPreference(NSPreference.RESET_SETTINGS); | |||||
preference.setOnPreferenceClickListener(this); | |||||
preference = findPreference(NSPreference.VERSION); | |||||
preference.setOnPreferenceClickListener(this); | |||||
} | |||||
private boolean CheckValueIsNumber(Object newValue, Integer min) | |||||
{ | |||||
if(null != newValue) | |||||
{ | |||||
String str = newValue.toString(); | |||||
if(StrUtil.isNotBlank(str)) | |||||
{ | |||||
if(!NumberUtil.isInteger(str)) | |||||
{ | |||||
Toast.makeText(getContext(), "请输入数字", Toast.LENGTH_SHORT).show(); | |||||
return false; | |||||
} | |||||
int i = Integer.parseInt(str); | |||||
if(null != min) | |||||
{ | |||||
if(i < min) | |||||
{ | |||||
Toast.makeText(getContext(), "必须大于等于" + min, Toast.LENGTH_SHORT).show(); | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean onPreferenceChange(Preference preference, Object newValue) { | |||||
String key = preference.getKey(); | |||||
switch(key) | |||||
{ | |||||
case NSPreference.LOCATION_GAODE_INTERVAL: | |||||
if(!CheckValueIsNumber(newValue, 1000)) | |||||
return false; | |||||
break; | |||||
case NSPreference.LOCATION_GAODE_READ_COUNT: | |||||
if(!CheckValueIsNumber(newValue, 1)) | |||||
return false; | |||||
break; | |||||
case NSPreference.LOCATION_REALTIME_INTERVAL: | |||||
if(!CheckValueIsNumber(newValue, 1000)) | |||||
return false; | |||||
break; | |||||
case NSPreference.LOCATION_REALTIME_DISTANCE: | |||||
if(!CheckValueIsNumber(newValue, 0)) | |||||
return false; | |||||
break; | |||||
case NSPreference.LOCATION_REALTIME_READ_COUNT: | |||||
if(!CheckValueIsNumber(newValue, 1)) | |||||
return false; | |||||
break; | |||||
case NSPreference.FONT_SCALE: { | |||||
int i = (int)newValue; | |||||
int newi = Math.round((float)i / 10.0f) * 10; | |||||
if(i < 50) | |||||
i = 50; | |||||
if(i != newi) | |||||
{ | |||||
getView().post(() -> { | |||||
((SeekBarPreference)preference).setValue(newi); | |||||
preference.callChangeListener(newi); | |||||
}); | |||||
return false; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
SetSummary(key, newValue); | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean onPreferenceClick(final Preference preference) { | |||||
String key = preference.getKey(); | |||||
final Context context = getContext(); | |||||
switch(key) | |||||
{ | |||||
case NSPreference.RESET_SETTINGS: | |||||
OpenQueryDialog("警告", "确定要重置到默认设置?", (dialog, which) -> { | |||||
ResetSettings(); | |||||
SetSummary(null, null); | |||||
Toast.makeText(context, "设置已重置", Toast.LENGTH_LONG).show(); | |||||
}); | |||||
break; | |||||
case NSPreference.VERSION: | |||||
OpenAbout(); | |||||
break; | |||||
} | |||||
return false; | |||||
} | |||||
private void ResetSettings() | |||||
{ | |||||
NSPreference preference = new NSPreference(getContext()); | |||||
preference.Write() | |||||
.putString(NSPreference.LOCATION_SCHEME, NSConstants.DEFAULT_LOCATION_SCHEME) | |||||
.putString(NSPreference.LOCATION_GAODE_INTERVAL, "" + NSConstants.DEFAULT_LOCATION_GAODE_INTERVAL) | |||||
.putString(NSPreference.LOCATION_GAODE_READ_COUNT, "" + NSConstants.DEFAULT_LOCATION_GAODE_READ_COUNT) | |||||
.putBoolean(NSPreference.OPEN_LAST_URL, NSConstants.DEFAULT_OPEN_LAST_URL) | |||||
.putString(NSPreference.LOCATION_PROVIDER, NSConstants.DEFAULT_LOCATION_PROVIDER) | |||||
.putString(NSPreference.LOCATION_REALTIME_INTERVAL, "" + NSConstants.DEFAULT_LOCATION_REALTIME_INTERVAL) | |||||
.putString(NSPreference.LOCATION_REALTIME_DISTANCE, "" + NSConstants.DEFAULT_LOCATION_REALTIME_DISTANCE) | |||||
.putString(NSPreference.LOCATION_REALTIME_READ_COUNT, "" + NSConstants.DEFAULT_LOCATION_REALTIME_READ_COUNT) | |||||
.putInt(NSPreference.FONT_SCALE, NSConstants.DEFAULT_FONT_SCALE) | |||||
.commit() | |||||
; | |||||
} | |||||
private void OpenQueryDialog(String title, String message, DialogInterface.OnClickListener listener) | |||||
{ | |||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); | |||||
builder.setTitle(title); | |||||
builder.setMessage(message); | |||||
builder.setIcon(R.drawable.no_words); | |||||
builder.setPositiveButton("确定", listener); | |||||
builder.setNegativeButton("取消", null); | |||||
AlertDialog dialog = builder.create(); | |||||
dialog.show(); | |||||
} | |||||
private void OpenAbout() | |||||
{ | |||||
Context context = getContext(); | |||||
AlertDialog.Builder builder = new AlertDialog.Builder(context); | |||||
String msg = "<br/>" + NSContextUtils.tr(context, R.string.company) + "<br/><br/>" + NSContextUtils.tr(context, R.string.copyright); | |||||
msg = "<div style='text-align:center;'>" + msg + "</div>"; | |||||
builder.setTitle(R.string.app_name2); | |||||
builder.setMessage(Html.fromHtml(msg, Html.FROM_HTML_MODE_LEGACY, null, null)); | |||||
int icon = context.getResources().getIdentifier("ic_launcher_" + NSConstants.AppIcon()/* + "_round"*/, "mipmap", context.getApplicationContext().getPackageName()); | |||||
builder.setIcon(icon); | |||||
AlertDialog dialog = builder.create(); | |||||
dialog.show(); | |||||
} | |||||
@Override | |||||
public void onResume() { | |||||
super.onResume(); | |||||
SetSummary(null, null); | |||||
} | |||||
public void SetSummary(String key, Object newValue) | |||||
{ | |||||
Context context = getContext(); | |||||
Preference preference; | |||||
String summary; | |||||
String value; | |||||
boolean b; | |||||
int i; | |||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); | |||||
if(key == null || NSPreference.LOCATION_SCHEME.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_SCHEME); | |||||
value = newValue != null ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_SCHEME, NSConstants.DEFAULT_LOCATION_SCHEME); | |||||
summary = NSContextUtils.GetListName(context, value, R.array.location_scheme_values, R.array.location_scheme_labels); | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_GAODE_INTERVAL.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_GAODE_INTERVAL); | |||||
value = newValue != null && StrUtil.isNotBlank(newValue.toString()) ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_GAODE_INTERVAL, "" + NSConstants.DEFAULT_LOCATION_GAODE_INTERVAL); | |||||
summary = value + "毫秒"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_GAODE_READ_COUNT.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_GAODE_READ_COUNT); | |||||
value = newValue != null && StrUtil.isNotBlank(newValue.toString()) ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_GAODE_READ_COUNT, "" + NSConstants.DEFAULT_LOCATION_GAODE_READ_COUNT); | |||||
summary = value + "次"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.OPEN_LAST_URL.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.OPEN_LAST_URL); | |||||
b = newValue != null ? (Boolean)newValue : sharedPreferences.getBoolean(NSPreference.OPEN_LAST_URL, NSConstants.DEFAULT_OPEN_LAST_URL); | |||||
summary = b ? "打开最近页面" : "打开默认页面"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_PROVIDER.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_PROVIDER); | |||||
value = newValue != null ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_PROVIDER, NSConstants.DEFAULT_LOCATION_PROVIDER); | |||||
summary = NSContextUtils.GetListName(context, value, R.array.location_provider_values, R.array.location_provider_labels); | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_REALTIME_INTERVAL.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_INTERVAL); | |||||
value = newValue != null && StrUtil.isNotBlank(newValue.toString()) ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_REALTIME_INTERVAL, "" + NSConstants.DEFAULT_LOCATION_REALTIME_INTERVAL); | |||||
summary = value + "毫秒"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_REALTIME_DISTANCE.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_DISTANCE); | |||||
value = newValue != null && StrUtil.isNotBlank(newValue.toString()) ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_REALTIME_DISTANCE, "" + NSConstants.DEFAULT_LOCATION_REALTIME_DISTANCE); | |||||
summary = value + "米"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.LOCATION_REALTIME_READ_COUNT.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.LOCATION_REALTIME_READ_COUNT); | |||||
value = newValue != null && StrUtil.isNotBlank(newValue.toString()) ? newValue.toString() : sharedPreferences.getString(NSPreference.LOCATION_REALTIME_READ_COUNT, "" + NSConstants.DEFAULT_LOCATION_REALTIME_READ_COUNT); | |||||
summary = value + "次"; | |||||
preference.setSummary(summary); | |||||
} | |||||
if(key == null || NSPreference.FONT_SCALE.equals(key)) | |||||
{ | |||||
preference = findPreference(NSPreference.FONT_SCALE); | |||||
i = newValue != null ? (int)newValue : sharedPreferences.getInt(NSPreference.FONT_SCALE, NSConstants.DEFAULT_FONT_SCALE); | |||||
if(i == 0) | |||||
i = 100; | |||||
summary = i + "%"; | |||||
preference.setSummary(summary); | |||||
} | |||||
findPreference(NSPreference.VERSION).setSummary(NSContextUtils.GetAppVersion(getContext())); | |||||
} | |||||
} | |||||
@@ -1,16 +0,0 @@ | |||||
package com.nsgk.ruralWeb.utils; | |||||
import android.app.Activity; | |||||
import android.content.Intent; | |||||
import android.provider.Settings; | |||||
public final class ContextUtils | |||||
{ | |||||
public static void RequestLocationPermission(Activity context, int requestCode) | |||||
{ | |||||
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); | |||||
context.startActivityForResult(intent, requestCode); | |||||
} | |||||
private ContextUtils() {} | |||||
} |
@@ -0,0 +1,79 @@ | |||||
package com.nsgk.ruralWeb.utils; | |||||
import android.app.Activity; | |||||
import android.content.Context; | |||||
import android.content.Intent; | |||||
import android.content.pm.ApplicationInfo; | |||||
import android.content.pm.PackageInfo; | |||||
import android.content.pm.PackageManager; | |||||
import android.content.res.Resources; | |||||
import android.net.Uri; | |||||
import android.provider.Settings; | |||||
public final class NSContextUtils | |||||
{ | |||||
public static void RequestLocationPermission(Activity context, int requestCode) | |||||
{ | |||||
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); | |||||
context.startActivityForResult(intent, requestCode); | |||||
} | |||||
public static void OpenAppSetting(Context context) | |||||
{ | |||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | |||||
Uri uri = Uri.fromParts("package", context.getApplicationContext().getPackageName(), null); | |||||
intent.setData(uri); | |||||
context.startActivity(intent); | |||||
} | |||||
public static String GetAppVersion(Context context) | |||||
{ | |||||
String version = "UNKNOWN"; | |||||
try | |||||
{ | |||||
PackageManager manager = context.getPackageManager(); | |||||
PackageInfo info = manager.getPackageInfo(context.getApplicationContext().getPackageName(), 0); | |||||
version = info.versionName; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return version; | |||||
} | |||||
public static boolean BuildIsDebug(Context context) | |||||
{ | |||||
try | |||||
{ | |||||
ApplicationInfo info = context.getApplicationInfo(); | |||||
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
e.printStackTrace(); | |||||
return false; // default is release | |||||
} | |||||
} | |||||
public static String GetListName(Context context, String value, int keyResource, int nameResource, String... def) | |||||
{ | |||||
Resources resources = context.getResources(); | |||||
String[] keys = resources.getStringArray(keyResource); | |||||
String[] names = resources.getStringArray(nameResource); | |||||
for(int i = 0; i < keys.length; i++) | |||||
{ | |||||
if(keys[i].equals(value)) | |||||
return names[i]; | |||||
} | |||||
return null != def && def.length > 0 ? def[0] : null; | |||||
} | |||||
public static String tr(Context context, int id, Object...args) | |||||
{ | |||||
return context.getResources().getString(id, args); | |||||
} | |||||
private NSContextUtils() {} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package com.nsgk.ruralWeb.utils; | |||||
public final class NSMisc | |||||
{ | |||||
public static void noexcept(Runnable runnable) | |||||
{ | |||||
if(null != runnable) | |||||
{ | |||||
try | |||||
{ | |||||
runnable.run(); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.nsgk.ruralWeb.utils; | |||||
import java.net.URL; | |||||
import cn.hutool.core.util.StrUtil; | |||||
import cn.hutool.core.util.URLUtil; | |||||
public final class NSStr | |||||
{ | |||||
public static String GetUrlPart(String str) | |||||
{ | |||||
URL url = URLUtil.url(str); | |||||
String protocol = url.getProtocol(); | |||||
String host = url.getHost(); | |||||
int port = url.getPort(); | |||||
StringBuilder buf = new StringBuilder(); | |||||
buf.append(protocol).append("://").append(host); | |||||
if(port >= 0) | |||||
buf.append(':').append(port); | |||||
return buf.toString(); | |||||
} | |||||
public static int parseInt_s(String str, int...def) | |||||
{ | |||||
int res = null != def && def.length > 0 ? def[0] : 0; | |||||
if(StrUtil.isBlank(str)) | |||||
return res; | |||||
try | |||||
{ | |||||
return Integer.parseInt(str); | |||||
} | |||||
catch(Exception e) | |||||
{ | |||||
e.printStackTrace(); | |||||
return res; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,288 @@ | |||||
package com.nsgk.ruralWeb.web; | |||||
import android.app.Activity; | |||||
import android.Manifest; | |||||
import android.content.Context; | |||||
import android.content.pm.PackageManager; | |||||
import android.location.LocationManager; | |||||
import android.os.Handler; | |||||
import android.util.Log; | |||||
import android.webkit.JavascriptInterface; | |||||
import android.webkit.ValueCallback; | |||||
import android.webkit.WebView; | |||||
import android.widget.Toast; | |||||
import androidx.core.app.ActivityCompat; | |||||
import com.nsgk.ruralWeb.FullscreenActivity; | |||||
import com.nsgk.ruralWeb.enums.NSEnums; | |||||
import com.nsgk.ruralWeb.location.NSLocationInfo; | |||||
import com.nsgk.ruralWeb.location.NSAMapLocation; | |||||
import com.nsgk.ruralWeb.location.NSRealtimeLocation; | |||||
import com.nsgk.ruralWeb.location.NSSystemLocation; | |||||
import com.nsgk.ruralWeb.sys.NSConstants; | |||||
import com.nsgk.ruralWeb.sys.NSPreference; | |||||
import com.nsgk.ruralWeb.utils.NSContextUtils; | |||||
public class NSEnvWindowObject | |||||
{ | |||||
private static final String ID_TAG = NSEnvWindowObject.class.getName(); | |||||
private static final String FUSED_PROVIDER = "fused"; | |||||
private final Handler m_handler; | |||||
private final Context m_context; | |||||
private final WebView m_webView; | |||||
private final NSSystemLocation location; | |||||
private final NSAMapLocation amapLocation; | |||||
private final NSRealtimeLocation realtimeLocation; | |||||
private final NSPreference preference; | |||||
// 最近一次获取到的定位坐标, 如果获取定位失败则返回此值. 可能不需要, 因为PASSIVE_PROVIDER就为最近的定位 | |||||
private String lastLocation = null; | |||||
public NSEnvWindowObject(Context context, Handler handler, WebView webView) | |||||
{ | |||||
m_context = context; | |||||
m_handler = handler; | |||||
m_webView = webView; | |||||
location = new NSSystemLocation(m_context); | |||||
amapLocation = new NSAMapLocation(m_context); | |||||
realtimeLocation = new NSRealtimeLocation(m_context); | |||||
preference = new NSPreference(m_context); | |||||
} | |||||
public void OnDestroy() | |||||
{ | |||||
if(amapLocation.IsInitialized()) | |||||
amapLocation.Shutdown(); | |||||
if(realtimeLocation.IsInitialized()) | |||||
realtimeLocation.Shutdown(); | |||||
} | |||||
@JavascriptInterface | |||||
public void Toast(final String message) | |||||
{ | |||||
ShowToast(message, Toast.LENGTH_LONG); | |||||
} | |||||
private void ShowToast(final String message, int duration) | |||||
{ | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() | |||||
{ | |||||
Toast.makeText(m_context, message, duration).show(); | |||||
} | |||||
}); | |||||
} | |||||
@JavascriptInterface | |||||
public void Log(final String message) | |||||
{ | |||||
Log.i(ID_TAG, message); | |||||
} | |||||
@JavascriptInterface | |||||
public String GetLocation(final String type) | |||||
{ | |||||
Log.d(ID_TAG, "Web线程: " + Thread.currentThread().getId()); | |||||
if(!CheckLocationPermission()) | |||||
{ | |||||
return lastLocation; | |||||
} | |||||
NSLocationInfo loc; | |||||
String location = preference.GetString(NSPreference.LOCATION_SCHEME, NSConstants.DEFAULT_LOCATION_SCHEME); | |||||
String provider = preference.GetString(NSPreference.LOCATION_PROVIDER, NSConstants.DEFAULT_LOCATION_PROVIDER); | |||||
Log.d(ID_TAG, "定位提供器: " + provider); | |||||
switch(location) | |||||
{ | |||||
case NSEnums.LocationScheme.GAODE: | |||||
loc = GetAMapLocation(); | |||||
if(null == loc) | |||||
loc = GetBuiltInLocation(provider); | |||||
break; | |||||
case NSEnums.LocationScheme.REALTIME: | |||||
loc = GetRealtimeLocation(); | |||||
if(null == loc) | |||||
loc = GetBuiltInLocation(provider); | |||||
break; | |||||
case NSEnums.LocationScheme.SYSTEM: | |||||
default: | |||||
loc = GetBuiltInLocation(provider); | |||||
break; | |||||
} | |||||
return SetLastLocation(loc); | |||||
} | |||||
private String ReturnLocation(NSLocationInfo loc) | |||||
{ | |||||
return loc.longitude + "," + loc.latitude; | |||||
} | |||||
private String SetLastLocation(NSLocationInfo loc) | |||||
{ | |||||
if(null != loc) | |||||
lastLocation = ReturnLocation(loc); | |||||
return lastLocation; | |||||
} | |||||
private NSLocationInfo GetBuiltInLocation(String type) | |||||
{ | |||||
Log.i(ID_TAG, "使用系统定位"); | |||||
NSLocationInfo loc = null; | |||||
try | |||||
{ | |||||
long start = System.currentTimeMillis(); | |||||
loc = location.GetLocation(type); | |||||
long end = System.currentTimeMillis(); | |||||
Log.i(ID_TAG, "系统定位结果: " + loc + ", 耗时=" + (end - start) + "毫秒"); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return loc; | |||||
} | |||||
private NSLocationInfo GetRealtimeLocation() | |||||
{ | |||||
Log.i(ID_TAG, "使用系统实时定位"); | |||||
NSLocationInfo loc = null; | |||||
try | |||||
{ | |||||
NSPreference preference = new NSPreference(m_context); | |||||
if(!realtimeLocation.IsInitialized()) | |||||
{ | |||||
realtimeLocation.SetOption(NSRealtimeLocation.OPTION_MIN_TIME, preference.GetIntFromString(NSPreference.LOCATION_REALTIME_INTERVAL, NSConstants.DEFAULT_LOCATION_REALTIME_INTERVAL)); | |||||
realtimeLocation.SetOption(NSRealtimeLocation.OPTION_MIN_DISTANCE, preference.GetIntFromString(NSPreference.LOCATION_REALTIME_DISTANCE, NSConstants.DEFAULT_LOCATION_REALTIME_DISTANCE)); | |||||
int flag = NSRealtimeLocation.DEFAULT_FLAG | NSRealtimeLocation.FLAG_BUILTIN_THREAD; | |||||
String provider = preference.GetString(NSPreference.LOCATION_PROVIDER, NSConstants.DEFAULT_LOCATION_PROVIDER); | |||||
if(LocationManager.GPS_PROVIDER.equals(provider)) | |||||
{ | |||||
flag &= ~NSRealtimeLocation.FLAG_PRIORITY_NETWORK; | |||||
flag |= NSRealtimeLocation.FLAG_PRIORITY_GPS; | |||||
} | |||||
realtimeLocation.Init(flag); | |||||
} | |||||
long start = System.currentTimeMillis(); | |||||
loc = realtimeLocation.Read(preference.GetIntFromString(NSPreference.LOCATION_REALTIME_READ_COUNT, NSConstants.DEFAULT_LOCATION_REALTIME_READ_COUNT)); | |||||
long end = System.currentTimeMillis(); | |||||
Log.i(ID_TAG, "系统实时定位结果: " + loc + ", 耗时=" + (end - start) + "毫秒"); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return loc; | |||||
} | |||||
public NSLocationInfo GetAMapLocation() | |||||
{ | |||||
Log.i(ID_TAG, "使用高德定位"); | |||||
NSLocationInfo loc = null; | |||||
try | |||||
{ | |||||
NSPreference preference = new NSPreference(m_context); | |||||
if(!amapLocation.IsInitialized()) | |||||
{ | |||||
amapLocation.SetOption(NSAMapLocation.OPTION_INTERVAL, preference.GetIntFromString(NSPreference.LOCATION_GAODE_INTERVAL, NSConstants.DEFAULT_LOCATION_GAODE_INTERVAL)); | |||||
amapLocation.Init(NSAMapLocation.DEFAULT_FLAG | NSAMapLocation.FLAG_BUILTIN_THREAD); | |||||
} | |||||
long start = System.currentTimeMillis(); | |||||
loc = amapLocation.Read(preference.GetIntFromString(NSPreference.LOCATION_GAODE_READ_COUNT, NSConstants.DEFAULT_LOCATION_GAODE_READ_COUNT)); | |||||
long end = System.currentTimeMillis(); | |||||
Log.i(ID_TAG, "高德定位结果: " + loc + ", 耗时=" + (end - start) + "毫秒"); | |||||
} | |||||
catch(Throwable e) | |||||
{ | |||||
e.printStackTrace(); | |||||
} | |||||
return loc; | |||||
} | |||||
private boolean CheckLocationPermission() | |||||
{ | |||||
if(ActivityCompat.checkSelfPermission(m_context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(m_context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) | |||||
{ | |||||
Activity activity = (Activity) m_context; | |||||
if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) && activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)) // do not ask | |||||
{ | |||||
Toast.makeText(m_context, "请先允许定位服务", Toast.LENGTH_LONG).show(); | |||||
NSContextUtils.RequestLocationPermission(activity, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
} | |||||
else | |||||
{ | |||||
activity.requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, FullscreenActivity.PERMISSION_LOCATION_REQUEST_CODE); | |||||
} | |||||
return false; | |||||
} | |||||
else | |||||
return true; | |||||
} | |||||
protected void RunOnUIThread(Runnable runnable) | |||||
{ | |||||
if(null != m_handler) | |||||
m_handler.post(runnable); | |||||
else if(m_context instanceof Activity) | |||||
((Activity)m_context).runOnUiThread(runnable); | |||||
else | |||||
{ | |||||
Log.e(ID_TAG, "无法在UI线程中执行"); | |||||
} | |||||
} | |||||
protected void CallJSFunc(final String func, Object...args) | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
for(int i = 0; i < args.length; i++) | |||||
{ | |||||
sb.append("'").append(args[i].toString()).append("'"); | |||||
if(i < args.length - 1) | |||||
sb.append(", "); | |||||
} | |||||
final String arg = sb.toString(); | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() | |||||
{ | |||||
String script = "javascript:typeof(" + func + ") == 'function' && " + func + "(" + arg + ");"; | |||||
Log.e(ID_TAG, String.format("调用js函数: 函数(%s), 参数(%s)", func, arg)); | |||||
m_webView.evaluateJavascript(script, new ValueCallback<String>() { | |||||
@Override | |||||
public void onReceiveValue(String value) { | |||||
Log.e(ID_TAG, String.format("js函数 %s 返回: %s", func, value)); | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
protected void CallJSFunc_beforeAndroid4_4(final String func, Object...args) | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
for(int i = 0; i < args.length; i++) | |||||
{ | |||||
sb.append("'").append(args[i].toString()).append("'"); | |||||
if(i < args.length - 1) | |||||
sb.append(", "); | |||||
} | |||||
final String arg = sb.toString(); | |||||
RunOnUIThread(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
Log.e(ID_TAG, String.format("调用js函数: 函数(%s), 参数(%s)", func, arg)); | |||||
String script = "javascript:typeof(" + func + ") == 'function' && " + func + "(" + arg + ");"; | |||||
Log.e(ID_TAG, script); | |||||
m_webView.loadUrl(script, null); | |||||
} | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<resources> | |||||
<string-array name="location_scheme_values"> | |||||
<item>system</item> | |||||
<item>gaode</item> | |||||
<item>realtime</item> | |||||
</string-array> | |||||
<string-array name="location_scheme_labels"> | |||||
<item>系统</item> | |||||
<item>高德</item> | |||||
<item>系统实时</item> | |||||
</string-array> | |||||
<string-array name="location_provider_values"> | |||||
<item>gps</item> | |||||
<item>network</item> | |||||
</string-array> | |||||
<string-array name="location_provider_labels"> | |||||
<item>GPS</item> | |||||
<item>网络</item> | |||||
</string-array> | |||||
</resources> |
@@ -5,4 +5,7 @@ | |||||
<string name="dummy_content">DUMMY\nCONTENT</string> | <string name="dummy_content">DUMMY\nCONTENT</string> | ||||
<string name="copyright">Copyright © 2024 ZNRX All Rights Reserved.</string> | <string name="copyright">Copyright © 2024 ZNRX All Rights Reserved.</string> | ||||
<string name="company">中农融信(北京)科技股份有限公司</string> | <string name="company">中农融信(北京)科技股份有限公司</string> | ||||
<string name="settings_name">设置</string> | |||||
</resources> | </resources> |
@@ -0,0 +1,125 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" | |||||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
android:title="设置"> | |||||
<PreferenceCategory | |||||
android:key="base" | |||||
android:title="基础" | |||||
> | |||||
<SwitchPreference | |||||
android:title="进入App打开最近的页面" | |||||
android:key="open_last_url" | |||||
android:persistent="true" | |||||
android:defaultValue="false" | |||||
android:summary="打开最近的页面" | |||||
/> | |||||
<SeekBarPreference | |||||
android:title="字体缩放倍数(%)" | |||||
android:key="font_scale" | |||||
android:persistent="true" | |||||
android:defaultValue="100" | |||||
app:min="50" | |||||
android:max="300" | |||||
app:showSeekBarValue="true" | |||||
android:summary="字体缩放倍数" | |||||
/> | |||||
<ListPreference | |||||
android:dialogTitle="定位方式" | |||||
android:key="location_scheme" | |||||
android:entries="@array/location_scheme_labels" | |||||
android:entryValues="@array/location_scheme_values" | |||||
android:summary="定位方式" | |||||
android:persistent="true" | |||||
android:title="选择定位方式" | |||||
android:defaultValue="realtime" | |||||
/> | |||||
</PreferenceCategory> | |||||
<PreferenceCategory | |||||
android:key="location_gaode" | |||||
android:title="高德定位" | |||||
> | |||||
<EditTextPreference | |||||
android:dialogTitle="定位间隔(毫秒)" | |||||
android:key="location_gaode_interval" | |||||
android:summary="定位间隔" | |||||
android:persistent="true" | |||||
android:title="设置定位间隔" | |||||
android:inputType="number" | |||||
android:defaultValue="1000" | |||||
/> | |||||
<EditTextPreference | |||||
android:dialogTitle="每次定位次数" | |||||
android:key="location_gaode_read_count" | |||||
android:summary="定位次数" | |||||
android:persistent="true" | |||||
android:title="设置定位次数" | |||||
android:inputType="number" | |||||
android:defaultValue="1" | |||||
/> | |||||
</PreferenceCategory> | |||||
<PreferenceCategory | |||||
android:key="location_system" | |||||
android:title="系统定位" | |||||
> | |||||
<ListPreference | |||||
android:dialogTitle="定位提供器" | |||||
android:key="location_provider" | |||||
android:entries="@array/location_provider_labels" | |||||
android:entryValues="@array/location_provider_values" | |||||
android:summary="定位提供器" | |||||
android:persistent="true" | |||||
android:title="选择定位提供器" | |||||
android:defaultValue="gps" | |||||
/> | |||||
<EditTextPreference | |||||
android:dialogTitle="定位间隔(毫秒)" | |||||
android:key="location_realtime_interval" | |||||
android:summary="定位间隔" | |||||
android:persistent="true" | |||||
android:title="设置定位间隔" | |||||
android:inputType="number" | |||||
android:defaultValue="1000" | |||||
/> | |||||
<EditTextPreference | |||||
android:dialogTitle="定位距离(米)" | |||||
android:key="location_realtime_distance" | |||||
android:summary="定位距离" | |||||
android:persistent="true" | |||||
android:title="设置定位距离" | |||||
android:inputType="number" | |||||
android:defaultValue="1" | |||||
/> | |||||
<EditTextPreference | |||||
android:dialogTitle="每次定位次数" | |||||
android:key="location_realtime_read_count" | |||||
android:summary="定位次数" | |||||
android:persistent="true" | |||||
android:title="设置定位次数" | |||||
android:inputType="number" | |||||
android:defaultValue="1" | |||||
/> | |||||
</PreferenceCategory> | |||||
<PreferenceCategory | |||||
android:key="other" | |||||
android:title="其他" | |||||
> | |||||
<Preference | |||||
android:key="RESET_SETTINGS" | |||||
android:summary="重置所有设置为默认值" | |||||
android:title="重置设置" | |||||
android:persistent="false"> | |||||
</Preference> | |||||
<Preference | |||||
android:key="VERSION" | |||||
android:title="版本" | |||||
android:summary="" | |||||
android:persistent="false"> | |||||
</Preference> | |||||
</PreferenceCategory> | |||||
</PreferenceScreen> |
@@ -0,0 +1,17 @@ | |||||
<shortcuts xmlns:tools="http://schemas.android.com/tools" | |||||
xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
<shortcut | |||||
android:shortcutId="shortcut_settings" | |||||
android:enabled="true" | |||||
android:icon="@drawable/no_words" | |||||
android:shortcutShortLabel="@string/settings_name" | |||||
tools:targetApi="n_mr1"> | |||||
<intent | |||||
android:action="android.intent.action.VIEW" | |||||
android:targetPackage="com.nsgk.ruralWeb" | |||||
android:targetClass="com.nsgk.ruralWeb.SettingsActivity" | |||||
android:name="android.shortcut.conversation"/> | |||||
</shortcut> | |||||
</shortcuts> |