This commit is contained in:
2025-04-29 16:37:13 +08:00
34 changed files with 1378 additions and 23 deletions

View File

@@ -1,5 +1,6 @@
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
@@ -18,6 +19,9 @@ android {
//设置支持的SO库架构开发者可以根据需要选择一个或多个平台的so
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
}
vectorDrawables {
useSupportLibrary true
}
}
sourceSets {
@@ -41,6 +45,18 @@ android {
buildFeatures {
viewBinding true
compose true
}
kotlinOptions {
jvmTarget = '1.8'
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.1'
}
packaging {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
@@ -66,11 +82,24 @@ dependencies {
implementation libs.material
implementation libs.activity
implementation libs.constraintlayout
implementation libs.filament.android
implementation libs.lifecycle.runtime.ktx
implementation libs.activity.compose
implementation platform(libs.compose.bom)
implementation libs.ui
implementation libs.ui.graphics
implementation libs.ui.tooling.preview
implementation libs.material3
// 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'
// implementation 'com.amap.api:location:6.4.9
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
androidTestImplementation platform(libs.compose.bom)
androidTestImplementation libs.ui.test.junit4
debugImplementation libs.ui.tooling
debugImplementation libs.ui.test.manifest
}

BIN
app/libs/Msc.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,10 +1,9 @@
iid=11600
sid=3528001
bid=1350002
version=13.08.0.10009081
time=2025-03-17 18:00:51
sid=3535967
bid=1359895
version=13.08.0.10009084
time=2025-03-26 09:54:38
FEATURE_LOCATION=1
FEATURE_ROUTE_OVERLAY=1
FEATURE_MVT=1
FEATURE_3DTiles=1
FEATURE_GLTF=1
FEATURE_MVT=0
FEATURE_3DTiles=0
FEATURE_GLTF=0

View File

@@ -12,16 +12,15 @@
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- 如果您的应用需要后台定位权限且有可能运行在Android Q设备上,并且设置了target>28必须增加这个权限声明 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 允许写设备缓存,用于问题排查 -->
<!-- 读取电话状态权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" /> <!-- 允许读设备等信息,用于问题排查 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 添加 BLE权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 添加 BLE权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- 操纵蓝牙的开启-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- 操纵蓝牙的开启 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
@@ -40,6 +39,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.ManGoWalking"
tools:targetApi="31">
<activity
android:name=".GuideMap"
android:exported="false" />
<activity
android:name=".RouteDetailActivity"
android:exported="false" />
@@ -49,7 +51,8 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:hardwareAccelerated="true">
android:hardwareAccelerated="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -0,0 +1,438 @@
package com.example.mangowalking;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMapException;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.AMapNaviView;
import com.amap.api.navi.AMapNaviViewListener;
import com.amap.api.navi.ParallelRoadListener;
import com.amap.api.navi.enums.AMapNaviParallelRoadStatus;
import com.amap.api.navi.enums.NaviType;
import com.amap.api.navi.enums.TransportType;
import com.amap.api.navi.model.*;
import com.example.mangowalking.utils.TTSController;
import com.google.android.filament.View;
import java.util.ArrayList;
import java.util.List;
public class GuideMap extends AppCompatActivity implements AMapNaviListener, AMapNaviViewListener, ParallelRoadListener {
private static final String TAG = "GuideMap";
private AMapNaviView mNaviView; // 对应DEMO mAMapNaviView;
private AMapNavi mNavi; // mAMapNavi;
protected TTSController mTtsManager;
private NaviLatLng mStartPoint; // 起点
private NaviLatLng mEndPoint; // 终点
private List<NaviLatLng> mWayPoints = new ArrayList<>(); // 途经点
protected final List<NaviLatLng> sList = new ArrayList<NaviLatLng>();
protected final List<NaviLatLng> eList = new ArrayList<NaviLatLng>();
protected List<NaviLatLng> mWayPointList = new ArrayList<NaviLatLng>();
//与定位相关
private AMapLocationClient mapLocationClient;
private AMapLocationClientOption mLocationOption;
private boolean isAutoStartLocation = false;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// mNavi.setIsNaviTravelView(true);
setContentView(R.layout.activity_guide_map);
// 1. 初始化导航组件
mNaviView = findViewById(R.id.naviView);
mNaviView.onCreate(savedInstanceState);
try {
mNavi = AMapNavi.getInstance(getApplicationContext());
} catch (AMapException e) {
throw new RuntimeException(e);
}
// 2. 设置导航监听器
mNavi.addAMapNaviListener(this);
mNaviView.setAMapNaviViewListener(new AMapNaviViewListener() {
@Override
public void onNaviSetting() {
}
@Override
public void onNaviCancel() {
}
@Override
public boolean onNaviBackClick() {
return false;
}
@Override
public void onNaviMapMode(int i) {
}
@Override
public void onNaviTurnClick() {
}
@Override
public void onNextRoadClick() {
}
@Override
public void onScanViewButtonClick() {
}
@Override
public void onLockMap(boolean b) {
}
@Override
public void onNaviViewLoaded() {
Log.d("wlx","导航页面加载成功");
Log.d("wlx","请不要使用AMapNaviView.getMap().setOnMapLoadedListener();会overwrite导航SDK内部画线逻辑");
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
// 处理导航视图事件...
});
mNaviView.setNaviMode(mNaviView.NORTH_UP_MODE);
initLocation();
// 3. 添加途经点(可选)
// mWayPoints.add(new NaviLatLng(39.993706, 116.400865));
//初始化定位
// initLocation();
}
@Override
public void onNaviSetting() {
}
@Override
public void onNaviCancel() {
}
@Override
public boolean onNaviBackClick() {
return false;
}
@Override
public void onNaviMapMode(int i) {
}
@Override
public void onNaviTurnClick() {
}
@Override
public void onNextRoadClick() {
}
@Override
public void onScanViewButtonClick() {
}
@Override
public void onLockMap(boolean b) {
}
@Override
public void onNaviViewLoaded() {
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
@Override
public void notifyParallelRoad(AMapNaviParallelRoadStatus aMapNaviParallelRoadStatus) {
if (aMapNaviParallelRoadStatus.getmElevatedRoadStatusFlag() == 1) {
Toast.makeText(this, "当前在高架上", Toast.LENGTH_SHORT).show();
Log.d("wlx", "当前在高架上");
} else if (aMapNaviParallelRoadStatus.getmElevatedRoadStatusFlag() == 2) {
Toast.makeText(this, "当前在高架下", Toast.LENGTH_SHORT).show();
Log.d("wlx", "当前在高架下");
}
if (aMapNaviParallelRoadStatus.getmParallelRoadStatusFlag() == 1) {
Toast.makeText(this, "当前在主路", Toast.LENGTH_SHORT).show();
Log.d("wlx", "当前在主路");
} else if (aMapNaviParallelRoadStatus.getmParallelRoadStatusFlag() == 2) {
Toast.makeText(this, "当前在辅路", Toast.LENGTH_SHORT).show();
Log.d("wlx", "当前在辅路");
}
}
public enum NaviMode {WALK,BUS};
private NaviMode currentNaviMode = NaviMode.WALK;
// 初始化定位配置
private void initLocation() {
try {
mapLocationClient = new AMapLocationClient(getApplicationContext());
mLocationOption = new AMapLocationClientOption();
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
mLocationOption.setInterval(2000);
mapLocationClient.setLocationListener((AMapLocationListener) this);
mapLocationClient.setLocationOption(mLocationOption);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected void onResume() {
super.onResume();
mNaviView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mNaviView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mNaviView.onDestroy();
//since 1.6.0 不再在naviview destroy的时候自动执行AMapNavi.stopNavi();请自行执行
if (mNavi!=null){
mNavi.stopNavi();
mNavi.destroy();
}
}
@Override
public void onInitNaviFailure() {
Toast.makeText(this, "init navi Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onInitNaviSuccess() {
//这里无需super 因为我是直接在父类上直接写
mNavi.setTravelInfo(new AMapTravelInfo(TransportType.Walk));
mNavi.calculateWalkRoute(new NaviLatLng(39.925846, 116.435765), new NaviLatLng(39.925846, 116.532765));
}
@Override
public void onStartNavi(int i) {
}
@Override
public void onTrafficStatusUpdate() {
}
@Override
public void onLocationChange(AMapNaviLocation aMapNaviLocation) {
}
@Override
public void onGetNavigationText(int i, String s) {
}
@Override
public void onGetNavigationText(String s) {
}
@Override
public void onEndEmulatorNavi() {
}
@Override
public void onArriveDestination() {
}
@Override
public void onCalculateRouteFailure(int i) {
}
@Override
public void onReCalculateRouteForYaw() {
}
@Override
public void onReCalculateRouteForTrafficJam() {
}
@Override
public void onArrivedWayPoint(int i) {
}
@Override
public void onGpsOpenStatus(boolean b) {
}
@Override
public void onNaviInfoUpdate(NaviInfo naviInfo) {
}
@Override
public void updateCameraInfo(AMapNaviCameraInfo[] aMapNaviCameraInfos) {
}
@Override
public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {
}
@Override
public void onServiceAreaUpdate(AMapServiceAreaInfo[] aMapServiceAreaInfos) {
}
@Override
public void showCross(AMapNaviCross aMapNaviCross) {
}
@Override
public void hideCross() {
}
@Override
public void showModeCross(AMapModelCross aMapModelCross) {
}
@Override
public void hideModeCross() {
}
@Override
public void showLaneInfo(AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1) {
}
@Override
public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {
}
@Override
public void hideLaneInfo() {
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
}
@Override
public void notifyParallelRoad(int i) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {
}
@Override
public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {
}
@Override
public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {
}
@Override
public void onPlayRing(int i) {
}
@Override
public void onCalculateRouteSuccess(AMapCalcRouteResult aMapCalcRouteResult) {
mNavi.startNavi(NaviType.GPS);
}
@Override
public void onCalculateRouteFailure(AMapCalcRouteResult aMapCalcRouteResult) {
//路线计算失败
Log.e("dm", "--------------------------------------------");
Log.i("dm", "路线计算失败:错误码=" + aMapCalcRouteResult.getErrorCode() + ",Error Message= " + aMapCalcRouteResult.getErrorDescription());
Log.i("dm", "错误码详细链接见http://lbs.amap.com/api/android-navi-sdk/guide/tools/errorcode/");
Log.e("dm", "--------------------------------------------");
Toast.makeText(this, "errorInfo" + aMapCalcRouteResult.getErrorDetail() + ", Message" + aMapCalcRouteResult.getErrorDescription(), Toast.LENGTH_LONG).show();
}
@Override
public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {
}
@Override
public void onGpsSignalWeak(boolean b) {
}
}

View File

@@ -1,5 +1,6 @@
package com.example.mangowalking;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.example.mangowalking.utils.MapUtil.convertToLatLng;
import static com.example.mangowalking.utils.MapUtil.convertToLatLonPoint;
@@ -31,6 +32,7 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -41,6 +43,7 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
@@ -56,6 +59,7 @@ 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.navi.AMapNavi;
import com.amap.api.services.busline.BusLineItem;
import com.amap.api.services.core.AMapException;
import com.amap.api.services.core.LatLonPoint;
@@ -151,6 +155,7 @@ public class RouteActivity extends AppCompatActivity implements
private BluetoothGattCharacteristic writeCharacteristic;
private ArrayList<LatLng> poiListForGuideMap = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -163,6 +168,7 @@ public class RouteActivity extends AppCompatActivity implements
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();
@@ -182,6 +188,8 @@ public class RouteActivity extends AppCompatActivity implements
@SuppressLint("MissingPermission")
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //创建一个蓝牙启动的意图
launcher.launch(enableBtIntent);//使用launcer启动这个意图就可以了。
//此处可行
//初始化定位
initLocation();
@@ -193,6 +201,7 @@ public class RouteActivity extends AppCompatActivity implements
//初始化出行方式
initTravelMode();
binding.buttonmy.setOnClickListener(v->startActivity(new Intent(this,GuideMap.class)));
// 新加入的
scanner = bluetoothAdapter.getBluetoothLeScanner();
//不进行权限验证
@@ -213,13 +222,31 @@ public class RouteActivity extends AppCompatActivity implements
ScanFilter sn = new ScanFilter.Builder().setDeviceName("蓝牙设备的名称").setServiceUuid(ParcelUuid.fromString("0000FFE0-0000-1000-8000-00805F9B34FB")).build();
List<ScanFilter> scanFilters = new ArrayList<>();
scanFilters.add(sn);
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
return;
}
scanner.startScan(scanFilters, new ScanSettings.Builder().build(), callback);
bluetoothGatt = device.connectGatt(this, false, gattCallback);
// Button btn_1 = findViewById(R.id.btn_ble);
//
// // 重写一个内部类
// btn_1.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// Log.d("RouteActivity", "点击了跳转按钮");
//// ArrayList<LatLng> poiList = new ArrayList<>();
// //这只是一个意图
// Intent intent = new Intent(RouteActivity.this,GuideMap.class);
//// intent.putParcelableArrayListExtra("poi_list",poiList);
// startActivity(intent);
// }
// });
// RouteActivity.java 中修改点击事件
}
BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@@ -228,7 +255,7 @@ public class RouteActivity extends AppCompatActivity implements
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) {
if (ActivityCompat.checkSelfPermission(RouteActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
return;
}
gatt.discoverServices();
@@ -300,7 +327,7 @@ public class RouteActivity extends AppCompatActivity implements
// 假设写特征的 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) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
return;
}
gatt.setCharacteristicNotification(writeCharacteristic, true);
@@ -327,7 +354,7 @@ public class RouteActivity extends AppCompatActivity implements
writeCharacteristic.setValue(data);
// 3) 发写命令,异步执行
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
return;
}
boolean success = bluetoothGatt.writeCharacteristic(writeCharacteristic);
@@ -400,7 +427,10 @@ public class RouteActivity extends AppCompatActivity implements
mLocationClient.setLocationListener(this);
mLocationOption = new AMapLocationClientOption();
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
mLocationOption.setOnceLocationLatest(true);
// 设置定位间隔 2000ms更新一次
mLocationOption.setInterval(2000);
//
mLocationOption.setOnceLocationLatest(false);
mLocationOption.setNeedAddress(true);
mLocationOption.setHttpTimeOut(20000);
mLocationOption.setLocationCacheEnable(false);
@@ -408,6 +438,8 @@ public class RouteActivity extends AppCompatActivity implements
}
}
/**
* 初始化地图
*
@@ -571,7 +603,7 @@ public class RouteActivity extends AppCompatActivity implements
binding.mapView.onDestroy();
if (bluetoothGatt != null) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
return;
}
bluetoothGatt.close();
@@ -673,6 +705,8 @@ public class RouteActivity extends AppCompatActivity implements
}
}
}
poiListForGuideMap.clear();
poiListForGuideMap.addAll(allPoiPoints);
// // 🧪 测试打印
// for (LatLng p : poiList) {
@@ -753,6 +787,8 @@ public class RouteActivity extends AppCompatActivity implements
Log.d("POI_POINT", p.latitude + "," + p.longitude);
}
sendPoiList(poiList);
poiListForGuideMap.clear();
poiListForGuideMap.addAll(poiList);
}

View File

@@ -0,0 +1,11 @@
package com.example.mangowalking.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -0,0 +1,70 @@
package com.example.mangowalking.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun ManGoWalkingTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -0,0 +1,34 @@
package com.example.mangowalking.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@@ -0,0 +1,5 @@
package com.example.mangowalking.utils;
public interface ICallBack {
void onCompleted(int code);
}

View File

@@ -0,0 +1,170 @@
package com.example.mangowalking.utils;
import static com.iflytek.cloud.SpeechSynthesizer.createSynthesizer;
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.SynthesizerListener;
public class IFlyTTS implements TTS, SynthesizerListener, AudioManager.OnAudioFocusChangeListener {
private static IFlyTTS iflyTTS = null;
Context mContext = null;
private boolean isPlaying = false;
private AudioManager mAm = null;
ICallBack callBack = null;
/**
* 请务必替换为您自己申请的ID。
*/
private final String appId = "1342cc62"; //这个是我自己的ID
public static IFlyTTS getInstance(Context context) {
if (iflyTTS == null) {
iflyTTS = new IFlyTTS(context);
}
return iflyTTS;
}
private IFlyTTS(Context context) {
mContext = context;
SpeechUtility.createUtility(mContext, SpeechConstant.APPID + "="
+ appId);
createSynthesizer();
mAm = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
private void createSynthesizer() {
mTts = SpeechSynthesizer.createSynthesizer(mContext,
new InitListener() {
@Override
public void onInit(int errorcode) {
if (ErrorCode.SUCCESS == errorcode) {
//初始化成功
}
}
});
}
private SpeechSynthesizer mTts;
@Override
public void onAudioFocusChange(int focusChange) {
}
@Override
public void init() {
if (mTts!=null){
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
//设置语速,值范围:[0, 100],默认值50
mTts.setParameter(SpeechConstant.SPEED, "55");
//设置音量
mTts.setParameter(SpeechConstant.VOLUME, "tts_volume");
//设置语调
mTts.setParameter(SpeechConstant.PITCH, "tts_pitch");
//设置与其他音频软件冲突的时候是否暂停其他音频
mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false");
//女生仅vixy支持多音字播报
mTts.setParameter(SpeechConstant.VOICE_NAME, "vixy");
}
}
@Override
public void playText(String playText) {
//多音字处理举例
if (playText != null && playText.contains("京藏")) {
playText = playText.replace("京藏", "京藏[=zang4]");
}
if (playText != null && playText.length() > 0) {
int result = mAm.requestAudioFocus(this,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
int code = mTts.startSpeaking(playText, this);
isPlaying = true;
}
}
}
@Override
public void stopSpeak() {
if(mTts != null){
mTts.stopSpeaking();
}
isPlaying = false;
}
@Override
public void destroy() {
stopSpeak();
if(mTts != null){
mTts.destroy();
}
iflyTTS = null;
}
@Override
public boolean isPlaying() {
return isPlaying;
}
@Override
public void onSpeakBegin() {
isPlaying = true;
}
@Override
public void onBufferProgress(int i, int i1, int i2, String s) {
}
@Override
public void onSpeakPaused() {
isPlaying = false;
}
@Override
public void onSpeakResumed() {
}
@Override
public void onSpeakProgress(int i, int i1, int i2) {
}
@Override
public void onCompleted(SpeechError arg0) {
isPlaying = false;
if (mAm != null) {
mAm.abandonAudioFocus(this);
}
if (callBack != null) {
if (arg0 == null) {
callBack.onCompleted(0);
}
}
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
@Override
public void setCallback(ICallBack callback) {
callBack = callback;
}
}

View File

@@ -0,0 +1,119 @@
package com.example.mangowalking.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import java.util.Locale;
@SuppressLint("NewApi")
public class SystemTTS extends UtteranceProgressListener implements TTS, TextToSpeech.OnUtteranceCompletedListener {
private Context mContext;
private static SystemTTS singleton;
private TextToSpeech textToSpeech; // 系统语音播报类
private boolean isSuccess = true;
ICallBack callBack = null;
public static SystemTTS getInstance(Context context) {
if (singleton == null) {
synchronized (SystemTTS.class) {
if (singleton == null) {
singleton = new SystemTTS(context);
}
}
}
return singleton;
}
private SystemTTS(Context context) {
this.mContext = context.getApplicationContext();
textToSpeech = new TextToSpeech(mContext, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int i) {
//系统语音初始化成功
if (i == TextToSpeech.SUCCESS) {
int result = textToSpeech.setLanguage(Locale.CHINA);
textToSpeech.setPitch(1.0f);// 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
textToSpeech.setSpeechRate(1.0f);
textToSpeech.setOnUtteranceProgressListener(SystemTTS.this);
textToSpeech.setOnUtteranceCompletedListener(SystemTTS.this);
if (result == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
//系统不支持中文播报
isSuccess = false;
}
}
}
});
}
@Override
public void onUtteranceCompleted(String utteranceId) {
}
@Override
public void onStart(String utteranceId) {
}
@Override
public void onDone(String utteranceId) {
}
@Override
public void onError(String utteranceId) {
}
@Override
public void init() {
}
@Override
public void playText(String playText) {
if(!isSuccess){
return;
}
if(textToSpeech != null)
{
textToSpeech.speak(playText,
TextToSpeech.QUEUE_ADD,null,null);
}
}
@Override
public void stopSpeak() {
if(textToSpeech != null)
{
textToSpeech.stop();
}
}
@Override
public void destroy() {
stopSpeak();
if(textToSpeech != null)
{
textToSpeech.shutdown();
}
singleton = null;
}
@Override
public boolean isPlaying() {
return textToSpeech.isSpeaking();
}
@Override
public void setCallback(ICallBack callback) {
callBack = callback;
}
}

View File

@@ -0,0 +1,10 @@
package com.example.mangowalking.utils;
public interface TTS {
public void init();
public void playText(String playText);
public void stopSpeak();
public void destroy();
public boolean isPlaying();
public void setCallback(ICallBack callback);
}

View File

@@ -0,0 +1,321 @@
package com.example.mangowalking.utils;
import android.content.Context;
import android.os.Message;
import android.os.Handler;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.model.AMapCalcRouteResult;
import com.amap.api.navi.model.AMapLaneInfo;
import com.amap.api.navi.model.AMapModelCross;
import com.amap.api.navi.model.AMapNaviCameraInfo;
import com.amap.api.navi.model.AMapNaviCross;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.AMapNaviRouteNotifyData;
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
import com.amap.api.navi.model.AMapServiceAreaInfo;
import com.amap.api.navi.model.AimLessModeCongestionInfo;
import com.amap.api.navi.model.AimLessModeStat;
import com.amap.api.navi.model.NaviInfo;
import java.util.LinkedList;
import java.util.logging.LogRecord;
public class TTSController implements AMapNaviListener,ICallBack{
@Override
public void onCompleted(int code) {
if (handler != null) {
handler.obtainMessage(1).sendToTarget();
}
}
public static enum TTSType {
/**
* 讯飞语音
*/
IFLYTTS,
/**
* 系统语音
*/
SYSTEMTTS;
}
public static TTSController ttsManager;
private Context mContext;
private TTS tts = null;
private SystemTTS systemTTS;
private IFlyTTS iflyTTS = null;
private LinkedList<String> wordList = new LinkedList<String>();
private final int TTS_PLAY = 1;
private final int CHECK_TTS_PLAY = 2;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case TTS_PLAY:
if (tts != null && wordList.size() > 0) {
tts.playText(wordList.removeFirst());
}
break;
case CHECK_TTS_PLAY:
if (!tts.isPlaying()) {
handler.obtainMessage(1).sendToTarget();
}
break;
default:
}
}
};
public void setTTSType(TTSType type) {
if (type == TTSType.SYSTEMTTS) {
tts = systemTTS;
} else {
tts = iflyTTS;
}
tts.setCallback(this);
}
private TTSController(Context context) {
mContext = context.getApplicationContext();
systemTTS = SystemTTS.getInstance(mContext);
iflyTTS = IFlyTTS.getInstance(mContext);
tts = iflyTTS;
}
public void init() {
if (systemTTS != null) {
systemTTS.init();
}
if (iflyTTS != null) {
iflyTTS.init();
}
tts.setCallback(this);
}
public static TTSController getInstance(Context context) {
if (ttsManager == null) {
ttsManager = new TTSController(context);
}
return ttsManager;
}
public void stopSpeaking() {
if (systemTTS != null) {
systemTTS.stopSpeak();
}
if (iflyTTS != null) {
iflyTTS.stopSpeak();
}
wordList.clear();
}
public void destroy() {
if (systemTTS != null) {
systemTTS.destroy();
}
if (iflyTTS != null) {
iflyTTS.destroy();
}
ttsManager = null;
}
/****************************************************************************
* 以下都是导航相关接口
****************************************************************************/
@Override
public void onInitNaviFailure() {
}
@Override
public void onInitNaviSuccess() {
}
@Override
public void onStartNavi(int i) {
}
@Override
public void onTrafficStatusUpdate() {
}
@Override
public void onLocationChange(AMapNaviLocation aMapNaviLocation) {
}
@Override
public void onGetNavigationText(int i, String s) {
}
@Override
public void onGetNavigationText(String s) {
}
@Override
public void onEndEmulatorNavi() {
}
@Override
public void onArriveDestination() {
}
@Override
public void onCalculateRouteFailure(int i) {
}
@Override
public void onReCalculateRouteForYaw() {
if (wordList != null) {
wordList.addLast("路线重新规划");
}
}
@Override
public void onReCalculateRouteForTrafficJam() {
if (wordList != null) {
wordList.addLast("前方路线拥堵,路线重新规划");
}
}
@Override
public void onArrivedWayPoint(int i) {
}
@Override
public void onGpsOpenStatus(boolean b) {
}
@Override
public void onNaviInfoUpdate(NaviInfo naviInfo) {
}
@Override
public void updateCameraInfo(AMapNaviCameraInfo[] aMapNaviCameraInfos) {
}
@Override
public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {
}
@Override
public void onServiceAreaUpdate(AMapServiceAreaInfo[] aMapServiceAreaInfos) {
}
@Override
public void showCross(AMapNaviCross aMapNaviCross) {
}
@Override
public void hideCross() {
}
@Override
public void showModeCross(AMapModelCross aMapModelCross) {
}
@Override
public void hideModeCross() {
}
@Override
public void showLaneInfo(AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1) {
}
@Override
public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {
}
@Override
public void hideLaneInfo() {
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
}
@Override
public void notifyParallelRoad(int i) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {
}
@Override
public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {
}
@Override
public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {
}
@Override
public void onPlayRing(int i) {
}
@Override
public void onCalculateRouteSuccess(AMapCalcRouteResult aMapCalcRouteResult) {
if(wordList != null)
{
wordList.addLast("主人你好棒呀");
}
}
@Override
public void onCalculateRouteFailure(AMapCalcRouteResult aMapCalcRouteResult) {
if (wordList != null) {
wordList.addLast("路线规划失败");
}
}
@Override
public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {
}
@Override
public void onGpsSignalWeak(boolean b) {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 高德导航视图(必须全屏显示) -->
<!-- 顶部状态栏 -->
<com.amap.api.navi.AMapNaviView
android:id="@+id/naviView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#80000000"
android:padding="8dp">
<TextView
android:id="@+id/tvRouteInfo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:gravity="center"/>
</LinearLayout>
<!-- 底部控制栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:background="#80000000"
android:padding="16dp">
<Button
android:id="@+id/btnStartNavi"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="开始导航"
android:textAllCaps="false"/>
<Button
android:id="@+id/btnSwitchMode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="切换模式"
android:textAllCaps="false"/>
</LinearLayout>
</FrameLayout>

View File

@@ -27,6 +27,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_start"
android:layout_width="wrap_content"
@@ -106,7 +107,23 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view2" />
app:layout_constraintTop_toBottomOf="@+id/view2" >
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/buttonmy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:clickable="true"
android:focusable="true"
app:hoveredFocusedTranslationZ="18dp"
app:pressedTranslationZ="18dp"
tools:ignore="MissingConstraints" />
</com.amap.api.maps.MapView>
<!--浮动按钮-->
<!--地图路线规划详情布局-->
<androidx.constraintlayout.widget.ConstraintLayout
@@ -148,7 +165,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_time" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,3 +1,4 @@
<resources>
<string name="app_name">ManGoWalking</string>
<string name="title_activity_base">BaseActivity</string>
</resources>