[ios] implement TrackRecording place page

1. add an new screen (layout)
2. add TR icon for the bottom tabbar
3. share current location from the TR PP
4. refactor TR manager to properly handle state updates and pass them to the LiveActivityManager and PlacePage
5. add init/update with TrackInfo/EleInfo methods to the PlacePageData and PlacePagePreviewData to update the PP state
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
Kiryl Kaveryn
2025-01-09 16:35:31 +04:00
committed by Yannik Bloscheck
parent 5d0b8f1c04
commit b79724f248
35 changed files with 554 additions and 216 deletions

View File

@@ -10,7 +10,7 @@ final class TrackRecordingButtonArea: AvailableArea {
}
override func notifyObserver() {
TrackRecordingViewController.updateAvailableArea(areaFrame)
TrackRecordingButtonViewController.updateAvailableArea(areaFrame)
}
}

View File

@@ -66,12 +66,9 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
}
func shareLocation(cell: BottomMenuItemCell) {
let lastLocation = LocationManager.lastLocation()
guard let coordinates = lastLocation?.coordinate else {
let alert = UIAlertController(title: L("unknown_current_position"), message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: L("ok"), style: .default, handler: nil))
viewController?.present(alert, animated: true, completion: nil)
return;
guard let coordinates = LocationManager.lastLocation()?.coordinate else {
viewController?.present(UIAlertController.unknownCurrentPosition(), animated: true, completion: nil)
return
}
guard let viewController = viewController else { return }
let vc = ActivityViewController.share(forMyPosition: coordinates)
@@ -79,8 +76,13 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
}
func toggleTrackRecording() {
trackRecorder.processAction(trackRecorder.recordingState == .active ? .stop : .start) { [weak self] in
self?.close()
switch trackRecorder.recordingState {
case .active:
break
case .inactive:
trackRecorder.processAction(.start)
}
close()
MapViewController.shared()?.showTrackRecordingPlacePage()
}
}

View File

@@ -62,19 +62,27 @@ final class ActionBarViewController: UIViewController {
fatalError()
}
}
var buttons: [ActionBarButtonType] = []
if isRoutePlanning {
buttons.append(.routeFrom)
}
let hasAnyPhones = !(placePageData.infoData?.phones ?? []).isEmpty
if hasAnyPhones, AppInfo.shared().canMakeCalls {
buttons.append(.call)
}
if !isRoutePlanning {
buttons.append(.routeFrom)
switch placePageData.objectType {
case .POI, .bookmark, .track:
if isRoutePlanning {
buttons.append(.routeFrom)
}
let hasAnyPhones = !(placePageData.infoData?.phones ?? []).isEmpty
if hasAnyPhones, AppInfo.shared().canMakeCalls {
buttons.append(.call)
}
if !isRoutePlanning {
buttons.append(.routeFrom)
}
case .trackRecording:
break
@unknown default:
fatalError()
}
assert(buttons.count > 0)
guard !buttons.isEmpty else { return }
visibleButtons.append(buttons[0])
if buttons.count > 1 {
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
@@ -83,21 +91,24 @@ final class ActionBarViewController: UIViewController {
private func configButton2() {
var buttons: [ActionBarButtonType] = []
if canAddStop {
buttons.append(.routeAddStop)
}
switch placePageData.objectType {
case .POI, .bookmark:
if canAddStop {
buttons.append(.routeAddStop)
}
buttons.append(.bookmark)
case .track:
if canAddStop {
buttons.append(.routeAddStop)
}
buttons.append(.track)
case .trackRecording:
// TODO: implement for track recording
break
buttons.append(.saveTrackRecording)
@unknown default:
fatalError()
}
assert(buttons.count > 0)
visibleButtons.append(buttons[0])
if buttons.count > 1 {
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
@@ -105,7 +116,14 @@ final class ActionBarViewController: UIViewController {
}
private func configButton3() {
visibleButtons.append(.routeTo)
switch placePageData.objectType {
case .POI, .bookmark, .track:
visibleButtons.append(.routeTo)
case .trackRecording:
break
@unknown default:
fatalError()
}
}
private func configButton4() {

View File

@@ -85,6 +85,11 @@ extension ElevationProfilePresenter: ElevationProfilePresenterProtocol {
view?.setChartData(ChartPresentationData(chartData, formatter: formatter))
view?.reloadDescription()
guard !profileData.isTrackRecording else {
view?.isChartViewInfoHidden = true
return
}
view?.setActivePoint(profileData.activePoint)
view?.setMyPosition(profileData.myPosition)
bookmarkManager.setElevationActivePointChanged(profileData.trackId) { [weak self] distance in

View File

@@ -51,8 +51,6 @@ extension PlacePageHeaderPresenter: PlacePageHeaderPresenterProtocol {
view?.isExpandViewHidden = true
view?.isShadowViewHidden = false
}
// TODO: (KK) Enable share button for the tracks to share the whole track gpx/kml
view?.isShareButtonHidden = false
}
func onClosePress() {

View File

@@ -2,7 +2,6 @@ protocol PlacePageHeaderViewProtocol: AnyObject {
var presenter: PlacePageHeaderPresenterProtocol? { get set }
var isExpandViewHidden: Bool { get set }
var isShadowViewHidden: Bool { get set }
var isShareButtonHidden: Bool { get set }
func setTitle(_ title: String?, secondaryTitle: String?)
func showShareTrackMenu()
@@ -78,15 +77,6 @@ extension PlacePageHeaderViewController: PlacePageHeaderViewProtocol {
}
}
var isShareButtonHidden: Bool {
get {
shareButton.isHidden
}
set {
shareButton.isHidden = newValue
}
}
func setTitle(_ title: String?, secondaryTitle: String?) {
titleText = title
secondaryText = secondaryTitle

View File

@@ -15,8 +15,7 @@
case .track:
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
case .trackRecording:
// TODO: Implement PlacePageTrackRecordingLayout
fatalError("PlacePageTrackRecordingLayout is not implemented")
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
@unknown default:
fatalError()
}
@@ -34,14 +33,14 @@
data: data,
mapViewController: MapViewController.shared()!)
let layout: IPlacePageLayout
let storyboard = viewController.storyboard!
switch data.objectType {
case .POI, .bookmark:
layout = PlacePageCommonLayout(interactor: interactor, storyboard: viewController.storyboard!, data: data)
layout = PlacePageCommonLayout(interactor: interactor, storyboard: storyboard, data: data)
case .track:
layout = PlacePageTrackLayout(interactor: interactor, storyboard: viewController.storyboard!, data: data)
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
case .trackRecording:
// TODO: Implement PlacePageTrackRecordingLayout
fatalError("PlacePageTrackRecordingLayout is not implemented")
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
@unknown default:
fatalError()
}

View File

@@ -246,9 +246,19 @@ extension PlacePageInteractor: ActionBarViewControllerDelegate {
fatalError("More button should've been handled in ActionBarViewContoller")
case .track:
guard placePageData.trackData != nil else { return }
// TODO: This is temporary solution. Remove the dialog and use the MWMPlacePageManagerHelper.removeTrack
// TODO: (KK) This is temporary solution. Remove the dialog and use the MWMPlacePageManagerHelper.removeTrack
// directly here when the track recovery mechanism will be implemented.
showTrackDeletionConfirmationDialog()
case .saveTrackRecording:
// TODO: (KK) pass name typed by user
TrackRecordingManager.shared.processAction(.stopAndSave(name: "")) { [weak self] result in
switch result {
case .success:
break
case .error:
self?.presenter?.closeAnimated()
}
}
@unknown default:
fatalError()
}
@@ -298,8 +308,8 @@ extension PlacePageInteractor: ElevationProfileViewControllerDelegate {
}
func updateMapPoint(_ point: CLLocationCoordinate2D, distance: Double) {
guard let trackId = placePageData.trackData?.trackId else { return }
BookmarksManager.shared().setElevationActivePoint(point, distance: distance, trackId: trackId)
guard let trackData = placePageData.trackData, trackData.elevationProfileData?.isTrackRecording == false else { return }
BookmarksManager.shared().setElevationActivePoint(point, distance: distance, trackId: trackData.trackId)
}
}
@@ -323,7 +333,12 @@ extension PlacePageInteractor: PlacePageHeaderViewControllerDelegate {
case .track:
presenter?.showShareTrackMenu()
default:
fatalError()
guard let coordinates = LocationManager.lastLocation()?.coordinate else {
viewController?.present(UIAlertController.unknownCurrentPosition(), animated: true, completion: nil)
return
}
let activity = ActivityViewController.share(forMyPosition: coordinates)
activity.present(inParentViewController: mapViewController, anchorView: sourceView)
}
}

View File

@@ -3,6 +3,7 @@ typedef NS_ENUM(NSInteger, MWMActionBarButtonType) {
MWMActionBarButtonTypeBookingSearch,
MWMActionBarButtonTypeBookmark,
MWMActionBarButtonTypeTrack,
MWMActionBarButtonTypeSaveTrackRecording,
MWMActionBarButtonTypeCall,
MWMActionBarButtonTypeDownload,
MWMActionBarButtonTypeMore,

View File

@@ -19,6 +19,8 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
case MWMActionBarButtonTypeBookmark:
case MWMActionBarButtonTypeTrack:
return L(isSelected ? @"delete" : @"save");
case MWMActionBarButtonTypeSaveTrackRecording:
return L(@"save");
case MWMActionBarButtonTypeRouteFrom:
return L(@"p2p_from_here");
case MWMActionBarButtonTypeRouteTo:
@@ -55,7 +57,8 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
self.label.text = titleForButton(self.type, isSelected);
self.extraBackground.hidden = YES;
self.button.coloring = MWMButtonColoringBlack;
[self.button.imageView setContentMode:UIViewContentModeScaleAspectFit];
switch (self.type) {
case MWMActionBarButtonTypeDownload: {
if (self.mapDownloadProgress)
@@ -108,6 +111,9 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
[self.button setImage:[[UIImage imageNamed:@"ic_route_manager_trash"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
self.button.coloring = MWMButtonColoringRed;
break;
case MWMActionBarButtonTypeSaveTrackRecording:
[self.button setImage:[UIImage imageNamed:@"ic_placepage_save_track_recording"] forState:UIControlStateNormal];
break;
case MWMActionBarButtonTypeRouteFrom:
[self.button setImage:[UIImage imageNamed:@"ic_route_from"] forState:UIControlStateNormal];
break;

View File

@@ -0,0 +1,92 @@
final class PlacePageTrackRecordingLayout: IPlacePageLayout {
private var placePageData: PlacePageData
private var interactor: PlacePageInteractor
private let storyboard: UIStoryboard
weak var presenter: PlacePagePresenterProtocol?
lazy var bodyViewControllers: [UIViewController] = {
return configureViewControllers()
}()
var actionBar: ActionBarViewController? {
actionBarViewController
}
var navigationBar: UIViewController? {
placePageNavigationViewController
}
lazy var headerViewControllers: [UIViewController] = {
[headerViewController]
}()
lazy var headerViewController: PlacePageHeaderViewController = {
return PlacePageHeaderBuilder.build(data: placePageData, delegate: interactor, headerType: .flexible)
}()
lazy var placePageNavigationViewController: PlacePageHeaderViewController = {
return PlacePageHeaderBuilder.build(data: placePageData, delegate: interactor, headerType: .fixed)
}()
lazy var editTrackViewController: PlacePageEditBookmarkOrTrackViewController = {
let vc = storyboard.instantiateViewController(ofType: PlacePageEditBookmarkOrTrackViewController.self)
vc.view.isHidden = true
vc.delegate = interactor
return vc
}()
lazy var elevationProfileViewController: ElevationProfileViewController? = {
guard let trackData = placePageData.trackData else {
return nil
}
return ElevationProfileBuilder.build(trackInfo: trackData.trackInfo,
elevationProfileData: trackData.elevationProfileData,
delegate: interactor)
}()
lazy var actionBarViewController: ActionBarViewController = {
let vc = storyboard.instantiateViewController(ofType: ActionBarViewController.self)
vc.placePageData = placePageData
vc.canAddStop = MWMRouter.canAddIntermediatePoint()
vc.isRoutePlanning = MWMNavigationDashboardManager.shared().state != .hidden
vc.delegate = interactor
return vc
}()
init(interactor: PlacePageInteractor, storyboard: UIStoryboard, data: PlacePageData) {
self.interactor = interactor
self.storyboard = storyboard
self.placePageData = data
}
private func configureViewControllers() -> [UIViewController] {
var viewControllers = [UIViewController]()
if let elevationProfileViewController {
viewControllers.append(elevationProfileViewController)
}
placePageData.onTrackRecordingProgressUpdate = { [weak self] in
self?.updateTrackRecordingRelatedSections()
}
return viewControllers
}
func calculateSteps(inScrollView scrollView: UIScrollView, compact: Bool) -> [PlacePageState] {
var steps: [PlacePageState] = []
let scrollHeight = scrollView.height
steps.append(.closed(-scrollHeight))
steps.append(.full(0))
return steps
}
}
private extension PlacePageTrackRecordingLayout {
func updateTrackRecordingRelatedSections() {
guard let elevationProfileViewController, let trackInfo = placePageData.trackData?.trackInfo else { return }
headerViewController.setTitle(placePageData.previewData.title, secondaryTitle: nil)
elevationProfileViewController.presenter?.update(trackInfo: trackInfo, profileData: placePageData.trackData?.elevationProfileData)
presenter?.layoutIfNeeded()
}
}