174 Commits

Author SHA1 Message Date
14b2e6b35c Merge branch 'dev_map' into dev 2026-04-13 16:54:07 +08:00
20c39a4a12 样式调整 2026-04-13 16:38:46 +08:00
xiaogg
5c74c7ccc0 fix:优化完善; 2026-04-13 16:13:23 +08:00
23fc0da5c8 新增字段 2026-04-13 15:05:29 +08:00
7efd933416 新增字段 2026-04-09 14:05:27 +08:00
2b33dac384 语音 2026-04-08 10:59:43 +08:00
ef60b8ed62 Merge branch 'map_dev' into dev 2026-04-03 10:25:53 +08:00
bec6952707 Merge branch 'dev_map' into map_dev 2026-04-02 09:11:14 +08:00
f68349a208 Merge branch 'dev_map' into dev 2026-04-01 17:38:48 +08:00
xiaogg
08dcc64ef4 fix:优化完善; 2026-04-01 15:12:54 +08:00
9242dcaf70 优化 2026-04-01 15:06:04 +08:00
95fdfe6269 增加演示账号 2026-04-01 11:41:44 +08:00
ee88b3ada9 优化 2026-04-01 11:34:32 +08:00
ba27799c41 显示问题 2026-03-31 17:12:00 +08:00
c527732532 Merge branch 'main' into dev
# Conflicts:
#	ln_jq_app/android/app/src/main/AndroidManifest.xml
#	ln_jq_app/pubspec.lock
同步线上
2026-03-31 15:07:16 +08:00
cd223fa9bf Merge branch 'map_dev' into dev
# Conflicts:
#	ln_jq_app/android/app/src/main/AndroidManifest.xml
#	ln_jq_app/ios/Runner/Info.plist
2026-03-31 13:49:42 +08:00
fdd6653fce Merge branch 'dev_map' into map_dev
# Conflicts:
#	ln_jq_app/ios/Podfile.lock
#	ln_jq_app/lib/pages/c_page/base_widgets/view.dart
地图导航相关
2026-03-31 10:07:43 +08:00
8748a60f7f 优化算路 2026-03-30 16:13:22 +08:00
xiaogg
5843ef6e87 feat:增加途经点,完善规划,导航; 2026-03-27 15:39:36 +08:00
0123025296 优化搜索 2026-03-27 13:23:10 +08:00
587dd48896 调整地址输入 2026-03-27 11:45:07 +08:00
569780ffb4 样式 2026-03-27 09:37:52 +08:00
3aac4e7330 调整地图选点 2026-03-26 18:01:05 +08:00
b275c66383 ui 2026-03-26 16:58:57 +08:00
bf80931f37 bugfix 2026-03-25 17:56:37 +08:00
67c36f436c bugfix 2026-03-25 17:18:06 +08:00
c10c92afdc 增加token 2026-03-25 11:02:15 +08:00
d3edf95df3 单独缓存车牌 2026-03-24 09:38:16 +08:00
930953780d 路径接口 2026-03-20 10:23:45 +08:00
a497bc6469 清理插件 2026-03-19 17:41:28 +08:00
3f282d15c1 协议修改 2026-03-19 16:37:57 +08:00
03b35f660c 协议前置 2026-03-19 14:41:32 +08:00
9b6f93ca95 隐私调整 2026-03-17 13:31:58 +08:00
eb41ecaec4 更新插件 2026-03-16 16:00:38 +08:00
384de27f2c Merge branch 'dev' 2026-03-13 17:18:43 +08:00
84b174c4a5 协议通知后注册推送 2026-03-13 17:09:30 +08:00
cd14469d79 调整定位 2026-03-12 13:27:08 +08:00
b846d352a2 android 地图 2026-03-10 17:31:27 +08:00
xiaogg
a24f41a8d5 fix:添加本地库 2026-03-09 09:38:22 +08:00
xiaogg
65b4a3ac34 接入地图SDK 2026-03-09 08:53:34 +08:00
02e1946319 文字边界 2026-03-05 11:20:14 +08:00
d5083c1939 Merge branch 'dev'
v1.2.4
2026-03-04 14:58:45 +08:00
fe44848529 号码 2026-03-04 14:50:21 +08:00
572c416827 枪号回填,v1.2.4 2026-03-04 13:38:26 +08:00
8df16f0787 pdf查看 2026-03-03 17:51:06 +08:00
ce6bd3edd2 bugfix 2026-03-03 13:09:10 +08:00
6997b4ac9e 调整权限和库 2026-03-02 11:28:44 +08:00
a26d2478f3 样式 2026-02-28 17:31:28 +08:00
0dded3b928 ocr识别,历史新增类型 2026-02-28 17:15:42 +08:00
b7caf58adf 加氢站-查看证件 2026-02-28 15:00:56 +08:00
0df1902df2 无预约 2026-02-28 11:59:07 +08:00
a8314d8a7a 关闭弹窗 2026-02-27 10:55:55 +08:00
39cae906e9 加氢站-预约 2026-02-27 10:54:55 +08:00
14fd6c75d0 fix 2026-02-25 15:35:33 +08:00
1724852a39 司机-可上传证件 2026-02-25 15:35:02 +08:00
a05d4ebb9b 调整 2026-02-12 18:01:56 +08:00
600cea4379 进度条 2026-02-12 17:34:06 +08:00
3dfc1dfc2c 样式调整 2026-02-12 16:54:50 +08:00
909dc95771 增加 提示 2026-02-11 17:41:30 +08:00
cf0896453b 样式 2026-02-11 11:28:49 +08:00
dce9718320 显示周边加氢站 2026-02-11 09:35:42 +08:00
4491aa9b91 ui调整 2026-02-10 16:35:02 +08:00
5364612a6f 更新样式 2026-02-10 13:37:24 +08:00
10867178fa 筛选框样式 2026-02-10 13:35:22 +08:00
a5e2a89e4f 预约列表样式 2026-02-10 11:51:47 +08:00
26c5f9d67a 消息样式修改 2026-02-09 17:57:00 +08:00
9cd87b0535 规则和历史 2026-02-09 17:28:12 +08:00
45e45d8160 积分兑换 2026-02-09 15:10:00 +08:00
87e890f97e 积分兑换首页 2026-02-06 17:37:43 +08:00
dcf925b8c1 商场页 2026-02-06 15:13:33 +08:00
c45863eda6 增加商城页面 2026-02-06 15:11:12 +08:00
756bf53cf5 司机预约时间调整 2026-02-06 14:16:26 +08:00
f68c2d0938 未车辆显示 2026-02-05 14:50:03 +08:00
211d0225e4 车辆图片动态 2026-02-05 13:54:10 +08:00
7d9b4d99e8 应用更新 2026-02-05 10:30:31 +08:00
3dd583a278 401增加节流 2026-02-03 10:59:05 +08:00
35bd3a78a5 Merge branch 'dev' 2026-01-30 17:33:15 +08:00
1278c38b7e 地图样式调整 2026-01-30 17:32:54 +08:00
032e60d362 Merge branch 'dev'
ui调整
# Conflicts:
#	ln_jq_app/lib/pages/login/view.dart
2026-01-30 17:08:49 +08:00
171f556b40 viersion 1.2.3 2026-01-30 16:06:58 +08:00
55eade54b6 问题修改 2026-01-30 16:02:35 +08:00
bc99ffd691 增加参数 2026-01-30 13:01:50 +08:00
aa52a56bcf 地图相关的修改 2026-01-30 11:36:33 +08:00
73343ca297 调整 2026-01-30 10:01:31 +08:00
d09faac1d2 调整 2026-01-29 19:26:59 +08:00
1177be821a 样式调整 2026-01-29 17:01:21 +08:00
e59b89c225 Merge branch 'dev_feature' into dev
ui调整
# Conflicts:
#	ln_jq_app/lib/pages/b_page/reservation/controller.dart
#	ln_jq_app/lib/pages/b_page/site/controller.dart
#	ln_jq_app/lib/pages/b_page/site/view.dart
#	ln_jq_app/lib/pages/c_page/mine/view.dart
#	ln_jq_app/lib/pages/c_page/reservation/controller.dart
#	ln_jq_app/lib/pages/c_page/reservation/view.dart
#	ln_jq_app/lib/pages/login/view.dart
2026-01-29 11:45:17 +08:00
79fe3257b5 状态样式 2026-01-28 17:59:26 +08:00
55569839a7 搜索框 2026-01-28 16:27:37 +08:00
7112d70aba 站点样式 2026-01-28 15:00:30 +08:00
f8a8ecb0ed 司机 预约 样式修改 2026-01-27 17:35:37 +08:00
18c04272e2 车辆信息的部分 2026-01-27 13:57:14 +08:00
14e7fb3d78 首页ui 2026-01-27 13:15:53 +08:00
5ffaf81223 欢迎页 登录 2026-01-27 09:21:36 +08:00
907983a1d1 登录修改 2026-01-26 14:07:58 +08:00
9fdca9136d 导航栏 2026-01-23 17:00:17 +08:00
16bae6a1e9 车辆信息 ui 2026-01-23 15:02:16 +08:00
aabfbfae0c 车辆样式调整 2026-01-23 10:49:02 +08:00
5236670e7c ui修改 2026-01-23 09:22:07 +08:00
cf3ad579d3 新ui调整 2026-01-22 17:29:54 +08:00
70a752b6e5 协议同意 2026-01-21 14:29:03 +08:00
45f5035d1b 更换logo 2026-01-19 10:13:37 +08:00
f792915429 bugfix 2026-01-16 17:47:53 +08:00
edbacc502b 线上域名修改 2026-01-16 17:25:52 +08:00
5722e3ace0 ios build 2026-01-16 15:00:45 +08:00
d41b21654a 改用账号推送 2026-01-16 14:35:11 +08:00
2eb059defd 定时器黄字2位小数 2026-01-16 13:35:38 +08:00
fbcc85af2a 站点增加消息入口 2026-01-16 13:06:35 +08:00
9a97b56505 定时器显示调整 2026-01-15 17:55:07 +08:00
8302d7c179 优化问题修改 2026-01-15 16:28:59 +08:00
e7a9e4483a 优化定时器 弹窗 2026-01-15 13:29:02 +08:00
9b64fdfa52 优化定时器 2026-01-15 13:24:02 +08:00
d8f335eb4e 优化 2026-01-15 09:30:47 +08:00
d1b7a9eb76 v1.2.2 version 2026-01-14 13:21:54 +08:00
f25feaa55a 联调修改结构 2026-01-14 13:18:22 +08:00
16639e2384 站点增加广播 2026-01-12 17:52:08 +08:00
20ef495571 非营业状态 增加时间选择 2026-01-12 16:41:36 +08:00
285a20f070 地址切换 2026-01-12 09:13:37 +08:00
baee5dba83 消息中心,待测试 2026-01-08 15:33:40 +08:00
7d9c879a4e 消息中心 2026-01-07 17:43:22 +08:00
953e5e773c 消息入口 2026-01-07 16:02:10 +08:00
8a4bc1d1ab 取消预约按钮 2026-01-07 09:59:12 +08:00
c57c849073 样式交互修改 2026-01-06 17:57:56 +08:00
6cc123f272 更换导航栏位置 2026-01-05 14:09:15 +08:00
5168b23609 增加请求头 2026-01-04 16:59:08 +08:00
c5299dd655 增加版本号 2026-01-04 10:27:39 +08:00
4bedd8c04b 优化 2025-12-31 17:37:07 +08:00
295b71c819 推送配置,测试 2025-12-31 17:22:13 +08:00
6629c8047f Merge branch 'dev' 2025-12-25 10:41:04 +08:00
bfa615a7f4 扫码无权限优化,司机预约多弹窗 2025-12-25 10:40:26 +08:00
288d629f99 bugfix 2025-12-24 10:53:34 +08:00
15fdbc7043 v1.2.1 2025-12-23 10:43:12 +08:00
f2f2348b54 逆地理编码 2025-12-23 10:18:44 +08:00
9ba152b3c3 v1.2.1 待发布 2025-12-22 16:15:32 +08:00
21a528d6d1 调整和修改 2025-12-19 13:18:04 +08:00
62ca3888d3 历史统计数据,错误提示 2025-12-18 15:20:54 +08:00
3ec56a925c 修改搜索条件 2025-12-18 10:33:24 +08:00
95c08818cb 取消dark主题 2025-12-18 09:03:08 +08:00
42355bd1ef 站点-新增历史记录 2025-12-17 17:40:02 +08:00
fe2ce75cec 修改绑定车辆接口
登录后查询车辆信息
2025-12-17 09:18:34 +08:00
98cac8a0a5 预约列表增加筛选条件 2025-12-16 16:18:28 +08:00
9ce46a0c7d 加氢站默认选择,演示账号数据
附件默认显示,详情
2025-12-16 11:58:00 +08:00
266a43c09d 主题色 2025-12-16 09:54:32 +08:00
8434301d1f 版本号 2025-12-12 10:34:24 +08:00
adfe3bf34e 版本号修改 2025-12-11 15:50:25 +08:00
6a3c6db7a8 Merge branch 'dev'
司机端-加氢预约  预约时间段修改  1、开始结束时间整合成一个  2、时间只开放当日和次日
司机端-加氢预约  提交预约不可提交限制 1、非营业站点 2、预约时间不可重复
司机端-预约列表 1、新增到站时间、氢量修改 2、显示拒绝加氢原因
加氢站-加氢预约  1、新增当日预约加车牌/手机号筛选  2、新增加氢总量 未加氢量  3、 确认拒绝流程优化:完成可填写具体加氢量,新增拒绝原因
# Conflicts:
#	ln_jq_app/lib/common/styles/theme.dart
2025-12-11 15:48:10 +08:00
4ace3c9f27 刷新条件 2025-12-11 13:45:26 +08:00
b52659df6c 司机预约 显示拒绝原因 2025-12-11 13:27:28 +08:00
20877a3eb1 增加弹窗限制条件 2025-12-11 10:36:59 +08:00
69875f27ad 搜索字段 2025-12-10 17:51:00 +08:00
829f5e454e 文本处理 2025-12-10 17:37:56 +08:00
1c916a7fad 调整司机预约时间
站点工单状态
2025-12-10 17:34:46 +08:00
88b24b5228 修改配置ios 2025-12-10 13:16:10 +08:00
ee8cbde296 logo 2025-12-09 13:16:09 +08:00
f638704fba ios 说明 2025-12-09 11:24:45 +08:00
4c63d5ebf3 bugfix 2025-12-08 17:52:55 +08:00
a3fb0d8018 站点 确认拒绝流程 2025-12-08 09:32:06 +08:00
88b16ca69e 今日预约搜索 2025-12-03 16:00:18 +08:00
ba3467810c 新增数据 2025-12-03 15:04:58 +08:00
0bfedd54cb 可编辑预约 2025-12-03 10:39:34 +08:00
f25d7e4567 增加 加氢站可用标记 2025-12-02 10:36:56 +08:00
4cdedff654 默认时间段修改 2025-12-01 17:03:16 +08:00
4f99ab4164 修改选择器 2025-12-01 15:27:00 +08:00
cdc5af7f45 限制 2025-12-01 13:59:02 +08:00
20e5e58ded 增加提交预约限制 2025-12-01 13:56:17 +08:00
93a39e440a v1.1.0 2025-11-28 14:48:03 +08:00
ec1c554eb3 Merge branch 'dev'
v1.1.0
2025-11-28 10:12:44 +08:00
d3b7ab8fbe 先限制输入起点 2025-11-28 10:09:18 +08:00
5609594439 地图修改 2025-11-27 17:21:07 +08:00
da4149ec60 调整地图样式 2025-11-27 15:25:58 +08:00
aa30d13b91 更新插件 2025-11-27 09:30:52 +08:00
b854d295f9 调整预约时间格式 2025-11-26 17:33:04 +08:00
26a24efeb8 限制条件 2025-11-26 13:44:00 +08:00
496d77482d logo 预约弹出列表
车辆无绑定弹窗
2025-11-26 09:20:42 +08:00
247 changed files with 20230 additions and 3316 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(flutter build apk --debug 2>&1 | tail -15)",
"Bash(flutter build apk --debug 2>&1 | tail -10)"
]
}
}

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(./gradlew assembleDebug --stacktrace)"
]
}
}

View File

@@ -1,3 +1,6 @@
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,6 +8,14 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.inputStream().use { input ->
keystoreProperties.load(input.reader(Charsets.UTF_8))
}
}
android {
namespace = "com.lnkj.ln_jq_app"
compileSdk = flutter.compileSdkVersion
@@ -21,20 +32,35 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.lnkj.ln_jq_app"
applicationId = "com.lingniu.driver"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
versionCode = 7
versionName = "1.2.4"
}
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String?
keyPassword = keystoreProperties["keyPassword"] as String?
val storeFilePath = keystoreProperties["storeFile"] as String?
storeFile = if (storeFilePath != null) file(storeFilePath) else null
storePassword = keystoreProperties["storePassword"] as String?
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
getByName("release") {
// 使用上面定义的 release 签名
signingConfig = signingConfigs.getByName("release")
// 修复混淆规则引用语法
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
@@ -42,3 +68,9 @@ android {
flutter {
source = "../.."
}
dependencies {
implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("androidx.appcompat:appcompat:1.7.1")
}

View File

@@ -0,0 +1,88 @@
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
# 忽略 Google Play Core 相关的缺失警告(解决你目前的报错)
-dontwarn com.google.android.play.core.**
# Flutter 基础规则
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn com.huawei.android.os.BuildEx$VERSION
-dontwarn com.huawei.hianalytics.process.HiAnalyticsConfig$Builder
-dontwarn com.huawei.hianalytics.process.HiAnalyticsConfig
-dontwarn com.huawei.hianalytics.process.HiAnalyticsInstance$Builder
-dontwarn com.huawei.hianalytics.process.HiAnalyticsInstance
-dontwarn com.huawei.hianalytics.process.HiAnalyticsManager
-dontwarn com.huawei.hianalytics.util.HiAnalyticTools
-dontwarn com.huawei.hms.availableupdate.UpdateAdapterMgr
-dontwarn com.huawei.libcore.io.ExternalStorageFile
-dontwarn com.huawei.libcore.io.ExternalStorageFileInputStream
-dontwarn com.huawei.libcore.io.ExternalStorageFileOutputStream
-dontwarn com.huawei.libcore.io.ExternalStorageRandomAccessFile
-dontwarn org.android.netutil.PingEntry
-dontwarn org.android.netutil.PingResponse
-dontwarn org.android.netutil.PingTask
-dontwarn org.bouncycastle.crypto.BlockCipher
-dontwarn org.bouncycastle.crypto.engines.AESEngine
-dontwarn org.bouncycastle.crypto.prng.SP800SecureRandom
-dontwarn org.bouncycastle.crypto.prng.SP800SecureRandomBuilder
-keepclasseswithmembernames class ** {
native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-keep class com.ut.** {*;}
-keep class com.ta.** {*;}
-keep class anet.**{*;}
-keep class anetwork.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-keep class android.os.**{*;}
-keep class org.json.**{*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
-dontwarn anetwork.**
-dontwarn com.ut.**
-dontwarn com.ta.**
#3D 地图 V5.0.0之前:
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.amap.mapcore.*{*;}
-keep class com.amap.api.trace.**{*;}
#3D 地图 V5.0.0之后:
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.**{*;}
-keep class com.amap.api.trace.**{*;}
#定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}
#搜索
-keep class com.amap.api.services.**{*;}
#2D地图
-keep class com.amap.api.maps2d.**{*;}
-keep class com.amap.api.mapcore2d.**{*;}
#导航
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}

View File

@@ -1,11 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -14,20 +17,54 @@
<!--定位权限-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<!--用于申请调用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" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="小羚羚"
android:requestLegacyExternalStorage="true"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/logo"
android:label="小羚羚">
<!-- 高德地图Key -->
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="92495660f7bc990cb475426c47c03b65" />
<!-- 高德地图定位服务 -->
<service android:name="com.amap.api.location.APSService" />
<!--高德导航-->
<activity
android:name="com.amap.api.navi.AmapRouteActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation" />
<!--自定义导航 Activity-->
<activity
android:name="com.lnkj.ln_jq_app.NavigationActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
android:screenOrientation="portrait" />
<!--搜索目的地 Activity-->
<activity
android:name="com.lnkj.ln_jq_app.SearchDestinationActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
@@ -35,8 +72,7 @@
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -48,6 +84,48 @@
android:name="flutterEmbedding"
android:value="2" />
<!-- 请填写你自己的- appKey -->
<meta-data
android:name="com.alibaba.app.appkey"
android:value="335642645" />
<!-- 请填写你自己的appSecret -->
<meta-data
android:name="com.alibaba.app.appsecret"
android:value="39628204345a4240b5b645b68a5896c7" />
<!-- 接收推送消息 -->
<receiver
android:name="com.aliyun.ams.push.AliyunPushMessageReceiver"
android:exported="false"> <!-- 为保证receiver安全建议设置不可导出如需对其他应用开放可通过androidpermission进行限制 -->
<intent-filter>
<action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" />
</intent-filter>
<intent-filter>
<action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" />
</intent-filter>
<intent-filter>
<action android:name="com.alibaba.sdk.android.push.RECEIVE" />
</intent-filter>
</receiver>
<!-- 辅助弹窗Activity -->
<activity
android:name="com.aliyun.ams.push.PushPopupActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="${applicationId}"
android:path="/thirdpush"
android:scheme="agoo" />
</intent-filter>
</activity>
</application>
<!-- Required to query activities that can process text, see:

View File

@@ -1,6 +1,161 @@
package com.lnkj.ln_jq_app;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.lnkj.ln_jq_app/map";
private static final String TAG = "MainActivity";
// 权限请求码
private static final int PERMISSION_REQUEST_CODE = 1001;
private NativeMapView mapView;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// 注册高德地图导航Platform View
flutterEngine
.getPlatformViewsController()
.getRegistry()
.registerViewFactory(
"NativeFirstPage",
new NativeMapFactory(this)
);
// 设置 Activity 实例到地图
NativeMapFactory.setActivity(this);
}
/**
* 获取当前系统版本需要申请的权限列表
*/
private String[] getRequiredPermissions() {
List<String> permissions = new ArrayList<>();
// 定位权限是必须的
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
// 存储权限处理Android 13 (API 33) 以下才需要申请 legacy 存储权限
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
return permissions.toArray(new String[0]);
}
/**
* 检查并申请权限
*/
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] requiredPermissions = getRequiredPermissions();
List<String> deniedPermissions = new ArrayList<>();
for (String permission : requiredPermissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permission);
}
}
if (!deniedPermissions.isEmpty()) {
ActivityCompat.requestPermissions(
this,
deniedPermissions.toArray(new String[0]),
PERMISSION_REQUEST_CODE
);
} else {
Log.d(TAG, "所有必要权限已授予");
if (mapView != null) {
mapView.startLocation();
}
}
} else {
if (mapView != null) {
mapView.startLocation();
}
}
}
private void requestPermissions() {
checkAndRequestPermissions();
}
public void setMapView(NativeMapView mapView) {
this.mapView = mapView;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.dispose();
mapView = null;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 处理从搜索目的地页面返回的结果
if (requestCode == 1000 && resultCode == RESULT_OK && data != null) {
String name = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_NAME);
String address = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_ADDRESS);
double lat = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LAT, 0);
double lon = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LON, 0);
String district = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_DISTRICT);
if (name != null && lat != 0 && lon != 0) {
// 获取地图实例并设置目的地
NativeMapView mapView = NativeMapFactory.getMapView();
if (mapView != null) {
mapView.setDestination(name, address, lat, lon, district);
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean locationGranted = false;
for (int i = 0; i < permissions.length; i++) {
if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i])
&& grantResults[i] == PackageManager.PERMISSION_GRANTED) {
locationGranted = true;
break;
}
}
if (locationGranted) {
if (mapView != null) {
mapView.startLocation();
}
} else {
// 只有在定位权限确实被拒绝时才弹出提示
Toast.makeText(this, "请授予应用定位权限以正常使用地图功能", Toast.LENGTH_LONG).show();
}
}
}
}

View File

@@ -0,0 +1,47 @@
package com.lnkj.ln_jq_app;
import android.app.Activity;
import android.content.Context;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
/**
* 高德地图导航 Platform View Factory
* 对应iOS的NativeViewFactory
*/
public class NativeMapFactory extends PlatformViewFactory {
private static final String VIEW_TYPE_ID = "NativeFirstPage";
private static NativeMapView mapViewInstance = null;
private final Context context;
public NativeMapFactory(Context context) {
super(StandardMessageCodec.INSTANCE);
this.context = context;
}
@Override
public PlatformView create(Context context, int viewId, Object args) {
mapViewInstance = new NativeMapView(context, viewId, args);
return mapViewInstance;
}
/**
* 获取地图实例供MainActivity使用
*/
public static NativeMapView getMapView() {
return mapViewInstance;
}
/**
* 设置 Activity 实例
*/
public static void setActivity(Activity activity) {
if (mapViewInstance != null) {
mapViewInstance.setActivity(activity);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,408 @@
package com.lnkj.ln_jq_app;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.amap.api.maps.AMapException;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.AMapNaviView;
import com.amap.api.navi.AMapNaviViewListener;
import com.amap.api.navi.AMapNaviViewOptions;
import com.amap.api.navi.enums.NaviType;
import com.amap.api.navi.enums.PathPlanningStrategy;
import com.amap.api.navi.model.AMapCalcRouteResult;
import com.amap.api.navi.model.AMapCarInfo;
import com.amap.api.navi.model.AMapLaneInfo;
import com.amap.api.navi.model.AMapModelCross;
import com.amap.api.navi.model.AMapNaviCameraInfo;
import com.amap.api.navi.model.AMapNaviCross;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.AMapNaviRouteNotifyData;
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
import com.amap.api.navi.model.AMapServiceAreaInfo;
import com.amap.api.navi.model.AimLessModeCongestionInfo;
import com.amap.api.navi.model.AimLessModeStat;
import com.amap.api.navi.model.NaviInfo;
import com.amap.api.navi.model.NaviPoi;
import java.util.List;
import androidx.activity.ComponentActivity;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
/**
* 导航
*/
public class NavigationActivity extends ComponentActivity implements AMapNaviListener {
private static final String TAG = "NavigationActivity";
private AMapNavi mAMapNavi;
private AMapNaviView naviView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建导航视图
naviView = new AMapNaviView(this);
naviView.onCreate(savedInstanceState);
naviView.setAMapNaviViewListener(new AMapNaviViewListener() {
@Override
public void onNaviSetting() {
}
@Override
public void onNaviCancel() {
finish();
}
@Override
public boolean onNaviBackClick() {
return false;
}
@Override
public void onNaviMapMode(int i) {
}
@Override
public void onNaviTurnClick() {
}
@Override
public void onNextRoadClick() {
}
@Override
public void onScanViewButtonClick() {
}
@Override
public void onLockMap(boolean b) {
}
@Override
public void onNaviViewLoaded() {
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
});
AMapNaviViewOptions aMapNaviViewOptions = new AMapNaviViewOptions();
Bitmap carBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.car);
aMapNaviViewOptions.setCarBitmap(carBitmap);
aMapNaviViewOptions.setSettingMenuEnabled(false);
naviView.setViewOptions(aMapNaviViewOptions);
// 创建 AMapNavi 实例
try {
mAMapNavi = AMapNavi.getInstance(this);
mAMapNavi.addAMapNaviListener(this);
} catch (AMapException e) {
Log.e(TAG, "初始化导航失败", e);
Toast.makeText(this, "初始化导航失败", Toast.LENGTH_SHORT).show();
finish();
return;
}
// 添加到容器
FrameLayout container = new FrameLayout(this);
container.addView(naviView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(container);
// 获取 Intent 中的参数
processIntent(getIntent());
// 处理返回键
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
finish();
}
});
Log.d(TAG, "NavigationActivity created");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent);
}
/**
* 处理 Intent 参数并启动导航
*/
@SuppressWarnings("unchecked")
private void processIntent(Intent intent) {
if (intent == null) {
Log.w(TAG, "Intent is null");
return;
}
try {
// 获取起点、终点和途经点
NaviPoi startNaviPoi = (NaviPoi) intent.getParcelableExtra("startPoi");
NaviPoi endNaviPoi = (NaviPoi) intent.getParcelableExtra("endPoi");
List<NaviPoi> wayPoints = intent.getParcelableArrayListExtra("wayPoints");
if (wayPoints != null) {
Log.d("Navi", "获取到途径点数量: " + wayPoints.size());
}
if (startNaviPoi == null || endNaviPoi == null) {
Log.e(TAG, "Missing start or end point");
return;
}
// 设置车辆信息
AMapCarInfo carInfo = (AMapCarInfo) intent.getParcelableExtra("carInfo");
if (carInfo != null) {
mAMapNavi.setCarInfo(carInfo);
}
mAMapNavi.setUseInnerVoice(true);
// 计算并启动导航
mAMapNavi.calculateDriveRoute(startNaviPoi, endNaviPoi, wayPoints, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
Log.d(TAG, "Navigation started from: " + startNaviPoi.getName() + " - " + endNaviPoi.getName());
} catch (Exception e) {
Log.e(TAG, "Process intent error", e);
Toast.makeText(this, "导航启动失败", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onResume() {
super.onResume();
if (naviView != null) {
naviView.onResume();
}
}
@Override
protected void onPause() {
super.onPause();
if (naviView != null) {
naviView.onPause();
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (naviView != null) {
naviView.onSaveInstanceState(outState);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 停止导航
if (mAMapNavi != null) {
mAMapNavi.stopNavi();
mAMapNavi.removeAMapNaviListener(this);
}
if (naviView != null) {
naviView.onDestroy();
}
Log.d(TAG, "NavigationActivity destroyed");
}
private int dp2px(float dp) {
float density = getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5f);
}
// ============ AMapNaviListener 回调实现 ============
@Override
public void onInitNaviFailure() {
Log.e(TAG, "onInitNaviFailure");
}
@Override
public void onInitNaviSuccess() {
Log.d(TAG, "onInitNaviSuccess");
}
@Override
public void onStartNavi(int i) {
Log.d(TAG, "onStartNavi: " + i);
}
@Override
public void onTrafficStatusUpdate() {
}
@Override
public void onLocationChange(AMapNaviLocation aMapNaviLocation) {
}
@Override
public void onGetNavigationText(int i, String s) {
}
@Override
public void onGetNavigationText(String s) {
}
@Override
public void onEndEmulatorNavi() {
}
@Override
public void onArriveDestination() {
Log.d(TAG, "onArriveDestination");
}
@Override
public void onCalculateRouteFailure(int i) {
Log.e(TAG, "onCalculateRouteFailure: " + i);
Toast.makeText(this, "路径计算失败: " + i, Toast.LENGTH_SHORT).show();
}
@Override
public void updateCameraInfo(AMapNaviCameraInfo[] aMapNaviCameraInfos) {
}
@Override
public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {
}
@Override
public void onServiceAreaUpdate(AMapServiceAreaInfo[] aMapServiceAreaInfos) {
}
@Override
public void showCross(AMapNaviCross aMapNaviCross) {
}
@Override
public void hideCross() {
}
@Override
public void showModeCross(AMapModelCross aMapModelCross) {
}
@Override
public void hideModeCross() {
}
@Override
public void showLaneInfo(AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1) {
}
@Override
public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {
}
@Override
public void hideLaneInfo() {
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
Log.d(TAG, "onCalculateRouteSuccess");
if (mAMapNavi != null) {
mAMapNavi.startNavi(NaviType.GPS);
}
}
@Override
public void notifyParallelRoad(int i) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {
}
@Override
public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {
}
@Override
public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {
}
@Override
public void onPlayRing(int i) {
}
@Override
public void onCalculateRouteSuccess(AMapCalcRouteResult aMapCalcRouteResult) {
Log.d(TAG, "onCalculateRouteSuccess (result)");
if (mAMapNavi != null) {
mAMapNavi.startNavi(NaviType.GPS);
}
}
@Override
public void onCalculateRouteFailure(AMapCalcRouteResult aMapCalcRouteResult) {
Log.e(TAG, "onCalculateRouteFailure: " + aMapCalcRouteResult.getErrorCode());
Toast.makeText(this, "路径计算失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {
}
@Override
public void onGpsSignalWeak(boolean b) {
}
@Override
public void onReCalculateRouteForYaw() {
Log.d(TAG, "onReCalculateRouteForYaw");
}
@Override
public void onReCalculateRouteForTrafficJam() {
}
@Override
public void onArrivedWayPoint(int i) {
}
@Override
public void onGpsOpenStatus(boolean b) {
}
@Override
public void onNaviInfoUpdate(NaviInfo naviInfo) {
}
}

View File

@@ -0,0 +1,261 @@
package com.lnkj.ln_jq_app;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import java.util.ArrayList;
import java.util.List;
import androidx.activity.ComponentActivity;
/**
* 搜索目的地页面
*/
public class SearchDestinationActivity extends ComponentActivity implements Inputtips.InputtipsListener {
private static final String TAG = "SearchDestinationActivity";
public static final String EXTRA_RESULT_NAME = "result_name";
public static final String EXTRA_RESULT_ADDRESS = "result_address";
public static final String EXTRA_RESULT_LAT = "result_lat";
public static final String EXTRA_RESULT_LON = "result_lon";
public static final String EXTRA_RESULT_DISTRICT = "result_district";
// 静态回调接口
public interface DestinationCallback {
void onDestinationSelected(String name, String address, double lat, double lon, String district);
}
private static DestinationCallback callback;
public static void setCallback(DestinationCallback callback) {
SearchDestinationActivity.callback = callback;
}
private EditText searchInput;
private ListView suggestionList;
private ImageView backBtn;
private ArrayAdapter<String> suggestionAdapter;
private List<Tip> currentTipList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.activity_search_destination);
initViews();
setupListeners();
// 自动显示键盘
showKeyboard();
} catch (Exception e) {
Log.e(TAG, "Error in onCreate", e);
e.printStackTrace();
finish();
}
}
private void initViews() {
try {
Log.d(TAG, "initViews started");
searchInput = findViewById(R.id.search_input);
suggestionList = findViewById(R.id.suggestion_list);
backBtn = findViewById(R.id.back_btn);
currentTipList = new ArrayList<>();
Log.d(TAG, "initViews completed: searchInput=" + (searchInput != null ? "ok" : "null") + ", suggestionList=" + (suggestionList != null ? "ok" : "null") + ", backBtn=" + (backBtn != null ? "ok" : "null"));
} catch (Exception e) {
Log.e(TAG, "Error in initViews", e);
e.printStackTrace();
}
suggestionAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<>()) {
@Override
public View getView(int position, android.view.View convertView, android.view.ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView text = view.findViewById(android.R.id.text1);
text.setTextColor(Color.parseColor("#333333"));
text.setTextSize(14);
view.setBackgroundColor(Color.TRANSPARENT);
return view;
}
};
suggestionList.setAdapter(suggestionAdapter);
suggestionList.setDivider(new ColorDrawable(Color.LTGRAY));
suggestionList.setDividerHeight(1);
}
private void setupListeners() {
try {
Log.d(TAG, "setupListeners started");
// 返回按钮
backBtn.setOnClickListener(v -> {
Log.d(TAG, "Back button clicked");
hideKeyboard();
finish();
});
// 文本变化监听
searchInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d(TAG, "Text changed: " + (s != null ? s.toString() : "null") + ", length=" + (s != null ? s.length() : 0));
if (s.length() > 1) {
searchLocation(s.toString());
} else {
suggestionList.setVisibility(View.GONE);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
// 列表点击
suggestionList.setOnItemClickListener((parent, view, position, id) -> {
Log.d(TAG, "Suggestion clicked at position: " + position);
try {
Tip tip = currentTipList.get(position);
if (tip.getPoint() != null) {
String name = tip.getName();
String address = tip.getAddress();
String district = tip.getDistrict();
double lat = tip.getPoint().getLatitude();
double lon = tip.getPoint().getLongitude();
Log.d(TAG, "Selected destination: " + name + ", lat=" + lat + ", lon=" + lon);
// 优先使用静态回调
if (callback != null) {
callback.onDestinationSelected(name, address, lat, lon, district);
} else {
// 降级使用 Intent 回调
Intent result = new Intent();
result.putExtra(EXTRA_RESULT_NAME, name);
result.putExtra(EXTRA_RESULT_ADDRESS, address);
result.putExtra(EXTRA_RESULT_LAT, lat);
result.putExtra(EXTRA_RESULT_LON, lon);
result.putExtra(EXTRA_RESULT_DISTRICT, district);
setResult(RESULT_OK, result);
}
hideKeyboard();
finish();
}
} catch (Exception e) {
Log.e(TAG, "Error handling suggestion click", e);
e.printStackTrace();
}
});
Log.d(TAG, "setupListeners completed");
} catch (Exception e) {
Log.e(TAG, "Error in setupListeners", e);
e.printStackTrace();
}
}
private void searchLocation(String keyword) {
try {
Log.d(TAG, "Searching for: " + keyword);
InputtipsQuery query = new InputtipsQuery(keyword, "");
query.setCityLimit(false);
Inputtips inputtips = new Inputtips(this, query);
inputtips.setInputtipsListener(this);
inputtips.requestInputtipsAsyn();
} catch (Exception e) {
Log.e(TAG, "Error in searchLocation", e);
e.printStackTrace();
}
}
@Override
public void onGetInputtips(List<Tip> tipList, int rCode) {
Log.d(TAG, "onGetInputtips called, rCode=" + rCode + ", tipList size=" + (tipList != null ? tipList.size() : "null"));
if (rCode == 1000 && tipList != null && !tipList.isEmpty()) {
currentTipList.clear();
currentTipList.addAll(tipList);
List<String> suggestionNames = new ArrayList<>();
for (Tip tip : tipList) {
String name = tip.getName();
String district = tip.getDistrict();
String displayText = district != null && !district.isEmpty() ? name + " " + district : name;
suggestionNames.add(displayText);
}
runOnUiThread(() -> {
suggestionAdapter.clear();
suggestionAdapter.addAll(suggestionNames);
suggestionAdapter.notifyDataSetChanged();
if (suggestionNames.size() > 0) {
suggestionList.setVisibility(View.VISIBLE);
} else {
suggestionList.setVisibility(View.GONE);
}
});
} else {
runOnUiThread(() -> {
suggestionList.setVisibility(View.GONE);
});
}
}
private void showKeyboard() {
searchInput.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);
}
}
private void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 清理回调,避免内存泄漏
callback = null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景颜色:使用浅灰色 -->
<solid android:color="#ffffffff" />
<!-- 圆角8dp左右 -->
<corners android:radius="8dp" />
<!-- 边框:可选,如果需要更清晰的边缘可以加上 -->
<stroke android:width="1dp" android:color="#EAEAEA" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners
android:topLeftRadius="24dp"
android:topRightRadius="24dp"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp" />
</shape>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="center">
<ImageView
android:id="@+id/back_btn"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="10dp"
android:src="@drawable/back" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="选择地点"
android:textColor="#000000"
android:textSize="18sp" />
</LinearLayout>
<EditText
android:id="@+id/search_input"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="12dp"
android:background="@drawable/bg_search_input"
android:hint="输入地址"
android:imeOptions="actionDone"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:singleLine="true"
android:textColor="#333333"
android:textColorHint="#999999"
android:textSize="14sp" />
<ListView
android:id="@+id/suggestion_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="8dp"
android:visibility="gone" />
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -3,6 +3,17 @@ allprojects {
// 使用阿里云镜像
maven("https://maven.aliyun.com/repository/public")
maven("https://maven.aliyun.com/repository/google")
maven(
"https://maven.aliyun.com/nexus/content/repositories/releases/"
)
// 集成华为通道需要配置 HMS Core SDK 的 Maven地址
maven(
"https://developer.huawei.com/repo/"
)
maven(
"https://developer.hihonor.com/repo"
)
google()
mavenCentral()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,101 +1,276 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>导航规划</title>
<style>
html, body, #container { width: 100%; height: 100%; margin: 0; }
<title>路径规划</title>
/* 搜索栏样式 */
<style>
/* --- 全局样式 --- */
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
/* --- 暴力隐藏高德自带的导流链接和Logo (关键) --- */
.amap-callamap,
.amap-lib-driving-callBtn,
.amap-copyright,
.amap-logo{bottom: 60px}
/* 去除高德默认的 label 边框 and 背景 */
.amap-marker-label {
border: none !important;
background-color: transparent !important;
}
/* 自定义气泡样式 */
.custom-bubble {
position: relative;
background: rgba(51, 51, 51, 0.7);
/* #33333399 对应 rgba(51,51,51,0.7) */
color: #fff;
padding: 6px 15px;
border-radius: 20px;
/* 圆角 */
font-size: 13px;
white-space: nowrap;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
text-align: center;
line-height: 1.4;
}
/* 气泡下方的向下小箭头 */
.custom-bubble::after {
content: '';
position: absolute;
bottom: -6px;
/* 箭头高度 */
left: 50%;
transform: translateX(-50%);
border-width: 6px 6px 0 6px;
border-style: solid;
border-color: rgba(51, 51, 51, 0.7) transparent transparent transparent;
}
#panel .amap-call {
display: none;
}
/* --- 搜索栏样式 --- */
#search-box {
position: absolute;
top: 10px;
top: 40px;
left: 10px;
right: 10px;
z-index: 100;
background: #fff;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
gap: 8px;
}
.input-row { display: flex; gap: 5px; }
input { flex: 1; padding: 8px; border: 1px solid #eee; border-radius: 4px; }
button { padding: 0 15px; background: #3366FF; color: #fff; border: none; border-radius: 4px; font-weight: bold; }
/* 导航结果面板 (仿高德原生) */
.input-row {
display: flex;
gap: 8px;
align-items: center;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #eee;
border-radius: 4px;
background-color: #f9f9f9;
font-size: 14px;
}
button {
padding: 0 15px;
height: 38px;
background: #017143FF;
color: #fff;
border: none;
border-radius: 4px;
font-weight: bold;
font-size: 14px;
}
/* --- 导航结果面板 (底部弹出) --- */
#panel {
position: fixed;
bottom: 0;
bottom: 95px;
left: 0;
width: 100%;
height: 30%; /* 占据底部30% */
height: 35%;
/* 面板高度 */
background-color: white;
overflow-y: auto;
border-top: 1px solid #ccc;
border-top: 1px solid #eee;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 99;
display: none; /* 默认隐藏,规划成功后显示 */
display: none;
/* 默认隐藏 */
}
/* --- 自定义定位按钮样式 --- */
#location-btn {
position: fixed;
right: 10px;
bottom: 105px;
/* 默认位置 */
width: 44px;
height: 44px;
background-color: #fff;
border-radius: 50%;
/* 圆形更符合现代审美 */
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
z-index: 150;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: bottom 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
/* 平滑动画 */
}
#location-btn:active {
background-color: #f2f2f2;
}
#location-btn svg {
width: 24px;
height: 24px;
fill: #555;
}
/* --- 调整比例尺位置 --- */
.amap-scalecontrol {
/* 初始状态:避开底部的定位按钮或留出安全间距 */
bottom: 110px !important;
left: 10px !important;
transition: bottom 0.3s ease;
/* 增加平滑动画 */
}
/* --- 当路径规划面板显示时,比例尺自动上移 --- */
body.panel-active .amap-scalecontrol {
bottom: 38% !important;
/* 移动到面板上方 (面板高度35% + 3%间距) */
}
/* --- 关键:当 body 有 panel-active 类时,按钮上移 --- */
body.panel-active #location-btn {
bottom: 45%;
/* 对应 #panel 的 height + 一点间距 */
}
</style>
<!-- 1. 必须最先配置安全密钥 -->
<!-- 1. 配置安全密钥 -->
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode: '0529b72df6bf0c577ff2182cb8b1d970',
securityJsCode: 'aa3a22c19ed76b27f8a587555d6981c8',
}
</script>
<!-- 2. 加载地图和插件 -->
<!-- 注意如果你要货车规划需要在plugin里加上 AMap.TruckDriving -->
<script src="https://webapi.amap.com/maps?v=2.0&key=2cc1d822e313307fe311c3127a1deeb5&plugin=AMap.MoveAnimation,AMap.Driving,AMap.TruckDriving,AMap.AutoComplete"></script>
<!-- 2. 加载地图和插件 (去掉了 Geolocation 插件,避免弹窗) -->
<script
src="https://webapi.amap.com/maps?v=2.0&key=ecd74ece8cb14c9dad67675f83c3274d&plugin=AMap.MoveAnimation,AMap.Driving,AMap.TruckDriving,AMap.AutoComplete,AMap.ToolBar,AMap.Scale,AMap.Geocoder">
</script>
</head>
<body>
<div id="search-box">
<div class="input-row">
<input id="startInput" placeholder="起点: 默认使用当前位置" />
<input id="startInput" placeholder="起点: 请输入当前地点" onfocus="this.select()" />
</div>
<div class="input-row">
<input id="endInput" placeholder="终点: 请输入目的地" />
<input id="endInput" placeholder="终点: 请输入目的地" onfocus="this.select()" />
<button onclick="startRouteSearch()">路径规划</button>
</div>
</div>
<div id="container"></div>
<div id="panel"></div>
<script>
var map, marker, driving, truckDriving;
var currentLat, currentLng;
<!-- 自定义定位按钮 -->
<div id="location-btn" onclick="backToLocation()">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path
d="M512 85.333333a426.666667 426.666667 0 1 0 0 853.333334 426.666667 426.666667 0 0 0 0-853.333334z m0 149.333334a277.333333 277.333333 0 1 1 0 554.666666 277.333333 277.333333 0 0 1 0-554.666666z m0 149.333333a128 128 0 1 0 0 256 128 128 0 0 0 0-256z"
fill="#666666"></path>
</svg>
</div>
// 标记是否使用货车模式 (true: 货车, false: 轿车)
<script>
var map, marker, destMarker, driving, truckDriving, geocoder;
var currentLat, currentLng;
var isTruckMode = false;
var isInitialLocationSet = false;
var stationMarkers = []; // 存储所有站点的标记
function initMap() {
map = new AMap.Map('container', {
resizeEnable: true,
zoom: 15,
zoom: 17,
viewMode: '3D'
});
// --- 2. 初始化 geocoder ---
geocoder = new AMap.Geocoder({
city: "全国" // 设置地理编码范围
});
// 通知 Flutter 地图加载完毕
map.on('complete', function () {
console.log("JS->: Map is ready.");
if (window.flutter_inappwebview) {
window.flutter_inappwebview.callHandler('mapReady');
}
});
// 点击地图空白处重置状态
map.on('click', function() {
resetSearchState();
});
// 添加基础控件
map.addControl(new AMap.Scale());
map.addControl(new AMap.ToolBar({
visible: true,
position: {
top: '200px',
right: '10px'
} // 稍微避开定位按钮
}));
// --- 初始化轿车规划 ---
driving = new AMap.Driving({
map: map,
panel: "panel", // 结果显示在 panel div 中
panel: "panel",
policy: AMap.DrivingPolicy.LEAST_TIME
});
// --- 初始化货车规划 (按需开启) ---
// 货车需要设置 size (1:微型, 2:轻型, 3:中型, 4:重型)
// --- 初始化货车规划 ---
if (AMap.TruckDriving) {
truckDriving = new AMap.TruckDriving({
map: map,
panel: "panel",
size: 2,
policy: 0, // 0: 躲避拥堵
policy: 0,
width: 2.5,
height: 2,
load: 1,
@@ -104,107 +279,376 @@
});
}
// --- 输入提示 (自动补全) ---
new AMap.AutoComplete({ input: "startInput" });
new AMap.AutoComplete({ input: "endInput" });
// --- 输入提示 ---
new AMap.AutoComplete({
input: "startInput"
});
new AMap.AutoComplete({
input: "endInput"
});
}
// --- 被动接收 Flutter 传来的定位 ---
function updateMyLocation(lat, lng, angle) {
currentLat = lat;
currentLng = lng;
var position = [lng, lat];
/**
* 重置搜索状态,隐藏面板和路线
*/
function resetSearchState() {
if (document.body.classList.contains('panel-active')) {
console.log("JS->: 重置地图状态");
document.body.classList.remove('panel-active');
var panel = document.getElementById('panel');
panel.style.display = 'none';
if (driving) driving.clear();
}
}
/**
* 核心功能 1: 接收 Flutter 传来的定位数据
* Flutter 端调用: webViewController.evaluateJavascript("updateMyLocation(...)")
* 纬度 经度
*/
function updateMyLocation(lat, lng, angle) {
var rawLat = parseFloat(lat);
var rawLng = parseFloat(lng);
var rawAngle = parseFloat(angle);
var gps = [rawLng, rawLat];
AMap.convertFrom(gps, 'gps', function (status, result) {
if (result.info === 'ok') {
var mPoint = result.locations[0];
currentLng = mPoint.lng;
currentLat = mPoint.lat;
var position = [currentLng, currentLat];
// 更新车辆标记位置 (保持不变)
if (!marker) {
marker = new AMap.Marker({
map: map,
position: position,
icon: "https://webapi.amap.com/images/car.png",
offset: new AMap.Pixel(-26, -13),
icon: "car.png",
offset: new AMap.Pixel(-23.5, -15),
autoRotation: true,
angle: angle || 0,
angle: isNaN(rawAngle) ? 0 : rawAngle,
});
// 第一次定位移动中心
map.setCenter(position);
map.setZoom(13);
} else {
marker.setPosition(position);
if (angle) marker.setAngle(angle);
marker.moveTo(position, {
duration: 1000,
autoRotation: true
});
if (!isNaN(rawAngle)) marker.setAngle(rawAngle);
}
// --- 4. 逆地理编码并设置默认起点 ---
// 只有在第一次获取到位置时,才设置默认起点,避免覆盖用户手动输入的起点
if (!isInitialLocationSet) {
geocoder.getAddress(position, function (status, result) {
if (status === 'complete' && result.regeocode) {
let shortAddress = '';
const regeo = result.regeocode;
const addressComponent = regeo.addressComponent;
const pois = regeo.pois;
console.log("地理:" + JSON.stringify(result));
fetchStationInfo(addressComponent.province, addressComponent.city,
addressComponent.district, lat, lng);
fetchStationInfoList(lat, lng);
// 策略1: 优先使用最近的、类型合适的POI的名称
if (pois && pois.length > 0) {
// 查找第一个类型不是“商务住宅”或“地名地址信息”的POI这类POI通常是具体的建筑或地点名
const significantPoi = pois.find(p => p.type.indexOf('商务住宅') === -
1 && p.type.indexOf('地名地址信息') === -1);
if (significantPoi) {
shortAddress = significantPoi.name;
} else {
// 如果找不到就用第一个POI的名字
shortAddress = pois[0].name;
}
}
// 策略2: 如果没有POI使用"道路+门牌号"
else if (addressComponent.street && addressComponent.streetNumber) {
shortAddress = addressComponent.district +
addressComponent.township +
addressComponent.street + addressComponent.streetNumber;
}
// 策略3: 如果还没有,使用"区+乡镇"
else if (addressComponent.district) {
shortAddress = addressComponent.district + (addressComponent
.township || '');
}
// 策略4: 降级到使用完整的、但可能很长的地址
else {
shortAddress = regeo.formattedAddress;
}
// 如果拼接出的地址过长,可以再做一次截断
if (shortAddress.length > 20) {
// 可以在这里添加更复杂的截断逻辑,比如按关键字
shortAddress = regeo.formattedAddress.substring(0, 20) + '...';
}
// 将获取到的地址填充到起点输入框
document.getElementById('startInput').value = shortAddress;
isInitialLocationSet = true; // 标记为已设置,不再更新
} else {
// 如果逆地理编码失败,依然使用“当前位置”作为提示
document.getElementById('startInput').placeholder = "当前位置";
console.error('逆地理编码失败:', result);
}
});
}
}
});
}
/**
* 调用后端接口获取站点
*/
function fetchStationInfo(province, city, district, lat, lng) {
// 注意:某些直辖市在高德中 city 字段可能为空,需做兼容处理
console.log("JS->: 开始请求." + province + city + district);
var cityName = (typeof city === 'string' && city.length > 0) ? city : province;
fetch('https://beta-esg.api.lnh2e.com/appointment/station/getStationInfoByArea', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
province: province,
city: cityName,
district: district,
longitude: lng,
latitude: lat,
})
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应错误: ' + response.status);
}
return response.json(); // 解析 JSON
})
.then(res => {
// 打印完整的返回结果,方便调试观察结构
console.log("JS->: 接口完整返回:", JSON.stringify(res));
// 安全校验:判断 res.data 是否存在
if (res.code === 0 && res.data) {
if (res.data.address) {
console.log("JS->: 找到地址:", res.data.address);
var destAddress = res.data.address;
document.getElementById('endInput').value = destAddress;
// 标记终点
markDestination(destAddress, res.data.name || "目的地",
res.data.longitude, res.data.latitude
);
} else {
console.log("JS->: 接口请求成功,但该区域暂无站点地址");
}
} else {
console.log("JS->: 业务报错或无数据:", res.message);
}
})
.catch(err => console.error('JS->:获取站点信息失败:', err));
}
/**
* 获取站点列表
*/
function fetchStationInfoList(lat, lng) {
fetch('https://beta-esg.api.lnh2e.com/appointment/station/getNearbyHydrogenStationsByLocation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
longitude: lng,
latitude: lat,
})
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应错误: ' + response.status);
}
return response.json(); // 解析 JSON
})
.then(res => {
console.log("JS->:2 接口完整返回:", JSON.stringify(res));
if (res.code === 0 && res.data && Array.isArray(res.data)) {
// 1. 清除旧的站点标记
stationMarkers.forEach(m => m.setMap(null));
stationMarkers = [];
// 2. 循环标记所有加氢站
res.data.forEach(station => {
var stationIcon = new AMap.Icon({
size: new AMap.Size(32, 32),
image: 'ic_tag.png',
imageSize: new AMap.Size(32, 32)
});
var sMarker = new AMap.Marker({
map: map,
position: [station.longitude, station.latitude],
icon: stationIcon,
offset: new AMap.Pixel(-16, -32),
title: station.name,
label: {
content: '<div class="custom-bubble">' + station.name +
'</div>',
direction: 'top'
}
});
// 3. 绑定点击事件:选中即为目的地,并开始规划
sMarker.on('click', function () {
var stationName = station.name || "目的地";
document.getElementById('endInput').value = station.address ||
stationName;
// 更新当前的 destMarker
if (destMarker && destMarker !== sMarker) destMarker.setMap(null);
destMarker = sMarker;
// 直接传入坐标对象,避免关键字搜索失败
var loc = new AMap.LngLat(station.longitude, station.latitude);
startRouteSearch(loc);
});
stationMarkers.push(sMarker);
});
} else {
console.log("JS->: 业务报错或无数据:", res.message);
}
})
.catch(err => console.error('JS->:获取站点信息失败:', err));
}
/**
* 地理编码并在地图标记终点
*/
function markDestination(address, name, longitude, latitude) {
// 1. 清除旧的终点标记
if (destMarker) destMarker.setMap(null);
// 2. 创建自定义图标
var destIcon = new AMap.Icon({
size: new AMap.Size(32, 32), // 图标尺寸
image: 'ic_tag.png', // 本地图片路径
imageSize: new AMap.Size(32, 32) // 图片在图标内拉伸的大小
});
// 3. 创建标记
destMarker = new AMap.Marker({
map: map,
position: [longitude, latitude],
icon: destIcon, // 使用自定义图标
offset: new AMap.Pixel(-16, -32),
title: name,
label: {
content: '<div class="custom-bubble">' + name + '</div>',
direction: 'top'
}
});
console.log("JS->: 终点标记已添加", address);
}
/**
* 点击按钮回到当前位置
*/
function backToLocation() {
if (currentLng && currentLat) {
map.panTo([currentLng, currentLat]);
map.setZoom(17);
console.log("JS: 已回到当前位置");
} else {
console.log("JS: 暂无定位数据,向 Flutter 请求...");
if (window.flutter_inappwebview) {
window.flutter_inappwebview.callHandler('requestLocation');
}
}
}
// --- 核心:规划路线 ---
function startRouteSearch() {
/**
* 路径规划
* @param {AMap.LngLat} [destLoc] 可选的终点坐标
*/
function startRouteSearch(destLoc) {
var startKw = document.getElementById('startInput').value;
var endKw = document.getElementById('endInput').value;
if (!startKw) {
alert("请输入起点");
return;
}
if (!endKw) {
console.log("FlutterLog: 终点不能为空");
alert("请输入终点");
return;
}
console.log("FlutterLog: 开始规划..." + (isTruckMode ? "货车" : "轿车"));
// 清除之前的路线
if (driving) driving.clear();
if(truckDriving) truckDriving.clear();
document.getElementById('startInput').blur();
document.getElementById('endInput').blur();
// 构造起点和终点参数
// 高德API Search方法支持点对象数组[{keyword: '名字', city: '城市'}, {keyword: '...'}]
var points = [];
// 1. 处理起点
if (startKw) {
// 如果用户输入了文字
points.push({ keyword: startKw });
} else {
// 如果用户没输入,使用当前定位
if (currentLng && currentLat) {
// 关键点:当混合使用坐标和关键字时,必须构建 LngLat 对象
// 1. 起点逻辑
if (!startKw || startKw === '我的位置' || startKw.includes('当前位置')) {
if (!currentLng || !currentLat) {
if (window.flutter_inappwebview) window.flutter_inappwebview.callHandler('requestLocation');
alert("正在获取定位,请稍后...");
return;
}
points.push({
keyword: '我的位置',
location: new AMap.LngLat(currentLng, currentLat)
});
} else {
console.log("FlutterLog: 无定位数据且无输入");
alert("未获取到定位,请输入起点");
return;
}
}
// 2. 处理终点
points.push({ keyword: endKw });
// 显示底部面板
document.getElementById('panel').style.display = 'block';
// 3. 发起请求
if (isTruckMode && truckDriving) {
// 货车接口略有不同,需要传入 path 数组
// truckDriving.search(path, callback)
// 这里为了简化,我们先演示轿车。货车通常需要具体的经纬度,建议先通过 Geocoder 把 endKw 转成经纬度再传给货车接口
console.log("FlutterLog: 货车API需要更严格的经纬度参数建议先使用轿车演示");
}
// 默认使用轿车规划 (支持 keyword + location 混合)
driving.search(points, function(status, result) {
if (status === 'complete') {
console.log('FlutterLog: 规划成功');
} else {
console.log('FlutterLog: 规划失败: ' + result);
// 常见错误: "USER_DAILY_QUERY_OVER_LIMIT" (Key额度超限)
// "INVALID_USER_KEY" (Key错误)
// "no_data" (地点没找到)
}
points.push({
keyword: startKw
});
}
// --- 绘制自定义路径 (保持原有功能) ---
// 2. 终点逻辑:如果有传入坐标,则直接使用坐标导航,成功率最高
if (destLoc) {
points.push({
keyword: endKw,
location: destLoc // 关键:使用精确坐标
});
} else {
points.push({
keyword: endKw
});
}
// 3. 发起搜索
driving.search(points, function (status, result) {
if (status === 'complete') {
console.log('JS: 规划成功');
var panel = document.getElementById('panel');
panel.style.display = 'block';
document.body.classList.add('panel-active');
}
// else {
// console.error('JS: 规划失败', result);
// // 如果坐标规划都失败了,通常是由于起终点距离过近或政策限制(如货车禁行)
// alert("路径规划未成功,请尝试微调起终点");
// }
});
}
// 辅助功能:手动绘制路线 (Flutter 可能用到)
function drawCustomRoute(pointsJsonString) {
// 如果是从Flutter传来的JSON字符串需要Parse如果是对象则直接用
var path = typeof pointsJsonString === 'string' ? JSON.parse(pointsJsonString) : pointsJsonString;
if (driving) driving.clear();
var polyline = new AMap.Polyline({
path: path,
isOutline: true,
@@ -221,4 +665,5 @@
window.onload = initMap;
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

View File

@@ -0,0 +1,51 @@
#
# Be sure to run `pod lib lint AMapNavIOSSDK.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'AMapNavIOSSDK'
s.version = '0.1.0'
s.summary = 'A short description of AMapNavIOSSDK.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/xiaoshuai/AMapNavIOSSDK'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'xiaoshuai' => 'xiaoshuai@net.cn' }
s.source = { :git => 'https://github.com/xiaoshuai/AMapNavIOSSDK.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '12.0'
s.source_files = 'AMapNavIOSSDK/Classes/**/*'
s.resource = 'AMapNavIOSSDK/**/*.bundle'
s.resource_bundles = {
'AMapNavIOSSDKPrivacyInfo' => ['AMapNavIOSSDK/**/PrivacyInfo.xcprivacy']
}
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
s.dependency 'Masonry'
s.dependency 'MJExtension'
s.dependency 'AMapNavi-NO-IDFA'
s.dependency 'AMapLocation-NO-IDFA'
s.dependency 'AMapSearch-NO-IDFA'
s.dependency 'MBProgressHUD'
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>0A2A.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>85F4.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,30 @@
//
// AAddHPopView.h
// AMapNavIOSSDK
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
* 气泡弹框视图
*
* 用法:
* AAddHPopView *pop = [[AAddHPopView alloc] init];
* [pop showInView:self.view sourceView:addHbtn];
*
* - 点击气泡外部自动消失
* - 箭头在右下角45度指向 sourceView 中心
*/
@interface AAddHPopView : UIView
/// 展示气泡sourceView 为箭头指向的锚定按钮(坐标系属于 view
- (void)showInView:(UIView *)view sourceView:(UIView *)sourceView;
/// 手动消失(点击空白处会自动调用)
- (void)dismiss;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More