|
|
@@ -1,79 +1,51 @@
|
|
|
-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:flutter_screenutil/flutter_screenutil.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/bean/track_days.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_detail/track_detail_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';
|
|
|
+import '../../dialog/location_permission_dialog.dart';
|
|
|
+import '../../sdk/map/map_helper.dart';
|
|
|
+import '../../utils/permission_util.dart';
|
|
|
|
|
|
@injectable
|
|
|
-class TrackController extends BaseController {
|
|
|
- final int errorQueryOriginalDataEmpty = 10; //查询原始数据集为空
|
|
|
- final int errorQueryOriginalTooFew = 11; //查询原始数据集少于2点
|
|
|
-
|
|
|
+class TrackController extends BaseController
|
|
|
+ with GetSingleTickerProviderStateMixin {
|
|
|
final Rxn<UserInfo> _userInfo = Rxn<UserInfo>();
|
|
|
|
|
|
UserInfo? get userInfo => _userInfo.value;
|
|
|
|
|
|
final MapController mapController = MapController();
|
|
|
|
|
|
- final Rxn<DateTime> _trackStartTime = Rxn<DateTime>();
|
|
|
- final Rxn<DateTime> _trackEndTime = Rxn<DateTime>();
|
|
|
-
|
|
|
- DateTime? get trackStartTime => _trackStartTime.value;
|
|
|
-
|
|
|
- DateTime? get trackEndTime => _trackEndTime.value;
|
|
|
SheetController sheetController = SheetController();
|
|
|
|
|
|
- final Rxn<String> _startAddress = Rxn<String>();
|
|
|
- final Rxn<String> _endAddress = Rxn<String>();
|
|
|
-
|
|
|
- String? get startAddress => _startAddress.value;
|
|
|
-
|
|
|
- String? get endAddress => _endAddress.value;
|
|
|
-
|
|
|
- final Duration maxDuration = Duration(days: 1);
|
|
|
- final String timeFormat = "yyyy年-MM月-dd日 HH时:mm分";
|
|
|
- final RxInt _currentIndex = 0.obs;
|
|
|
+ final RxList<TrackDays> daysList = RxList<TrackDays>();
|
|
|
|
|
|
- int get currentIndex => _currentIndex.value;
|
|
|
+ final Rxn<TabController> _tabController = Rxn<TabController>();
|
|
|
|
|
|
- final Rxn<LocationInfo> _currentLocation = Rxn<LocationInfo>();
|
|
|
+ TabController? get tabController => _tabController.value;
|
|
|
|
|
|
- LocationInfo? get currentLocation => _currentLocation.value;
|
|
|
+ final Rxn<TrackDays> currentTrackDay = Rxn();
|
|
|
|
|
|
- List<LatLng>? points;
|
|
|
- List<AtmobTrackPoint>? originPoints;
|
|
|
+ final RxDouble _sheetProgress = 0.0.obs;
|
|
|
|
|
|
- final RxBool _isShowTraceDetailBtn = false.obs;
|
|
|
+ double get sheetProgress => _sheetProgress.value;
|
|
|
|
|
|
- bool get isShowTraceDetailBtn => _isShowTraceDetailBtn.value;
|
|
|
+ final mapPadding =
|
|
|
+ MapPadding(left: 50.w, top: 100.w, right: 50.w, bottom: Get.height / 2);
|
|
|
|
|
|
- final RxList<TrackDays> daysList = RxList<TrackDays>();
|
|
|
+ final selectPadding =
|
|
|
+ MapPadding(left: 80.w, top: 150.w, right: 80.w, bottom: Get.height / 2);
|
|
|
|
|
|
final TrackRepository trackRepository;
|
|
|
final FriendsRepository friendsRepository;
|
|
|
@@ -88,8 +60,6 @@ class TrackController extends BaseController {
|
|
|
if (param is UserInfo) {
|
|
|
_userInfo.value = param;
|
|
|
}
|
|
|
- _initTime();
|
|
|
- _onCurrentLocationQuery(isShow: false);
|
|
|
_onRequestTrackDateList();
|
|
|
}
|
|
|
|
|
|
@@ -103,11 +73,31 @@ class TrackController extends BaseController {
|
|
|
void _onRequestTrackDateList() {
|
|
|
trackRepository.getLocationTrackDays().then((list) {
|
|
|
daysList.assignAll(list);
|
|
|
+ _createTabController();
|
|
|
}).catchError((error) {
|
|
|
ErrorHandler.toastError(error);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ void _createTabController() {
|
|
|
+ tabController?.dispose();
|
|
|
+ final tab = TabController(
|
|
|
+ length: daysList.length,
|
|
|
+ vsync: this,
|
|
|
+ );
|
|
|
+ _tabController.value = tab;
|
|
|
+ tab.addListener(() {
|
|
|
+ if (tab.indexIsChanging == false) {
|
|
|
+ //控制请求当前页数据或者数据已有时跟换数据
|
|
|
+ mapController.clear();
|
|
|
+ currentTrackDay.value = daysList[tab.index];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ Future.delayed(Duration(milliseconds: 100), () {
|
|
|
+ currentTrackDay.value = daysList.isNotEmpty ? daysList[0] : null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
///记录查看轨迹的次数
|
|
|
void _recordNumberTrajectoryViewed() {
|
|
|
if (accountRepository.memberStatusInfo.value?.trialed == true &&
|
|
|
@@ -116,245 +106,120 @@ class TrackController extends BaseController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void _handleTabChange() {}
|
|
|
+ ///显示轨迹以及标记点
|
|
|
+ void showMapTrack(List<LatLng> points, List<Marker> markers) {
|
|
|
+ mapController.clear();
|
|
|
+ //画折线
|
|
|
+ if (points.length > 1) {
|
|
|
+ mapController.addPolyline(Constants.traceNormalLineId, points,
|
|
|
+ mapPadding: mapPadding);
|
|
|
+ }
|
|
|
+ //画标记点
|
|
|
+ if (markers.isNotEmpty) {
|
|
|
+ mapController.updateOrAddMarkers(markers);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- void _initTime() {
|
|
|
- //开始时间往前推一天
|
|
|
- _trackStartTime.value = DateUtil.getNow(subtract: Duration(days: 1));
|
|
|
- _trackEndTime.value = DateUtil.getNow();
|
|
|
+ void showSelectMarker(List<LatLng> points, Marker selectMarker) {
|
|
|
+ _clearSelectMapMarker();
|
|
|
+ mapController.updateOrAddMarker(selectMarker);
|
|
|
+ mapController.moveToSuitableLocation(points, mapPadding: selectPadding);
|
|
|
}
|
|
|
|
|
|
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 showMovingTrack(List<LatLng> movingPoints) {
|
|
|
+ _clearSelectMapMarker();
|
|
|
+ mapController.addPolyline(Constants.traceSelectLineId, movingPoints,
|
|
|
+ lineType: PolylineType.selected, mapPadding: mapPadding);
|
|
|
}
|
|
|
|
|
|
- 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(trackStartTime!, dateTime, maxDuration)) {
|
|
|
- ToastUtil.show(StringName.trackChooseTimeError);
|
|
|
- _trackStartTime.value = dateTime.subtract(maxDuration);
|
|
|
- }
|
|
|
- _trackEndTime.value = dateTime;
|
|
|
- });
|
|
|
+ void showTrackError(List<LatLng> errorPoints, Marker errorMarker) {
|
|
|
+ _clearSelectMapMarker();
|
|
|
+ mapController.updateOrAddMarker(errorMarker);
|
|
|
+ mapController.addPolyline(Constants.traceSelectLineId, errorPoints,
|
|
|
+ lineType: PolylineType.error, mapPadding: mapPadding);
|
|
|
}
|
|
|
|
|
|
- void onTrackQueryClick() {
|
|
|
- if (userInfo?.virtual == false && accountRepository.memberIsExpired()) {
|
|
|
- MemberPage.start();
|
|
|
- return;
|
|
|
- }
|
|
|
- if (currentIndex == 0) {
|
|
|
- _onTrackQuery();
|
|
|
- } else {
|
|
|
- _onCurrentLocationQuery();
|
|
|
- }
|
|
|
+ void _clearSelectMapMarker() {
|
|
|
+ mapController.removeMarker(Constants.mineLocationId);
|
|
|
+ mapController.removePolyline(Constants.traceSelectLineId);
|
|
|
+ mapController.removeMarker(Constants.tracePopupId);
|
|
|
+ mapController.removeMarker(Constants.traceErrorId);
|
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
- });
|
|
|
- }
|
|
|
+ setSheetProgress(double progress) {
|
|
|
+ _sheetProgress.value = progress;
|
|
|
}
|
|
|
|
|
|
- void _onTrackQuery() {
|
|
|
- if (trackStartTime == null || trackEndTime == null || userInfo == null) {
|
|
|
- return;
|
|
|
+ void onCurrentLocationClick() async {
|
|
|
+ //权限检查
|
|
|
+ bool isGranted = await PermissionUtil.checkLocationPermission();
|
|
|
+ if (!isGranted) {
|
|
|
+ LocationPermissionDialog.show(onNextStep: _requestLocationPermission);
|
|
|
+ } else {
|
|
|
+ _updateCurrentLocation();
|
|
|
}
|
|
|
- LoadingDialog.show(StringName.trackLoadingTxt);
|
|
|
- _startAddress.value = '';
|
|
|
- _endAddress.value = '';
|
|
|
- originPoints = null;
|
|
|
- points = null;
|
|
|
- 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<LatLng>? 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) {
|
|
|
- originPoints = pair.first;
|
|
|
- points = pair.second;
|
|
|
- _showTrack();
|
|
|
- _setStartAndEndAddress(start: pair.first.first, end: pair.first.last);
|
|
|
- }).catchError((error) {
|
|
|
- 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);
|
|
|
- }
|
|
|
- }).whenComplete(() {
|
|
|
- LoadingDialog.hide();
|
|
|
- _isShowTraceDetailBtn.value = true;
|
|
|
- });
|
|
|
}
|
|
|
|
|
|
+ void _updateCurrentLocation() {
|
|
|
+ var lastLocation = MapHelper.getLastLocation();
|
|
|
+ if (lastLocation == null) {
|
|
|
+ locationListener(MapLocation location) {
|
|
|
+ MapHelper.removeLocationListener(locationListener);
|
|
|
+ showMineLocationMarker(location);
|
|
|
+ }
|
|
|
|
|
|
- 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;
|
|
|
+ MapHelper.addLocationListener(locationListener);
|
|
|
+ } else {
|
|
|
+ showMineLocationMarker(lastLocation);
|
|
|
}
|
|
|
- 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,
|
|
|
- customAvatarUrl: userInfo!.avatar,
|
|
|
- ));
|
|
|
- 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,
|
|
|
- customAvatarUrl: userInfo?.avatar,
|
|
|
- ));
|
|
|
+ void showMineLocationMarker(MapLocation location) {
|
|
|
+ _clearSelectMapMarker();
|
|
|
+ Marker mineMarker = Marker(
|
|
|
+ id: Constants.mineLocationId,
|
|
|
+ latitude: location.latitude,
|
|
|
+ longitude: location.longitude,
|
|
|
+ isSelected: false,
|
|
|
+ customAvatarUrl: accountRepository.mineUserInfo.value.avatar,
|
|
|
+ markerName: StringName.locationMine,
|
|
|
+ markerType: MarkerType.mine);
|
|
|
+ mapController.updateOrAddMarker(mineMarker);
|
|
|
+ animateCamera(latitude: location.latitude, longitude: location.longitude);
|
|
|
}
|
|
|
|
|
|
- void showQueryErrorDialog() {
|
|
|
- showTraceNoDataDialog(onConfirm: () {});
|
|
|
+ void animateCamera({required double? latitude, required double? longitude}) {
|
|
|
+ mapController.animateCamera(
|
|
|
+ CameraPosition(longitude: longitude, latitude: latitude, zoom: 18));
|
|
|
}
|
|
|
|
|
|
- void onTraceDetailClick() {
|
|
|
- if (accountRepository.memberIsExpired()) {
|
|
|
- MemberPage.start();
|
|
|
- return;
|
|
|
+ void _requestLocationPermission() async {
|
|
|
+ bool isGranted = await PermissionUtil.requestLocationPermission();
|
|
|
+ if (isGranted) {
|
|
|
+ _showLocationAlways();
|
|
|
+ _updateCurrentLocation();
|
|
|
}
|
|
|
- if (originPoints == null || originPoints!.length < 2) {
|
|
|
- showTraceNoDataDialog(onConfirm: () {});
|
|
|
- return;
|
|
|
+ }
|
|
|
+
|
|
|
+ void _showLocationAlways() async {
|
|
|
+ bool isGranted = await PermissionUtil.checkShowLocationAlways();
|
|
|
+ if (!isGranted) {
|
|
|
+ LocationAlwaysPermissionDialog.show(onNextStep: () async {
|
|
|
+ isGranted = await PermissionUtil.requestShowLocationAlways();
|
|
|
+ if (isGranted) {
|
|
|
+ _updateCurrentLocation();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- TrackDetailPage.start(originPoints!);
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
void onClose() {
|
|
|
super.onClose();
|
|
|
+ tabController?.dispose();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-class TrackQueryException implements Exception {
|
|
|
- final int code;
|
|
|
-
|
|
|
- TrackQueryException(this.code);
|
|
|
-}
|