commit 59b796a4a18e20eb745b981154778d7ea6c6f912
Author: MADAO-LUV <3335075714@qq.com>
Date: Tue Apr 29 16:12:29 2025 +0800
最初的app
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..eaf91e2
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..8fabff5
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..4fe9fa8
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..de8d7f4
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..0b0534c
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..48052b2
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..5954a2b
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..356b2e9
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,76 @@
+plugins {
+ alias(libs.plugins.androidApplication)
+}
+
+android {
+ namespace 'com.example.mangowalking'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.example.mangowalking"
+ minSdk 24
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ ndk {
+ //设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so)
+ abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
+ }
+
+ }
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ }
+ debug.setRoot('build-types/debug')
+ release.setRoot('build-types/release')
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildFeatures {
+ viewBinding true
+ }
+
+}
+
+// 构建时间
+def generateTime(){
+ return new Date().format("yyyy_MM_dd_HH_mm_ss")
+}
+
+
+// 自定义打包名称
+android.applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = "ManGoWalk_v${versionName}_${generateTime()}.apk"
+ }
+}
+
+
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation libs.appcompat
+ implementation libs.material
+ implementation libs.activity
+ implementation libs.constraintlayout
+// implementtation 'cn.shanyaliux.serialport:serialport:4.2.0'
+// implementation 'com.amap.api:3dmap-location-search:latest.integration'
+// implementation 'com.amap.api:3dmap:latest.integration'
+// implementation 'com.amap.api:location:6.4.9'
+ testImplementation libs.junit
+ androidTestImplementation libs.ext.junit
+ androidTestImplementation libs.espresso.core
+}
\ No newline at end of file
diff --git a/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar b/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar
new file mode 100644
index 0000000..123b4eb
Binary files /dev/null and b/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar differ
diff --git a/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so b/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so
new file mode 100644
index 0000000..910ba6d
Binary files /dev/null and b/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so differ
diff --git a/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so b/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so
new file mode 100644
index 0000000..af3d0ee
Binary files /dev/null and b/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so differ
diff --git a/app/libs/build.properties b/app/libs/build.properties
new file mode 100644
index 0000000..b3b8338
--- /dev/null
+++ b/app/libs/build.properties
@@ -0,0 +1,10 @@
+iid=11600
+sid=3528001
+bid=1350002
+version=13.08.0.10009081
+time=2025-03-17 18:00:51
+FEATURE_LOCATION=1
+FEATURE_ROUTE_OVERLAY=1
+FEATURE_MVT=1
+FEATURE_3DTiles=1
+FEATURE_GLTF=1
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..cf50408
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,22 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
diff --git a/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk b/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk
new file mode 100644
index 0000000..cb1be24
Binary files /dev/null and b/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk differ
diff --git a/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm b/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm
new file mode 100644
index 0000000..22c3047
Binary files /dev/null and b/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm differ
diff --git a/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm b/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm
new file mode 100644
index 0000000..d8a6ebd
Binary files /dev/null and b/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm differ
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
new file mode 100644
index 0000000..6cfef45
--- /dev/null
+++ b/app/release/output-metadata.json
@@ -0,0 +1,37 @@
+{
+ "version": 3,
+ "artifactType": {
+ "type": "APK",
+ "kind": "Directory"
+ },
+ "applicationId": "com.example.mangowalking",
+ "variantName": "release",
+ "elements": [
+ {
+ "type": "SINGLE",
+ "filters": [],
+ "attributes": [],
+ "versionCode": 1,
+ "versionName": "1.0",
+ "outputFile": "ManGoWalk_v1.0_2025_04_17_19_59_18.apk"
+ }
+ ],
+ "elementType": "File",
+ "baselineProfiles": [
+ {
+ "minApi": 28,
+ "maxApi": 30,
+ "baselineProfiles": [
+ "baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm"
+ ]
+ },
+ {
+ "minApi": 31,
+ "maxApi": 2147483647,
+ "baselineProfiles": [
+ "baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm"
+ ]
+ }
+ ],
+ "minSdkVersionForDexing": 24
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..8931820
--- /dev/null
+++ b/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.example.mangowalking;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.example.mangowalking", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed80cb6
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/mangowalking/App.java b/app/src/main/java/com/example/mangowalking/App.java
new file mode 100644
index 0000000..7232271
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/App.java
@@ -0,0 +1,27 @@
+package com.example.mangowalking;
+
+import android.app.Application;
+import android.content.Context;
+
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.maps.MapsInitializer;
+import com.amap.api.services.core.ServiceSettings;
+
+public class App extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Context mContext = this;
+ // 定位隐私政策同意
+ AMapLocationClient.updatePrivacyShow(mContext,true,true);
+ AMapLocationClient.updatePrivacyAgree(mContext,true);
+ // 地图隐私政策同意
+ MapsInitializer.updatePrivacyShow(mContext,true,true);
+ MapsInitializer.updatePrivacyAgree(mContext,true);
+ // 搜索隐私政策同意
+ ServiceSettings.updatePrivacyShow(mContext,true,true);
+ ServiceSettings.updatePrivacyAgree(mContext,true);
+ }
+}
+
diff --git a/app/src/main/java/com/example/mangowalking/MainActivity.java b/app/src/main/java/com/example/mangowalking/MainActivity.java
new file mode 100644
index 0000000..6aa4f33
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/MainActivity.java
@@ -0,0 +1,635 @@
+package com.example.mangowalking;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.Manifest; // 针对于ACCESS_FINE_LOCATION
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.activity.EdgeToEdge;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+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.maps.AMap;
+import com.amap.api.maps.CameraUpdate;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.LocationSource;
+import com.amap.api.maps.UiSettings;
+import com.amap.api.maps.model.BitmapDescriptorFactory;
+import com.amap.api.maps.model.CameraPosition;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.Marker;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.maps.model.MyLocationStyle;
+import com.amap.api.maps.model.animation.Animation;
+import com.amap.api.maps.model.animation.RotateAnimation;
+import com.amap.api.services.core.AMapException;
+import com.amap.api.services.core.LatLonPoint;
+import com.amap.api.services.core.PoiItem;
+import com.amap.api.services.geocoder.GeocodeAddress;
+import com.amap.api.services.geocoder.GeocodeQuery;
+import com.amap.api.services.geocoder.GeocodeResult;
+import com.amap.api.services.geocoder.GeocodeSearch;
+import com.amap.api.services.geocoder.RegeocodeAddress;
+import com.amap.api.services.geocoder.RegeocodeQuery;
+import com.amap.api.services.geocoder.RegeocodeResult;
+import com.amap.api.services.poisearch.PoiResult;
+import com.amap.api.services.poisearch.PoiSearch;
+import com.example.mangowalking.databinding.ActivityMainBinding;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity implements AMapLocationListener, LocationSource, PoiSearch.OnPoiSearchListener, AMap.OnMapClickListener, AMap.OnMapLongClickListener, GeocodeSearch.OnGeocodeSearchListener, AMap.OnMarkerClickListener, AMap.OnMarkerDragListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener {
+ private static final String TAG = "MainActivity";
+ private ActivityMainBinding binding;
+ private ActivityResultLauncher requestPermission;
+
+ //声明AMapLocationClient类对象
+ public AMapLocationClient mLocationClient = null;
+ //声明AMapLocationClientOption对象
+ public AMapLocationClientOption mLocationOption = null;
+ // 声明地图控制器
+ private AMap aMap = null;
+ // 声明地图定位监听
+ private LocationSource.OnLocationChangedListener mListener = null;
+
+ //POI查询对象
+ private PoiSearch.Query query;
+ //POI搜索对象
+ private PoiSearch poiSearch;
+ //城市码
+ private String cityCode = null;
+
+ //地理编码搜索
+ private GeocodeSearch geocodeSearch;
+ //解析成功标识码
+ private static final int PARSE_SUCCESS_CODE = 1000;
+ //城市
+ private String city;
+
+ //标点列表
+ private final List markerList = new ArrayList<>();
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ requestPermission = registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> {
+ // 权限申请结果
+ Log.d(TAG, "权限申请结果: " + result);
+ showMsg(result ? "已获取权限" : "权限申请失败");
+ });
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ binding = ActivityMainBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot()); //这里要和博主一致
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+ Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ return insets;
+ });
+ initLocation();
+ binding.mapView.onCreate(savedInstanceState);
+ initMap();
+ initSearch();
+ initView();
+
+ }
+
+ /**
+ * 初始化搜索
+ */
+ private void initSearch() {
+ // 构造 GeocodeSearch 对象
+ try {
+ geocodeSearch = new GeocodeSearch(this);
+ // 设置监听
+ geocodeSearch.setOnGeocodeSearchListener(this);
+ } catch (com.amap.api.services.core.AMapException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private void initLocation() {
+ try {
+ //初始化定位
+ mLocationClient = new AMapLocationClient(getApplicationContext());
+ //设置定位回调监听
+ mLocationClient.setLocationListener(this);
+ //初始化AMapLocationClientOption对象
+ mLocationOption = new AMapLocationClientOption();
+ //设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ //获取最近3s内精度最高的一次定位结果
+ mLocationOption.setOnceLocationLatest(true);
+ //设置是否返回地址信息(默认返回地址信息)
+ mLocationOption.setNeedAddress(true);
+ //设置定位超时时间,单位是毫秒
+ mLocationOption.setHttpTimeOut(6000);
+ //给定位客户端对象设置定位参数
+ mLocationClient.setLocationOption(mLocationOption);
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 开始定位
+ */
+ private void startLocation() {
+ if (mLocationClient != null) mLocationClient.startLocation();
+ }
+
+ /**
+ * 停止定位
+ */
+ private void stopLocation() {
+ if (mLocationClient != null) mLocationClient.stopLocation();
+ }
+
+
+ /**
+ * 初始化地图
+ */
+ private void initMap() {
+ if (aMap == null) {
+ aMap = binding.mapView.getMap();
+ // 创建定位蓝点的样式
+ MyLocationStyle myLocationStyle = new MyLocationStyle();
+ // 自定义定位蓝点图标
+ myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point));
+ // 自定义精度范围的圆形边框颜色 都为0则透明
+ myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
+ // 自定义精度范围的圆形边框宽度 0 无宽度
+ myLocationStyle.strokeWidth(0);
+ // 设置圆形的填充颜色 都为0则透明
+ myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));
+ // 设置定位蓝点的样式
+ aMap.setMyLocationStyle(myLocationStyle);
+ // 设置定位监听
+ aMap.setLocationSource(this);
+ // 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。
+ aMap.setMyLocationEnabled(true);
+
+ aMap.setMinZoomLevel(15); //设置最小缩放等级为15 缩放等级为(3,20)
+ aMap.showIndoorMap(true); //开启室内地图
+ // 设置地图点击事件
+ aMap.setOnMapClickListener(this);
+ // 设置地图长按事件
+ aMap.setOnMapLongClickListener(this);
+ // 设置地图标点点击事件
+ aMap.setOnMarkerClickListener(this);
+ // 设置地图标点拖拽事件
+ aMap.setOnMarkerDragListener(this);
+ // 设置InfoWindowAdapter监听
+ aMap.setInfoWindowAdapter(this);
+ // 设置InfoWindow点击事件
+ aMap.setOnInfoWindowClickListener(this);
+
+ // 地图控件设置
+ UiSettings uiSettings = aMap.getUiSettings();
+ // 隐藏缩放按钮
+ uiSettings.setZoomControlsEnabled(false);
+ // 显示比例尺,默认不显示
+ uiSettings.setScaleControlsEnabled(true);
+ }
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // 检查是否已经获取到定位权限
+ binding.mapView.onResume();
+ if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ // 获取到权限
+ requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // 绑定生命周期 onPause
+ binding.mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ // 绑定生命周期 onSaveInstanceState
+ binding.mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // 绑定生命周期 onDestroy
+ binding.mapView.onDestroy();
+ }
+
+
+ /**
+ * 初始化定位
+ */
+
+ private void showMsg(CharSequence llw) {
+ Toast.makeText(this, llw, Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * 初始化控件
+ */
+ private void initView() {
+ // Poi搜索按钮点击事件
+ binding.fabPoi.setOnClickListener(v -> {
+ //构造query对象
+ query = new PoiSearch.Query("购物", "", cityCode);
+ // 设置每页最多返回多少条poiItem
+ query.setPageSize(10);
+ //设置查询页码
+ query.setPageNum(1);
+ //构造 PoiSearch 对象
+ try {
+ poiSearch = new PoiSearch(this, query);
+ //设置搜索回调监听
+ poiSearch.setOnPoiSearchListener(this);
+ //发起搜索附近POI异步请求
+ poiSearch.searchPOIAsyn();
+ } catch (AMapException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ // 清除标点按钮点击事件
+ binding.fabClearMarker.setOnClickListener(v -> clearAllMarker());
+ // 路线按钮点击事件
+ binding.fabRoute.setOnClickListener(v -> startActivity(new Intent(this, RouteActivity.class)));
+ // 键盘按键监听
+ binding.etAddress.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+ //获取输入框的值
+ String address = binding.etAddress.getText().toString().trim();
+ if (address.isEmpty()) {
+ showMsg("请输入地址");
+ } else {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ //隐藏软键盘
+ imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
+
+ // name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode
+ GeocodeQuery query = new GeocodeQuery(address, city);
+ geocodeSearch.getFromLocationNameAsyn(query);
+ }
+ return true;
+ }
+ return false;
+ });
+
+ }
+
+
+
+
+ /**
+ * 定位回调结果
+ */
+ @Override
+ public void onLocationChanged(AMapLocation aMapLocation) {
+ if (aMapLocation == null) {
+ showMsg("定位失败,aMapLocation 为空");
+ return;
+ }
+ // 获取定位结果
+ if (aMapLocation.getErrorCode() == 0) {
+ // 定位成功
+ showMsg("定位成功");
+// aMapLocation.getLocationType();//获取当前定位结果来源,如网络定位结果,详见定位类型表
+// aMapLocation.getLatitude();//获取纬度
+// aMapLocation.getLongitude();//获取经度
+// aMapLocation.getAccuracy();//获取精度信息
+// aMapLocation.getAddress();//详细地址,如果option中设置isNeedAddress为false,则没有此结果,网络定位结果中会有地址信息,GPS定位不返回地址信息。
+// aMapLocation.getCountry();//国家信息
+// aMapLocation.getProvince();//省信息
+// aMapLocation.getCity();//城市信息
+ String result = aMapLocation.getDistrict();//城区信息
+// aMapLocation.getStreet();//街道信息
+// aMapLocation.getStreetNum();//街道门牌号信息
+// aMapLocation.getCityCode();//城市编码
+// aMapLocation.getAdCode();//地区编码
+// aMapLocation.getAoiName();//获取当前定位点的AOI信息
+// aMapLocation.getBuildingId();//获取当前室内定位的建筑物Id
+// aMapLocation.getFloor();//获取当前室内定位的楼层
+// aMapLocation.getGpsAccuracyStatus();//获取GPS的当前状态
+ // 停止定位
+ stopLocation();
+ // 显示地图定位结果
+ if (mListener != null) {
+ mListener.onLocationChanged(aMapLocation);
+ }
+ // 显示浮动按钮
+ binding.fabPoi.show();
+ // 城市编码赋值
+ cityCode = aMapLocation.getCityCode();
+ //城市
+ city = aMapLocation.getCity();
+ } else {
+ // 定位失败
+ showMsg("定位失败,错误:" + aMapLocation.getErrorInfo());
+ Log.e(TAG,"location Error, ErrCode:"
+ + aMapLocation.getErrorCode() + ", errInfo:"
+ + aMapLocation.getErrorInfo());
+ }
+ }
+
+ /**
+ * 激活定位
+ */
+ @Override
+ public void activate(OnLocationChangedListener onLocationChangedListener) {
+ if (mListener == null) {
+ mListener = onLocationChangedListener;
+ }
+ startLocation();
+ }
+
+ /**
+ * 禁用
+ */
+ @Override
+ public void deactivate() {
+ mListener = null;
+ if (mLocationClient != null) {
+ mLocationClient.stopLocation();
+ mLocationClient.onDestroy();
+ }
+ mLocationClient = null;
+ }
+
+ /**
+ * POI搜索返回
+ *
+ * @param poiResult POI所有数据
+ * @param i
+ */
+ @Override
+ public void onPoiSearched(PoiResult poiResult, int i) {
+ //解析result获取POI信息
+
+ //获取POI组数列表
+ ArrayList poiItems = poiResult.getPois();
+ for (PoiItem poiItem : poiItems) {
+// Log.d("MainActivity", " Title:" + poiItem.getTitle() + " Snippet:" + poiItem.getSnippet());
+ Log.d("MainActivity", "Title: " + poiItem.getTitle());
+ Log.d("MainActivity", "Snippet: " + poiItem.getSnippet());
+ }
+ }
+
+ /**
+ * POI中的项目搜索返回
+ *
+ * @param poiItem 获取POI item
+ * @param i
+ */
+ @Override
+ public void onPoiItemSearched(PoiItem poiItem, int i) {
+
+ }
+
+ /**
+ * 通过经纬度获取地址
+ * @param latLng
+ */
+ private void latLonToAddress(LatLng latLng) {
+ //位置点 通过经纬度进行构建
+ LatLonPoint latLonPoint = new LatLonPoint(latLng.latitude, latLng.longitude);
+ //逆编码查询 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系
+ RegeocodeQuery query = new RegeocodeQuery(latLonPoint, 20, GeocodeSearch.AMAP);
+ //异步获取地址信息
+ geocodeSearch.getFromLocationAsyn(query);
+ }
+
+ /**
+ * 添加地图标点
+ *
+ * @param latLng
+ */
+ private void addMarker(LatLng latLng) {
+ //显示浮动按钮
+ binding.fabClearMarker.show();
+ //添加标点
+ Marker marker = aMap.addMarker(new MarkerOptions().draggable(true).position(latLng).title("标题").snippet("DefaultMarker"));
+ //显示InfoWindow
+ marker.showInfoWindow();
+ //设置标点的绘制动画效果
+ Animation animation = new RotateAnimation(marker.getRotateAngle(),marker.getRotateAngle()+360,0,0,0);
+ long duration = 1000L;
+ animation.setDuration(duration);
+ animation.setInterpolator(new LinearInterpolator());
+
+ marker.setAnimation(animation);
+ marker.startAnimation();
+
+
+
+ markerList.add(marker);
+ }
+
+ /**
+ * 清空地图Marker
+ */
+ public void clearAllMarker() {
+ if (markerList != null && !markerList.isEmpty()) {
+ for (Marker markerItem : markerList) {
+ markerItem.remove();
+ }
+ }
+ binding.fabClearMarker.hide();
+ }
+
+
+ /**
+ * 改变地图中心位置
+ * @param latLng 位置
+ */
+ private void updateMapCenter(LatLng latLng) {
+ // CameraPosition 第一个参数: 目标位置的屏幕中心点经纬度坐标。
+ // CameraPosition 第二个参数: 目标可视区域的缩放级别
+ // CameraPosition 第三个参数: 目标可视区域的倾斜度,以角度为单位。
+ // CameraPosition 第四个参数: 可视区域指向的方向,以角度为单位,从正北向顺时针方向计算,从0度到360度
+ CameraPosition cameraPosition = new CameraPosition(latLng, 16, 30, 0);
+ //位置变更
+ CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition);
+ //改变位置(使用动画)
+ aMap.animateCamera(cameraUpdate);
+ }
+
+ /**
+ * 地图点击事件
+ * @param latLng
+ */
+ @Override
+ public void onMapClick(LatLng latLng) {
+ latLonToAddress(latLng);
+ addMarker(latLng);
+ updateMapCenter(latLng);
+ }
+
+ /**
+ * 地图长按事件
+ * @param latLng
+ */
+ @Override
+ public void onMapLongClick(LatLng latLng) {
+ latLonToAddress(latLng);
+ }
+
+ @Override
+ public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int rCode) {
+ //解析result获取地址描述信息
+ if(rCode == PARSE_SUCCESS_CODE){
+ RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
+ //显示解析后的地址
+ showMsg("地址:"+regeocodeAddress.getFormatAddress());
+ }else {
+ showMsg("获取地址失败");
+ }
+ }
+
+ @Override
+ public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
+ if (rCode != PARSE_SUCCESS_CODE) {
+ showMsg("获取坐标失败");
+ return;
+ }
+ List geocodeAddressList = geocodeResult.getGeocodeAddressList();
+ if (geocodeAddressList != null && !geocodeAddressList.isEmpty()) {
+ LatLonPoint latLonPoint = geocodeAddressList.get(0).getLatLonPoint();
+ //显示解析后的坐标
+ showMsg("坐标:" + latLonPoint.getLongitude() + "," + latLonPoint.getLatitude());
+ }
+ }
+
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ if (!marker.isInfoWindowShown()) { // 显示
+ marker.showInfoWindow();
+ } else { // 隐藏
+ marker.hideInfoWindow();
+ }
+ return true;
+ }
+
+
+ @Override
+ public void onMarkerDragStart(Marker marker) {
+ Log.d(TAG, "开始拖拽");
+ }
+
+ @Override
+ public void onMarkerDrag(Marker marker) {
+ Log.d(TAG, "拖拽中...");
+ }
+
+ @Override
+ public void onMarkerDragEnd(Marker marker) {
+ showMsg("拖拽完成");
+ }
+
+ /**
+ * 修改内容
+ *
+ * @param marker
+ * @return
+ */
+ @Override
+ public View getInfoContents(Marker marker) {
+ View infoContent = getLayoutInflater().inflate(
+ R.layout.custom_info_contents, null);
+ render(marker, infoContent);
+ return infoContent;
+ }
+
+ /**
+ * 修改背景
+ *
+ * @param marker
+ */
+ @Override
+ public View getInfoWindow(Marker marker) {
+ View infoWindow = getLayoutInflater().inflate(
+ R.layout.custom_info_window, null);
+ render(marker, infoWindow);
+ return infoWindow;
+ }
+
+ /**
+ * 渲染
+ *
+ * @param marker
+ * @param view
+ */
+ private void render(Marker marker, View view) {
+ ((ImageView) view.findViewById(R.id.badge))
+ .setImageResource(R.mipmap.ic_yuan);
+
+ //修改InfoWindow标题内容样式
+ String title = marker.getTitle();
+ TextView titleUi = ((TextView) view.findViewById(R.id.title));
+ if (title != null) {
+ SpannableString titleText = new SpannableString(title);
+ titleText.setSpan(new ForegroundColorSpan(Color.RED), 0,
+ titleText.length(), 0);
+ titleUi.setTextSize(15);
+ titleUi.setText(titleText);
+
+ } else {
+ titleUi.setText("");
+ }
+ //修改InfoWindow片段内容样式
+ String snippet = marker.getSnippet();
+ TextView snippetUi = ((TextView) view.findViewById(R.id.snippet));
+ if (snippet != null) {
+ SpannableString snippetText = new SpannableString(snippet);
+ snippetText.setSpan(new ForegroundColorSpan(Color.GREEN), 0,
+ snippetText.length(), 0);
+ snippetUi.setTextSize(20);
+ snippetUi.setText(snippetText);
+ } else {
+ snippetUi.setText("");
+ }
+ }
+
+ /**
+ * InfoWindow点击事件
+ *
+ * @param marker
+ */
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ showMsg("弹窗内容:标题:" + marker.getTitle() + "\n内容:" + marker.getSnippet());
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/mangowalking/RouteActivity.java b/app/src/main/java/com/example/mangowalking/RouteActivity.java
new file mode 100644
index 0000000..e9e2652
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/RouteActivity.java
@@ -0,0 +1,841 @@
+package com.example.mangowalking;
+
+import static com.example.mangowalking.utils.MapUtil.convertToLatLng;
+import static com.example.mangowalking.utils.MapUtil.convertToLatLonPoint;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.RelativeLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.activity.EdgeToEdge;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+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.maps.AMap;
+import com.amap.api.maps.LocationSource;
+import com.amap.api.maps.UiSettings;
+import com.amap.api.maps.model.BitmapDescriptorFactory;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.maps.model.MyLocationStyle;
+import com.amap.api.services.busline.BusLineItem;
+import com.amap.api.services.core.AMapException;
+import com.amap.api.services.core.LatLonPoint;
+import com.amap.api.services.geocoder.GeocodeAddress;
+import com.amap.api.services.geocoder.GeocodeQuery;
+import com.amap.api.services.geocoder.GeocodeResult;
+import com.amap.api.services.geocoder.GeocodeSearch;
+import com.amap.api.services.geocoder.RegeocodeResult;
+import com.amap.api.services.route.BusPath;
+import com.amap.api.services.route.BusRouteResult;
+import com.amap.api.services.route.BusStep;
+import com.amap.api.services.route.DriveRouteResult;
+import com.amap.api.services.route.RideRouteResult;
+import com.amap.api.services.route.RouteBusLineItem;
+import com.amap.api.services.route.RouteSearch;
+import com.amap.api.services.route.WalkPath;
+import com.amap.api.services.route.WalkRouteResult;
+import com.amap.api.services.route.WalkStep;
+import com.example.mangowalking.databinding.ActivityRouteBinding;
+import com.example.mangowalking.overlay.BusRouteOverlay;
+import com.example.mangowalking.overlay.WalkRouteOverlay;
+import com.example.mangowalking.utils.MapUtil;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class RouteActivity extends AppCompatActivity implements
+ AMapLocationListener, LocationSource, AMap.OnMapClickListener, RouteSearch.OnRouteSearchListener, GeocodeSearch.OnGeocodeSearchListener, View.OnKeyListener {
+
+
+ private static final String TAG = "RouteActivity";
+ private ActivityRouteBinding binding;
+ //地图控制器
+ private AMap aMap = null;
+ //声明AMapLocationClient类对象
+ public AMapLocationClient mLocationClient = null;
+ //声明AMapLocationClientOption对象
+ public AMapLocationClientOption mLocationOption = null;
+ //位置更改监听
+ private LocationSource.OnLocationChangedListener mListener;
+ //定义一个UiSettings对象
+ private UiSettings mUiSettings;
+ //定位样式
+ private MyLocationStyle myLocationStyle = new MyLocationStyle();
+
+ //起点
+ private LatLonPoint mStartPoint;
+ //终点
+ private LatLonPoint mEndPoint;
+ //路线搜索对象
+ private RouteSearch routeSearch;
+
+ //出行方式数组
+ private static final String[] travelModeArray = {"步行出行", "公交出行"};
+
+ //出行方式值
+ private static int TRAVEL_MODE = 0;
+
+ //数组适配器
+ private ArrayAdapter arrayAdapter;
+ //地理编码搜索
+ private GeocodeSearch geocodeSearch;
+ //解析成功标识码
+ private static final int PARSE_SUCCESS_CODE = 1000;
+ //定位地址
+ private String locationAddress;
+ //城市
+ private String city;
+ //起点地址转坐标标识 1
+ private int tag = -1;
+
+ //路线规划详情
+ private RelativeLayout bottomLayout;
+ //花费时间
+ private TextView tvTime;
+
+ //添加关于蓝牙的变量
+ private BluetoothLeScanner scanner;
+
+ private BluetoothDevice device;
+
+ private BluetoothGatt bluetoothGatt;
+
+ private boolean isBluetoothConnected = false;
+
+ private String SERVICE_UUID = "0000FFE0-0000-1000-8000-00805F9B34FB";
+ private String READ_UUID = "0000FFE0-0000-1000-8000-00805F9B34FB";
+
+ private String READ_DEDSCRIPTION_UUID = "00002902-0000-1000-8000-00805f9b34fb";
+
+ private BluetoothGattCharacteristic writeCharacteristic;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ binding = ActivityRouteBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+ Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ return insets;
+ });
+ //添加蓝牙开启 蓝牙部分是成功开启了
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Toast.makeText(RouteActivity.this, "没有蓝牙", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(RouteActivity.this, "该设备支持蓝牙", Toast.LENGTH_SHORT).show();
+ }
+
+ final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
+
+ ActivityResultLauncher launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
+ if (result.getResultCode() == RESULT_OK) {
+ //处理返回结果
+ Toast.makeText(RouteActivity.this, "已成功开启蓝牙", Toast.LENGTH_SHORT).show();
+ }
+ });
+ @SuppressLint("MissingPermission")
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //创建一个蓝牙启动的意图
+ launcher.launch(enableBtIntent);//使用launcer启动这个意图就可以了。
+ //此处可行
+ //初始化定位
+ initLocation();
+ //初始化地图
+ initMap(savedInstanceState);
+ //启动定位
+ mLocationClient.startLocation();
+ initRoute();
+ //初始化出行方式
+ initTravelMode();
+
+ // 新加入的
+ scanner = bluetoothAdapter.getBluetoothLeScanner();
+ //不进行权限验证
+ ScanCallback callback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ device = result.getDevice();//得到设备
+ // Log.e(TAG, "发现设备" + device.getName());
+
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ super.onScanFailed(errorCode);
+ Log.e(TAG, "搜索错误" + errorCode);
+ }
+ };
+ ScanFilter sn = new ScanFilter.Builder().setDeviceName("蓝牙设备的名称").setServiceUuid(ParcelUuid.fromString("0000FFE0-0000-1000-8000-00805F9B34FB")).build();
+ List scanFilters = new ArrayList<>();
+ scanFilters.add(sn);
+ if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
+ // TODO: Consider calling
+ // ActivityCompat#requestPermissions
+ return;
+ }
+ scanner.startScan(scanFilters, new ScanSettings.Builder().build(), callback);
+ bluetoothGatt = device.connectGatt(this, false, gattCallback);
+ }
+
+ BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
+ //GATT的链接状态回调
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ super.onConnectionStateChange(gatt, status, newState);
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ if (ActivityCompat.checkSelfPermission(RouteActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ gatt.discoverServices();
+ Log.v(TAG, "连接成功");
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.e(TAG, "连接断开");
+ } else if (newState == BluetoothProfile.STATE_CONNECTING) {
+ //TODO 在实际过程中,该方法并没有调用
+ Log.e(TAG, "连接中....");
+ }
+ }
+
+ //获取GATT服务发现后的回调
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ Log.e(TAG, "GATT_SUCCESS"); //服务发现
+ for (BluetoothGattService bluetoothGattService : gatt.getServices()) {
+ Log.e(TAG, "Service_UUID" + bluetoothGattService.getUuid()); // 我们可以遍历到该蓝牙设备的全部Service对象。然后通过比较Service的UUID,我们可以区分该服务是属于什么业务的
+ if (SERVICE_UUID.equals(bluetoothGattService.getUuid().toString())) {
+
+ for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) {
+ prepareBroadcastDataNotify(gatt, characteristic); //给满足条件的属性配置上消息通知
+ }
+ return;//结束循环操作
+ }
+ }
+ } else {
+ Log.e(TAG, "onServicesDiscovered received: " + status);
+ }
+ }
+
+ //蓝牙设备发送消息后的自动监听
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ // readUUID 是我要链接的蓝牙设备的消息读UUID值,跟通知的特性的UUID比较。这样可以避免其他消息的污染。
+ if (READ_UUID.equals(characteristic.getUuid().toString())) {
+ try {
+ String chara = new String(characteristic.getValue(), "UTF-8");
+ Log.e(TAG, "消息内容:" + chara);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+
+ @SuppressLint("MissingPermission")
+ private void prepareBroadcastDataNotify(BluetoothGatt mBluetoothGatt, BluetoothGattCharacteristic characteristic) {
+ Log.e(TAG, "CharacteristicUUID:" + characteristic.getUuid().toString());
+ int charaProp = characteristic.getProperties();
+ //判断属性是否支持消息通知
+ if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
+ BluetoothGattDescriptor descriptor =
+ characteristic.getDescriptor(UUID.fromString(READ_DEDSCRIPTION_UUID));
+ if (descriptor != null) {
+ //注册消息通知
+ mBluetoothGatt.setCharacteristicNotification(characteristic, true);
+ descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ mBluetoothGatt.writeDescriptor(descriptor);
+ }
+ }
+ }
+
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_UUID));
+ if (service != null) {
+ // 假设写特征的 UUID 就是 READ_UUID(或者你需要再定义一个 WRITE_UUID)
+ writeCharacteristic = service.getCharacteristic(UUID.fromString(READ_UUID));
+ // 如果该特征需要先 enableNotification,也可在这里设置:
+ if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ gatt.setCharacteristicNotification(writeCharacteristic, true);
+ }
+ }
+ }
+
+ /**
+ * 通过 BLE 将 poiList 中的每个坐标点逐条发送到从机。
+ * 由于单包 MTU 默认为 20 字节,必要时需要分包。
+ */
+ private void sendPoiList(List poiList) {
+ if (writeCharacteristic == null || bluetoothGatt == null) {
+ showMsg("蓝牙特征或连接未准备好");
+ return;
+ }
+
+ for (LatLng p : poiList) {
+ // 1) 将坐标格式化为字符串,比如:"31.2304,121.4737\n"
+ String str = p.latitude + "," + p.longitude + "\n";
+ byte[] data = str.getBytes(StandardCharsets.UTF_8);
+
+ // 2) 如果 data.length > MTU,需要分包;这里简单示例不分包
+ writeCharacteristic.setValue(data);
+
+ // 3) 发写命令,异步执行
+ if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ boolean success = bluetoothGatt.writeCharacteristic(writeCharacteristic);
+ if (!success) {
+ Log.e(TAG, "写入特征失败:" + str);
+ }
+
+ // 4) 为避免短时间内连续写多包导致丢包,可在这里短暂停一下(示例用 Handler)
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException ignored) {}
+ }
+ }
+
+ /**
+ * 初始化出行方式
+ */
+ private void initTravelMode() {
+ Spinner spinner = findViewById(R.id.spinner);
+
+ //将可选内容与ArrayAdapter连接起来
+ arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, travelModeArray);
+ //设置下拉列表的风格
+ arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ //将adapter 添加到spinner中
+ spinner.setAdapter(arrayAdapter);
+ //添加事件Spinner事件监听
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ TRAVEL_MODE = position;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ }
+ });
+ //键盘按键监听
+ //起点 键盘按键监听
+ binding.etStartAddress.setOnKeyListener(this);
+ //终点 键盘按键监听
+ binding.etEndAddress.setOnKeyListener(this);
+
+
+ }
+
+ /**
+ * 初始化路线
+ */
+ private void initRoute() {
+ try {
+ routeSearch = new RouteSearch(this);
+ } catch (AMapException e) {
+ e.printStackTrace();
+ }
+ routeSearch.setRouteSearchListener(this);
+ }
+
+ /**
+ * 初始化定位
+ */
+ private void initLocation() {
+ //初始化定位
+ try {
+ mLocationClient = new AMapLocationClient(getApplicationContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (mLocationClient != null) {
+ mLocationClient.setLocationListener(this);
+ mLocationOption = new AMapLocationClientOption();
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ mLocationOption.setOnceLocationLatest(true);
+ mLocationOption.setNeedAddress(true);
+ mLocationOption.setHttpTimeOut(20000);
+ mLocationOption.setLocationCacheEnable(false);
+ mLocationClient.setLocationOption(mLocationOption);
+ }
+ }
+
+ /**
+ * 初始化地图
+ *
+ * @param savedInstanceState
+ */
+ private void initMap(Bundle savedInstanceState) {
+ binding.mapView.onCreate(savedInstanceState);
+ //初始化地图控制器对象
+ aMap = binding.mapView.getMap();
+ //设置最小缩放等级为12 ,缩放级别范围为[3, 20]
+ aMap.setMinZoomLevel(12);
+ //开启室内地图
+ aMap.showIndoorMap(true);
+ //实例化UiSettings类对象
+ mUiSettings = aMap.getUiSettings();
+ //隐藏缩放按钮 默认显示
+ mUiSettings.setZoomControlsEnabled(false);
+ //显示比例尺 默认不显示
+ mUiSettings.setScaleControlsEnabled(true);
+ // 自定义定位蓝点图标
+ myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point));
+ //设置定位蓝点的Style
+ aMap.setMyLocationStyle(myLocationStyle);
+ // 设置定位监听
+ aMap.setLocationSource(this);
+ // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
+ aMap.setMyLocationEnabled(true);
+ //地图点击监听
+ aMap.setOnMapClickListener(this);
+ //构造 GeocodeSearch 对象
+ try {
+ geocodeSearch = new GeocodeSearch(this);
+ } catch (AMapException e) {
+ throw new RuntimeException(e);
+ }
+ //设置监听
+ geocodeSearch.setOnGeocodeSearchListener(this);
+
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation aMapLocation) {
+ if (aMapLocation != null) {
+ if (aMapLocation.getErrorCode() == 0) {
+ //地址
+ String address = aMapLocation.getAddress();
+ //获取纬度
+ double latitude = aMapLocation.getLatitude();
+ //获取经度
+ double longitude = aMapLocation.getLongitude();
+ //设置当前所在地
+ //地址
+ locationAddress = aMapLocation.getAddress();
+ //设置当前所在地
+ binding.etStartAddress.setText(locationAddress);
+ //binding.etStartAddress.setEnabled(false);//禁用输入
+ //城市赋值
+ city = aMapLocation.getCity();
+ Log.d(TAG, address);
+ //设置起点
+ mStartPoint = convertToLatLonPoint(new LatLng(latitude, longitude));
+ //停止定位后,本地定位服务并不会被销毁
+ mLocationClient.stopLocation();
+
+ //显示地图定位结果
+ if (mListener != null) {
+ // 显示系统图标
+ mListener.onLocationChanged(aMapLocation);
+ }
+
+ } else {
+ //定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
+ Log.e("AmapError", "location Error, ErrCode:"
+ + aMapLocation.getErrorCode() + ", errInfo:"
+ + aMapLocation.getErrorInfo());
+ }
+ }
+ }
+
+ /**
+ * 开始路线搜索
+ */
+ private void startRouteSearch() {
+ //在地图上添加起点Marker
+ aMap.addMarker(new MarkerOptions()
+ .position(convertToLatLng(mStartPoint))
+ .icon(BitmapDescriptorFactory.fromResource(R.drawable.start)));
+ //在地图上添加终点Marker
+ aMap.addMarker(new MarkerOptions()
+ .position(convertToLatLng(mEndPoint))
+ .icon(BitmapDescriptorFactory.fromResource(R.drawable.end)));
+
+ //搜索路线 构建路径的起终点
+ final RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(
+ mStartPoint, mEndPoint);
+ //出行方式判断
+ switch (TRAVEL_MODE) {
+ case 0://步行
+ //构建步行路线搜索对象
+ RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WalkDefault);
+ // 异步路径规划步行模式查询
+ routeSearch.calculateWalkRouteAsyn(query);
+ break;
+ case 1://骑行
+ //构建驾车路线搜索对象 第三个参数表示公交查询城市区号,第四个参数表示是否计算夜班车,0表示不计算,1表示计算
+ RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, city, 1);
+ //公交规划路径计算
+ routeSearch.calculateBusRouteAsyn(busQuery);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+
+ @Override
+ public void activate(LocationSource.OnLocationChangedListener onLocationChangedListener) {
+ mListener = onLocationChangedListener;
+ if (mLocationClient == null) {
+ mLocationClient.startLocation();//启动定位
+ }
+ }
+
+ @Override
+ public void deactivate() {
+ mListener = null;
+ if (mLocationClient != null) {
+ mLocationClient.stopLocation();
+ mLocationClient.onDestroy();
+ }
+ mLocationClient = null;
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ binding.mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ binding.mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ binding.mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ //销毁定位客户端,同时销毁本地定位服务。
+ if (mLocationClient != null) {
+ mLocationClient.onDestroy();
+ }
+ binding.mapView.onDestroy();
+
+ if (bluetoothGatt != null) {
+ if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ bluetoothGatt.close();
+ bluetoothGatt.disconnect();
+ bluetoothGatt = null;
+ }
+
+ }
+
+ private void showMsg(CharSequence llw) {
+ Toast.makeText(this, llw, Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * 点击地图
+ */
+ @Override
+ public void onMapClick(LatLng latLng) {
+ //终点
+ mEndPoint = convertToLatLonPoint(latLng);
+ //开始路线搜索
+ startRouteSearch();
+ }
+
+ /**
+ * 公交规划路径结果
+ *
+ * @param busRouteResult 结果
+ * @param code 结果码
+ */
+ @Override
+ public void onBusRouteSearched(BusRouteResult busRouteResult, int code) {
+ aMap.clear();// 清理地图上的所有覆盖物
+ if (code != AMapException.CODE_AMAP_SUCCESS) {
+ showMsg("错误码;" + code);
+ return;
+ }
+ if (busRouteResult == null || busRouteResult.getPaths() == null) {
+ showMsg("对不起,没有搜索到相关数据!");
+ return;
+ }
+ if (busRouteResult.getPaths().isEmpty()) {
+ showMsg("对不起,没有搜索到相关数据!");
+ return;
+ }
+ final BusPath busPath = busRouteResult.getPaths().get(0);
+ if (busPath == null) {
+ return;
+ }
+ // 绘制路线
+ BusRouteOverlay busRouteOverlay = new BusRouteOverlay(
+ this, aMap, busPath,
+ busRouteResult.getStartPos(),
+ busRouteResult.getTargetPos());
+ busRouteOverlay.removeFromMap();
+ busRouteOverlay.addToMap();
+ busRouteOverlay.zoomToSpan();
+
+ int dis = (int) busPath.getDistance();
+ int dur = (int) busPath.getDuration();
+ String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")";
+ Log.d(TAG, des);
+ //显示公交花费时间
+ //显示公交花费时间
+ binding.tvTime.setText(des);
+ binding.layBottom.setVisibility(View.VISIBLE);
+ //跳转到路线详情页面
+ binding.tvDetail.setOnClickListener(v -> {
+ Intent intent = new Intent(RouteActivity.this,
+ RouteDetailActivity.class);
+ intent.putExtra("type",1);
+ intent.putExtra("path", busPath);
+ startActivity(intent);
+ });
+ List allPoiPoints = new ArrayList<>();
+
+ List steps = busPath.getSteps();
+ for (BusStep step : steps) {
+
+ // 🟢 步行部分
+ if (step.getWalk() != null && step.getWalk().getSteps() != null) {
+ List walkSteps = step.getWalk().getSteps();
+ for (WalkStep walkStep : walkSteps) {
+ List polyline = walkStep.getPolyline();
+ for (LatLonPoint point : polyline) {
+ allPoiPoints.add(new LatLng(point.getLatitude(), point.getLongitude()));
+ Log.d("Walk:",point.getLatitude() + "," + point.getLongitude());
+ }
+ }
+ }
+
+ // 🟠 公交部分
+ if (step.getBusLines() != null && !step.getBusLines().isEmpty()) {
+ BusLineItem busLine = step.getBusLines().get(0); // 取当前段第一条公交车
+ List busPoints = ((RouteBusLineItem) busLine).getPolyline(); // 🚍 公交线路坐标
+ for (LatLonPoint point : busPoints) {
+ allPoiPoints.add(new LatLng(point.getLatitude(), point.getLongitude()));
+ Log.d("Bus:",point.getLatitude() + "," + point.getLongitude());
+ }
+ }
+ }
+
+// // 🧪 测试打印
+// for (LatLng p : poiList) {
+// Log.d("POI_POINT", p.latitude + "," + p.longitude);
+// }
+
+
+ }
+
+
+ @Override
+ public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int i) {
+
+ }
+
+ /**
+ * 步行规划路径结果
+ *
+ * @param walkRouteResult 结果
+ * @param code 结果码
+ */
+ @Override
+ public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int code) {
+ aMap.clear();// 清理地图上的所有覆盖物
+ if (code != AMapException.CODE_AMAP_SUCCESS) {
+ showMsg("错误码;" + code);
+ return;
+ }
+ if (walkRouteResult == null || walkRouteResult.getPaths() == null) {
+ showMsg("对不起,没有搜索到相关数据!");
+ return;
+ }
+ if (walkRouteResult.getPaths().isEmpty()) {
+ showMsg("对不起,没有搜索到相关数据!");
+ return;
+ }
+ final WalkPath walkPath = walkRouteResult.getPaths().get(0);
+ if (walkPath == null) {
+ return;
+ }
+ //绘制路线
+ WalkRouteOverlay walkRouteOverlay = new WalkRouteOverlay(
+ this, aMap, walkPath,
+ walkRouteResult.getStartPos(),
+ walkRouteResult.getTargetPos());
+ walkRouteOverlay.removeFromMap();
+ walkRouteOverlay.addToMap();
+ walkRouteOverlay.zoomToSpan();
+
+ int dis = (int) walkPath.getDistance();
+ int dur = (int) walkPath.getDuration();
+ String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")";
+ Log.d(TAG, des);
+ //显示步行花费时间
+ binding.tvTime.setText(des);
+ binding.layBottom.setVisibility(View.VISIBLE);
+ //跳转到路线详情页面
+ binding.tvDetail.setOnClickListener(v -> {
+ Intent intent = new Intent(RouteActivity.this,
+ RouteDetailActivity.class);
+ intent.putExtra("type",0);
+ intent.putExtra("path", walkPath);
+ startActivity(intent);
+ });
+ List poiList = new ArrayList<>();
+
+ List steps = walkPath.getSteps();
+ for (WalkStep step : steps) {
+ List polyline = step.getPolyline();
+ for (LatLonPoint point : polyline) {
+ LatLng latLng = new LatLng(point.getLatitude(), point.getLongitude());
+ poiList.add(latLng);
+ }
+ }
+
+// 🧪 测试打印
+ for (LatLng p : poiList) {
+ Log.d("POI_POINT", p.latitude + "," + p.longitude);
+ }
+ sendPoiList(poiList);
+ }
+
+
+ @Override
+ public void onRideRouteSearched(RideRouteResult rideRouteResult, int i) {
+
+ }
+
+ @Override
+ public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
+
+ }
+
+ /**
+ * 地址转坐标
+ *
+ * @param geocodeResult
+ * @param rCode
+ */
+ @Override
+ public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
+ if (rCode != PARSE_SUCCESS_CODE) {
+ showMsg("获取坐标失败,错误码:" + rCode);
+ return;
+ }
+ List geocodeAddressList = geocodeResult.getGeocodeAddressList();
+ if (geocodeAddressList != null && !geocodeAddressList.isEmpty()) {
+ //判断是不是起点的搜索
+ if (tag == 1) {
+ //起点
+ mStartPoint = geocodeAddressList.get(0).getLatLonPoint();
+ } else {
+ //终点
+ mEndPoint = geocodeAddressList.get(0).getLatLonPoint();
+ }
+ if (mStartPoint != null && mEndPoint != null) {
+ //开始路线搜索
+ startRouteSearch();
+ }
+ }
+ }
+
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+ //获取输入框的值 出发地(起点)
+ String startAddress = binding.etStartAddress.getText().toString().trim();
+ //获取输入框的值 目的地(终点)
+ String endAddress = binding.etEndAddress.getText().toString().trim();
+
+ //判断出发地是否有值 不管这个值是定位还是手动输入
+ if (startAddress.isEmpty()) {
+ showMsg("请输入当前的出发地");
+ return false;
+ }
+ //判断目的地是否有值
+ if (endAddress.isEmpty()) {
+ showMsg("请输入要前往的目的地");
+ return false;
+ }
+
+ //当出发地输入框有值的时候,判断这个值是否是定位的地址,是则说明你没有更改过,则不需要进行地址转坐标,不是则需要转换。
+ if (!locationAddress.equals(startAddress)) {
+ tag = 1;
+ GeocodeQuery startQuery = new GeocodeQuery(startAddress, city);
+ geocodeSearch.getFromLocationNameAsyn(startQuery);
+ } else {
+ tag = -1;
+ }
+
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ //隐藏软键盘
+ imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
+
+ //通过输入的目的地转为经纬度,然后进行地图上添加标点,最后计算出行路线规划
+
+ // name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode
+ GeocodeQuery endQuery = new GeocodeQuery(endAddress, city);
+ geocodeSearch.getFromLocationNameAsyn(endQuery);
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java b/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java
new file mode 100644
index 0000000..32a4c03
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java
@@ -0,0 +1,147 @@
+package com.example.mangowalking;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.amap.api.services.route.BusPath;
+import com.amap.api.services.route.BusStep;
+import com.amap.api.services.route.WalkPath;
+import com.example.mangowalking.adapter.BusSegmentListAdapter;
+import com.example.mangowalking.adapter.WalkSegmentListAdapter;
+import com.example.mangowalking.databinding.ActivityRouteDetailBinding;
+import com.example.mangowalking.utils.MapUtil;
+import com.example.mangowalking.utils.SchemeBusStep;
+
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RouteDetailActivity extends AppCompatActivity {
+
+ private ActivityRouteDetailBinding binding;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ binding = ActivityRouteDetailBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+ Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ return insets;
+ });
+ initView();
+ }
+
+
+ /**
+ * 初始化视图
+ */
+ private void initView() {
+ binding.toolbar.setNavigationOnClickListener(v -> finish());
+ Intent intent = getIntent();
+ if (intent == null) {
+ return;
+ }
+ switch (intent.getIntExtra("type", 0)) {
+ case 0://步行
+ walkDetail(intent);
+ break;
+ case 1://
+ busDetail(intent);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 步行详情
+ * @param intent
+ */
+ private void walkDetail(Intent intent) {
+ binding.toolbar.setTitle("步行路线规划");
+ WalkPath walkPath = intent.getParcelableExtra("path");
+ String dur = MapUtil.getFriendlyTime((int) walkPath.getDuration());
+ String dis = MapUtil.getFriendlyLength((int) walkPath.getDistance());
+ binding.tvTime.setText(dur + "(" + dis + ")");
+ binding.rvRouteDetail.setLayoutManager(new LinearLayoutManager(this));
+ binding.rvRouteDetail.setAdapter(new WalkSegmentListAdapter(walkPath.getSteps()));
+ }
+
+ private void busDetail(Intent intent) {
+ binding.toolbar.setTitle("公交路线规划");
+
+ BusPath busPath = intent.getParcelableExtra("path");
+ if (busPath == null) {
+ Log.e("RouteDetail", "busPath is null!");
+ return;
+ }
+
+ String dur = MapUtil.getFriendlyTime((int) busPath.getDuration());
+ String dis = MapUtil.getFriendlyLength((int) busPath.getDistance());
+ binding.tvTime.setText(dur + "(" + dis + ")");
+
+ List stepList = getBusSteps(busPath.getSteps());
+ Log.d("RouteDetail", "Steps count: " + stepList.size());
+
+ binding.rvRouteDetail.setLayoutManager(new LinearLayoutManager(this));
+ binding.rvRouteDetail.setAdapter(new BusSegmentListAdapter(R.layout.item_segment, stepList));
+ }
+
+
+
+
+
+ /**
+ * 公交方案数据组装
+ * @param list
+ * @return
+ */
+ private List getBusSteps(List list) {
+ List busStepList = new ArrayList<>();
+ SchemeBusStep start = new SchemeBusStep(null);
+ start.setStart(true);
+ busStepList.add(start);
+ for (BusStep busStep : list) {
+ if (busStep.getWalk() != null && busStep.getWalk().getDistance() > 0) {
+ SchemeBusStep walk = new SchemeBusStep(busStep);
+ walk.setWalk(true);
+ busStepList.add(walk);
+ }
+ if (busStep.getBusLine() != null) {
+ SchemeBusStep bus = new SchemeBusStep(busStep);
+ bus.setBus(true);
+ busStepList.add(bus);
+ }
+ if (busStep.getRailway() != null) {
+ SchemeBusStep railway = new SchemeBusStep(busStep);
+ railway.setRailway(true);
+ busStepList.add(railway);
+ }
+
+ if (busStep.getTaxi() != null) {
+ SchemeBusStep taxi = new SchemeBusStep(busStep);
+ taxi.setTaxi(true);
+ busStepList.add(taxi);
+ }
+ }
+ SchemeBusStep end = new SchemeBusStep(null);
+ end.setEnd(true);
+ busStepList.add(end);
+ return busStepList;
+ }
+
+
+}
diff --git a/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java b/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java
new file mode 100644
index 0000000..82ad250
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java
@@ -0,0 +1,175 @@
+package com.example.mangowalking.adapter;
+
+import static com.amap.api.maps.model.BitmapDescriptorFactory.getContext;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.amap.api.services.busline.BusStationItem;
+import com.amap.api.services.route.RailwayStationItem;
+import com.example.mangowalking.R;
+import com.example.mangowalking.databinding.ItemSegmentBinding;
+import com.example.mangowalking.utils.SchemeBusStep;
+
+import java.util.List;
+
+/**
+ * 公交段列表适配器
+ */
+public class BusSegmentListAdapter extends RecyclerView.Adapter {
+
+ private List mItemList;
+
+ public BusSegmentListAdapter(int item_segment, List data) {
+ this.mItemList = data;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ViewHolder(ItemSegmentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ SchemeBusStep item = mItemList.get(position);
+ if (position == 0) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir_start);
+ holder.binding.busLineName.setText("出发");
+ holder.binding.busDirIconUp.setVisibility(View.INVISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busSegSplitLine.setVisibility(View.INVISIBLE);
+ } else if (position == mItemList.size() - 1) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir_end);
+ holder.binding.busLineName.setText("到达终点");
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.INVISIBLE);
+ } else {
+ if (item.isWalk() && item.getWalk() != null && item.getWalk().getDistance() > 0) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir13);
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busLineName.setText("步行"
+ + (int) item.getWalk().getDistance() + "米");
+ holder.binding.busStationNum.setVisibility(View.GONE);
+ holder.binding.busExpandImage.setVisibility(View.GONE);
+ } else if (item.isBus() && item.getBusLines().size() > 0) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir14);
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busLineName.setText(item.getBusLines().get(0).getBusLineName());
+ holder.binding.busStationNum.setVisibility(View.VISIBLE);
+ holder.binding.busStationNum
+ .setText((item.getBusLines().get(0).getPassStationNum() + 1) + "站");
+ holder.binding.busExpandImage.setVisibility(View.VISIBLE);
+ } else if (item.isRailway() && item.getRailway() != null) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir16);
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busLineName.setText(item.getRailway().getName());
+ holder.binding.busStationNum.setVisibility(View.VISIBLE);
+ holder.binding.busStationNum
+ .setText((item.getRailway().getViastops().size() + 1) + "站");
+ holder.binding.busExpandImage.setVisibility(View.VISIBLE);
+ } else if (item.isTaxi() && item.getTaxi() != null) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir14);
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busLineName.setText("打车到终点");
+ holder.binding.busStationNum.setVisibility(View.GONE);
+ holder.binding.busExpandImage.setVisibility(View.GONE);
+ }
+ }
+
+ holder.binding.busItem.setOnClickListener(v -> {
+ if (item.isBus()) {//公交
+ if (!item.isArrowExpend()) {
+ item.setArrowExpend(true);
+ holder.binding.busExpandImage.setImageResource(R.drawable.up);
+ addBusStation(item.getBusLine().getDepartureBusStation(), holder.binding.expandContent);
+ for (BusStationItem station : item.getBusLine()
+ .getPassStations()) {
+ addBusStation(station, holder.binding.expandContent);
+ }
+ addBusStation(item.getBusLine().getArrivalBusStation(), holder.binding.expandContent);
+
+ } else {
+ item.setArrowExpend(false);
+ holder.binding.busExpandImage.setImageResource(R.drawable.down);
+ holder.binding.expandContent.removeAllViews();
+ }
+ } else if (item.isRailway()) {//火车
+ if (!item.isArrowExpend()) {
+ item.setArrowExpend(true);
+ holder.binding.busExpandImage.setImageResource(R.drawable.up);
+ addRailwayStation(item.getRailway().getDeparturestop(), holder.binding.expandContent);
+ for (RailwayStationItem station : item.getRailway().getViastops()) {
+ addRailwayStation(station, holder.binding.expandContent);
+ }
+ addRailwayStation(item.getRailway().getArrivalstop(), holder.binding.expandContent);
+
+ } else {
+ item.setArrowExpend(false);
+ holder.binding.busExpandImage.setImageResource(R.drawable.down);
+ holder.binding.expandContent.removeAllViews();
+ }
+ }
+ });
+ }
+
+ /**
+ * 添加公交车站
+ * @param station
+ * @param expandContent
+ */
+ private void addBusStation(BusStationItem station, LinearLayout expandContent) {
+ LinearLayout ll = (LinearLayout) View.inflate(getContext(),
+ R.layout.item_segment_ex, null);
+ TextView tv = ll.findViewById(R.id.bus_line_station_name);
+ tv.setText(station.getBusStationName());
+ expandContent.addView(ll);
+ }
+
+ /**
+ * 添加火车站
+ * @param station
+ * @param expandContent
+ */
+ private void addRailwayStation(RailwayStationItem station, LinearLayout expandContent) {
+ LinearLayout ll = (LinearLayout) View.inflate(getContext(),
+ R.layout.item_segment_ex, null);
+ TextView tv = ll
+ .findViewById(R.id.bus_line_station_name);
+ tv.setText(station.getName() + " " + getRailwayTime(station.getTime()));
+ expandContent.addView(ll);
+ }
+
+ /**
+ * 获取铁路时间
+ * @param time
+ * @return
+ */
+ public static String getRailwayTime(String time) {
+ return time.substring(0, 2) + ":" + time.substring(2, time.length());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemList == null || mItemList.isEmpty() ? 0 : mItemList.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ItemSegmentBinding binding;
+
+ public ViewHolder(ItemSegmentBinding itemView) {
+ super(itemView.getRoot());
+ this.binding = itemView;
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java b/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java
new file mode 100644
index 0000000..20d6c1b
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java
@@ -0,0 +1,72 @@
+package com.example.mangowalking.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.amap.api.services.route.WalkStep;
+import com.example.mangowalking.R;
+import com.example.mangowalking.databinding.ItemSegmentBinding;
+import com.example.mangowalking.utils.MapUtil;
+
+import java.util.List;
+
+/**
+ * 步行段列表适配器
+ */
+public class WalkSegmentListAdapter extends RecyclerView.Adapter {
+
+ private List mItemList;
+
+ public WalkSegmentListAdapter(List data) {
+ this.mItemList = data;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ViewHolder(ItemSegmentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ WalkStep item = mItemList.get(position);
+ if (position == 0) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir_start);
+ holder.binding.busLineName.setText("出发");
+ holder.binding.busDirIconUp.setVisibility(View.INVISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ holder.binding.busSegSplitLine.setVisibility(View.INVISIBLE);
+ } else if (position == mItemList.size() - 1) {
+ holder.binding.busDirIcon.setImageResource(R.drawable.dir_end);
+ holder.binding.busLineName.setText("到达终点");
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.INVISIBLE);
+ } else {
+ holder.binding.busSegSplitLine.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconUp.setVisibility(View.VISIBLE);
+ holder.binding.busDirIconDown.setVisibility(View.VISIBLE);
+ String actionName = item.getAction();
+ int resID = MapUtil.getWalkActionID(actionName);
+ holder.binding.busDirIcon.setImageResource(resID);
+ holder.binding.busLineName.setText(item.getInstruction());
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemList == null || mItemList.isEmpty() ? 0 : mItemList.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ItemSegmentBinding binding;
+
+ public ViewHolder(ItemSegmentBinding itemView) {
+ super(itemView.getRoot());
+ this.binding = itemView;
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java b/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java
new file mode 100644
index 0000000..e961054
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java
@@ -0,0 +1,60 @@
+package com.example.mangowalking.overlay;
+
+/**
+ * 地图服务工具类
+ */
+
+import android.graphics.Bitmap;
+
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.services.core.LatLonPoint;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+class AMapServicesUtil {
+ public static int BUFFER_SIZE = 2048;
+
+ public static byte[] inputStreamToByte(InputStream in) throws IOException {
+
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ byte[] data = new byte[BUFFER_SIZE];
+ int count = -1;
+ while ((count = in.read(data, 0, BUFFER_SIZE)) != -1){
+ outStream.write(data, 0, count);
+ }
+
+ data = null;
+ return outStream.toByteArray();
+ }
+ public static LatLonPoint convertToLatLonPoint(LatLng latlon) {
+ return new LatLonPoint(latlon.latitude, latlon.longitude);
+ }
+ public static LatLng convertToLatLng(LatLonPoint latLonPoint) {
+ return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
+ }
+ public static ArrayList convertArrList(List shapes) {
+ ArrayList lineShapes = new ArrayList();
+ for (LatLonPoint point : shapes) {
+ LatLng latLngTemp = AMapServicesUtil.convertToLatLng(point);
+ lineShapes.add(latLngTemp);
+ }
+ return lineShapes;
+ }
+ public static Bitmap zoomBitmap(Bitmap bitmap, float res) {
+ if (bitmap == null) {
+ return null;
+ }
+ int width, height;
+ width = (int) (bitmap.getWidth() * res);
+ height = (int) (bitmap.getHeight() * res);
+ Bitmap newbmp = Bitmap.createScaledBitmap(bitmap, width, height, true);
+ return newbmp;
+ }
+
+
+
+}
diff --git a/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java
new file mode 100644
index 0000000..38a5636
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java
@@ -0,0 +1,496 @@
+package com.example.mangowalking.overlay;
+
+import android.content.Context;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.maps.model.PolylineOptions;
+import com.amap.api.services.busline.BusStationItem;
+import com.amap.api.services.core.LatLonPoint;
+import com.amap.api.services.route.BusPath;
+import com.amap.api.services.route.BusStep;
+import com.amap.api.services.route.Doorway;
+import com.amap.api.services.route.RailwayStationItem;
+import com.amap.api.services.route.RouteBusLineItem;
+import com.amap.api.services.route.RouteBusWalkItem;
+import com.amap.api.services.route.RouteRailwayItem;
+import com.amap.api.services.route.TaxiItem;
+import com.amap.api.services.route.WalkStep;
+import com.example.mangowalking.utils.MapUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 公交路线图层类。在高德地图API里,如果需要显示公交路线,可以用此类来创建公交路线图层。如不满足需求,也可以自己创建自定义的公交路线图层。
+ * @since V2.1.0
+ */
+public class BusRouteOverlay extends RouteOverlay {
+
+ private BusPath busPath;
+ private LatLng latLng;
+
+ /**
+ * 通过此构造函数创建公交路线图层。
+ * @param context 当前activity。
+ * @param amap 地图对象。
+ * @param path 公交路径规划的一个路段。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 BusPath。
+ * @param start 起点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 LatLonPoint。
+ * @param end 终点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 LatLonPoint。
+ * @since V2.1.0
+ */
+ public BusRouteOverlay(Context context, AMap amap, BusPath path,
+ LatLonPoint start, LatLonPoint end) {
+ super(context);
+ this.busPath = path;
+ startPoint = MapUtil.convertToLatLng(start);
+ endPoint = MapUtil.convertToLatLng(end);
+ mAMap = amap;
+ }
+
+ /**
+ * 添加公交路线到地图上。
+ * @since V2.1.0
+ */
+
+ public void addToMap() {
+ /**
+ * 绘制节点和线
+ * 细节情况较多
+ * 两个step之间,用step和step1区分
+ * 1.一个step内可能有步行和公交,然后有可能他们之间连接有断开
+ * 2.step的公交和step1的步行,有可能连接有断开
+ * 3.step和step1之间是公交换乘,且没有步行,需要把step的终点和step1的起点连起来
+ * 4.公交最后一站和终点间有步行,加入步行线路,还会有一些步行marker
+ * 5.公交最后一站和终点间无步行,之间连起来
+ */
+ try {
+ List busSteps = busPath.getSteps();
+ for (int i = 0; i < busSteps.size(); i++) {
+ BusStep busStep = busSteps.get(i);
+ if (i < busSteps.size() - 1) {
+ BusStep busStep1 = busSteps.get(i + 1);// 取得当前下一个BusStep对象
+ // 假如步行和公交之间连接有断开,就把步行最后一个经纬度点和公交第一个经纬度点连接起来,避免断线问题
+ if (busStep.getWalk() != null
+ && busStep.getBusLine() != null) {
+ checkWalkToBusline(busStep);
+ }
+
+ // 假如公交和步行之间连接有断开,就把上一公交经纬度点和下一步行第一个经纬度点连接起来,避免断线问题
+ if (busStep.getBusLine() != null
+ && busStep1.getWalk() != null
+ && busStep1.getWalk().getSteps().size() > 0) {
+ checkBusLineToNextWalk(busStep, busStep1);
+ }
+ // 假如两个公交换乘中间没有步行,就把上一公交经纬度点和下一步公交第一个经纬度点连接起来,避免断线问题
+ if (busStep.getBusLine() != null
+ && busStep1.getWalk() == null
+ && busStep1.getBusLine() != null) {
+ checkBusEndToNextBusStart(busStep, busStep1);
+ }
+ // 和上面的很类似
+ if (busStep.getBusLine() != null
+ && busStep1.getWalk() == null
+ && busStep1.getBusLine() != null) {
+ checkBusToNextBusNoWalk(busStep, busStep1);
+ }
+ if (busStep.getBusLine() != null
+ && busStep1.getRailway() != null ) {
+ checkBusLineToNextRailway(busStep, busStep1);
+ }
+ if (busStep1.getWalk() != null &&
+ busStep1.getWalk().getSteps().size() > 0 &&
+ busStep.getRailway() != null) {
+ checkRailwayToNextWalk(busStep, busStep1);
+ }
+
+ if ( busStep1.getRailway() != null &&
+ busStep.getRailway() != null) {
+ checkRailwayToNextRailway(busStep, busStep1);
+ }
+
+ if (busStep.getRailway() != null &&
+ busStep1.getTaxi() != null ){
+ checkRailwayToNextTaxi(busStep, busStep1);
+ }
+
+
+ }
+
+ if (busStep.getWalk() != null
+ && busStep.getWalk().getSteps().size() > 0) {
+ addWalkSteps(busStep);
+ } else {
+ if (busStep.getBusLine() == null && busStep.getRailway() == null && busStep.getTaxi() == null) {
+ addWalkPolyline(latLng, endPoint);
+ }
+ }
+ if (busStep.getBusLine() != null) {
+ RouteBusLineItem routeBusLineItem = busStep.getBusLine();
+ addBusLineSteps(routeBusLineItem);
+ addBusStationMarkers(routeBusLineItem);
+ if (i == busSteps.size() - 1) {
+ addWalkPolyline(MapUtil.convertToLatLng(getLastBuslinePoint(busStep)), endPoint);
+ }
+ }
+ if (busStep.getRailway() != null) {
+ addRailwayStep(busStep.getRailway());
+ addRailwayMarkers(busStep.getRailway());
+ if (i == busSteps.size() - 1) {
+ addWalkPolyline(MapUtil.convertToLatLng(busStep.getRailway().getArrivalstop().getLocation()), endPoint);
+ }
+ }
+ if (busStep.getTaxi() != null) {
+ addTaxiStep(busStep.getTaxi());
+ addTaxiMarkers(busStep.getTaxi());
+ }
+ }
+ addStartAndEndMarker();
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+
+
+ private void checkRailwayToNextTaxi(BusStep busStep, BusStep busStep1) {
+ LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
+ LatLonPoint taxiFirstPoint = busStep1.getTaxi().getOrigin();
+ if (!railwayLastPoint.equals(taxiFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(railwayLastPoint, taxiFirstPoint);
+ }
+ }
+
+ private void checkRailwayToNextRailway(BusStep busStep, BusStep busStep1) {
+ LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
+ LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
+ if (!railwayLastPoint.equals(railwayFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(railwayLastPoint, railwayFirstPoint);
+ }
+
+ }
+
+ private void checkBusLineToNextRailway(BusStep busStep, BusStep busStep1) {
+ LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
+ LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
+ if (!busLastPoint.equals(railwayFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(busLastPoint, railwayFirstPoint);
+ }
+
+ }
+
+ private void checkRailwayToNextWalk(BusStep busStep, BusStep busStep1) {
+ LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
+ LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
+ if (!railwayLastPoint.equals(walkFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(railwayLastPoint, walkFirstPoint);
+ }
+
+ }
+
+ private void addRailwayStep(RouteRailwayItem railway) {
+ List railwaylistpoint = new ArrayList();
+ List railwayStationItems = new ArrayList();
+ railwayStationItems.add(railway.getDeparturestop());
+ railwayStationItems.addAll(railway.getViastops());
+ railwayStationItems.add(railway.getArrivalstop());
+ for (int i = 0; i < railwayStationItems.size(); i++) {
+ railwaylistpoint.add(MapUtil.convertToLatLng(railwayStationItems.get(i).getLocation()));
+ }
+ addRailwayPolyline(railwaylistpoint);
+ }
+
+ private void addTaxiStep(TaxiItem taxi){
+ addPolyLine(new PolylineOptions().width(getRouteWidth())
+ .color(getBusColor())
+ .add(MapUtil.convertToLatLng(taxi.getOrigin()))
+ .add(MapUtil.convertToLatLng(taxi.getDestination())));
+ }
+
+ /**
+ * @param busStep
+ */
+ private void addWalkSteps(BusStep busStep) {
+ RouteBusWalkItem routeBusWalkItem = busStep.getWalk();
+ List walkSteps = routeBusWalkItem.getSteps();
+ for (int j = 0; j < walkSteps.size(); j++) {
+ WalkStep walkStep = walkSteps.get(j);
+ if (j == 0) {
+ LatLng latLng = MapUtil.convertToLatLng(walkStep
+ .getPolyline().get(0));
+ String road = walkStep.getRoad();// 道路名字
+ String instruction = getWalkSnippet(walkSteps);// 步行导航信息
+ addWalkStationMarkers(latLng, road, instruction);
+ }
+
+ List listWalkPolyline = MapUtil
+ .convertArrList(walkStep.getPolyline());
+ this.latLng = listWalkPolyline.get(listWalkPolyline.size() - 1);
+
+ addWalkPolyline(listWalkPolyline);
+
+ // 假如步行前一段的终点和下的起点有断开,断画直线连接起来,避免断线问题
+ if (j < walkSteps.size() - 1) {
+ LatLng lastLatLng = listWalkPolyline.get(listWalkPolyline
+ .size() - 1);
+ LatLng firstlatLatLng = MapUtil
+ .convertToLatLng(walkSteps.get(j + 1).getPolyline()
+ .get(0));
+ if (!(lastLatLng.equals(firstlatLatLng))) {
+ addWalkPolyline(lastLatLng, firstlatLatLng);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * 添加一系列的bus PolyLine
+ *
+ * @param routeBusLineItem
+ */
+ private void addBusLineSteps(RouteBusLineItem routeBusLineItem) {
+ addBusLineSteps(routeBusLineItem.getPolyline());
+ }
+
+ private void addBusLineSteps(List listPoints) {
+ if (listPoints.size() < 1) {
+ return;
+ }
+ addPolyLine(new PolylineOptions().width(getRouteWidth())
+ .color(getBusColor())
+ .addAll(MapUtil.convertArrList(listPoints)));
+ }
+
+ /**
+ * @param latLng
+ * marker
+ * @param title
+ * @param snippet
+ */
+ private void addWalkStationMarkers(LatLng latLng, String title,
+ String snippet) {
+ addStationMarker(new MarkerOptions().position(latLng).title(title)
+ .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
+ .icon(getWalkBitmapDescriptor()));
+ }
+
+ /**
+ * @param routeBusLineItem
+ */
+ private void addBusStationMarkers(RouteBusLineItem routeBusLineItem) {
+ BusStationItem startBusStation = routeBusLineItem
+ .getDepartureBusStation();
+ LatLng position = MapUtil.convertToLatLng(startBusStation
+ .getLatLonPoint());
+ String title = routeBusLineItem.getBusLineName();
+ String snippet = getBusSnippet(routeBusLineItem);
+
+ addStationMarker(new MarkerOptions().position(position).title(title)
+ .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
+ .icon(getBusBitmapDescriptor()));
+ }
+
+ private void addTaxiMarkers(TaxiItem taxiItem) {
+
+ LatLng position = MapUtil.convertToLatLng(taxiItem
+ .getOrigin());
+ String title = taxiItem.getmSname()+"打车";
+ String snippet = "到终点";
+
+ addStationMarker(new MarkerOptions().position(position).title(title)
+ .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
+ .icon(getDriveBitmapDescriptor()));
+ }
+
+ private void addRailwayMarkers(RouteRailwayItem railway) {
+ LatLng Departureposition = MapUtil.convertToLatLng(railway
+ .getDeparturestop().getLocation());
+ String Departuretitle = railway.getDeparturestop().getName()+"上车";
+ String Departuresnippet = railway.getName();
+
+ addStationMarker(new MarkerOptions().position(Departureposition).title(Departuretitle)
+ .snippet(Departuresnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
+ .icon(getBusBitmapDescriptor()));
+
+
+ LatLng Arrivalposition = MapUtil.convertToLatLng(railway
+ .getArrivalstop().getLocation());
+ String Arrivaltitle = railway.getArrivalstop().getName()+"下车";
+ String Arrivalsnippet = railway.getName();
+
+ addStationMarker(new MarkerOptions().position(Arrivalposition).title(Arrivaltitle)
+ .snippet(Arrivalsnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
+ .icon(getBusBitmapDescriptor()));
+ }
+ /**
+ * 如果换乘没有步行 检查bus最后一点和下一个step的bus起点是否一致
+ *
+ * @param busStep
+ * @param busStep1
+ */
+ private void checkBusToNextBusNoWalk(BusStep busStep, BusStep busStep1) {
+ LatLng endbusLatLng = MapUtil
+ .convertToLatLng(getLastBuslinePoint(busStep));
+ LatLng startbusLatLng = MapUtil
+ .convertToLatLng(getFirstBuslinePoint(busStep1));
+ if (startbusLatLng.latitude - endbusLatLng.latitude > 0.0001
+ || startbusLatLng.longitude - endbusLatLng.longitude > 0.0001) {
+ drawLineArrow(endbusLatLng, startbusLatLng);// 断线用带箭头的直线连?
+ }
+ }
+
+ /**
+ *
+ * checkBusToNextBusNoWalk 和这个类似
+ *
+ * @param busStep
+ * @param busStep1
+ */
+ private void checkBusEndToNextBusStart(BusStep busStep, BusStep busStep1) {
+ LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
+ LatLng endbusLatLng = MapUtil.convertToLatLng(busLastPoint);
+ LatLonPoint busFirstPoint = getFirstBuslinePoint(busStep1);
+ LatLng startbusLatLng = MapUtil.convertToLatLng(busFirstPoint);
+ if (!endbusLatLng.equals(startbusLatLng)) {
+ drawLineArrow(endbusLatLng, startbusLatLng);//
+ }
+ }
+
+ /**
+ * 检查bus最后一步和下一各step的步行起点是否一致
+ *
+ * @param busStep
+ * @param busStep1
+ */
+ private void checkBusLineToNextWalk(BusStep busStep, BusStep busStep1) {
+ LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
+ LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
+ if (!busLastPoint.equals(walkFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(busLastPoint, walkFirstPoint);
+ }
+ }
+
+ /**
+ * 检查 步行最后一点 和 bus的起点 是否一致
+ *
+ * @param busStep
+ */
+ private void checkWalkToBusline(BusStep busStep) {
+ LatLonPoint walkLastPoint = getLastWalkPoint(busStep);
+ LatLonPoint buslineFirstPoint = getFirstBuslinePoint(busStep);
+
+ if (!walkLastPoint.equals(buslineFirstPoint)) {
+ addWalkPolyLineByLatLonPoints(walkLastPoint, buslineFirstPoint);
+ }
+ }
+
+ /**
+ * @param busStep1
+ * @return
+ */
+ private LatLonPoint getFirstWalkPoint(BusStep busStep1) {
+ return busStep1.getWalk().getSteps().get(0).getPolyline().get(0);
+ }
+
+ /**
+ *
+ */
+ private void addWalkPolyLineByLatLonPoints(LatLonPoint pointFrom,
+ LatLonPoint pointTo) {
+ LatLng latLngFrom = MapUtil.convertToLatLng(pointFrom);
+ LatLng latLngTo = MapUtil.convertToLatLng(pointTo);
+
+ addWalkPolyline(latLngFrom, latLngTo);
+ }
+
+ /**
+ * @param latLngFrom
+ * @param latLngTo
+ * @return
+ */
+ private void addWalkPolyline(LatLng latLngFrom, LatLng latLngTo) {
+ addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo)
+ .width(getRouteWidth()).color(getWalkColor()).setDottedLine(true));
+ }
+
+ /**
+ * @param listWalkPolyline
+ */
+ private void addWalkPolyline(List listWalkPolyline) {
+
+ addPolyLine(new PolylineOptions().addAll(listWalkPolyline)
+ .color(getWalkColor()).width(getRouteWidth()).setDottedLine(true));
+ }
+
+ private void addRailwayPolyline(List listPolyline) {
+
+ addPolyLine(new PolylineOptions().addAll(listPolyline)
+ .color(getDriveColor()).width(getRouteWidth()));
+ }
+
+
+ private String getWalkSnippet(List walkSteps) {
+ float disNum = 0;
+ for (WalkStep step : walkSteps) {
+ disNum += step.getDistance();
+ }
+ return "\u6B65\u884C" + disNum + "\u7C73";
+ }
+
+ public void drawLineArrow(LatLng latLngFrom, LatLng latLngTo) {
+
+ addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo).width(3)
+ .color(getBusColor()).width(getRouteWidth()));// 绘制直线
+ }
+
+ private String getBusSnippet(RouteBusLineItem routeBusLineItem) {
+ return "("
+ + routeBusLineItem.getDepartureBusStation().getBusStationName()
+ + "-->"
+ + routeBusLineItem.getArrivalBusStation().getBusStationName()
+ + ") \u7ECF\u8FC7" + (routeBusLineItem.getPassStationNum() + 1)
+ + "\u7AD9";
+ }
+
+ /**
+ * @param busStep
+ * @return
+ */
+ private LatLonPoint getLastWalkPoint(BusStep busStep) {
+
+ List walkSteps = busStep.getWalk().getSteps();
+ WalkStep walkStep = walkSteps.get(walkSteps.size() - 1);
+ List lonPoints = walkStep.getPolyline();
+ return lonPoints.get(lonPoints.size() - 1);
+ }
+
+ private LatLonPoint getExitPoint(BusStep busStep) {
+ Doorway doorway = busStep.getExit();
+ if (doorway == null) {
+ return null;
+ }
+ return doorway.getLatLonPoint();
+ }
+
+ private LatLonPoint getLastBuslinePoint(BusStep busStep) {
+ List lonPoints = busStep.getBusLine().getPolyline();
+
+ return lonPoints.get(lonPoints.size() - 1);
+ }
+
+ private LatLonPoint getEntrancePoint(BusStep busStep) {
+ Doorway doorway = busStep.getEntrance();
+ if (doorway == null) {
+ return null;
+ }
+ return doorway.getLatLonPoint();
+ }
+
+ private LatLonPoint getFirstBuslinePoint(BusStep busStep) {
+ return busStep.getBusLine().getPolyline().get(0);
+ }
+}
+
diff --git a/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java
new file mode 100644
index 0000000..30817ee
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java
@@ -0,0 +1,221 @@
+package com.example.mangowalking.overlay;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.model.BitmapDescriptor;
+import com.amap.api.maps.model.BitmapDescriptorFactory;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.LatLngBounds;
+import com.amap.api.maps.model.Marker;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.maps.model.Polyline;
+import com.amap.api.maps.model.PolylineOptions;
+import com.example.mangowalking.R;
+
+/**
+ * 路线图层叠加
+ */
+public class RouteOverlay {
+ protected List stationMarkers = new ArrayList();
+ protected List allPolyLines = new ArrayList();
+ protected Marker startMarker;
+ protected Marker endMarker;
+ protected LatLng startPoint;
+ protected LatLng endPoint;
+ protected AMap mAMap;
+ private Context mContext;
+ private Bitmap startBit, endBit, busBit, walkBit, driveBit;
+ protected boolean nodeIconVisible = true;
+
+ public RouteOverlay(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * 去掉BusRouteOverlay上所有的Marker。
+ * @since V2.1.0
+ */
+ public void removeFromMap() {
+ if (startMarker != null) {
+ startMarker.remove();
+
+ }
+ if (endMarker != null) {
+ endMarker.remove();
+ }
+ for (Marker marker : stationMarkers) {
+ marker.remove();
+ }
+ for (Polyline line : allPolyLines) {
+ line.remove();
+ }
+ destroyBit();
+ }
+
+ private void destroyBit() {
+ if (startBit != null) {
+ startBit.recycle();
+ startBit = null;
+ }
+ if (endBit != null) {
+ endBit.recycle();
+ endBit = null;
+ }
+ if (busBit != null) {
+ busBit.recycle();
+ busBit = null;
+ }
+ if (walkBit != null) {
+ walkBit.recycle();
+ walkBit = null;
+ }
+ if (driveBit != null) {
+ driveBit.recycle();
+ driveBit = null;
+ }
+ }
+ /**
+ * 给起点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
+ * @return 更换的Marker图片。
+ * @since V2.1.0
+ */
+ protected BitmapDescriptor getStartBitmapDescriptor() {
+ return BitmapDescriptorFactory.fromResource(R.drawable.amap_start);
+ }
+ /**
+ * 给终点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
+ * @return 更换的Marker图片。
+ * @since V2.1.0
+ */
+ protected BitmapDescriptor getEndBitmapDescriptor() {
+ return BitmapDescriptorFactory.fromResource(R.drawable.amap_end);
+ }
+ /**
+ * 给公交Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
+ * @return 更换的Marker图片。
+ * @since V2.1.0
+ */
+ protected BitmapDescriptor getBusBitmapDescriptor() {
+ return BitmapDescriptorFactory.fromResource(R.drawable.amap_bus);
+ }
+ /**
+ * 给步行Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
+ * @return 更换的Marker图片。
+ * @since V2.1.0
+ */
+ protected BitmapDescriptor getWalkBitmapDescriptor() {
+ return BitmapDescriptorFactory.fromResource(R.drawable.amap_man);
+ }
+
+ protected BitmapDescriptor getDriveBitmapDescriptor() {
+ return BitmapDescriptorFactory.fromResource(R.drawable.amap_car);
+ }
+
+ protected void addStartAndEndMarker() {
+ startMarker = mAMap.addMarker((new MarkerOptions())
+ .position(startPoint).icon(getStartBitmapDescriptor())
+ .title("\u8D77\u70B9"));
+ // startMarker.showInfoWindow();
+
+ endMarker = mAMap.addMarker((new MarkerOptions()).position(endPoint)
+ .icon(getEndBitmapDescriptor()).title("\u7EC8\u70B9"));
+ // mAMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPoint,
+ // getShowRouteZoom()));
+ }
+ /**
+ * 移动镜头到当前的视角。
+ * @since V2.1.0
+ */
+ public void zoomToSpan() {
+ if (startPoint != null) {
+ if (mAMap == null) {
+ return;
+ }
+ try {
+ LatLngBounds bounds = getLatLngBounds();
+ mAMap.animateCamera(CameraUpdateFactory
+ .newLatLngBounds(bounds, 100));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected LatLngBounds getLatLngBounds() {
+ LatLngBounds.Builder b = LatLngBounds.builder();
+ b.include(new LatLng(startPoint.latitude, startPoint.longitude));
+ b.include(new LatLng(endPoint.latitude, endPoint.longitude));
+ return b.build();
+ }
+ /**
+ * 路段节点图标控制显示接口。
+ * @param visible true为显示节点图标,false为不显示。
+ * @since V2.3.1
+ */
+ public void setNodeIconVisibility(boolean visible) {
+ try {
+ nodeIconVisible = visible;
+ if (this.stationMarkers != null && this.stationMarkers.size() > 0) {
+ for (int i = 0; i < this.stationMarkers.size(); i++) {
+ this.stationMarkers.get(i).setVisible(visible);
+ }
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void addStationMarker(MarkerOptions options) {
+ if(options == null) {
+ return;
+ }
+ Marker marker = mAMap.addMarker(options);
+ if(marker != null) {
+ stationMarkers.add(marker);
+ }
+
+ }
+
+ protected void addPolyLine(PolylineOptions options) {
+ if(options == null) {
+ return;
+ }
+ Polyline polyline = mAMap.addPolyline(options);
+ if(polyline != null) {
+ allPolyLines.add(polyline);
+ }
+ }
+
+ protected float getRouteWidth() {
+ return 18f;
+ }
+
+ protected int getWalkColor() {
+ return Color.parseColor("#6db74d");
+ }
+
+ /**
+ * 自定义路线颜色。
+ * return 自定义路线颜色。
+ * @since V2.2.1
+ */
+ protected int getBusColor() {
+ return Color.parseColor("#537edc");
+ }
+
+ protected int getDriveColor() {
+ return Color.parseColor("#537edc");
+ }
+
+ // protected int getShowRouteZoom() {
+ // return 15;
+ // }
+}
+
diff --git a/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java
new file mode 100644
index 0000000..19ad138
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java
@@ -0,0 +1,146 @@
+package com.example.mangowalking.overlay;
+
+import java.util.List;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.model.BitmapDescriptor;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.MarkerOptions;
+import com.amap.api.maps.model.PolylineOptions;
+import com.amap.api.services.core.LatLonPoint;
+import com.amap.api.services.route.WalkPath;
+import com.amap.api.services.route.WalkStep;
+
+import android.content.Context;
+
+/**
+ * 步行路线图层类。在高德地图API里,如果要显示步行路线规划,可以用此类来创建步行路线图层。如不满足需求,也可以自己创建自定义的步行路线图层。
+ * @since V2.1.0
+ */
+public class WalkRouteOverlay extends RouteOverlay {
+
+ private PolylineOptions mPolylineOptions;
+
+ private BitmapDescriptor walkStationDescriptor= null;
+
+ private WalkPath walkPath;
+ /**
+ * 通过此构造函数创建步行路线图层。
+ * @param context 当前activity。
+ * @param amap 地图对象。
+ * @param path 步行路线规划的一个方案。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 WalkStep。
+ * @param start 起点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类LatLonPoint。
+ * @param end 终点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类LatLonPoint。
+ * @since V2.1.0
+ */
+ public WalkRouteOverlay(Context context, AMap amap, WalkPath path,
+ LatLonPoint start, LatLonPoint end) {
+ super(context);
+ this.mAMap = amap;
+ this.walkPath = path;
+ startPoint = AMapServicesUtil.convertToLatLng(start);
+ endPoint = AMapServicesUtil.convertToLatLng(end);
+ }
+ /**
+ * 添加步行路线到地图中。
+ * @since V2.1.0
+ */
+ public void addToMap() {
+
+ initPolylineOptions();
+ try {
+ List walkPaths = walkPath.getSteps();
+ for (int i = 0; i < walkPaths.size(); i++) {
+ WalkStep walkStep = walkPaths.get(i);
+ LatLng latLng = AMapServicesUtil.convertToLatLng(walkStep
+ .getPolyline().get(0));
+
+ addWalkStationMarkers(walkStep, latLng);
+ addWalkPolyLines(walkStep);
+
+ }
+ addStartAndEndMarker();
+
+ showPolyline();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 检查这一步的最后一点和下一步的起始点之间是否存在空隙
+ */
+ private void checkDistanceToNextStep(WalkStep walkStep,
+ WalkStep walkStep1) {
+ LatLonPoint lastPoint = getLastWalkPoint(walkStep);
+ LatLonPoint nextFirstPoint = getFirstWalkPoint(walkStep1);
+ if (!(lastPoint.equals(nextFirstPoint))) {
+ addWalkPolyLine(lastPoint, nextFirstPoint);
+ }
+ }
+
+ /**
+ * @param walkStep
+ * @return
+ */
+ private LatLonPoint getLastWalkPoint(WalkStep walkStep) {
+ return walkStep.getPolyline().get(walkStep.getPolyline().size() - 1);
+ }
+
+ /**
+ * @param walkStep
+ * @return
+ */
+ private LatLonPoint getFirstWalkPoint(WalkStep walkStep) {
+ return walkStep.getPolyline().get(0);
+ }
+
+
+ private void addWalkPolyLine(LatLonPoint pointFrom, LatLonPoint pointTo) {
+ addWalkPolyLine(AMapServicesUtil.convertToLatLng(pointFrom), AMapServicesUtil.convertToLatLng(pointTo));
+ }
+
+ private void addWalkPolyLine(LatLng latLngFrom, LatLng latLngTo) {
+ mPolylineOptions.add(latLngFrom, latLngTo);
+ }
+
+ /**
+ * @param walkStep
+ */
+ private void addWalkPolyLines(WalkStep walkStep) {
+ mPolylineOptions.addAll(AMapServicesUtil.convertArrList(walkStep.getPolyline()));
+ }
+
+ /**
+ * @param walkStep
+ * @param position
+ */
+ private void addWalkStationMarkers(WalkStep walkStep, LatLng position) {
+ addStationMarker(new MarkerOptions()
+ .position(position)
+ .title("\u65B9\u5411:" + walkStep.getAction()
+ + "\n\u9053\u8DEF:" + walkStep.getRoad())
+ .snippet(walkStep.getInstruction()).visible(nodeIconVisible)
+ .anchor(0.5f, 0.5f).icon(walkStationDescriptor));
+ }
+
+ /**
+ * 初始化线段属性
+ */
+ private void initPolylineOptions() {
+
+ if(walkStationDescriptor == null) {
+ walkStationDescriptor = getWalkBitmapDescriptor();
+ }
+
+ mPolylineOptions = null;
+
+ mPolylineOptions = new PolylineOptions();
+ mPolylineOptions.color(getWalkColor()).width(getRouteWidth());
+ }
+
+
+ private void showPolyline() {
+ addPolyLine(mPolylineOptions);
+ }
+}
diff --git a/app/src/main/java/com/example/mangowalking/utils/ChString.java b/app/src/main/java/com/example/mangowalking/utils/ChString.java
new file mode 100644
index 0000000..430d3f7
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/utils/ChString.java
@@ -0,0 +1,26 @@
+package com.example.mangowalking.utils;
+
+public class ChString {
+ public static final String Kilometer = "\u516c\u91cc";// "公里";
+ public static final String Meter = "\u7c73";// "米";
+ public static final String ByFoot = "\u6b65\u884c";// "步行";
+ public static final String To = "\u53bb\u5f80";// "去往";
+ public static final String Station = "\u8f66\u7ad9";// "车站";
+ public static final String TargetPlace = "\u76ee\u7684\u5730";// "目的地";
+ public static final String StartPlace = "\u51fa\u53d1\u5730";// "出发地";
+ public static final String About = "\u5927\u7ea6";// "大约";
+ public static final String Direction = "\u65b9\u5411";// "方向";
+
+ public static final String GetOn = "\u4e0a\u8f66";// "上车";
+ public static final String GetOff = "\u4e0b\u8f66";// "下车";
+ public static final String Zhan = "\u7ad9";// "站";
+
+ public static final String cross = "\u4ea4\u53c9\u8def\u53e3"; // 交叉路口
+ public static final String type = "\u7c7b\u522b"; // 类别
+ public static final String address = "\u5730\u5740"; // 地址
+ public static final String PrevStep = "\u4e0a\u4e00\u6b65";
+ public static final String NextStep = "\u4e0b\u4e00\u6b65";
+ public static final String Gong = "\u516c\u4ea4";
+ public static final String ByBus = "\u4e58\u8f66";
+ public static final String Arrive = "\u5230\u8FBE";// 到达
+}
diff --git a/app/src/main/java/com/example/mangowalking/utils/MapUtil.java b/app/src/main/java/com/example/mangowalking/utils/MapUtil.java
new file mode 100644
index 0000000..7cdaa7c
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/utils/MapUtil.java
@@ -0,0 +1,150 @@
+package com.example.mangowalking.utils;
+
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.services.core.LatLonPoint;
+import com.example.mangowalking.R;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 地图帮助类
+ * @author llw
+ */
+public class MapUtil {
+
+ /**
+ * 把LatLng对象转化为LatLonPoint对象
+ */
+ public static LatLonPoint convertToLatLonPoint(LatLng latLng) {
+ return new LatLonPoint(latLng.latitude, latLng.longitude);
+ }
+
+ /**
+ * 把LatLonPoint对象转化为LatLon对象
+ */
+ public static LatLng convertToLatLng(LatLonPoint latLonPoint) {
+ return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
+ }
+
+ public static String getFriendlyTime(int second) {
+ if (second > 3600) {
+ int hour = second / 3600;
+ int miniate = (second % 3600) / 60;
+ return hour + "小时" + miniate + "分钟";
+ }
+ if (second >= 60) {
+ int miniate = second / 60;
+ return miniate + "分钟";
+ }
+ return second + "秒";
+ }
+
+ public static String getFriendlyLength(int lenMeter) {
+ if (lenMeter > 10000) // 10 km
+ {
+ int dis = lenMeter / 1000;
+ return dis + ChString.Kilometer;
+ }
+
+ if (lenMeter > 1000) {
+ float dis = (float) lenMeter / 1000;
+ DecimalFormat fnum = new DecimalFormat("##0.0");
+ String dstr = fnum.format(dis);
+ return dstr + ChString.Kilometer;
+ }
+
+ if (lenMeter > 100) {
+ int dis = lenMeter / 50 * 50;
+ return dis + ChString.Meter;
+ }
+
+ int dis = lenMeter / 10 * 10;
+ if (dis == 0) {
+ dis = 10;
+ }
+
+ return dis + ChString.Meter;
+ }
+ /**
+ * 把集合体的LatLonPoint转化为集合体的LatLng
+ */
+ public static ArrayList convertArrList(List shapes) {
+ ArrayList lineShapes = new ArrayList();
+ for (LatLonPoint point : shapes) {
+ LatLng latLngTemp = convertToLatLng(point);
+ lineShapes.add(latLngTemp);
+ }
+ return lineShapes;
+ }
+
+ public static int getWalkActionID(String actionName) {
+ if (actionName == null || actionName.equals("")) {
+ return R.drawable.dir13;
+ }
+ if ("左转".equals(actionName)) {
+ return R.drawable.dir2;
+ }
+ if ("右转".equals(actionName)) {
+ return R.drawable.dir1;
+ }
+ if ("向左前方".equals(actionName) || "靠左".equals(actionName) || actionName.contains("向左前方")) {
+ return R.drawable.dir6;
+ }
+ if ("向右前方".equals(actionName) || "靠右".equals(actionName) || actionName.contains("向右前方")) {
+ return R.drawable.dir5;
+ }
+ if ("向左后方".equals(actionName)|| actionName.contains("向左后方")) {
+ return R.drawable.dir7;
+ }
+ if ("向右后方".equals(actionName)|| actionName.contains("向右后方")) {
+ return R.drawable.dir8;
+ }
+ if ("直行".equals(actionName)) {
+ return R.drawable.dir3;
+ }
+ if ("通过人行横道".equals(actionName)) {
+ return R.drawable.dir9;
+ }
+ if ("通过过街天桥".equals(actionName)) {
+ return R.drawable.dir11;
+ }
+ if ("通过地下通道".equals(actionName)) {
+ return R.drawable.dir10;
+ }
+ return R.drawable.dir13;
+ }
+
+ public static int getDriveActionID(String actionName) {
+ if (actionName == null || actionName.isEmpty()) {
+ return R.drawable.dir3;
+ }
+ if ("左转".equals(actionName)) {
+ return R.drawable.dir2;
+ }
+ if ("右转".equals(actionName)) {
+ return R.drawable.dir1;
+ }
+ if ("向左前方行驶".equals(actionName) || "靠左".equals(actionName)) {
+ return R.drawable.dir6;
+ }
+ if ("向右前方行驶".equals(actionName) || "靠右".equals(actionName)) {
+ return R.drawable.dir5;
+ }
+ if ("向左后方行驶".equals(actionName) || "左转调头".equals(actionName)) {
+ return R.drawable.dir7;
+ }
+ if ("向右后方行驶".equals(actionName)) {
+ return R.drawable.dir8;
+ }
+ if ("直行".equals(actionName)) {
+ return R.drawable.dir3;
+ }
+ if ("减速行驶".equals(actionName)) {
+ return R.drawable.dir4;
+ }
+ return R.drawable.dir3;
+ }
+
+}
diff --git a/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java b/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java
new file mode 100644
index 0000000..4273ac2
--- /dev/null
+++ b/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java
@@ -0,0 +1,80 @@
+package com.example.mangowalking.utils;
+
+import com.amap.api.services.route.BusStep;
+
+public class SchemeBusStep extends BusStep {
+
+ private boolean isWalk = false;
+ private boolean isBus = false;
+ private boolean israilway = false;
+ private boolean istaxi = false;
+ private boolean isStart = false;
+ private boolean isEnd = false;
+ private boolean arrowExpend = false;
+
+ public SchemeBusStep(BusStep step) {
+ if (step != null) {
+ this.setBusLine(step.getBusLine());
+ this.setWalk(step.getWalk());
+ this.setRailway(step.getRailway());
+ this.setTaxi(step.getTaxi());
+ }
+ }
+
+ public boolean isArrowExpend() {
+ return arrowExpend;
+ }
+
+ public void setArrowExpend(boolean arrowExpend) {
+ this.arrowExpend = arrowExpend;
+ }
+
+ public boolean isWalk() {
+ return isWalk;
+ }
+
+ public void setWalk(boolean isWalk) {
+ this.isWalk = isWalk;
+ }
+
+ public boolean isBus() {
+ return isBus;
+ }
+
+ public void setBus(boolean isBus) {
+ this.isBus = isBus;
+ }
+
+ public boolean isStart() {
+ return isStart;
+ }
+
+ public void setStart(boolean isStart) {
+ this.isStart = isStart;
+ }
+
+ public boolean isEnd() {
+ return isEnd;
+ }
+
+ public void setEnd(boolean isEnd) {
+ this.isEnd = isEnd;
+ }
+
+ public boolean isRailway() {
+ return israilway;
+ }
+
+ public boolean isTaxi() {
+ return istaxi;
+ }
+
+ public void setRailway(boolean israilway) {
+ this.israilway = israilway;
+ }
+
+ public void setTaxi(boolean istaxi) {
+ this.istaxi = istaxi;
+ }
+
+}
diff --git a/app/src/main/res/drawable/amap_bus.png b/app/src/main/res/drawable/amap_bus.png
new file mode 100644
index 0000000..519d81e
Binary files /dev/null and b/app/src/main/res/drawable/amap_bus.png differ
diff --git a/app/src/main/res/drawable/amap_car.png b/app/src/main/res/drawable/amap_car.png
new file mode 100644
index 0000000..0d81d7d
Binary files /dev/null and b/app/src/main/res/drawable/amap_car.png differ
diff --git a/app/src/main/res/drawable/amap_end.png b/app/src/main/res/drawable/amap_end.png
new file mode 100644
index 0000000..5503dc5
Binary files /dev/null and b/app/src/main/res/drawable/amap_end.png differ
diff --git a/app/src/main/res/drawable/amap_man.png b/app/src/main/res/drawable/amap_man.png
new file mode 100644
index 0000000..62a598b
Binary files /dev/null and b/app/src/main/res/drawable/amap_man.png differ
diff --git a/app/src/main/res/drawable/amap_ride.png b/app/src/main/res/drawable/amap_ride.png
new file mode 100644
index 0000000..98d6ee1
Binary files /dev/null and b/app/src/main/res/drawable/amap_ride.png differ
diff --git a/app/src/main/res/drawable/amap_start.png b/app/src/main/res/drawable/amap_start.png
new file mode 100644
index 0000000..cd716c8
Binary files /dev/null and b/app/src/main/res/drawable/amap_start.png differ
diff --git a/app/src/main/res/drawable/amap_through.png b/app/src/main/res/drawable/amap_through.png
new file mode 100644
index 0000000..e636b44
Binary files /dev/null and b/app/src/main/res/drawable/amap_through.png differ
diff --git a/app/src/main/res/drawable/amap_train.png b/app/src/main/res/drawable/amap_train.png
new file mode 100644
index 0000000..0466c78
Binary files /dev/null and b/app/src/main/res/drawable/amap_train.png differ
diff --git a/app/src/main/res/drawable/dir1.png b/app/src/main/res/drawable/dir1.png
new file mode 100644
index 0000000..fec3f4e
Binary files /dev/null and b/app/src/main/res/drawable/dir1.png differ
diff --git a/app/src/main/res/drawable/dir10.png b/app/src/main/res/drawable/dir10.png
new file mode 100644
index 0000000..34c2228
Binary files /dev/null and b/app/src/main/res/drawable/dir10.png differ
diff --git a/app/src/main/res/drawable/dir11.png b/app/src/main/res/drawable/dir11.png
new file mode 100644
index 0000000..df9e8dc
Binary files /dev/null and b/app/src/main/res/drawable/dir11.png differ
diff --git a/app/src/main/res/drawable/dir12.png b/app/src/main/res/drawable/dir12.png
new file mode 100644
index 0000000..a4ba8c2
Binary files /dev/null and b/app/src/main/res/drawable/dir12.png differ
diff --git a/app/src/main/res/drawable/dir13.png b/app/src/main/res/drawable/dir13.png
new file mode 100644
index 0000000..39ad870
Binary files /dev/null and b/app/src/main/res/drawable/dir13.png differ
diff --git a/app/src/main/res/drawable/dir14.png b/app/src/main/res/drawable/dir14.png
new file mode 100644
index 0000000..a3c1632
Binary files /dev/null and b/app/src/main/res/drawable/dir14.png differ
diff --git a/app/src/main/res/drawable/dir15.png b/app/src/main/res/drawable/dir15.png
new file mode 100644
index 0000000..c99fb65
Binary files /dev/null and b/app/src/main/res/drawable/dir15.png differ
diff --git a/app/src/main/res/drawable/dir16.png b/app/src/main/res/drawable/dir16.png
new file mode 100644
index 0000000..86898c2
Binary files /dev/null and b/app/src/main/res/drawable/dir16.png differ
diff --git a/app/src/main/res/drawable/dir2.png b/app/src/main/res/drawable/dir2.png
new file mode 100644
index 0000000..750b6fb
Binary files /dev/null and b/app/src/main/res/drawable/dir2.png differ
diff --git a/app/src/main/res/drawable/dir3.png b/app/src/main/res/drawable/dir3.png
new file mode 100644
index 0000000..8cbe886
Binary files /dev/null and b/app/src/main/res/drawable/dir3.png differ
diff --git a/app/src/main/res/drawable/dir4.png b/app/src/main/res/drawable/dir4.png
new file mode 100644
index 0000000..cfe7d6c
Binary files /dev/null and b/app/src/main/res/drawable/dir4.png differ
diff --git a/app/src/main/res/drawable/dir5.png b/app/src/main/res/drawable/dir5.png
new file mode 100644
index 0000000..e465d5e
Binary files /dev/null and b/app/src/main/res/drawable/dir5.png differ
diff --git a/app/src/main/res/drawable/dir6.png b/app/src/main/res/drawable/dir6.png
new file mode 100644
index 0000000..0d2f3c0
Binary files /dev/null and b/app/src/main/res/drawable/dir6.png differ
diff --git a/app/src/main/res/drawable/dir7.png b/app/src/main/res/drawable/dir7.png
new file mode 100644
index 0000000..e53c9ff
Binary files /dev/null and b/app/src/main/res/drawable/dir7.png differ
diff --git a/app/src/main/res/drawable/dir8.png b/app/src/main/res/drawable/dir8.png
new file mode 100644
index 0000000..edfdbe1
Binary files /dev/null and b/app/src/main/res/drawable/dir8.png differ
diff --git a/app/src/main/res/drawable/dir9.png b/app/src/main/res/drawable/dir9.png
new file mode 100644
index 0000000..a762d0b
Binary files /dev/null and b/app/src/main/res/drawable/dir9.png differ
diff --git a/app/src/main/res/drawable/dir_end.png b/app/src/main/res/drawable/dir_end.png
new file mode 100644
index 0000000..f3cb78a
Binary files /dev/null and b/app/src/main/res/drawable/dir_end.png differ
diff --git a/app/src/main/res/drawable/dir_start.png b/app/src/main/res/drawable/dir_start.png
new file mode 100644
index 0000000..4d2a797
Binary files /dev/null and b/app/src/main/res/drawable/dir_start.png differ
diff --git a/app/src/main/res/drawable/dir_station.png b/app/src/main/res/drawable/dir_station.png
new file mode 100644
index 0000000..2ed6222
Binary files /dev/null and b/app/src/main/res/drawable/dir_station.png differ
diff --git a/app/src/main/res/drawable/down.png b/app/src/main/res/drawable/down.png
new file mode 100644
index 0000000..6768a17
Binary files /dev/null and b/app/src/main/res/drawable/down.png differ
diff --git a/app/src/main/res/drawable/end.png b/app/src/main/res/drawable/end.png
new file mode 100644
index 0000000..5503dc5
Binary files /dev/null and b/app/src/main/res/drawable/end.png differ
diff --git a/app/src/main/res/drawable/et_bg.xml b/app/src/main/res/drawable/et_bg.xml
new file mode 100644
index 0000000..b11a1fe
--- /dev/null
+++ b/app/src/main/res/drawable/et_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/gps_point.png b/app/src/main/res/drawable/gps_point.png
new file mode 100644
index 0000000..ec4332d
Binary files /dev/null and b/app/src/main/res/drawable/gps_point.png differ
diff --git a/app/src/main/res/drawable/ic_black_back.xml b/app/src/main/res/drawable/ic_black_back.xml
new file mode 100644
index 0000000..5168fa7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_black_back.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_blue_open.xml b/app/src/main/res/drawable/ic_blue_open.xml
new file mode 100644
index 0000000..bfd3e96
--- /dev/null
+++ b/app/src/main/res/drawable/ic_blue_open.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..a6af65f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_clear.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_favorite_red.xml b/app/src/main/res/drawable/ic_favorite_red.xml
new file mode 100644
index 0000000..6ec39d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite_red.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_route.xml b/app/src/main/res/drawable/ic_route.xml
new file mode 100644
index 0000000..ff3035e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_route.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/start.png b/app/src/main/res/drawable/start.png
new file mode 100644
index 0000000..cd716c8
Binary files /dev/null and b/app/src/main/res/drawable/start.png differ
diff --git a/app/src/main/res/drawable/up.png b/app/src/main/res/drawable/up.png
new file mode 100644
index 0000000..f17662e
Binary files /dev/null and b/app/src/main/res/drawable/up.png differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..6809c32
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_route.xml b/app/src/main/res/layout/activity_route.xml
new file mode 100644
index 0000000..65a8d22
--- /dev/null
+++ b/app/src/main/res/layout/activity_route.xml
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_route_detail.xml b/app/src/main/res/layout/activity_route_detail.xml
new file mode 100644
index 0000000..5e2a967
--- /dev/null
+++ b/app/src/main/res/layout/activity_route_detail.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/custom_info_contents.xml b/app/src/main/res/layout/custom_info_contents.xml
new file mode 100644
index 0000000..b0f6f79
--- /dev/null
+++ b/app/src/main/res/layout/custom_info_contents.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/custom_info_window.xml b/app/src/main/res/layout/custom_info_window.xml
new file mode 100644
index 0000000..6762b51
--- /dev/null
+++ b/app/src/main/res/layout/custom_info_window.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_segment.xml b/app/src/main/res/layout/item_segment.xml
new file mode 100644
index 0000000..0eb0ccb
--- /dev/null
+++ b/app/src/main/res/layout/item_segment.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_segment_ex.xml b/app/src/main/res/layout/item_segment_ex.xml
new file mode 100644
index 0000000..9030560
--- /dev/null
+++ b/app/src/main/res/layout/item_segment_ex.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png b/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png
new file mode 100644
index 0000000..b1cfec3
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_my.png b/app/src/main/res/mipmap-xhdpi/ic_my.png
new file mode 100644
index 0000000..f8c5b0e
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_my.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_yuan.png b/app/src/main/res/mipmap-xhdpi/ic_yuan.png
new file mode 100644
index 0000000..41636ef
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_yuan.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..7429e36
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c8524cd
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..36cf53f
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ ManGoWalking
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..9dd6e6d
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/example/mangowalking/ExampleUnitTest.java b/app/src/test/java/com/example/mangowalking/ExampleUnitTest.java
new file mode 100644
index 0000000..c9a86f1
--- /dev/null
+++ b/app/src/test/java/com/example/mangowalking/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.example.mangowalking;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..e61198d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+alias(libs.plugins.androidApplication) apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..a456279
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.suppressUnsupportedCompileSdk=34
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..b026330
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,22 @@
+[versions]
+agp = "8.3.1"
+junit = "4.13.2"
+junitVersion = "1.1.5"
+espressoCore = "3.5.1"
+appcompat = "1.6.1"
+material = "1.10.0"
+activity = "1.8.0"
+constraintlayout = "2.1.4"
+
+[libraries]
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+
+[plugins]
+androidApplication = { id = "com.android.application", version.ref = "agp" }
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..904cacd
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Apr 15 19:26:21 CST 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/map.jks b/map.jks
new file mode 100644
index 0000000..d21a2b0
Binary files /dev/null and b/map.jks differ
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..ecd0403
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,23 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "ManGoWalking"
+include ':app'