mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 13:23:59 +00:00
[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:
committed by
Yannik Bloscheck
parent
5d0b8f1c04
commit
b79724f248
@@ -4,7 +4,7 @@
|
||||
@implementation TrackInfo
|
||||
|
||||
+ (TrackInfo *)emptyInfo {
|
||||
return [[TrackInfo alloc] init];
|
||||
return [[TrackInfo alloc] initWithTrackStatistics:TrackStatistics()];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,12 +21,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
typedef void (^SearchInDownloaderCompletions)(NSArray<MWMMapSearchResult *> *results, BOOL finished);
|
||||
typedef void (^TrackRecordingUpdatedHandler)(TrackInfo * _Nonnull trackInfo);
|
||||
|
||||
@protocol TrackRecorder <NSObject>
|
||||
@protocol TrackRecorder
|
||||
|
||||
+ (void)startTrackRecording;
|
||||
+ (void)setTrackRecordingUpdateHandler:(TrackRecordingUpdatedHandler _Nullable)trackRecordingDidUpdate;
|
||||
+ (void)stopTrackRecording;
|
||||
+ (void)saveTrackRecordingWithName:(nullable NSString *)name;
|
||||
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name;
|
||||
+ (BOOL)isTrackRecordingEnabled;
|
||||
+ (BOOL)isTrackRecordingEmpty;
|
||||
/// Returns current track recording elevation info.
|
||||
|
||||
@@ -239,8 +239,8 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(
|
||||
GetFramework().StopTrackRecording();
|
||||
}
|
||||
|
||||
+ (void)saveTrackRecordingWithName:(nullable NSString *)name {
|
||||
GetFramework().SaveTrackRecordingWithName(name == nil ? "" : name.UTF8String);
|
||||
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name {
|
||||
GetFramework().SaveTrackRecordingWithName(name.UTF8String);
|
||||
}
|
||||
|
||||
+ (BOOL)isTrackRecordingEnabled {
|
||||
@@ -252,7 +252,7 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(
|
||||
}
|
||||
|
||||
+ (ElevationProfileData * _Nonnull)trackRecordingElevationInfo {
|
||||
return [[ElevationProfileData alloc] initWithElevationInfo:GetFramework().GetTrackRecordingCurrentElevationInfo()];
|
||||
return [[ElevationProfileData alloc] initWithElevationInfo:GetFramework().GetTrackRecordingElevationInfo()];
|
||||
}
|
||||
|
||||
// MARK: - ProductsManager
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class PlacePageScheduleData;
|
||||
@class TrackInfo;
|
||||
|
||||
typedef NS_ENUM(NSInteger, PlacePageDataHotelType) {
|
||||
PlacePageDataHotelTypeHotel,
|
||||
@@ -39,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property(nonatomic, readonly) PlacePageDataSchedule schedule;
|
||||
@property(nonatomic, readonly) BOOL isMyPosition;
|
||||
|
||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#import "PlacePagePreviewData+Core.h"
|
||||
#import "DistanceFormatter.h"
|
||||
#import "AltitudeFormatter.h"
|
||||
#import "DurationFormatter.h"
|
||||
#import "TrackInfo.h"
|
||||
|
||||
#include "3party/opening_hours/opening_hours.hpp"
|
||||
|
||||
@@ -46,6 +50,16 @@ static PlacePageDataSchedule convertOpeningHours(std::string_view rawOH)
|
||||
|
||||
@implementation PlacePagePreviewData
|
||||
|
||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// TODO: (KK) Replace separator with a shared static constant.
|
||||
NSString * kSeparator = @" • ";
|
||||
_title = [@[trackInfo.duration, trackInfo.distance] componentsJoinedByString:kSeparator];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlacePagePreviewData (Core)
|
||||
|
||||
@@ -10,8 +10,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property(nonatomic, readonly) MWMTrackID trackId;
|
||||
@property(nonatomic, readonly) MWMMarkGroupID groupId;
|
||||
@property(nonatomic, readonly, nonnull) TrackInfo * trackInfo;
|
||||
@property(nonatomic, readonly, nullable) ElevationProfileData * elevationProfileData;
|
||||
@property(nonatomic, readwrite, nonnull) TrackInfo * trackInfo;
|
||||
@property(nonatomic, readwrite, nullable) ElevationProfileData * elevationProfileData;
|
||||
|
||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
|
||||
@implementation PlacePageTrackData
|
||||
|
||||
- (nonnull instancetype)initWithTrackInfo:(TrackInfo *)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_trackInfo = trackInfo;
|
||||
_elevationProfileData = elevationInfo;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlacePageTrackData (Core)
|
||||
|
||||
@@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
elevationInfo:(ElevationInfo const &)elevationInfo
|
||||
activePoint:(double)activePoint
|
||||
myPosition:(double)myPosition;
|
||||
- (instancetype)initWithElevationInfo:(ElevationInfo const &)elevationInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ typedef NS_ENUM(NSInteger, ElevationDifficulty) {
|
||||
@interface ElevationProfileData : NSObject
|
||||
|
||||
@property(nonatomic, readonly) uint64_t trackId;
|
||||
@property(nonatomic, readonly) BOOL isTrackRecording;
|
||||
@property(nonatomic, readonly) ElevationDifficulty difficulty;
|
||||
@property(nonatomic, readonly) NSArray<ElevationHeightPoint *> * points;
|
||||
@property(nonatomic, readonly) double activePoint;
|
||||
|
||||
@@ -30,7 +30,25 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
|
||||
if (self) {
|
||||
_trackId = trackId;
|
||||
_difficulty = convertDifficulty(elevationInfo.GetDifficulty());
|
||||
_points = [ElevationProfileData pointsFromElevationInfo:elevationInfo];
|
||||
_activePoint = activePoint;
|
||||
_myPosition = myPosition;
|
||||
_isTrackRecording = false;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithElevationInfo:(ElevationInfo const &)elevationInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_difficulty = convertDifficulty(elevationInfo.GetDifficulty());
|
||||
_points = [ElevationProfileData pointsFromElevationInfo:elevationInfo];
|
||||
_isTrackRecording = true;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSArray<ElevationHeightPoint *> *)pointsFromElevationInfo:(ElevationInfo const &)elevationInfo {
|
||||
auto const & points = elevationInfo.GetPoints();
|
||||
NSMutableArray * pointsArray = [[NSMutableArray alloc] initWithCapacity:points.size()];
|
||||
for (auto const & point : points) {
|
||||
@@ -41,12 +59,7 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
|
||||
andAltitude:point.m_point.GetAltitude()];
|
||||
[pointsArray addObject:elevationPoint];
|
||||
}
|
||||
_points = [pointsArray copy];
|
||||
_activePoint = activePoint;
|
||||
_myPosition = myPosition;
|
||||
return [pointsArray copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@class PlacePageBookmarkData;
|
||||
@class MWMMapNodeAttributes;
|
||||
@class TrackInfo;
|
||||
@class ElevationProfileData;
|
||||
|
||||
typedef NS_ENUM(NSInteger, PlacePageRoadType) {
|
||||
PlacePageRoadTypeToll,
|
||||
@@ -49,12 +50,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property(nonatomic, readonly) CLLocationCoordinate2D locationCoordinate;
|
||||
@property(nonatomic, copy, nullable) MWMVoidBlock onBookmarkStatusUpdate;
|
||||
@property(nonatomic, copy, nullable) MWMVoidBlock onMapNodeStatusUpdate;
|
||||
@property(nonatomic, copy, nullable) MWMVoidBlock onTrackRecordingProgressUpdate;
|
||||
@property(nonatomic, copy, nullable) void (^onMapNodeProgressUpdate)(uint64_t downloadedBytes, uint64_t totalBytes);
|
||||
|
||||
- (instancetype)initWithLocalizationProvider:(id<IOpeningHoursLocalization>)localization;
|
||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (void)updateBookmarkStatus;
|
||||
- (void)updateWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#import "PlacePageInfoData+Core.h"
|
||||
#import "PlacePageBookmarkData+Core.h"
|
||||
#import "PlacePageTrackData+Core.h"
|
||||
#import "ElevationProfileData+Core.h"
|
||||
#import "MWMMapNodeAttributes.h"
|
||||
|
||||
#include <CoreApi/CoreApi.h>
|
||||
@@ -84,6 +85,25 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_objectType = PlacePageObjectTypeTrackRecording;
|
||||
_roadType = PlacePageRoadTypeNone;
|
||||
_previewData = [[PlacePagePreviewData alloc] initWithTrackInfo:trackInfo];
|
||||
_trackData = [[PlacePageTrackData alloc] initWithTrackInfo:trackInfo elevationInfo:elevationInfo];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo {
|
||||
_previewData = [[PlacePagePreviewData alloc] initWithTrackInfo:trackInfo];
|
||||
_trackData.trackInfo = trackInfo;
|
||||
_trackData.elevationProfileData = elevationInfo;
|
||||
if (self.onTrackRecordingProgressUpdate != nil)
|
||||
self.onTrackRecordingProgressUpdate();
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.mapNodeAttributes != nil) {
|
||||
[[MWMStorage sharedStorage] removeObserver:self];
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
extension UIAlertController {
|
||||
static func unknownCurrentPosition() -> UIAlertController {
|
||||
let alert = UIAlertController(title: L("unknown_current_position"), message: nil, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: L("ok"), style: .default, handler: nil))
|
||||
return alert
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,15 @@
|
||||
|
||||
@class MapViewController;
|
||||
@class BottomTabBarViewController;
|
||||
@class TrackRecordingViewController;
|
||||
@class TrackRecordingButtonViewController;
|
||||
@class SearchQuery;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TrackRecordingButtonState) {
|
||||
TrackRecordingButtonStateHidden,
|
||||
TrackRecordingButtonStateVisible,
|
||||
TrackRecordingButtonStateClosed,
|
||||
};
|
||||
|
||||
@protocol MWMFeatureHolder;
|
||||
|
||||
@interface MWMMapViewControlsManager : NSObject
|
||||
@@ -21,7 +27,7 @@
|
||||
@property(nonatomic) MWMBottomMenuState menuRestoreState;
|
||||
@property(nonatomic) BOOL isDirectionViewHidden;
|
||||
@property(nonatomic) BottomTabBarViewController * tabBarController;
|
||||
@property(nonatomic) TrackRecordingViewController * trackRecordingButton;
|
||||
@property(nonatomic) TrackRecordingButtonViewController * trackRecordingButton;
|
||||
|
||||
- (instancetype)init __attribute__((unavailable("init is not available")));
|
||||
- (instancetype)initWithParentController:(MapViewController *)controller;
|
||||
@@ -35,6 +41,8 @@
|
||||
- (void)viewWillTransitionToSize:(CGSize)size
|
||||
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
|
||||
|
||||
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state;
|
||||
|
||||
#pragma mark - MWMNavigationDashboardManager
|
||||
|
||||
- (void)onRoutePrepare;
|
||||
|
||||
@@ -63,17 +63,10 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
|
||||
self.menuState = MWMBottomMenuStateInactive;
|
||||
self.menuRestoreState = MWMBottomMenuStateInactive;
|
||||
self.isAddingPlace = NO;
|
||||
[TrackRecordingManager.shared addObserver:self recordingIsActiveDidChangeHandler:^(TrackRecordingState state, TrackInfo * trackInfo) {
|
||||
[self setTrackRecordingButtonHidden:state == TrackRecordingStateInactive];
|
||||
}];
|
||||
self.searchManager = controller.searchManager;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[TrackRecordingManager.shared removeObserver:self];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||
BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden &&
|
||||
self.navigationManager.state != MWMNavigationDashboardStateNavigation;
|
||||
@@ -280,18 +273,16 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
|
||||
self.trafficButton.hidden = self.hidden || _trafficButtonHidden;
|
||||
}
|
||||
|
||||
- (void)setTrackRecordingButtonHidden:(BOOL)trackRecordingButtonHidden {
|
||||
if (trackRecordingButtonHidden && _trackRecordingButton) {
|
||||
[self.trackRecordingButton closeWithCompletion:^{
|
||||
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state {
|
||||
if (!_trackRecordingButton) {
|
||||
_trackRecordingButton = [[TrackRecordingButtonViewController alloc] init];
|
||||
}
|
||||
[self.trackRecordingButton setState:state completion:^{
|
||||
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
|
||||
}];
|
||||
if (state == TrackRecordingButtonStateClosed)
|
||||
_trackRecordingButton = nil;
|
||||
}
|
||||
else if (!trackRecordingButtonHidden && !_trackRecordingButton) {
|
||||
_trackRecordingButton = [[TrackRecordingViewController alloc] init];
|
||||
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMenuState:(MWMBottomMenuState)menuState {
|
||||
_menuState = menuState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
final class TrackRecordingViewController: MWMViewController {
|
||||
final class TrackRecordingButtonViewController: MWMViewController {
|
||||
|
||||
private enum Constants {
|
||||
static let buttonDiameter = CGFloat(48)
|
||||
@@ -13,6 +13,7 @@ final class TrackRecordingViewController: MWMViewController {
|
||||
private var blinkingTimer: Timer?
|
||||
private var topConstraint = NSLayoutConstraint()
|
||||
private var trailingConstraint = NSLayoutConstraint()
|
||||
private var state: TrackRecordingButtonState = .hidden
|
||||
|
||||
private static var availableArea: CGRect = .zero
|
||||
private static var topConstraintValue: CGFloat {
|
||||
@@ -38,31 +39,29 @@ final class TrackRecordingViewController: MWMViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
UIView.transition(with: self.view,
|
||||
duration: kDefaultAnimationDuration,
|
||||
options: .transitionCrossDissolve,
|
||||
animations: {
|
||||
self.button.isHidden = false
|
||||
})
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// async is for smoother appearance
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + kDefaultAnimationDuration) {
|
||||
self.setState(self.state, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
@objc
|
||||
func close(completion: @escaping (() -> Void)) {
|
||||
stopTimer()
|
||||
UIView.transition(with: self.view,
|
||||
duration: kDefaultAnimationDuration,
|
||||
options: .transitionCrossDissolve,
|
||||
animations: {
|
||||
self.button.isHidden = true
|
||||
}, completion: { _ in
|
||||
self.removeFromParent()
|
||||
self.view.removeFromSuperview()
|
||||
completion()
|
||||
})
|
||||
func setState(_ state: TrackRecordingButtonState, completion: (() -> Void)?) {
|
||||
self.state = state
|
||||
switch state {
|
||||
case .visible:
|
||||
setHidden(false, completion: nil)
|
||||
case .hidden:
|
||||
setHidden(true, completion: completion)
|
||||
case .closed:
|
||||
close(completion: completion)
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
@@ -75,7 +74,7 @@ final class TrackRecordingViewController: MWMViewController {
|
||||
button.tintColor = Constants.color.darker
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setImage(UIImage(resource: .icMenuBookmarkTrackRecording), for: .normal)
|
||||
button.addTarget(self, action: #selector(onTrackRecordingButtonPressed), for: .touchUpInside)
|
||||
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
|
||||
button.isHidden = true
|
||||
}
|
||||
|
||||
@@ -97,7 +96,7 @@ final class TrackRecordingViewController: MWMViewController {
|
||||
}
|
||||
|
||||
private func updateLayout() {
|
||||
guard let superview = self.view.superview else { return }
|
||||
guard let superview = view.superview else { return }
|
||||
superview.animateConstraints {
|
||||
self.topConstraint.constant = Self.topConstraintValue
|
||||
self.trailingConstraint.constant = Self.trailingConstraintValue
|
||||
@@ -123,23 +122,39 @@ final class TrackRecordingViewController: MWMViewController {
|
||||
blinkingTimer = nil
|
||||
}
|
||||
|
||||
private func setHidden(_ hidden: Bool, completion: (() -> Void)?) {
|
||||
UIView.transition(with: self.view,
|
||||
duration: kDefaultAnimationDuration,
|
||||
options: .transitionCrossDissolve,
|
||||
animations: {
|
||||
self.button.isHidden = hidden
|
||||
}) { _ in
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
private func close(completion: (() -> Void)?) {
|
||||
stopTimer()
|
||||
setHidden(true) { [weak self] in
|
||||
guard let self else { return }
|
||||
self.removeFromParent()
|
||||
self.view.removeFromSuperview()
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
static func updateAvailableArea(_ frame: CGRect) {
|
||||
availableArea = frame
|
||||
guard let controller = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
|
||||
guard let button = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
|
||||
DispatchQueue.main.async {
|
||||
controller.updateLayout()
|
||||
button.updateLayout()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc
|
||||
private func onTrackRecordingButtonPressed(_ sender: Any) {
|
||||
switch trackRecordingManager.recordingState {
|
||||
case .inactive:
|
||||
trackRecordingManager.processAction(.start)
|
||||
case .active:
|
||||
trackRecordingManager.processAction(.stop)
|
||||
}
|
||||
private func didTap(_ sender: Any) {
|
||||
MapViewController.shared()?.showTrackRecordingPlacePage()
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
- (void)openBookmarkEditor;
|
||||
- (void)openFullPlaceDescriptionWithHtml:(NSString *_Nonnull)htmlString;
|
||||
- (void)openDrivingOptions;
|
||||
- (void)showTrackRecordingPlacePage;
|
||||
|
||||
- (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration;
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
|
||||
@property(nonatomic, readwrite) MWMMapViewControlsManager *controlsManager;
|
||||
@property(nonatomic, readwrite) SearchOnMapManager *searchManager;
|
||||
@property(nonatomic, readwrite) TrackRecordingManager *trackRecordingManager;
|
||||
|
||||
@property(nonatomic) BOOL disableStandbyOnLocationStateMode;
|
||||
|
||||
@@ -116,7 +117,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
return [MapsAppDelegate theApp].mapViewController;
|
||||
}
|
||||
|
||||
#pragma mark - Map Navigation
|
||||
#pragma mark - PlacePage
|
||||
|
||||
- (void)showOrUpdatePlacePage:(PlacePageData *)data {
|
||||
if (self.searchManager.isSearching)
|
||||
@@ -124,9 +125,10 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
|
||||
self.controlsManager.trafficButtonHidden = YES;
|
||||
if (self.placePageVC != nil) {
|
||||
[PlacePageBuilder update:(PlacePageViewController *)self.placePageVC with:data];
|
||||
[PlacePageBuilder update:self.placePageVC with:data];
|
||||
return;
|
||||
}
|
||||
|
||||
[self showPlacePageFor:data];
|
||||
}
|
||||
|
||||
@@ -204,6 +206,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
}
|
||||
|
||||
- (void)hideRegularPlacePage {
|
||||
[self stopObservingTrackRecordingUpdates];
|
||||
[self.placePageVC closeAnimatedWithCompletion:^{
|
||||
[self.placePageVC.view removeFromSuperview];
|
||||
[self.placePageVC willMoveToParentViewController:nil];
|
||||
@@ -247,6 +250,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
return;
|
||||
}
|
||||
PlacePageData * data = [[PlacePageData alloc] initWithLocalizationProvider:[[OpeinigHoursLocalization alloc] init]];
|
||||
[self stopObservingTrackRecordingUpdates];
|
||||
[self showOrUpdatePlacePage:data];
|
||||
}
|
||||
|
||||
@@ -425,6 +429,9 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
// After all users migrate to OAuth2 we can remove next code
|
||||
[self migrateOAuthCredentials];
|
||||
|
||||
if (self.trackRecordingManager.isActive)
|
||||
[self showTrackRecordingPlacePage];
|
||||
|
||||
/// @todo: Uncomment update dialog when will be ready to handle big traffic bursts.
|
||||
/*
|
||||
if (!DeepLinkHandler.shared.isLaunchedByDeeplink)
|
||||
@@ -742,6 +749,12 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
return _searchManager;
|
||||
}
|
||||
|
||||
- (TrackRecordingManager *)trackRecordingManager {
|
||||
if (!_trackRecordingManager)
|
||||
_trackRecordingManager = TrackRecordingManager.shared;
|
||||
return _trackRecordingManager;
|
||||
}
|
||||
|
||||
- (UIView * _Nullable)searchViewAvailableArea {
|
||||
return self.searchManager.viewController.availableAreaView;
|
||||
}
|
||||
@@ -858,6 +871,50 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Track Recording Place Page
|
||||
|
||||
- (void)showTrackRecordingPlacePage {
|
||||
if ([self.trackRecordingManager contains:self]) {
|
||||
[self dismissPlacePage];
|
||||
return;
|
||||
}
|
||||
PlacePageData * placePageData = [[PlacePageData alloc] initWithTrackInfo:self.trackRecordingManager.trackRecordingInfo
|
||||
elevationInfo:self.trackRecordingManager.trackRecordingElevationProfileData];
|
||||
[self.controlsManager setTrackRecordingButtonState:TrackRecordingButtonStateHidden];
|
||||
[self showOrUpdatePlacePage:placePageData];
|
||||
[self startObservingTrackRecordingUpdatesForPlacePageData:placePageData];
|
||||
}
|
||||
|
||||
- (void)startObservingTrackRecordingUpdatesForPlacePageData:(PlacePageData *)placePageData {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
[self.trackRecordingManager addObserver:self
|
||||
recordingIsActiveDidChangeHandler:^(TrackRecordingState state,
|
||||
TrackInfo * _Nonnull trackInfo,
|
||||
ElevationProfileData * _Nonnull (^ _Nullable elevationData) ()) {
|
||||
__strong __typeof(weakSelf) self = weakSelf;
|
||||
|
||||
switch (state) {
|
||||
case TrackRecordingStateInactive:
|
||||
[self stopObservingTrackRecordingUpdates];
|
||||
[self.controlsManager setTrackRecordingButtonState:TrackRecordingButtonStateClosed];
|
||||
break;
|
||||
case TrackRecordingStateActive:
|
||||
if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive)
|
||||
return;
|
||||
[self.controlsManager setTrackRecordingButtonState:TrackRecordingButtonStateHidden];
|
||||
[placePageData updateWithTrackInfo:trackInfo
|
||||
elevationInfo:elevationData()];
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stopObservingTrackRecordingUpdates {
|
||||
[self.trackRecordingManager removeObserver:self];
|
||||
if (self.trackRecordingManager.isActive)
|
||||
[self.controlsManager setTrackRecordingButtonState:TrackRecordingButtonStateVisible];
|
||||
}
|
||||
|
||||
// MARK: - Handle macOS trackpad gestures
|
||||
|
||||
- (void)handlePan:(UIPanGestureRecognizer *)recognizer API_AVAILABLE(ios(14.0)) {
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol LocationService
|
||||
|
||||
+ (BOOL)isLocationProhibited;
|
||||
+ (void)checkLocationStatus;
|
||||
|
||||
@end
|
||||
|
||||
NS_SWIFT_NAME(LocationManager)
|
||||
@interface MWMLocationManager : NSObject
|
||||
@interface MWMLocationManager : NSObject<LocationService>
|
||||
|
||||
+ (void)start;
|
||||
+ (void)stop;
|
||||
@@ -14,10 +21,8 @@ NS_SWIFT_NAME(LocationManager)
|
||||
+ (void)removeObserver:(id<MWMLocationObserver>)observer NS_SWIFT_NAME(remove(observer:));
|
||||
|
||||
+ (void)setMyPositionMode:(MWMMyPositionMode)mode;
|
||||
+ (void)checkLocationStatus;
|
||||
|
||||
+ (nullable CLLocation *)lastLocation;
|
||||
+ (BOOL)isLocationProhibited;
|
||||
+ (nullable CLHeading *)lastHeading;
|
||||
|
||||
+ (void)applicationDidBecomeActive;
|
||||
|
||||
@@ -198,6 +198,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
||||
case .trackRecordingWidgetButton:
|
||||
return .addFrom(Self.bottomTabBarButton) { s in
|
||||
s.cornerRadius = .custom(23)
|
||||
s.coloring = .red
|
||||
}
|
||||
case .blackOpaqueBackground:
|
||||
return .add { s in
|
||||
|
||||
@@ -4,31 +4,47 @@ enum TrackRecordingState: Int, Equatable {
|
||||
case active
|
||||
}
|
||||
|
||||
enum TrackRecordingAction: String, CaseIterable {
|
||||
enum TrackRecordingAction {
|
||||
case start
|
||||
case stop
|
||||
case stopAndSave(name: String)
|
||||
}
|
||||
|
||||
enum TrackRecordingError: Error {
|
||||
case locationIsProhibited
|
||||
case trackIsEmpty
|
||||
case systemError(Error)
|
||||
}
|
||||
|
||||
protocol TrackRecordingObserver: AnyObject {
|
||||
enum TrackRecordingActionResult {
|
||||
case success
|
||||
case error(TrackRecordingError)
|
||||
}
|
||||
|
||||
@objc
|
||||
protocol TrackRecordingObservable: AnyObject {
|
||||
var recordingState: TrackRecordingState { get }
|
||||
var trackRecordingInfo: TrackInfo { get }
|
||||
var trackRecordingElevationProfileData: ElevationProfileData { get }
|
||||
|
||||
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler)
|
||||
func removeObserver(_ observer: AnyObject)
|
||||
func contains(_ observer: AnyObject) -> Bool
|
||||
}
|
||||
|
||||
typealias TrackRecordingStateHandler = (TrackRecordingState, TrackInfo?) -> Void
|
||||
/// A handler type for extracting elevation profile data on demand.
|
||||
typealias ElevationProfileDataExtractionHandler = () -> ElevationProfileData
|
||||
|
||||
/// A callback type that notifies observers about track recording state changes.
|
||||
/// - Parameters:
|
||||
/// - state: The current recording state.
|
||||
/// - info: The current track recording info.
|
||||
/// - elevationProfileExtractor: A closure to fetch elevation profile data lazily.
|
||||
typealias TrackRecordingStateHandler = (TrackRecordingState, TrackInfo, ElevationProfileDataExtractionHandler?) -> Void
|
||||
|
||||
@objcMembers
|
||||
final class TrackRecordingManager: NSObject {
|
||||
|
||||
typealias CompletionHandler = () -> Void
|
||||
|
||||
private enum SavingOption {
|
||||
case withoutSaving
|
||||
case saveWithName(String? = nil)
|
||||
}
|
||||
typealias CompletionHandler = (TrackRecordingActionResult) -> Void
|
||||
|
||||
fileprivate struct Observation {
|
||||
weak var observer: AnyObject?
|
||||
@@ -37,28 +53,40 @@ final class TrackRecordingManager: NSObject {
|
||||
|
||||
static let shared: TrackRecordingManager = {
|
||||
let trackRecorder = FrameworkHelper.self
|
||||
let locationManager = LocationManager.self
|
||||
var activityManager: TrackRecordingActivityManager? = nil
|
||||
#if canImport(ActivityKit)
|
||||
if #available(iOS 16.2, *) {
|
||||
activityManager = TrackRecordingLiveActivityManager.shared
|
||||
}
|
||||
#endif
|
||||
return TrackRecordingManager(trackRecorder: trackRecorder, activityManager: activityManager)
|
||||
return TrackRecordingManager(trackRecorder: trackRecorder,
|
||||
locationService: locationManager,
|
||||
activityManager: activityManager)
|
||||
}()
|
||||
|
||||
private let trackRecorder: TrackRecorder.Type
|
||||
private var locationService: LocationService.Type
|
||||
private var activityManager: TrackRecordingActivityManager?
|
||||
private var observations: [Observation] = []
|
||||
private var trackRecordingInfo: TrackInfo?
|
||||
private(set) var trackRecordingInfo: TrackInfo = .empty()
|
||||
|
||||
var trackRecordingElevationProfileData: ElevationProfileData {
|
||||
FrameworkHelper.trackRecordingElevationInfo()
|
||||
}
|
||||
|
||||
var recordingState: TrackRecordingState {
|
||||
trackRecorder.isTrackRecordingEnabled() ? .active : .inactive
|
||||
}
|
||||
|
||||
private init(trackRecorder: TrackRecorder.Type, activityManager: TrackRecordingActivityManager?) {
|
||||
init(trackRecorder: TrackRecorder.Type,
|
||||
locationService: LocationService.Type,
|
||||
activityManager: TrackRecordingActivityManager?) {
|
||||
self.trackRecorder = trackRecorder
|
||||
self.locationService = locationService
|
||||
self.activityManager = activityManager
|
||||
super.init()
|
||||
self.subscribeOnTheAppLifecycleEvents()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
@@ -84,22 +112,42 @@ final class TrackRecordingManager: NSObject {
|
||||
}
|
||||
|
||||
func processAction(_ action: TrackRecordingAction, completion: (CompletionHandler)? = nil) {
|
||||
do {
|
||||
switch action {
|
||||
case .start:
|
||||
start(completion: completion)
|
||||
case .stop:
|
||||
stop(completion: completion)
|
||||
try startRecording()
|
||||
case .stopAndSave(let name):
|
||||
stopRecording()
|
||||
try checkIsTrackNotEmpty()
|
||||
saveTrackRecording(name: name)
|
||||
}
|
||||
completion?(.success)
|
||||
} catch {
|
||||
handleError(error, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func checkIsLocationEnabled() throws {
|
||||
if LocationManager.isLocationProhibited() {
|
||||
private func subscribeOnTheAppLifecycleEvents() {
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(notifyObservers),
|
||||
name: UIApplication.didBecomeActiveNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
private func checkIsLocationEnabled() throws(TrackRecordingError) {
|
||||
if locationService.isLocationProhibited() {
|
||||
throw TrackRecordingError.locationIsProhibited
|
||||
}
|
||||
}
|
||||
|
||||
private func checkIsTrackNotEmpty() throws(TrackRecordingError) {
|
||||
if trackRecorder.isTrackRecordingEmpty() {
|
||||
throw TrackRecordingError.trackIsEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Handle track recording process
|
||||
|
||||
private func subscribeOnTrackRecordingProgressUpdates() {
|
||||
@@ -113,95 +161,68 @@ final class TrackRecordingManager: NSObject {
|
||||
|
||||
private func unsubscribeFromTrackRecordingProgressUpdates() {
|
||||
trackRecorder.setTrackRecordingUpdateHandler(nil)
|
||||
trackRecordingInfo = nil
|
||||
}
|
||||
|
||||
// MARK: - Handle Start/Stop event and Errors
|
||||
|
||||
private func start(completion: (CompletionHandler)? = nil) {
|
||||
do {
|
||||
try checkIsLocationEnabled()
|
||||
private func startRecording() throws(TrackRecordingError) {
|
||||
switch recordingState {
|
||||
case .inactive:
|
||||
try checkIsLocationEnabled()
|
||||
subscribeOnTrackRecordingProgressUpdates()
|
||||
trackRecorder.startTrackRecording()
|
||||
notifyObservers()
|
||||
try? activityManager?.start(with: trackRecordingInfo ?? .empty())
|
||||
do {
|
||||
try activityManager?.start(with: trackRecordingInfo)
|
||||
} catch {
|
||||
LOG(.warning, "Failed to start activity manager")
|
||||
handleError(.systemError(error))
|
||||
}
|
||||
case .active:
|
||||
break
|
||||
}
|
||||
completion?()
|
||||
} catch {
|
||||
handleError(error, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func stop(completion: (CompletionHandler)? = nil) {
|
||||
guard !trackRecorder.isTrackRecordingEmpty() else {
|
||||
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
||||
stopRecording(.withoutSaving, completion: completion)
|
||||
return
|
||||
}
|
||||
Self.showOnFinishRecordingAlert(onSave: { [weak self] in
|
||||
guard let self else { return }
|
||||
// TODO: (KK) pass the user provided name from the track saving screen (when it will be implemented)
|
||||
self.stopRecording(.saveWithName(), completion: completion)
|
||||
},
|
||||
onStop: { [weak self] in
|
||||
guard let self else { return }
|
||||
self.stopRecording(.withoutSaving, completion: completion)
|
||||
},
|
||||
onContinue: {
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
private func stopRecording(_ savingOption: SavingOption, completion: (CompletionHandler)? = nil) {
|
||||
private func stopRecording() {
|
||||
unsubscribeFromTrackRecordingProgressUpdates()
|
||||
trackRecorder.stopTrackRecording()
|
||||
trackRecordingInfo = .empty()
|
||||
activityManager?.stop()
|
||||
notifyObservers()
|
||||
}
|
||||
|
||||
switch savingOption {
|
||||
case .withoutSaving:
|
||||
break
|
||||
case .saveWithName(let name):
|
||||
private func saveTrackRecording(name: String) {
|
||||
trackRecorder.saveTrackRecording(withName: name)
|
||||
}
|
||||
completion?()
|
||||
}
|
||||
|
||||
private func handleError(_ error: Error, completion: (CompletionHandler)? = nil) {
|
||||
LOG(.error, error.localizedDescription)
|
||||
private func handleError(_ error: TrackRecordingError, completion: (CompletionHandler)? = nil) {
|
||||
switch error {
|
||||
case TrackRecordingError.locationIsProhibited:
|
||||
// Show alert to enable location
|
||||
LocationManager.checkLocationStatus()
|
||||
default:
|
||||
locationService.checkLocationStatus()
|
||||
case TrackRecordingError.trackIsEmpty:
|
||||
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
||||
case TrackRecordingError.systemError(let error):
|
||||
LOG(.error, error.localizedDescription)
|
||||
break
|
||||
}
|
||||
stopRecording(.withoutSaving, completion: completion)
|
||||
DispatchQueue.main.async {
|
||||
completion?(.error(error))
|
||||
}
|
||||
|
||||
private static func showOnFinishRecordingAlert(onSave: @escaping CompletionHandler,
|
||||
onStop: @escaping CompletionHandler,
|
||||
onContinue: @escaping CompletionHandler) {
|
||||
let alert = UIAlertController(title: L("track_recording_alert_title"), message: nil, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: L("continue_recording"), style: .default, handler: { _ in onContinue() }))
|
||||
alert.addAction(UIAlertAction(title: L("stop_without_saving"), style: .default, handler: { _ in onStop() }))
|
||||
alert.addAction(UIAlertAction(title: L("save"), style: .cancel, handler: { _ in onSave() }))
|
||||
UIViewController.topViewController().present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TrackRecordingObserver
|
||||
|
||||
extension TrackRecordingManager: TrackRecordingObserver {
|
||||
extension TrackRecordingManager: TrackRecordingObservable {
|
||||
@objc
|
||||
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler) {
|
||||
guard !observations.contains(where: { $0.observer === observer }) else { return }
|
||||
let observation = Observation(observer: observer, recordingStateDidChangeHandler: recordingIsActiveDidChangeHandler)
|
||||
observations.append(observation)
|
||||
recordingIsActiveDidChangeHandler(recordingState, trackRecordingInfo)
|
||||
recordingIsActiveDidChangeHandler(recordingState, trackRecordingInfo) {
|
||||
self.trackRecordingElevationProfileData
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
@@ -209,8 +230,16 @@ extension TrackRecordingManager: TrackRecordingObserver {
|
||||
observations.removeAll { $0.observer === observer }
|
||||
}
|
||||
|
||||
@objc
|
||||
func contains(_ observer: AnyObject) -> Bool {
|
||||
observations.contains { $0.observer === observer }
|
||||
}
|
||||
|
||||
@objc
|
||||
private func notifyObservers() {
|
||||
observations = observations.filter { $0.observer != nil }
|
||||
observations.forEach { $0.recordingStateDidChangeHandler?(recordingState, trackRecordingInfo) }
|
||||
observations.removeAll { $0.observer == nil }
|
||||
observations.forEach {
|
||||
$0.recordingStateDidChangeHandler?(recordingState, trackRecordingInfo, { self.trackRecordingElevationProfileData })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
iphone/Maps/Images.xcassets/Place Page/ic_placepage_save_track_recording.imageset/Contents.json
vendored
Normal file
15
iphone/Maps/Images.xcassets/Place Page/ic_placepage_save_track_recording.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_track_save.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
iphone/Maps/Images.xcassets/Place Page/ic_placepage_save_track_recording.imageset/ic_track_save.png
vendored
Normal file
BIN
iphone/Maps/Images.xcassets/Place Page/ic_placepage_save_track_recording.imageset/ic_track_save.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -470,7 +470,7 @@
|
||||
ED2D74652D14357F00660FBF /* TrackRecordingLiveActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2D74302D14337500660FBF /* TrackRecordingLiveActivityAttributes.swift */; };
|
||||
ED2D74662D1435A600660FBF /* LiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2D742D2D14337500660FBF /* LiveActivityManager.swift */; };
|
||||
ED2E328E2D10500900807A08 /* TrackRecordingButtonArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED46DD922D06F804007CACD6 /* TrackRecordingButtonArea.swift */; };
|
||||
ED2E32912D10501700807A08 /* TrackRecordingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED49D76F2CF0E3A8004AF27E /* TrackRecordingViewController.swift */; };
|
||||
ED2E32912D10501700807A08 /* TrackRecordingButtonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED49D76F2CF0E3A8004AF27E /* TrackRecordingButtonViewController.swift */; };
|
||||
ED3EAC202B03C88100220A4A /* BottomTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3EAC1F2B03C88100220A4A /* BottomTabBarButton.swift */; };
|
||||
ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED43B8BC2C12063500D07BAA /* DocumentPicker.swift */; };
|
||||
ED46DDCE2D098A0B007CACD6 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED46DDCD2D098A0B007CACD6 /* WidgetKit.framework */; };
|
||||
@@ -510,6 +510,7 @@
|
||||
ED914ABE2D351FF800973C45 /* UILabel+SetFontStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED914ABD2D351FF800973C45 /* UILabel+SetFontStyle.swift */; };
|
||||
ED9857082C4ED02D00694F6C /* MailComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9857072C4ED02D00694F6C /* MailComposer.swift */; };
|
||||
ED9966802B94FBC20083CE55 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED99667D2B94FBC20083CE55 /* ColorPicker.swift */; };
|
||||
ED9DDF882D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9DDF872D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift */; };
|
||||
EDA1EAA42CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA1EAA32CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift */; };
|
||||
EDB71D8C2D8474A0004A6A7F /* CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */; };
|
||||
EDB71E002D8B0338004A6A7F /* ModalPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.swift */; };
|
||||
@@ -541,6 +542,7 @@
|
||||
EDFDFB4C2B722C9C0013A44C /* InfoTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB4B2B722C9C0013A44C /* InfoTableViewCell.swift */; };
|
||||
EDFDFB522B726F1A0013A44C /* ButtonsStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB512B726F1A0013A44C /* ButtonsStackView.swift */; };
|
||||
EDFDFB612B74E2500013A44C /* DonationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB602B74E2500013A44C /* DonationView.swift */; };
|
||||
EDFE1A4A2DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFE1A492DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift */; };
|
||||
F607C1881C032A8800B53A87 /* resources-hdpi_light in Resources */ = {isa = PBXBuildFile; fileRef = F607C1831C032A8800B53A87 /* resources-hdpi_light */; };
|
||||
F607C18A1C032A8800B53A87 /* resources-hdpi_dark in Resources */ = {isa = PBXBuildFile; fileRef = F607C1841C032A8800B53A87 /* resources-hdpi_dark */; };
|
||||
F623DA6C1C9C2731006A3436 /* opening_hours_how_to_edit.html in Resources */ = {isa = PBXBuildFile; fileRef = F623DA6A1C9C2731006A3436 /* opening_hours_how_to_edit.html */; };
|
||||
@@ -1444,7 +1446,7 @@
|
||||
ED46DDCF2D098A0B007CACD6 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
ED48BBB817C2B1E2003E7E92 /* CircleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleView.h; sourceTree = "<group>"; };
|
||||
ED48BBB917C2B1E2003E7E92 /* CircleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CircleView.m; sourceTree = "<group>"; };
|
||||
ED49D76F2CF0E3A8004AF27E /* TrackRecordingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackRecordingViewController.swift; sourceTree = "<group>"; };
|
||||
ED49D76F2CF0E3A8004AF27E /* TrackRecordingButtonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackRecordingButtonViewController.swift; sourceTree = "<group>"; };
|
||||
ED4DC7732CAEDECC0029B338 /* ProductButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductButton.swift; sourceTree = "<group>"; };
|
||||
ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewController.swift; sourceTree = "<group>"; };
|
||||
ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -1525,6 +1527,7 @@
|
||||
ED914ABD2D351FF800973C45 /* UILabel+SetFontStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+SetFontStyle.swift"; sourceTree = "<group>"; };
|
||||
ED9857072C4ED02D00694F6C /* MailComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposer.swift; sourceTree = "<group>"; };
|
||||
ED99667D2B94FBC20083CE55 /* ColorPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorPicker.swift; sourceTree = "<group>"; };
|
||||
ED9DDF872D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageTrackRecordingLayout.swift; sourceTree = "<group>"; };
|
||||
EDA1EAA32CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElevationProfileFormatter.swift; sourceTree = "<group>"; };
|
||||
EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadius.swift; sourceTree = "<group>"; };
|
||||
EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationAnimator.swift; sourceTree = "<group>"; };
|
||||
@@ -1556,6 +1559,7 @@
|
||||
EDFDFB4B2B722C9C0013A44C /* InfoTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoTableViewCell.swift; sourceTree = "<group>"; };
|
||||
EDFDFB512B726F1A0013A44C /* ButtonsStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsStackView.swift; sourceTree = "<group>"; };
|
||||
EDFDFB602B74E2500013A44C /* DonationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationView.swift; sourceTree = "<group>"; };
|
||||
EDFE1A492DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+UnknownCurrentPosition.swift"; sourceTree = "<group>"; };
|
||||
EE026F0511D6AC0D00645242 /* classificator.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = classificator.txt; path = ../../data/classificator.txt; sourceTree = SOURCE_ROOT; };
|
||||
EED10A4411F78D120095FAD4 /* MapViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = MapViewController.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
F607C1831C032A8800B53A87 /* resources-hdpi_light */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-hdpi_light"; path = "../../data/resources-hdpi_light"; sourceTree = "<group>"; };
|
||||
@@ -2596,7 +2600,7 @@
|
||||
34BC72091B0DECAE0012A34B /* MapViewControls */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED49D76F2CF0E3A8004AF27E /* TrackRecordingViewController.swift */,
|
||||
ED49D76F2CF0E3A8004AF27E /* TrackRecordingButtonViewController.swift */,
|
||||
340537621BBED98600D452C6 /* MWMMapViewControlsCommon.h */,
|
||||
34BC72101B0DECAE0012A34B /* MWMMapViewControlsManager.h */,
|
||||
34BC72111B0DECAE0012A34B /* MWMMapViewControlsManager.mm */,
|
||||
@@ -3060,6 +3064,7 @@
|
||||
99C6532123F2F506004322F3 /* IPlacePageLayout.swift */,
|
||||
99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */,
|
||||
993DF0B423F6B2EF00AC231A /* PlacePageTrackLayout.swift */,
|
||||
ED9DDF872D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift */,
|
||||
);
|
||||
path = Layouts;
|
||||
sourceTree = "<group>";
|
||||
@@ -3446,6 +3451,14 @@
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDFE1A462DF1986900FDEA38 /* UnknownCurrentPositionAlert */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDFE1A492DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift */,
|
||||
);
|
||||
path = UnknownCurrentPositionAlert;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F607C18B1C047FCA00B53A87 /* Segue */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -3496,6 +3509,7 @@
|
||||
F64F195F1AB8125C006EAF7E /* CustomAlert */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDFE1A462DF1986900FDEA38 /* UnknownCurrentPositionAlert */,
|
||||
447DB4B72BA7826D000DF4C2 /* ReauthAlert */,
|
||||
F62607FB207B78E300176C5A /* SpinnerAlert */,
|
||||
F6D67CDA2062B9810032FD38 /* CreateBookmarkCategory */,
|
||||
@@ -4489,8 +4503,9 @@
|
||||
99F3EB1223F418C900C713F8 /* PlacePageInteractor.swift in Sources */,
|
||||
340708651F2905A500029ECC /* NavigationInfoArea.swift in Sources */,
|
||||
993DF0CC23F6BD0600AC231A /* ElevationDetailsPresenter.swift in Sources */,
|
||||
EDFE1A4A2DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift in Sources */,
|
||||
34AB666B1FC5AA330078E451 /* TransportTransitCell.swift in Sources */,
|
||||
ED2E32912D10501700807A08 /* TrackRecordingViewController.swift in Sources */,
|
||||
ED2E32912D10501700807A08 /* TrackRecordingButtonViewController.swift in Sources */,
|
||||
47E8163323B17734008FD836 /* MWMStorage+UI.m in Sources */,
|
||||
993DF11123F6BDB100AC231A /* UILabelRenderer.swift in Sources */,
|
||||
34AB66471FC5AA330078E451 /* RouteManagerTableView.swift in Sources */,
|
||||
@@ -4633,6 +4648,7 @@
|
||||
1DFA2F6A20D3B57400FB2C66 /* UIColor+PartnerColor.m in Sources */,
|
||||
9989273B2449E60200260CE2 /* BottomMenuBuilder.swift in Sources */,
|
||||
993DF10F23F6BDB100AC231A /* UIActivityIndicatorRenderer.swift in Sources */,
|
||||
ED9DDF882D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift in Sources */,
|
||||
ED0B1FEF2CAA9A25006E31A4 /* UIView+Highlight.swift in Sources */,
|
||||
99A614E423CDD1D900D8D8D0 /* UIButton+RuntimeAttributes.m in Sources */,
|
||||
343E75981E5B1EE20041226A /* MWMCollectionViewController.m in Sources */,
|
||||
|
||||
@@ -10,7 +10,7 @@ final class TrackRecordingButtonArea: AvailableArea {
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
TrackRecordingViewController.updateAvailableArea(areaFrame)
|
||||
TrackRecordingButtonViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,10 @@ final class ActionBarViewController: UIViewController {
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
var buttons: [ActionBarButtonType] = []
|
||||
switch placePageData.objectType {
|
||||
case .POI, .bookmark, .track:
|
||||
if isRoutePlanning {
|
||||
buttons.append(.routeFrom)
|
||||
}
|
||||
@@ -73,8 +76,13 @@ final class ActionBarViewController: UIViewController {
|
||||
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] = []
|
||||
switch placePageData.objectType {
|
||||
case .POI, .bookmark:
|
||||
if canAddStop {
|
||||
buttons.append(.routeAddStop)
|
||||
}
|
||||
switch placePageData.objectType {
|
||||
case .POI, .bookmark:
|
||||
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() {
|
||||
switch placePageData.objectType {
|
||||
case .POI, .bookmark, .track:
|
||||
visibleButtons.append(.routeTo)
|
||||
case .trackRecording:
|
||||
break
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
private func configButton4() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ typedef NS_ENUM(NSInteger, MWMActionBarButtonType) {
|
||||
MWMActionBarButtonTypeBookingSearch,
|
||||
MWMActionBarButtonTypeBookmark,
|
||||
MWMActionBarButtonTypeTrack,
|
||||
MWMActionBarButtonTypeSaveTrackRecording,
|
||||
MWMActionBarButtonTypeCall,
|
||||
MWMActionBarButtonTypeDownload,
|
||||
MWMActionBarButtonTypeMore,
|
||||
|
||||
@@ -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,6 +57,7 @@ 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: {
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user