5 Incheckningar

Upphovsman SHA1 Meddelande Datum
  zhaodengke a7ad0aef3e 设置页面 1 vecka sedan
  zhaodengke 41e0d7cc0f 首页 1 vecka sedan
  zhaodengke 99654f3554 系统实时定位 1 vecka sedan
  zhaodengke 25ca6eb217 高德定位 1 vecka sedan
  zhaodengke 77b717cb63 定位 4 veckor sedan
28 ändrade filer med 2317 tillägg och 321 borttagningar
Delad Vy
  1. +6
    -1
      README.md
  2. +11
    -1
      app/build.gradle
  3. Binär
      app/libs/AMap_Location_V6.4.9_20241226.jar
  4. +22
    -0
      app/src/main/AndroidManifest.xml
  5. +0
    -34
      app/src/main/java/com/nsgk/ruralWeb/Constants.java
  6. +0
    -178
      app/src/main/java/com/nsgk/ruralWeb/EnvWindowObject.java
  7. +152
    -27
      app/src/main/java/com/nsgk/ruralWeb/FullscreenActivity.java
  8. +0
    -25
      app/src/main/java/com/nsgk/ruralWeb/LocationInfo.java
  9. +0
    -37
      app/src/main/java/com/nsgk/ruralWeb/Preference.java
  10. +27
    -0
      app/src/main/java/com/nsgk/ruralWeb/SettingsActivity.java
  11. +4
    -2
      app/src/main/java/com/nsgk/ruralWeb/WelcomeActivity.java
  12. +22
    -0
      app/src/main/java/com/nsgk/ruralWeb/enums/NSEnums.java
  13. +451
    -0
      app/src/main/java/com/nsgk/ruralWeb/location/NSAMapLocation.java
  14. +25
    -0
      app/src/main/java/com/nsgk/ruralWeb/location/NSLocationInfo.java
  15. +369
    -0
      app/src/main/java/com/nsgk/ruralWeb/location/NSRealtimeLocation.java
  16. +189
    -0
      app/src/main/java/com/nsgk/ruralWeb/location/NSSystemLocation.java
  17. +57
    -0
      app/src/main/java/com/nsgk/ruralWeb/sys/NSConstants.java
  18. +78
    -0
      app/src/main/java/com/nsgk/ruralWeb/sys/NSPreference.java
  19. +309
    -0
      app/src/main/java/com/nsgk/ruralWeb/ui/SettingsFragment.java
  20. +0
    -16
      app/src/main/java/com/nsgk/ruralWeb/utils/ContextUtils.java
  21. +79
    -0
      app/src/main/java/com/nsgk/ruralWeb/utils/NSContextUtils.java
  22. +19
    -0
      app/src/main/java/com/nsgk/ruralWeb/utils/NSMisc.java
  23. +38
    -0
      app/src/main/java/com/nsgk/ruralWeb/utils/NSStr.java
  24. +288
    -0
      app/src/main/java/com/nsgk/ruralWeb/web/NSEnvWindowObject.java
  25. +26
    -0
      app/src/main/res/values/arrays.xml
  26. +3
    -0
      app/src/main/res/values/strings.xml
  27. +125
    -0
      app/src/main/res/xml/settings_preference.xml
  28. +17
    -0
      app/src/main/res/xml/shortcuts.xml

+ 6
- 1
README.md Visa fil

@@ -45,4 +45,9 @@
* 路径: `/app-keystore.jks`
* 密码: `ns61GK32x%`
* Key Alias: `nsgk_rural_web`
* Key Password: `ns61GK32x%`
* Key Password: `ns61GK32x%`

> 示例
```shell
.\打包-正式.bat http://mxixiaxian.nongshen.net/sunVillage_info/login_code_new 阳光三资 ygcw
```

+ 11
- 1
app/build.gradle Visa fil

@@ -22,6 +22,9 @@ android {
// App启动页厂商名称
buildConfigField "String", "APP_VENDOR", "\"${project.properties.appVendor}\""

// App图标
buildConfigField "String", "APP_ICON", "\"${project.properties.appIconKey}\""

// AndroidManifest.xml占位符
manifestPlaceholders = [
// App名称
@@ -52,12 +55,14 @@ android {
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"")
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"")
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"")
resValue("string", "app_name2", "\"${project.properties.appName}\"")
}
debug {
signingConfig signingConfigs.release
resValue("string", "app_home_url", "\"${project.properties.appHomeUrl}\"")
resValue("string", "app_copyright", "\"${project.properties.appCopyright}\"")
resValue("string", "app_vendor", "\"${project.properties.appVendor}\"")
resValue("string", "app_name2", "\"${project.properties.appName}\"")
}
}
compileOptions {
@@ -78,4 +83,9 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.Justson.AgentWeb:agentweb-core:v4.1.9-androidx' // (必选)
implementation 'com.github.Justson.AgentWeb:agentweb-filechooser:v4.1.9-androidx'
}
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'
}

Binär
app/libs/AMap_Location_V6.4.9_20241226.jar Visa fil


+ 22
- 0
app/src/main/AndroidManifest.xml Visa fil

@@ -15,6 +15,16 @@
<!--AgentWeb 是默认允许定位的 ,如果你需要该功能 , 请在你的 AndroidManifest 文件里面加入如下权限 。-->
<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显示图标-->
<!--
APP_NAME占位符:
@@ -36,6 +46,10 @@
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.Nsgk_rural_web">
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="7e6911ad842a6a6f492d27efdbd42f30"/>

<activity
android:name=".WelcomeActivity"
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection|uiMode"
@@ -46,9 +60,16 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity
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" />

<!--开机广播接受者
@@ -58,6 +79,7 @@
</intent-filter>
</receiver>
-->
<service android:name="com.amap.api.location.APSService"></service>
</application>

</manifest>

+ 0
- 34
app/src/main/java/com/nsgk/ruralWeb/Constants.java Visa fil

@@ -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;
}
}

+ 0
- 178
app/src/main/java/com/nsgk/ruralWeb/EnvWindowObject.java Visa fil

@@ -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);
}
});
}
}

+ 152
- 27
app/src/main/java/com/nsgk/ruralWeb/FullscreenActivity.java Visa fil

@@ -3,35 +3,52 @@ package com.nsgk.ruralWeb;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;
import android.webkit.WebBackForwardList;
import android.webkit.WebHistoryItem;
import android.webkit.WebView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;

import com.just.agentweb.AgentWeb;
import com.just.agentweb.AgentWebConfig;
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.Nullable;
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.
* status bar and navigation/system bar) with user interaction.
*/
public class FullscreenActivity extends AppCompatActivity {
private static final String ID_TAG = FullscreenActivity.class.getName();
public static final int PERMISSION_LOCATION_REQUEST_CODE = 0x1001;


private AgentWeb mAgentWeb;
private Preference preference;
private NSPreference preference;
private NSEnvWindowObject envWindowObject;

private String m_lastUrl;
private String m_mainUrl;

@SuppressLint("SetJavaScriptEnabled")
@Override
@@ -41,24 +58,34 @@ public class FullscreenActivity extends AppCompatActivity {
setContentView(R.layout.activity_fullscreen);
String appHomeUrl = GetHomeUrl();
//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();
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。
/* .setWebChromeClient(new WebChromeClient() {
.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int 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,同时相应的中间件也会失效。
.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 加入。
@@ -66,10 +93,32 @@ public class FullscreenActivity extends AppCompatActivity {
.interceptUnkownUrl() //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。
.createAgentWeb()//创建AgentWeb。
.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项目的入口地址
事项审批 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
*/

Log.d(ID_TAG, "UI线程: " + Thread.currentThread().getId());
}

@Override
protected void onPause() {
if(IsRecordUrl())
{
DumpCookie();
}
mAgentWeb.getWebLifeCycle().onPause();
super.onPause();
}
@@ -101,7 +155,14 @@ public class FullscreenActivity extends AppCompatActivity {

@Override
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();
envWindowObject.OnDestroy();
super.onDestroy();
}

@@ -131,38 +192,102 @@ public class FullscreenActivity extends AppCompatActivity {
for(int i = 0; i < permissions.length; i++)
{
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)
granted++;
}
if (granted < permissions.length) {
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)
preference = new Preference(this);
preference = new NSPreference(this);
return preference;
}

private boolean IsRecordUrl()
{
return GetPreference().GetBool(NSPreference.OPEN_LAST_URL);
}

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()));
}
}
}

+ 0
- 25
app/src/main/java/com/nsgk/ruralWeb/LocationInfo.java Visa fil

@@ -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 +
'}';
}
}

+ 0
- 37
app/src/main/java/com/nsgk/ruralWeb/Preference.java Visa fil

@@ -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();
}
}

+ 27
- 0
app/src/main/java/com/nsgk/ruralWeb/SettingsActivity.java Visa fil

@@ -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();
}
}

+ 4
- 2
app/src/main/java/com/nsgk/ruralWeb/WelcomeActivity.java Visa fil

@@ -14,6 +14,8 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.nsgk.ruralWeb.sys.NSConstants;

public class WelcomeActivity extends AppCompatActivity {
private ImageView imageView;
private LinearLayout llcenter;
@@ -55,14 +57,14 @@ public class WelcomeActivity extends AppCompatActivity {
llcenter = findViewById(R.id.llcenter);

// 动态设置启动页版权信息
String text = GetTrimString(Constants.AppCopyright());
String text = GetTrimString(NSConstants.AppCopyright());
if(null != text)
{
TextView copyrightText = findViewById(R.id.welcome_text_copyright);
copyrightText.setText(text);
}
// 动态设置启动页厂商名称
text = GetTrimString(Constants.AppVendor());
text = GetTrimString(NSConstants.AppVendor());
if(null != text)
{
TextView vendorText = findViewById(R.id.welcome_text_vendor);


+ 22
- 0
app/src/main/java/com/nsgk/ruralWeb/enums/NSEnums.java Visa fil

@@ -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() {}
}

+ 451
- 0
app/src/main/java/com/nsgk/ruralWeb/location/NSAMapLocation.java Visa fil

@@ -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;
}
}

+ 25
- 0
app/src/main/java/com/nsgk/ruralWeb/location/NSLocationInfo.java Visa fil

@@ -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
;
}
}

+ 369
- 0
app/src/main/java/com/nsgk/ruralWeb/location/NSRealtimeLocation.java Visa fil

@@ -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();
}
}
}
}
}
}

+ 189
- 0
app/src/main/java/com/nsgk/ruralWeb/location/NSSystemLocation.java Visa fil

@@ -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;
}
}

+ 57
- 0
app/src/main/java/com/nsgk/ruralWeb/sys/NSConstants.java Visa fil

@@ -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() {}
}

+ 78
- 0
app/src/main/java/com/nsgk/ruralWeb/sys/NSPreference.java Visa fil

@@ -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);
}
}

+ 309
- 0
app/src/main/java/com/nsgk/ruralWeb/ui/SettingsFragment.java Visa fil

@@ -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()));
}
}


+ 0
- 16
app/src/main/java/com/nsgk/ruralWeb/utils/ContextUtils.java Visa fil

@@ -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() {}
}

+ 79
- 0
app/src/main/java/com/nsgk/ruralWeb/utils/NSContextUtils.java Visa fil

@@ -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() {}
}

+ 19
- 0
app/src/main/java/com/nsgk/ruralWeb/utils/NSMisc.java Visa fil

@@ -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();
}
}
}
}

+ 38
- 0
app/src/main/java/com/nsgk/ruralWeb/utils/NSStr.java Visa fil

@@ -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;
}
}
}

+ 288
- 0
app/src/main/java/com/nsgk/ruralWeb/web/NSEnvWindowObject.java Visa fil

@@ -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);
}
});
}
}

+ 26
- 0
app/src/main/res/values/arrays.xml Visa fil

@@ -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>

+ 3
- 0
app/src/main/res/values/strings.xml Visa fil

@@ -5,4 +5,7 @@
<string name="dummy_content">DUMMY\nCONTENT</string>
<string name="copyright">Copyright © 2024 ZNRX All Rights Reserved.</string>
<string name="company">中农融信(北京)科技股份有限公司</string>


<string name="settings_name">设置</string>
</resources>

+ 125
- 0
app/src/main/res/xml/settings_preference.xml Visa fil

@@ -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>

+ 17
- 0
app/src/main/res/xml/shortcuts.xml Visa fil

@@ -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>

Laddar…
Avbryt
Spara