import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_tool_android/flutter_tool_android.dart'; import 'package:get/get.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/user_info.dart'; import 'package:location/data/consts/constants.dart'; import 'package:location/data/repositories/account_repository.dart'; import 'package:location/data/repositories/friends_repository.dart'; import 'package:location/data/repositories/message_repository.dart'; import 'package:location/handler/error_handler.dart'; import 'package:location/module/friend/friend_page.dart'; import 'package:location/module/login/login_page.dart'; import 'package:location/module/main/today_track_helper.dart'; import 'package:location/module/member/member_page.dart'; import 'package:location/module/news/news_page.dart'; import 'package:location/module/urgent_contact/urgent_contact_page.dart'; import 'package:location/resource/string.gen.dart'; import 'package:location/sdk/map/map_helper.dart'; import 'package:location/utils/mmkv_util.dart'; import 'package:location/utils/toast_util.dart'; import 'package:permission_handler/permission_handler.dart'; import '../../data/bean/member_status_info.dart'; import '../../data/repositories/config_repository.dart'; import '../../data/repositories/track_repository.dart'; import '../../data/repositories/urgent_contact_repository.dart'; import '../../dialog/check_loation_permission_dialog.dart'; import '../../dialog/common_alert_dialog_impl.dart'; import '../../dialog/common_confirm_dialog_impl.dart'; import '../../dialog/friend_dialog.dart'; import '../../dialog/location_permission_dialog.dart'; import '../../helper/internet_connection_helper.dart'; import '../../sdk/wechat/wechat_share_util.dart'; import '../../socket/atmob_location_client.dart'; import '../../utils/de_bounce.dart'; import '../../utils/location_convert_marker_util.dart'; import '../../utils/permission_util.dart'; import '../add_friend/add_friend_page.dart'; import '../mine/mine_page.dart'; import '../track/track_dialog/track_choose_friend_dialog.dart'; import '../track/track_dialog/track_daily_report_dialog.dart'; import '../track/track_page.dart'; import '../urgent_contact/urgent_contact_click_help_dialog.dart'; ///求助提示框记录 const String _kChickHelpAlertKey = "urgent_chick_help_alder_key"; @injectable class MainController extends BaseController { RxList get _friendsList => friendsRepository.friendsList; Rxn get memberStatusInfo => accountRepository.memberStatusInfo; RxList integrateList = RxList(); UserInfo get mineUserInfo => accountRepository.mineUserInfo.value; final Rxn _selectedFriend = Rxn(); UserInfo? get selectedFriend => _selectedFriend.value; final MapController mapController = MapController(); final MapController todayTrackSmallMapController = MapController(); StreamSubscription? mineUserInfoSubscription; StreamSubscription? mineLocationSubscription; StreamSubscription? friendsListSubscription; StreamSubscription? loginSubscription; StreamSubscription? memberStatusInfoSubscription; bool isFirstShowMineLocation = true; DateTime _lastRefreshTime = DateTime.fromMillisecondsSinceEpoch(0); final FriendsRepository friendsRepository; final AccountRepository accountRepository; final MessageRepository messageRepository; final UrgentContactRepository urgentContactRepository; final TrackRepository trackRepository; final TodayTrackHelper todayTrackHelper; bool get hasUnreadMessage => messageRepository.hasUnreadMessage.value; DateTime? _lastPressedAt; DateTime? _lastRequestFavor; String? lastCheckFriendId; bool isExecuteAutoSelect = false; Timer? electricTimer; final Debounce _queryTodayTrackDebounce = Debounce(debounceTime: 500); final Map electricMap = {}; RxMap get todayTrackReportMap => todayTrackHelper.todayTrackReportMap; MainController( this.friendsRepository, this.accountRepository, this.messageRepository, this.todayTrackHelper, AtmobLocationClient atmobLocationClient, this.urgentContactRepository, ConfigRepository configRepository, this.trackRepository); @override void onReady() { super.onReady(); InternetConnectionHelper.getInstance().startInternetConnection(); InternetConnectionHelper.getInstance().startListenNetwork(); integrateList.add(mineUserInfo); initLastCheckFriendId(); AddFriendDialog.show( onAddClick: () async { await onAddFriendClick(); checkLocationPermissionCallback(); }, onNotAddDismiss: checkLocationPermissionCallback); loginSubscription = accountRepository.isLogin.listen((isLogin) { if (isLogin == false) { _selectedFriend.value = mineUserInfo; } }); //刷新好友列表 if (_friendsList.isNotEmpty) { _updateFriendList(_friendsList); } friendsListSubscription = _friendsList.listen((list) => _updateFriendList(list)); //刷新我的marker updateMineInfo(accountRepository.mineUserInfo.value); mineUserInfoSubscription = accountRepository.mineUserInfo.listen((mineInfo) { updateMineInfo(mineInfo); }); //刷新我的定位 final location = accountRepository.mineUserInfo.value.lastLocation.value; if (location != null) { _updateMineLocation(location); } mineLocationSubscription = accountRepository.mineUserInfo.value.lastLocation.listen((location) { _updateMineLocation(location); }); //根据会员状态刷新好友列表 memberStatusInfoSubscription = accountRepository.memberStatusInfo.listen((memberStatus) { _updateFriendList(_friendsList); }); ///刷新检查有没有广告 _refreshTrackDailyDialogs(); } void _updateMineLocation(LocationInfo? location) { final mineInfo = accountRepository.mineUserInfo.value; mapController.updateOrAddMarker(Location2MarkerUtil.userInfo2Marker( mineInfo, selectedFriend?.id == mineInfo.id, null)); if (isFirstShowMineLocation && location != null) { isFirstShowMineLocation = false; animateCameraToUser(mineInfo); } } void _updateFriendList(List list) { integrateList.clear(); integrateList.add(mineUserInfo); integrateList.addAll(list); mapController.replaceAllMarkers(Location2MarkerUtil.userInfoList2MarkerList( integrateList, accountRepository.memberIsExpired(), selectedFriend)); if (selectedFriend != null) { UserInfo? userInfo = integrateList .firstWhereOrNull((element) => element.id == selectedFriend?.id); if (userInfo == null) { _selectedFriend.value = null; } else { _setSelectUserInfo(userInfo); } } _autoSelectFriend(); } ///刷新检查有没有广告 void _refreshTrackDailyDialogs() { trackRepository.locationTrackDailyDialogs().then((trackResponse) { if (trackResponse.show ?? false) { TrackDailyReportDialog.show( trackDailyList: trackResponse.trackDailyList, confirmOnTap: () { TrackChooseFriendDialog.show( trackDailyList: trackResponse.trackDailyList, onSelectItem: (selectInfo) { onViewTraceClick(selectInfo); }); }); } }).catchError((error) { ErrorHandler.toastError(error); }); } void updateMineInfo(UserInfo mineInfo) { if (mineInfo == selectedFriend) { _selectedFriend.refresh(); } if (mineInfo.lastLocation.value == null) { return; } mapController.updateOrAddMarker(Location2MarkerUtil.userInfo2Marker( mineInfo, selectedFriend?.id == mineInfo.id, null)); } Future onAddFriendClick() { return AddFriendPage.show(); } void onMineClick() { MinePage.start(); } void checkLocationPermissionCallback() async { bool isGranted = await PermissionUtil.checkLocationPermission(); if (!isGranted) { CheckLocationPermissionDialog.show( onRequestPermissionClick: _requestLocationPermission); } } void onSelectUserClick(UserInfo userInfo) { KVUtil.putString(Constants.keyLastSelectFriendId, userInfo.id); if (userInfo == _selectedFriend.value) { return; } _setSelectUserInfo(userInfo); } void _setSelectUserInfo(UserInfo userInfo) { UserInfo? oldInfo = _selectedFriend.value; _selectedFriend.value = userInfo; //定时查询手机电量 _scheduleQueryBattery(); //修改地图选中 int? electric = electricMap[userInfo.id]; _updateMapSelected(oldInfo, userInfo, electric: electric); if (!accountRepository.memberIsExpired() || userInfo.isMine == true) { //移动到选中的位置 animateCameraToUser(userInfo); } } void _scheduleQueryBattery() { electricTimer?.cancel(); final friend = _selectedFriend.value; if (friend == null || friend.id == Constants.mineLocationId) { return; } queryFriendElectric(friend); electricTimer = Timer.periodic(const Duration(minutes: 3), (timer) { queryFriendElectric(friend); }); } void queryFriendElectric(UserInfo friend) async { friendsRepository .userElectricQuery(friend.id) .then((response) => response.electric) .then((electric) { //更新选中点位的电量 if (electric == null) { return; } electricMap[friend.id] = electric; final selectFriend = _selectedFriend.value; if (selectFriend == null || friend.id != selectFriend.id) { return; } _updateMapSelected(null, selectFriend, electric: electric); }); } void _updateMapSelected(UserInfo? oldInfo, UserInfo newInfo, {int? electric}) { List markers = []; if (oldInfo != null && oldInfo.id != newInfo.id) { markers.add(oldInfo); } markers.add(newInfo); mapController.updateOrAddMarkers( Location2MarkerUtil.userInfoList2MarkerList( markers, accountRepository.memberIsExpired(), selectedFriend, electric: electric)); } void animateCameraToUser(UserInfo userInfo) { LocationInfo? locationInfo = userInfo.lastLocation.value; if (locationInfo == null || locationInfo.longitude == null || locationInfo.latitude == null || locationInfo.longitude == 0 || locationInfo.latitude == 0 || userInfo.blockedMe == true) { return; } animateCamera( latitude: locationInfo.latitude, longitude: locationInfo.longitude); } void animateCamera({required double? latitude, required double? longitude}) { mapController.animateCamera( CameraPosition(longitude: longitude, latitude: latitude, zoom: 18)); } void onMarkerTap(Marker marker) { String id = marker.id; UserInfo? userInfo = integrateList.firstWhereOrNull((element) => element.id == id); if (userInfo == null) { return; } onSelectUserClick(userInfo); } void onCurrentLocationClick() async { //权限检查 bool isGranted = await PermissionUtil.checkLocationPermission(); if (!isGranted) { LocationPermissionDialog.show(onNextStep: _requestLocationPermission); } else { _updateCurrentLocation(); } } void onRefreshFriendLocationClick() { final now = DateTime.now(); final secondsSinceLastRefresh = now.difference(_lastRefreshTime).inSeconds; if (secondsSinceLastRefresh < 10) { final remainingSeconds = 10 - secondsSinceLastRefresh; ToastUtil.show('请等待 $remainingSeconds 秒后重试'); return; } _lastRefreshTime = now; friendsRepository.refreshFriends().then((_) { ToastUtil.show(StringName.refreshFriendDataSuccess); }).catchError((error) { ErrorHandler.toastError(error); _lastRefreshTime = DateTime.fromMillisecondsSinceEpoch(0); }); } void _requestLocationPermission() async { bool isGranted = await PermissionUtil.requestLocationPermission(); if (isGranted) { _showLocationAlways(); _updateCurrentLocation(); } else { permissionRefuseDialog(settingClick: () { openAppSettings(); }); ToastUtil.show(StringName.permissionRequestFail); } } void _showLocationAlways() async { bool isGranted = await PermissionUtil.checkShowLocationAlways(); if (!isGranted) { LocationAlwaysPermissionDialog.show(onNextStep: () async { isGranted = await PermissionUtil.requestShowLocationAlways(); if (isGranted) { _updateCurrentLocation(); } }); } } void _updateCurrentLocation() { var lastLocation = MapHelper.getLastLocation(); if (lastLocation == null) { locationListener(MapLocation location) { MapHelper.removeLocationListener(locationListener); animateCamera( latitude: location.latitude, longitude: location.longitude); } MapHelper.addLocationListener(locationListener); } else { animateCamera( latitude: lastLocation.latitude, longitude: lastLocation.longitude); } } onFriendClick() { FriendPage.start(); } void onViewTraceClick(UserInfo userInfo) { if (!accountRepository.isLogin.value) { showTraceTipsDialog(onConfirm: () { LoginPage.start(); }); return; } if (!(userInfo.virtual ?? false) && accountRepository.memberIsExpired()) { MemberPage.start(); return; } if (userInfo.blockedMe == true) { showBlockMeTipDialog(confirmOnTap: () { WechatShareUtil.shareWebPage().catchError((error) { ToastUtil.show(error.toString()); }); }); return; } TrackPage.start(userInfo); } void onNewsClick() { if (!accountRepository.isLogin.value) { LoginPage.start(); return; } NewsPage.start(); } onUrgentContactClick() { if (!accountRepository.isLogin.value) { LoginPage.start(); return; } //如果没有紧急联系人,则先进入页面 if (urgentContactRepository.contactList.isEmpty) { UrgentContactPage.start(); onShowRequestHelpTip(); return; } if (accountRepository.memberIsExpired()) { MemberPage.start(); return; } final defaultContact = urgentContactRepository.getDefaultContact(); if (defaultContact != null) { if (_lastRequestFavor != null && DateTime.now().difference(_lastRequestFavor!).inSeconds < 30) { ToastUtil.show(StringName.urgentContactHelpSendTooFast); return; } sendUrgentContactDialog(defaultContact.phone, confirmOnTap: () { urgentContactRepository.contactMayDayFavor().then((value) { ToastUtil.show(StringName.urgentContactHelpSendSuccess); _lastRequestFavor = DateTime.now(); }).catchError((error) { ErrorHandler.toastError(error); }); }); return; } else { ToastUtil.show(StringName.contactNoDefault); UrgentContactPage.start(); onShowRequestHelpTip(); } } //弹出求助提示 void onShowRequestHelpTip() { String? memberPageKeyStr = KVUtil.getString(_kChickHelpAlertKey, ''); if (memberStatusInfo.value?.expired == true && (memberPageKeyStr ?? '').length == 0) { ///永久化存储 KVUtil.putString(_kChickHelpAlertKey, _kChickHelpAlertKey); UrgentContactClickHelpDialog.show(confirmOnTap: () { MemberPage.start(); }); } } void onAppBack() { if ((_lastPressedAt == null || DateTime.now().difference(_lastPressedAt!) > const Duration(seconds: 2))) { _lastPressedAt = DateTime.now(); ToastUtil.show(StringName.exitAppTip); } else { back(); } } void back() { if (Platform.isAndroid) { FlutterToolAndroid.backHome(); } else { SystemNavigator.pop(); } } void initLastCheckFriendId() { lastCheckFriendId = KVUtil.getString(Constants.keyLastSelectFriendId, null); _autoSelectFriend(); } void _autoSelectFriend() { if (isExecuteAutoSelect) { return; } if (lastCheckFriendId == null) { _setSelectUserInfo(mineUserInfo); isExecuteAutoSelect = true; return; } for (var value in integrateList) { if (value.id == lastCheckFriendId) { isExecuteAutoSelect = true; _setSelectUserInfo(value); return; } } } void onFriendVisibleFraction(double visibleFraction) { if (visibleFraction > 0.2) { _queryTodayTrackDebounce.onClick(() { _requestSelectedFriendTrack(); }); } } void _requestSelectedFriendTrack() { //查看当前选中的好友是否有记录数据 final selectedFriend = _selectedFriend.value; if (selectedFriend == null || accountRepository.memberIsExpired()) { return; } todayTrackHelper.requestTodayTrackReport(selectedFriend.id); } void onTodayTraceClick() { final selectedUserInfo = _selectedFriend.value; if (selectedUserInfo == null) { return; } onViewTraceClick(selectedUserInfo); } void onTrackNoMemberClick() { MemberPage.start(); } @override void onClose() { mineLocationSubscription?.cancel(); friendsListSubscription?.cancel(); loginSubscription?.cancel(); memberStatusInfoSubscription?.cancel(); mineUserInfoSubscription?.cancel(); } }