import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:injectable/injectable.dart'; import 'package:location/base/base_controller.dart'; import 'package:location/data/bean/location_info.dart'; import 'package:location/data/consts/constants.dart'; import 'package:location/data/consts/error_code.dart'; import 'package:location/data/repositories/account_repository.dart'; import 'package:location/data/repositories/friends_repository.dart'; import 'package:location/data/repositories/track_repository.dart'; import 'package:location/dialog/loading_dialog.dart'; import 'package:location/handler/error_handler.dart'; import 'package:location/module/member/member_page.dart'; import 'package:location/module/track/track_util.dart'; import 'package:location/resource/string.gen.dart'; import 'package:location/utils/atmob_log.dart'; import 'package:location/utils/common_expand.dart'; import 'package:location/utils/http_handler.dart'; import 'package:location/utils/toast_util.dart'; import 'package:sliding_sheet2/sliding_sheet2.dart'; import '../../data/bean/atmob_track_point.dart'; import '../../data/bean/user_info.dart'; import '../../dialog/common_confirm_dialog_impl.dart'; import '../../utils/date_util.dart'; import '../../utils/pair.dart'; @injectable class TrackController extends BaseController with GetSingleTickerProviderStateMixin { final int errorQueryOriginalDataEmpty = 10; //查询原始数据集为空 final int errorQueryOriginalTooFew = 11; //查询原始数据集少于2点 final Rxn _userInfo = Rxn(); UserInfo? get userInfo => _userInfo.value; final MapController mapController = MapController(); final Rxn _trackStartTime = Rxn(); final Rxn _trackEndTime = Rxn(); DateTime? get trackStartTime => _trackStartTime.value; DateTime? get trackEndTime => _trackEndTime.value; SheetController sheetController = SheetController(); final Rxn _startAddress = Rxn(); final Rxn _endAddress = Rxn(); String? get startAddress => _startAddress.value; String? get endAddress => _endAddress.value; final Duration maxDuration = Duration(days: 1); final String timeFormat = "yyyy年-MM月-dd日 HH时:mm分"; late TabController tabController; final RxInt _currentIndex = 0.obs; int get currentIndex => _currentIndex.value; final Rxn _currentLocation = Rxn(); LocationInfo? get currentLocation => _currentLocation.value; List? points; final TrackRepository trackRepository; final FriendsRepository friendsRepository; final AccountRepository accountRepository; TrackController( this.trackRepository, this.friendsRepository, this.accountRepository); @override void onInit() { final param = Get.arguments; if (param is UserInfo) { _userInfo.value = param; } tabController = TabController( length: 2, vsync: this, initialIndex: _currentIndex.value); tabController.addListener(_handleTabChange); _initTime(); _onCurrentLocationQuery(isShow: false); } @override void onReady() { super.onReady(); sheetController.expand(); } void _handleTabChange() { if (tabController.indexIsChanging) return; _currentIndex.value = tabController.index; if (tabController.index == 0) { _showTrack(); } else if (tabController.index == 1) { _showCurrentLocation(); } } void _initTime() { //开始时间往前推一天 _trackStartTime.value = DateUtil.getNow(subtract: Duration(days: 1)); _trackEndTime.value = DateUtil.getNow(); } void back() { Get.back(); } void onTrackStartTimeClick(BuildContext context) { if (userInfo?.virtual == true && accountRepository.memberIsExpired()) { MemberPage.start(); return; } DatePicker.showDatePicker(context, locale: DateTimePickerLocale.zh_cn, initialDateTime: _trackStartTime.value, dateFormat: timeFormat, onConfirm: (dateTime, selectedIndex) { if (trackEndTime != null && DateUtil.isTimeRangeExceed(dateTime, trackEndTime!, maxDuration)) { ToastUtil.show(StringName.trackChooseTimeError); _trackEndTime.value = dateTime.add(maxDuration); } _trackStartTime.value = dateTime; }); } void onTrackEndTimeClick(BuildContext context) { if (userInfo?.virtual == true && accountRepository.memberIsExpired()) { MemberPage.start(); return; } DatePicker.showDatePicker(context, locale: DateTimePickerLocale.zh_cn, initialDateTime: _trackEndTime.value, dateFormat: timeFormat, onConfirm: (dateTime, selectedIndex) { if (trackStartTime != null && DateUtil.isTimeRangeExceed(dateTime, trackStartTime!, maxDuration)) { ToastUtil.show(StringName.trackChooseTimeError); _trackStartTime.value = dateTime.subtract(maxDuration); } _trackEndTime.value = dateTime; }); } void onTrackQueryClick() { if (userInfo?.virtual == false && accountRepository.memberIsExpired()) { MemberPage.start(); return; } if (currentIndex == 0) { _onTrackQuery(); } else { _onCurrentLocationQuery(); } } void _onCurrentLocationQuery({bool isShow = true}) { if (userInfo == null) { return; } if (userInfo?.isMine == true) { _currentLocation.value = accountRepository.mineUserInfo.value.lastLocation.value; if (isShow) { _showCurrentLocation(); } } else { if (isShow) LoadingDialog.show(StringName.trackLoadingTxt); friendsRepository .getUserInfoFromId(userInfo!.id, isVirtual: userInfo!.virtual) .then((location) { LoadingDialog.hide(); _currentLocation.value = userInfo?.lastLocation.value; if (isShow) { _showCurrentLocation(); } }).catchError((error) { debugPrint("error: $error"); ErrorHandler.toastError(error); }); } } void _onTrackQuery() { if (trackStartTime == null || trackEndTime == null || userInfo == null) { return; } LoadingDialog.show(StringName.trackLoadingTxt); _startAddress.value = ''; _endAddress.value = ''; Future.value().then((_) { if (userInfo?.virtual == true) { return trackRepository.queryVirtualTrack(); } else { return trackRepository.queryTrack( startTime: trackStartTime?.millisecondsSinceEpoch, endTime: trackEndTime?.millisecondsSinceEpoch, userId: userInfo?.id); } }).map((trackResponse) { final pointsList = trackResponse.trackPoints; if (pointsList == null || pointsList.isEmpty) { throw TrackQueryException(errorQueryOriginalDataEmpty); } if (pointsList.length < 2) { throw TrackQueryException(errorQueryOriginalTooFew); } if (userInfo?.virtual == true) { int nowMill = DateUtil.getNow().millisecondsSinceEpoch; int firstMill = pointsList.first.time; int differ = nowMill - firstMill; pointsList.first.time = nowMill; for (var element in pointsList) { element.time = element.time + differ; } } return pointsList; }).then((pointsList) async { final list = TrackUtil.points2TraceLocation(pointsList); List? convertList; try { convertList = await FlutterMap.queryProcessedTrace( lineID: pointsList.hashCode, locations: list); } catch (e) { AtmobLog.e("TrackController", "queryProcessedTrace error: $e"); } if (convertList == null || convertList.isEmpty) { //轨迹纠偏失败,使用原始数据 convertList = TrackUtil.traceLocation2LatLng(list); } return Pair(pointsList, convertList); }).then((pair) { LoadingDialog.hide(); points = pair.second; _showTrack(); _setStartAndEndAddress(start: pair.first.first, end: pair.first.last); }).catchError((error) { LoadingDialog.hide(); if (error is TrackQueryException) { showQueryErrorDialog(); } else if (error is ServerErrorException) { if (error.code == ErrorCode.noMember) { MemberPage.start(); ToastUtil.show(StringName.memberExpired); } else { ToastUtil.show(error.message); } } else { ErrorHandler.toastError(error); } }); } @override void onClose() { super.onClose(); tabController.dispose(); } void _setStartAndEndAddress( {required AtmobTrackPoint start, required AtmobTrackPoint end}) { _startAddress.value = start.addr; _endAddress.value = end.addr; } void _showCurrentLocation() { mapController.clear(); if (currentLocation == null || userInfo == null) { return; } mapController.updateOrAddMarker(Marker( id: userInfo!.id, markerName: userInfo!.getUserNickName(), longitude: userInfo!.lastLocation.value?.longitude, latitude: userInfo!.lastLocation.value?.latitude, markerType: userInfo!.isMine == true ? MarkerType.traceEndMinePoint : MarkerType.traceEndFriendPoint, )); mapController.animateCamera(CameraPosition( latitude: currentLocation!.latitude, longitude: currentLocation!.longitude, zoom: 18)); } void _showTrack() { mapController.clear(); if (points == null || points!.length < 2) { return; } mapController.addPolyline(points!, mapPadding: MapPadding(left: 50, top: 100, right: 50, bottom: Get.height / 2)); mapController.updateOrAddMarker(Marker( id: Constants.traceStartId, markerName: '', longitude: points!.first.longitude, latitude: points!.first.latitude, markerType: MarkerType.traceStartPoint)); mapController.updateOrAddMarker(Marker( id: Constants.traceEndId, markerName: userInfo?.getUserNickName() ?? '', longitude: points!.last.longitude, latitude: points!.last.latitude, markerType: userInfo?.isMine == true ? MarkerType.traceEndMinePoint : MarkerType.traceEndFriendPoint)); //显示起点标记 // drawMarker(); //显示终点标记 } void showQueryErrorDialog() { showTraceNoDataDialog(onConfirm: () {}); } } class TrackQueryException implements Exception { final int code; TrackQueryException(this.code); }