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
|
@implementation TrackInfo
|
||||||
|
|
||||||
+ (TrackInfo *)emptyInfo {
|
+ (TrackInfo *)emptyInfo {
|
||||||
return [[TrackInfo alloc] init];
|
return [[TrackInfo alloc] initWithTrackStatistics:TrackStatistics()];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
typedef void (^SearchInDownloaderCompletions)(NSArray<MWMMapSearchResult *> *results, BOOL finished);
|
typedef void (^SearchInDownloaderCompletions)(NSArray<MWMMapSearchResult *> *results, BOOL finished);
|
||||||
typedef void (^TrackRecordingUpdatedHandler)(TrackInfo * _Nonnull trackInfo);
|
typedef void (^TrackRecordingUpdatedHandler)(TrackInfo * _Nonnull trackInfo);
|
||||||
|
|
||||||
@protocol TrackRecorder <NSObject>
|
@protocol TrackRecorder
|
||||||
|
|
||||||
+ (void)startTrackRecording;
|
+ (void)startTrackRecording;
|
||||||
+ (void)setTrackRecordingUpdateHandler:(TrackRecordingUpdatedHandler _Nullable)trackRecordingDidUpdate;
|
+ (void)setTrackRecordingUpdateHandler:(TrackRecordingUpdatedHandler _Nullable)trackRecordingDidUpdate;
|
||||||
+ (void)stopTrackRecording;
|
+ (void)stopTrackRecording;
|
||||||
+ (void)saveTrackRecordingWithName:(nullable NSString *)name;
|
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name;
|
||||||
+ (BOOL)isTrackRecordingEnabled;
|
+ (BOOL)isTrackRecordingEnabled;
|
||||||
+ (BOOL)isTrackRecordingEmpty;
|
+ (BOOL)isTrackRecordingEmpty;
|
||||||
/// Returns current track recording elevation info.
|
/// Returns current track recording elevation info.
|
||||||
|
|||||||
@@ -239,8 +239,8 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(
|
|||||||
GetFramework().StopTrackRecording();
|
GetFramework().StopTrackRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)saveTrackRecordingWithName:(nullable NSString *)name {
|
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name {
|
||||||
GetFramework().SaveTrackRecordingWithName(name == nil ? "" : name.UTF8String);
|
GetFramework().SaveTrackRecordingWithName(name.UTF8String);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)isTrackRecordingEnabled {
|
+ (BOOL)isTrackRecordingEnabled {
|
||||||
@@ -252,7 +252,7 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (ElevationProfileData * _Nonnull)trackRecordingElevationInfo {
|
+ (ElevationProfileData * _Nonnull)trackRecordingElevationInfo {
|
||||||
return [[ElevationProfileData alloc] initWithElevationInfo:GetFramework().GetTrackRecordingCurrentElevationInfo()];
|
return [[ElevationProfileData alloc] initWithElevationInfo:GetFramework().GetTrackRecordingElevationInfo()];
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ProductsManager
|
// MARK: - ProductsManager
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@class PlacePageScheduleData;
|
@class PlacePageScheduleData;
|
||||||
|
@class TrackInfo;
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, PlacePageDataHotelType) {
|
typedef NS_ENUM(NSInteger, PlacePageDataHotelType) {
|
||||||
PlacePageDataHotelTypeHotel,
|
PlacePageDataHotelTypeHotel,
|
||||||
@@ -39,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property(nonatomic, readonly) PlacePageDataSchedule schedule;
|
@property(nonatomic, readonly) PlacePageDataSchedule schedule;
|
||||||
@property(nonatomic, readonly) BOOL isMyPosition;
|
@property(nonatomic, readonly) BOOL isMyPosition;
|
||||||
|
|
||||||
|
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
#import "PlacePagePreviewData+Core.h"
|
#import "PlacePagePreviewData+Core.h"
|
||||||
|
#import "DistanceFormatter.h"
|
||||||
|
#import "AltitudeFormatter.h"
|
||||||
|
#import "DurationFormatter.h"
|
||||||
|
#import "TrackInfo.h"
|
||||||
|
|
||||||
#include "3party/opening_hours/opening_hours.hpp"
|
#include "3party/opening_hours/opening_hours.hpp"
|
||||||
|
|
||||||
@@ -46,6 +50,16 @@ static PlacePageDataSchedule convertOpeningHours(std::string_view rawOH)
|
|||||||
|
|
||||||
@implementation PlacePagePreviewData
|
@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
|
@end
|
||||||
|
|
||||||
@implementation PlacePagePreviewData (Core)
|
@implementation PlacePagePreviewData (Core)
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
@property(nonatomic, readonly) MWMTrackID trackId;
|
@property(nonatomic, readonly) MWMTrackID trackId;
|
||||||
@property(nonatomic, readonly) MWMMarkGroupID groupId;
|
@property(nonatomic, readonly) MWMMarkGroupID groupId;
|
||||||
@property(nonatomic, readonly, nonnull) TrackInfo * trackInfo;
|
@property(nonatomic, readwrite, nonnull) TrackInfo * trackInfo;
|
||||||
@property(nonatomic, readonly, nullable) ElevationProfileData * elevationProfileData;
|
@property(nonatomic, readwrite, nullable) ElevationProfileData * elevationProfileData;
|
||||||
|
|
||||||
|
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,15 @@
|
|||||||
|
|
||||||
@implementation PlacePageTrackData
|
@implementation PlacePageTrackData
|
||||||
|
|
||||||
|
- (nonnull instancetype)initWithTrackInfo:(TrackInfo *)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_trackInfo = trackInfo;
|
||||||
|
_elevationProfileData = elevationInfo;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation PlacePageTrackData (Core)
|
@implementation PlacePageTrackData (Core)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
elevationInfo:(ElevationInfo const &)elevationInfo
|
elevationInfo:(ElevationInfo const &)elevationInfo
|
||||||
activePoint:(double)activePoint
|
activePoint:(double)activePoint
|
||||||
myPosition:(double)myPosition;
|
myPosition:(double)myPosition;
|
||||||
|
- (instancetype)initWithElevationInfo:(ElevationInfo const &)elevationInfo;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ typedef NS_ENUM(NSInteger, ElevationDifficulty) {
|
|||||||
@interface ElevationProfileData : NSObject
|
@interface ElevationProfileData : NSObject
|
||||||
|
|
||||||
@property(nonatomic, readonly) uint64_t trackId;
|
@property(nonatomic, readonly) uint64_t trackId;
|
||||||
|
@property(nonatomic, readonly) BOOL isTrackRecording;
|
||||||
@property(nonatomic, readonly) ElevationDifficulty difficulty;
|
@property(nonatomic, readonly) ElevationDifficulty difficulty;
|
||||||
@property(nonatomic, readonly) NSArray<ElevationHeightPoint *> * points;
|
@property(nonatomic, readonly) NSArray<ElevationHeightPoint *> * points;
|
||||||
@property(nonatomic, readonly) double activePoint;
|
@property(nonatomic, readonly) double activePoint;
|
||||||
|
|||||||
@@ -30,7 +30,25 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
|
|||||||
if (self) {
|
if (self) {
|
||||||
_trackId = trackId;
|
_trackId = trackId;
|
||||||
_difficulty = convertDifficulty(elevationInfo.GetDifficulty());
|
_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();
|
auto const & points = elevationInfo.GetPoints();
|
||||||
NSMutableArray * pointsArray = [[NSMutableArray alloc] initWithCapacity:points.size()];
|
NSMutableArray * pointsArray = [[NSMutableArray alloc] initWithCapacity:points.size()];
|
||||||
for (auto const & point : points) {
|
for (auto const & point : points) {
|
||||||
@@ -41,12 +59,7 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
|
|||||||
andAltitude:point.m_point.GetAltitude()];
|
andAltitude:point.m_point.GetAltitude()];
|
||||||
[pointsArray addObject:elevationPoint];
|
[pointsArray addObject:elevationPoint];
|
||||||
}
|
}
|
||||||
_points = [pointsArray copy];
|
return [pointsArray copy];
|
||||||
_activePoint = activePoint;
|
|
||||||
_myPosition = myPosition;
|
|
||||||
}
|
}
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@class PlacePageBookmarkData;
|
@class PlacePageBookmarkData;
|
||||||
@class MWMMapNodeAttributes;
|
@class MWMMapNodeAttributes;
|
||||||
@class TrackInfo;
|
@class TrackInfo;
|
||||||
|
@class ElevationProfileData;
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, PlacePageRoadType) {
|
typedef NS_ENUM(NSInteger, PlacePageRoadType) {
|
||||||
PlacePageRoadTypeToll,
|
PlacePageRoadTypeToll,
|
||||||
@@ -49,12 +50,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property(nonatomic, readonly) CLLocationCoordinate2D locationCoordinate;
|
@property(nonatomic, readonly) CLLocationCoordinate2D locationCoordinate;
|
||||||
@property(nonatomic, copy, nullable) MWMVoidBlock onBookmarkStatusUpdate;
|
@property(nonatomic, copy, nullable) MWMVoidBlock onBookmarkStatusUpdate;
|
||||||
@property(nonatomic, copy, nullable) MWMVoidBlock onMapNodeStatusUpdate;
|
@property(nonatomic, copy, nullable) MWMVoidBlock onMapNodeStatusUpdate;
|
||||||
|
@property(nonatomic, copy, nullable) MWMVoidBlock onTrackRecordingProgressUpdate;
|
||||||
@property(nonatomic, copy, nullable) void (^onMapNodeProgressUpdate)(uint64_t downloadedBytes, uint64_t totalBytes);
|
@property(nonatomic, copy, nullable) void (^onMapNodeProgressUpdate)(uint64_t downloadedBytes, uint64_t totalBytes);
|
||||||
|
|
||||||
- (instancetype)initWithLocalizationProvider:(id<IOpeningHoursLocalization>)localization;
|
- (instancetype)initWithLocalizationProvider:(id<IOpeningHoursLocalization>)localization;
|
||||||
|
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
- (void)updateBookmarkStatus;
|
- (void)updateBookmarkStatus;
|
||||||
|
- (void)updateWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#import "PlacePageInfoData+Core.h"
|
#import "PlacePageInfoData+Core.h"
|
||||||
#import "PlacePageBookmarkData+Core.h"
|
#import "PlacePageBookmarkData+Core.h"
|
||||||
#import "PlacePageTrackData+Core.h"
|
#import "PlacePageTrackData+Core.h"
|
||||||
|
#import "ElevationProfileData+Core.h"
|
||||||
#import "MWMMapNodeAttributes.h"
|
#import "MWMMapNodeAttributes.h"
|
||||||
|
|
||||||
#include <CoreApi/CoreApi.h>
|
#include <CoreApi/CoreApi.h>
|
||||||
@@ -84,6 +85,25 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
|
|||||||
return self;
|
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 {
|
- (void)dealloc {
|
||||||
if (self.mapNodeAttributes != nil) {
|
if (self.mapNodeAttributes != nil) {
|
||||||
[[MWMStorage sharedStorage] removeObserver:self];
|
[[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 MapViewController;
|
||||||
@class BottomTabBarViewController;
|
@class BottomTabBarViewController;
|
||||||
@class TrackRecordingViewController;
|
@class TrackRecordingButtonViewController;
|
||||||
@class SearchQuery;
|
@class SearchQuery;
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, TrackRecordingButtonState) {
|
||||||
|
TrackRecordingButtonStateHidden,
|
||||||
|
TrackRecordingButtonStateVisible,
|
||||||
|
TrackRecordingButtonStateClosed,
|
||||||
|
};
|
||||||
|
|
||||||
@protocol MWMFeatureHolder;
|
@protocol MWMFeatureHolder;
|
||||||
|
|
||||||
@interface MWMMapViewControlsManager : NSObject
|
@interface MWMMapViewControlsManager : NSObject
|
||||||
@@ -21,7 +27,7 @@
|
|||||||
@property(nonatomic) MWMBottomMenuState menuRestoreState;
|
@property(nonatomic) MWMBottomMenuState menuRestoreState;
|
||||||
@property(nonatomic) BOOL isDirectionViewHidden;
|
@property(nonatomic) BOOL isDirectionViewHidden;
|
||||||
@property(nonatomic) BottomTabBarViewController * tabBarController;
|
@property(nonatomic) BottomTabBarViewController * tabBarController;
|
||||||
@property(nonatomic) TrackRecordingViewController * trackRecordingButton;
|
@property(nonatomic) TrackRecordingButtonViewController * trackRecordingButton;
|
||||||
|
|
||||||
- (instancetype)init __attribute__((unavailable("init is not available")));
|
- (instancetype)init __attribute__((unavailable("init is not available")));
|
||||||
- (instancetype)initWithParentController:(MapViewController *)controller;
|
- (instancetype)initWithParentController:(MapViewController *)controller;
|
||||||
@@ -35,6 +41,8 @@
|
|||||||
- (void)viewWillTransitionToSize:(CGSize)size
|
- (void)viewWillTransitionToSize:(CGSize)size
|
||||||
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
|
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
|
||||||
|
|
||||||
|
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state;
|
||||||
|
|
||||||
#pragma mark - MWMNavigationDashboardManager
|
#pragma mark - MWMNavigationDashboardManager
|
||||||
|
|
||||||
- (void)onRoutePrepare;
|
- (void)onRoutePrepare;
|
||||||
|
|||||||
@@ -63,17 +63,10 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
|
|||||||
self.menuState = MWMBottomMenuStateInactive;
|
self.menuState = MWMBottomMenuStateInactive;
|
||||||
self.menuRestoreState = MWMBottomMenuStateInactive;
|
self.menuRestoreState = MWMBottomMenuStateInactive;
|
||||||
self.isAddingPlace = NO;
|
self.isAddingPlace = NO;
|
||||||
[TrackRecordingManager.shared addObserver:self recordingIsActiveDidChangeHandler:^(TrackRecordingState state, TrackInfo * trackInfo) {
|
|
||||||
[self setTrackRecordingButtonHidden:state == TrackRecordingStateInactive];
|
|
||||||
}];
|
|
||||||
self.searchManager = controller.searchManager;
|
self.searchManager = controller.searchManager;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
[TrackRecordingManager.shared removeObserver:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||||
BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden &&
|
BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden &&
|
||||||
self.navigationManager.state != MWMNavigationDashboardStateNavigation;
|
self.navigationManager.state != MWMNavigationDashboardStateNavigation;
|
||||||
@@ -280,18 +273,16 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
|
|||||||
self.trafficButton.hidden = self.hidden || _trafficButtonHidden;
|
self.trafficButton.hidden = self.hidden || _trafficButtonHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTrackRecordingButtonHidden:(BOOL)trackRecordingButtonHidden {
|
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state {
|
||||||
if (trackRecordingButtonHidden && _trackRecordingButton) {
|
if (!_trackRecordingButton) {
|
||||||
[self.trackRecordingButton closeWithCompletion:^{
|
_trackRecordingButton = [[TrackRecordingButtonViewController alloc] init];
|
||||||
|
}
|
||||||
|
[self.trackRecordingButton setState:state completion:^{
|
||||||
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
|
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
|
||||||
}];
|
}];
|
||||||
|
if (state == TrackRecordingButtonStateClosed)
|
||||||
_trackRecordingButton = nil;
|
_trackRecordingButton = nil;
|
||||||
}
|
}
|
||||||
else if (!trackRecordingButtonHidden && !_trackRecordingButton) {
|
|
||||||
_trackRecordingButton = [[TrackRecordingViewController alloc] init];
|
|
||||||
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMenuState:(MWMBottomMenuState)menuState {
|
- (void)setMenuState:(MWMBottomMenuState)menuState {
|
||||||
_menuState = menuState;
|
_menuState = menuState;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
final class TrackRecordingViewController: MWMViewController {
|
final class TrackRecordingButtonViewController: MWMViewController {
|
||||||
|
|
||||||
private enum Constants {
|
private enum Constants {
|
||||||
static let buttonDiameter = CGFloat(48)
|
static let buttonDiameter = CGFloat(48)
|
||||||
@@ -13,6 +13,7 @@ final class TrackRecordingViewController: MWMViewController {
|
|||||||
private var blinkingTimer: Timer?
|
private var blinkingTimer: Timer?
|
||||||
private var topConstraint = NSLayoutConstraint()
|
private var topConstraint = NSLayoutConstraint()
|
||||||
private var trailingConstraint = NSLayoutConstraint()
|
private var trailingConstraint = NSLayoutConstraint()
|
||||||
|
private var state: TrackRecordingButtonState = .hidden
|
||||||
|
|
||||||
private static var availableArea: CGRect = .zero
|
private static var availableArea: CGRect = .zero
|
||||||
private static var topConstraintValue: CGFloat {
|
private static var topConstraintValue: CGFloat {
|
||||||
@@ -38,31 +39,29 @@ final class TrackRecordingViewController: MWMViewController {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidLoad() {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidLoad()
|
||||||
UIView.transition(with: self.view,
|
// async is for smoother appearance
|
||||||
duration: kDefaultAnimationDuration,
|
DispatchQueue.main.asyncAfter(deadline: .now() + kDefaultAnimationDuration) {
|
||||||
options: .transitionCrossDissolve,
|
self.setState(self.state, completion: nil)
|
||||||
animations: {
|
}
|
||||||
self.button.isHidden = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public methods
|
// MARK: - Public methods
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
func close(completion: @escaping (() -> Void)) {
|
func setState(_ state: TrackRecordingButtonState, completion: (() -> Void)?) {
|
||||||
stopTimer()
|
self.state = state
|
||||||
UIView.transition(with: self.view,
|
switch state {
|
||||||
duration: kDefaultAnimationDuration,
|
case .visible:
|
||||||
options: .transitionCrossDissolve,
|
setHidden(false, completion: nil)
|
||||||
animations: {
|
case .hidden:
|
||||||
self.button.isHidden = true
|
setHidden(true, completion: completion)
|
||||||
}, completion: { _ in
|
case .closed:
|
||||||
self.removeFromParent()
|
close(completion: completion)
|
||||||
self.view.removeFromSuperview()
|
@unknown default:
|
||||||
completion()
|
fatalError()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private methods
|
// MARK: - Private methods
|
||||||
@@ -75,7 +74,7 @@ final class TrackRecordingViewController: MWMViewController {
|
|||||||
button.tintColor = Constants.color.darker
|
button.tintColor = Constants.color.darker
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
button.setImage(UIImage(resource: .icMenuBookmarkTrackRecording), for: .normal)
|
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
|
button.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +96,7 @@ final class TrackRecordingViewController: MWMViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateLayout() {
|
private func updateLayout() {
|
||||||
guard let superview = self.view.superview else { return }
|
guard let superview = view.superview else { return }
|
||||||
superview.animateConstraints {
|
superview.animateConstraints {
|
||||||
self.topConstraint.constant = Self.topConstraintValue
|
self.topConstraint.constant = Self.topConstraintValue
|
||||||
self.trailingConstraint.constant = Self.trailingConstraintValue
|
self.trailingConstraint.constant = Self.trailingConstraintValue
|
||||||
@@ -123,23 +122,39 @@ final class TrackRecordingViewController: MWMViewController {
|
|||||||
blinkingTimer = nil
|
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) {
|
static func updateAvailableArea(_ frame: CGRect) {
|
||||||
availableArea = frame
|
availableArea = frame
|
||||||
guard let controller = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
|
guard let button = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
controller.updateLayout()
|
button.updateLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func onTrackRecordingButtonPressed(_ sender: Any) {
|
private func didTap(_ sender: Any) {
|
||||||
switch trackRecordingManager.recordingState {
|
MapViewController.shared()?.showTrackRecordingPlacePage()
|
||||||
case .inactive:
|
|
||||||
trackRecordingManager.processAction(.start)
|
|
||||||
case .active:
|
|
||||||
trackRecordingManager.processAction(.stop)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
- (void)openBookmarkEditor;
|
- (void)openBookmarkEditor;
|
||||||
- (void)openFullPlaceDescriptionWithHtml:(NSString *_Nonnull)htmlString;
|
- (void)openFullPlaceDescriptionWithHtml:(NSString *_Nonnull)htmlString;
|
||||||
- (void)openDrivingOptions;
|
- (void)openDrivingOptions;
|
||||||
|
- (void)showTrackRecordingPlacePage;
|
||||||
|
|
||||||
- (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration;
|
- (void)setPlacePageTopBound:(CGFloat)bound duration:(double)duration;
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
|
|
||||||
@property(nonatomic, readwrite) MWMMapViewControlsManager *controlsManager;
|
@property(nonatomic, readwrite) MWMMapViewControlsManager *controlsManager;
|
||||||
@property(nonatomic, readwrite) SearchOnMapManager *searchManager;
|
@property(nonatomic, readwrite) SearchOnMapManager *searchManager;
|
||||||
|
@property(nonatomic, readwrite) TrackRecordingManager *trackRecordingManager;
|
||||||
|
|
||||||
@property(nonatomic) BOOL disableStandbyOnLocationStateMode;
|
@property(nonatomic) BOOL disableStandbyOnLocationStateMode;
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
return [MapsAppDelegate theApp].mapViewController;
|
return [MapsAppDelegate theApp].mapViewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Map Navigation
|
#pragma mark - PlacePage
|
||||||
|
|
||||||
- (void)showOrUpdatePlacePage:(PlacePageData *)data {
|
- (void)showOrUpdatePlacePage:(PlacePageData *)data {
|
||||||
if (self.searchManager.isSearching)
|
if (self.searchManager.isSearching)
|
||||||
@@ -124,9 +125,10 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
|
|
||||||
self.controlsManager.trafficButtonHidden = YES;
|
self.controlsManager.trafficButtonHidden = YES;
|
||||||
if (self.placePageVC != nil) {
|
if (self.placePageVC != nil) {
|
||||||
[PlacePageBuilder update:(PlacePageViewController *)self.placePageVC with:data];
|
[PlacePageBuilder update:self.placePageVC with:data];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self showPlacePageFor:data];
|
[self showPlacePageFor:data];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +206,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)hideRegularPlacePage {
|
- (void)hideRegularPlacePage {
|
||||||
|
[self stopObservingTrackRecordingUpdates];
|
||||||
[self.placePageVC closeAnimatedWithCompletion:^{
|
[self.placePageVC closeAnimatedWithCompletion:^{
|
||||||
[self.placePageVC.view removeFromSuperview];
|
[self.placePageVC.view removeFromSuperview];
|
||||||
[self.placePageVC willMoveToParentViewController:nil];
|
[self.placePageVC willMoveToParentViewController:nil];
|
||||||
@@ -247,6 +250,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlacePageData * data = [[PlacePageData alloc] initWithLocalizationProvider:[[OpeinigHoursLocalization alloc] init]];
|
PlacePageData * data = [[PlacePageData alloc] initWithLocalizationProvider:[[OpeinigHoursLocalization alloc] init]];
|
||||||
|
[self stopObservingTrackRecordingUpdates];
|
||||||
[self showOrUpdatePlacePage:data];
|
[self showOrUpdatePlacePage:data];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +429,9 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
// After all users migrate to OAuth2 we can remove next code
|
// After all users migrate to OAuth2 we can remove next code
|
||||||
[self migrateOAuthCredentials];
|
[self migrateOAuthCredentials];
|
||||||
|
|
||||||
|
if (self.trackRecordingManager.isActive)
|
||||||
|
[self showTrackRecordingPlacePage];
|
||||||
|
|
||||||
/// @todo: Uncomment update dialog when will be ready to handle big traffic bursts.
|
/// @todo: Uncomment update dialog when will be ready to handle big traffic bursts.
|
||||||
/*
|
/*
|
||||||
if (!DeepLinkHandler.shared.isLaunchedByDeeplink)
|
if (!DeepLinkHandler.shared.isLaunchedByDeeplink)
|
||||||
@@ -742,6 +749,12 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
return _searchManager;
|
return _searchManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (TrackRecordingManager *)trackRecordingManager {
|
||||||
|
if (!_trackRecordingManager)
|
||||||
|
_trackRecordingManager = TrackRecordingManager.shared;
|
||||||
|
return _trackRecordingManager;
|
||||||
|
}
|
||||||
|
|
||||||
- (UIView * _Nullable)searchViewAvailableArea {
|
- (UIView * _Nullable)searchViewAvailableArea {
|
||||||
return self.searchManager.viewController.availableAreaView;
|
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
|
// MARK: - Handle macOS trackpad gestures
|
||||||
|
|
||||||
- (void)handlePan:(UIPanGestureRecognizer *)recognizer API_AVAILABLE(ios(14.0)) {
|
- (void)handlePan:(UIPanGestureRecognizer *)recognizer API_AVAILABLE(ios(14.0)) {
|
||||||
|
|||||||
@@ -3,8 +3,15 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@protocol LocationService
|
||||||
|
|
||||||
|
+ (BOOL)isLocationProhibited;
|
||||||
|
+ (void)checkLocationStatus;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
NS_SWIFT_NAME(LocationManager)
|
NS_SWIFT_NAME(LocationManager)
|
||||||
@interface MWMLocationManager : NSObject
|
@interface MWMLocationManager : NSObject<LocationService>
|
||||||
|
|
||||||
+ (void)start;
|
+ (void)start;
|
||||||
+ (void)stop;
|
+ (void)stop;
|
||||||
@@ -14,10 +21,8 @@ NS_SWIFT_NAME(LocationManager)
|
|||||||
+ (void)removeObserver:(id<MWMLocationObserver>)observer NS_SWIFT_NAME(remove(observer:));
|
+ (void)removeObserver:(id<MWMLocationObserver>)observer NS_SWIFT_NAME(remove(observer:));
|
||||||
|
|
||||||
+ (void)setMyPositionMode:(MWMMyPositionMode)mode;
|
+ (void)setMyPositionMode:(MWMMyPositionMode)mode;
|
||||||
+ (void)checkLocationStatus;
|
|
||||||
|
|
||||||
+ (nullable CLLocation *)lastLocation;
|
+ (nullable CLLocation *)lastLocation;
|
||||||
+ (BOOL)isLocationProhibited;
|
|
||||||
+ (nullable CLHeading *)lastHeading;
|
+ (nullable CLHeading *)lastHeading;
|
||||||
|
|
||||||
+ (void)applicationDidBecomeActive;
|
+ (void)applicationDidBecomeActive;
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||||||
case .trackRecordingWidgetButton:
|
case .trackRecordingWidgetButton:
|
||||||
return .addFrom(Self.bottomTabBarButton) { s in
|
return .addFrom(Self.bottomTabBarButton) { s in
|
||||||
s.cornerRadius = .custom(23)
|
s.cornerRadius = .custom(23)
|
||||||
|
s.coloring = .red
|
||||||
}
|
}
|
||||||
case .blackOpaqueBackground:
|
case .blackOpaqueBackground:
|
||||||
return .add { s in
|
return .add { s in
|
||||||
|
|||||||
@@ -4,31 +4,47 @@ enum TrackRecordingState: Int, Equatable {
|
|||||||
case active
|
case active
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TrackRecordingAction: String, CaseIterable {
|
enum TrackRecordingAction {
|
||||||
case start
|
case start
|
||||||
case stop
|
case stopAndSave(name: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TrackRecordingError: Error {
|
enum TrackRecordingError: Error {
|
||||||
case locationIsProhibited
|
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 addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler)
|
||||||
func removeObserver(_ observer: AnyObject)
|
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
|
@objcMembers
|
||||||
final class TrackRecordingManager: NSObject {
|
final class TrackRecordingManager: NSObject {
|
||||||
|
|
||||||
typealias CompletionHandler = () -> Void
|
typealias CompletionHandler = (TrackRecordingActionResult) -> Void
|
||||||
|
|
||||||
private enum SavingOption {
|
|
||||||
case withoutSaving
|
|
||||||
case saveWithName(String? = nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate struct Observation {
|
fileprivate struct Observation {
|
||||||
weak var observer: AnyObject?
|
weak var observer: AnyObject?
|
||||||
@@ -37,28 +53,40 @@ final class TrackRecordingManager: NSObject {
|
|||||||
|
|
||||||
static let shared: TrackRecordingManager = {
|
static let shared: TrackRecordingManager = {
|
||||||
let trackRecorder = FrameworkHelper.self
|
let trackRecorder = FrameworkHelper.self
|
||||||
|
let locationManager = LocationManager.self
|
||||||
var activityManager: TrackRecordingActivityManager? = nil
|
var activityManager: TrackRecordingActivityManager? = nil
|
||||||
#if canImport(ActivityKit)
|
#if canImport(ActivityKit)
|
||||||
if #available(iOS 16.2, *) {
|
if #available(iOS 16.2, *) {
|
||||||
activityManager = TrackRecordingLiveActivityManager.shared
|
activityManager = TrackRecordingLiveActivityManager.shared
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return TrackRecordingManager(trackRecorder: trackRecorder, activityManager: activityManager)
|
return TrackRecordingManager(trackRecorder: trackRecorder,
|
||||||
|
locationService: locationManager,
|
||||||
|
activityManager: activityManager)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private let trackRecorder: TrackRecorder.Type
|
private let trackRecorder: TrackRecorder.Type
|
||||||
|
private var locationService: LocationService.Type
|
||||||
private var activityManager: TrackRecordingActivityManager?
|
private var activityManager: TrackRecordingActivityManager?
|
||||||
private var observations: [Observation] = []
|
private var observations: [Observation] = []
|
||||||
private var trackRecordingInfo: TrackInfo?
|
private(set) var trackRecordingInfo: TrackInfo = .empty()
|
||||||
|
|
||||||
|
var trackRecordingElevationProfileData: ElevationProfileData {
|
||||||
|
FrameworkHelper.trackRecordingElevationInfo()
|
||||||
|
}
|
||||||
|
|
||||||
var recordingState: TrackRecordingState {
|
var recordingState: TrackRecordingState {
|
||||||
trackRecorder.isTrackRecordingEnabled() ? .active : .inactive
|
trackRecorder.isTrackRecordingEnabled() ? .active : .inactive
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(trackRecorder: TrackRecorder.Type, activityManager: TrackRecordingActivityManager?) {
|
init(trackRecorder: TrackRecorder.Type,
|
||||||
|
locationService: LocationService.Type,
|
||||||
|
activityManager: TrackRecordingActivityManager?) {
|
||||||
self.trackRecorder = trackRecorder
|
self.trackRecorder = trackRecorder
|
||||||
|
self.locationService = locationService
|
||||||
self.activityManager = activityManager
|
self.activityManager = activityManager
|
||||||
super.init()
|
super.init()
|
||||||
|
self.subscribeOnTheAppLifecycleEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public methods
|
// MARK: - Public methods
|
||||||
@@ -84,22 +112,42 @@ final class TrackRecordingManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func processAction(_ action: TrackRecordingAction, completion: (CompletionHandler)? = nil) {
|
func processAction(_ action: TrackRecordingAction, completion: (CompletionHandler)? = nil) {
|
||||||
|
do {
|
||||||
switch action {
|
switch action {
|
||||||
case .start:
|
case .start:
|
||||||
start(completion: completion)
|
try startRecording()
|
||||||
case .stop:
|
case .stopAndSave(let name):
|
||||||
stop(completion: completion)
|
stopRecording()
|
||||||
|
try checkIsTrackNotEmpty()
|
||||||
|
saveTrackRecording(name: name)
|
||||||
|
}
|
||||||
|
completion?(.success)
|
||||||
|
} catch {
|
||||||
|
handleError(error, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private methods
|
// MARK: - Private methods
|
||||||
|
|
||||||
private func checkIsLocationEnabled() throws {
|
private func subscribeOnTheAppLifecycleEvents() {
|
||||||
if LocationManager.isLocationProhibited() {
|
NotificationCenter.default.addObserver(self,
|
||||||
|
selector: #selector(notifyObservers),
|
||||||
|
name: UIApplication.didBecomeActiveNotification,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkIsLocationEnabled() throws(TrackRecordingError) {
|
||||||
|
if locationService.isLocationProhibited() {
|
||||||
throw TrackRecordingError.locationIsProhibited
|
throw TrackRecordingError.locationIsProhibited
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func checkIsTrackNotEmpty() throws(TrackRecordingError) {
|
||||||
|
if trackRecorder.isTrackRecordingEmpty() {
|
||||||
|
throw TrackRecordingError.trackIsEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Handle track recording process
|
// MARK: - Handle track recording process
|
||||||
|
|
||||||
private func subscribeOnTrackRecordingProgressUpdates() {
|
private func subscribeOnTrackRecordingProgressUpdates() {
|
||||||
@@ -113,95 +161,68 @@ final class TrackRecordingManager: NSObject {
|
|||||||
|
|
||||||
private func unsubscribeFromTrackRecordingProgressUpdates() {
|
private func unsubscribeFromTrackRecordingProgressUpdates() {
|
||||||
trackRecorder.setTrackRecordingUpdateHandler(nil)
|
trackRecorder.setTrackRecordingUpdateHandler(nil)
|
||||||
trackRecordingInfo = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Handle Start/Stop event and Errors
|
// MARK: - Handle Start/Stop event and Errors
|
||||||
|
|
||||||
private func start(completion: (CompletionHandler)? = nil) {
|
private func startRecording() throws(TrackRecordingError) {
|
||||||
do {
|
|
||||||
try checkIsLocationEnabled()
|
|
||||||
switch recordingState {
|
switch recordingState {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
|
try checkIsLocationEnabled()
|
||||||
subscribeOnTrackRecordingProgressUpdates()
|
subscribeOnTrackRecordingProgressUpdates()
|
||||||
trackRecorder.startTrackRecording()
|
trackRecorder.startTrackRecording()
|
||||||
notifyObservers()
|
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:
|
case .active:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
completion?()
|
|
||||||
} catch {
|
|
||||||
handleError(error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stop(completion: (CompletionHandler)? = nil) {
|
private func stopRecording() {
|
||||||
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) {
|
|
||||||
unsubscribeFromTrackRecordingProgressUpdates()
|
unsubscribeFromTrackRecordingProgressUpdates()
|
||||||
trackRecorder.stopTrackRecording()
|
trackRecorder.stopTrackRecording()
|
||||||
|
trackRecordingInfo = .empty()
|
||||||
activityManager?.stop()
|
activityManager?.stop()
|
||||||
notifyObservers()
|
notifyObservers()
|
||||||
|
}
|
||||||
|
|
||||||
switch savingOption {
|
private func saveTrackRecording(name: String) {
|
||||||
case .withoutSaving:
|
|
||||||
break
|
|
||||||
case .saveWithName(let name):
|
|
||||||
trackRecorder.saveTrackRecording(withName: name)
|
trackRecorder.saveTrackRecording(withName: name)
|
||||||
}
|
}
|
||||||
completion?()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleError(_ error: Error, completion: (CompletionHandler)? = nil) {
|
private func handleError(_ error: TrackRecordingError, completion: (CompletionHandler)? = nil) {
|
||||||
LOG(.error, error.localizedDescription)
|
|
||||||
switch error {
|
switch error {
|
||||||
case TrackRecordingError.locationIsProhibited:
|
case TrackRecordingError.locationIsProhibited:
|
||||||
// Show alert to enable location
|
// Show alert to enable location
|
||||||
LocationManager.checkLocationStatus()
|
locationService.checkLocationStatus()
|
||||||
default:
|
case TrackRecordingError.trackIsEmpty:
|
||||||
|
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
||||||
|
case TrackRecordingError.systemError(let error):
|
||||||
|
LOG(.error, error.localizedDescription)
|
||||||
break
|
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
|
// MARK: - TrackRecordingObserver
|
||||||
|
|
||||||
extension TrackRecordingManager: TrackRecordingObserver {
|
extension TrackRecordingManager: TrackRecordingObservable {
|
||||||
@objc
|
@objc
|
||||||
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler) {
|
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler) {
|
||||||
|
guard !observations.contains(where: { $0.observer === observer }) else { return }
|
||||||
let observation = Observation(observer: observer, recordingStateDidChangeHandler: recordingIsActiveDidChangeHandler)
|
let observation = Observation(observer: observer, recordingStateDidChangeHandler: recordingIsActiveDidChangeHandler)
|
||||||
observations.append(observation)
|
observations.append(observation)
|
||||||
recordingIsActiveDidChangeHandler(recordingState, trackRecordingInfo)
|
recordingIsActiveDidChangeHandler(recordingState, trackRecordingInfo) {
|
||||||
|
self.trackRecordingElevationProfileData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -209,8 +230,16 @@ extension TrackRecordingManager: TrackRecordingObserver {
|
|||||||
observations.removeAll { $0.observer === observer }
|
observations.removeAll { $0.observer === observer }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func contains(_ observer: AnyObject) -> Bool {
|
||||||
|
observations.contains { $0.observer === observer }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
private func notifyObservers() {
|
private func notifyObservers() {
|
||||||
observations = observations.filter { $0.observer != nil }
|
observations.removeAll { $0.observer == nil }
|
||||||
observations.forEach { $0.recordingStateDidChangeHandler?(recordingState, trackRecordingInfo) }
|
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 */; };
|
ED2D74652D14357F00660FBF /* TrackRecordingLiveActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2D74302D14337500660FBF /* TrackRecordingLiveActivityAttributes.swift */; };
|
||||||
ED2D74662D1435A600660FBF /* LiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2D742D2D14337500660FBF /* LiveActivityManager.swift */; };
|
ED2D74662D1435A600660FBF /* LiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2D742D2D14337500660FBF /* LiveActivityManager.swift */; };
|
||||||
ED2E328E2D10500900807A08 /* TrackRecordingButtonArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED46DD922D06F804007CACD6 /* TrackRecordingButtonArea.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 */; };
|
ED3EAC202B03C88100220A4A /* BottomTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3EAC1F2B03C88100220A4A /* BottomTabBarButton.swift */; };
|
||||||
ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED43B8BC2C12063500D07BAA /* DocumentPicker.swift */; };
|
ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED43B8BC2C12063500D07BAA /* DocumentPicker.swift */; };
|
||||||
ED46DDCE2D098A0B007CACD6 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED46DDCD2D098A0B007CACD6 /* WidgetKit.framework */; };
|
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 */; };
|
ED914ABE2D351FF800973C45 /* UILabel+SetFontStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED914ABD2D351FF800973C45 /* UILabel+SetFontStyle.swift */; };
|
||||||
ED9857082C4ED02D00694F6C /* MailComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9857072C4ED02D00694F6C /* MailComposer.swift */; };
|
ED9857082C4ED02D00694F6C /* MailComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9857072C4ED02D00694F6C /* MailComposer.swift */; };
|
||||||
ED9966802B94FBC20083CE55 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED99667D2B94FBC20083CE55 /* ColorPicker.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 */; };
|
EDA1EAA42CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA1EAA32CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift */; };
|
||||||
EDB71D8C2D8474A0004A6A7F /* CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */; };
|
EDB71D8C2D8474A0004A6A7F /* CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */; };
|
||||||
EDB71E002D8B0338004A6A7F /* ModalPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.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 */; };
|
EDFDFB4C2B722C9C0013A44C /* InfoTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB4B2B722C9C0013A44C /* InfoTableViewCell.swift */; };
|
||||||
EDFDFB522B726F1A0013A44C /* ButtonsStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB512B726F1A0013A44C /* ButtonsStackView.swift */; };
|
EDFDFB522B726F1A0013A44C /* ButtonsStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB512B726F1A0013A44C /* ButtonsStackView.swift */; };
|
||||||
EDFDFB612B74E2500013A44C /* DonationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDFDFB602B74E2500013A44C /* DonationView.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 */; };
|
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 */; };
|
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 */; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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; };
|
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>"; };
|
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 */ = {
|
34BC72091B0DECAE0012A34B /* MapViewControls */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
ED49D76F2CF0E3A8004AF27E /* TrackRecordingViewController.swift */,
|
ED49D76F2CF0E3A8004AF27E /* TrackRecordingButtonViewController.swift */,
|
||||||
340537621BBED98600D452C6 /* MWMMapViewControlsCommon.h */,
|
340537621BBED98600D452C6 /* MWMMapViewControlsCommon.h */,
|
||||||
34BC72101B0DECAE0012A34B /* MWMMapViewControlsManager.h */,
|
34BC72101B0DECAE0012A34B /* MWMMapViewControlsManager.h */,
|
||||||
34BC72111B0DECAE0012A34B /* MWMMapViewControlsManager.mm */,
|
34BC72111B0DECAE0012A34B /* MWMMapViewControlsManager.mm */,
|
||||||
@@ -3060,6 +3064,7 @@
|
|||||||
99C6532123F2F506004322F3 /* IPlacePageLayout.swift */,
|
99C6532123F2F506004322F3 /* IPlacePageLayout.swift */,
|
||||||
99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */,
|
99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */,
|
||||||
993DF0B423F6B2EF00AC231A /* PlacePageTrackLayout.swift */,
|
993DF0B423F6B2EF00AC231A /* PlacePageTrackLayout.swift */,
|
||||||
|
ED9DDF872D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift */,
|
||||||
);
|
);
|
||||||
path = Layouts;
|
path = Layouts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3446,6 +3451,14 @@
|
|||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
EDFE1A462DF1986900FDEA38 /* UnknownCurrentPositionAlert */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EDFE1A492DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift */,
|
||||||
|
);
|
||||||
|
path = UnknownCurrentPositionAlert;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
F607C18B1C047FCA00B53A87 /* Segue */ = {
|
F607C18B1C047FCA00B53A87 /* Segue */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -3496,6 +3509,7 @@
|
|||||||
F64F195F1AB8125C006EAF7E /* CustomAlert */ = {
|
F64F195F1AB8125C006EAF7E /* CustomAlert */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EDFE1A462DF1986900FDEA38 /* UnknownCurrentPositionAlert */,
|
||||||
447DB4B72BA7826D000DF4C2 /* ReauthAlert */,
|
447DB4B72BA7826D000DF4C2 /* ReauthAlert */,
|
||||||
F62607FB207B78E300176C5A /* SpinnerAlert */,
|
F62607FB207B78E300176C5A /* SpinnerAlert */,
|
||||||
F6D67CDA2062B9810032FD38 /* CreateBookmarkCategory */,
|
F6D67CDA2062B9810032FD38 /* CreateBookmarkCategory */,
|
||||||
@@ -4489,8 +4503,9 @@
|
|||||||
99F3EB1223F418C900C713F8 /* PlacePageInteractor.swift in Sources */,
|
99F3EB1223F418C900C713F8 /* PlacePageInteractor.swift in Sources */,
|
||||||
340708651F2905A500029ECC /* NavigationInfoArea.swift in Sources */,
|
340708651F2905A500029ECC /* NavigationInfoArea.swift in Sources */,
|
||||||
993DF0CC23F6BD0600AC231A /* ElevationDetailsPresenter.swift in Sources */,
|
993DF0CC23F6BD0600AC231A /* ElevationDetailsPresenter.swift in Sources */,
|
||||||
|
EDFE1A4A2DF1989700FDEA38 /* UIAlertController+UnknownCurrentPosition.swift in Sources */,
|
||||||
34AB666B1FC5AA330078E451 /* TransportTransitCell.swift in Sources */,
|
34AB666B1FC5AA330078E451 /* TransportTransitCell.swift in Sources */,
|
||||||
ED2E32912D10501700807A08 /* TrackRecordingViewController.swift in Sources */,
|
ED2E32912D10501700807A08 /* TrackRecordingButtonViewController.swift in Sources */,
|
||||||
47E8163323B17734008FD836 /* MWMStorage+UI.m in Sources */,
|
47E8163323B17734008FD836 /* MWMStorage+UI.m in Sources */,
|
||||||
993DF11123F6BDB100AC231A /* UILabelRenderer.swift in Sources */,
|
993DF11123F6BDB100AC231A /* UILabelRenderer.swift in Sources */,
|
||||||
34AB66471FC5AA330078E451 /* RouteManagerTableView.swift in Sources */,
|
34AB66471FC5AA330078E451 /* RouteManagerTableView.swift in Sources */,
|
||||||
@@ -4633,6 +4648,7 @@
|
|||||||
1DFA2F6A20D3B57400FB2C66 /* UIColor+PartnerColor.m in Sources */,
|
1DFA2F6A20D3B57400FB2C66 /* UIColor+PartnerColor.m in Sources */,
|
||||||
9989273B2449E60200260CE2 /* BottomMenuBuilder.swift in Sources */,
|
9989273B2449E60200260CE2 /* BottomMenuBuilder.swift in Sources */,
|
||||||
993DF10F23F6BDB100AC231A /* UIActivityIndicatorRenderer.swift in Sources */,
|
993DF10F23F6BDB100AC231A /* UIActivityIndicatorRenderer.swift in Sources */,
|
||||||
|
ED9DDF882D6F151000645BC8 /* PlacePageTrackRecordingLayout.swift in Sources */,
|
||||||
ED0B1FEF2CAA9A25006E31A4 /* UIView+Highlight.swift in Sources */,
|
ED0B1FEF2CAA9A25006E31A4 /* UIView+Highlight.swift in Sources */,
|
||||||
99A614E423CDD1D900D8D8D0 /* UIButton+RuntimeAttributes.m in Sources */,
|
99A614E423CDD1D900D8D8D0 /* UIButton+RuntimeAttributes.m in Sources */,
|
||||||
343E75981E5B1EE20041226A /* MWMCollectionViewController.m in Sources */,
|
343E75981E5B1EE20041226A /* MWMCollectionViewController.m in Sources */,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ final class TrackRecordingButtonArea: AvailableArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func notifyObserver() {
|
override func notifyObserver() {
|
||||||
TrackRecordingViewController.updateAvailableArea(areaFrame)
|
TrackRecordingButtonViewController.updateAvailableArea(areaFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,12 +66,9 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func shareLocation(cell: BottomMenuItemCell) {
|
func shareLocation(cell: BottomMenuItemCell) {
|
||||||
let lastLocation = LocationManager.lastLocation()
|
guard let coordinates = LocationManager.lastLocation()?.coordinate else {
|
||||||
guard let coordinates = lastLocation?.coordinate else {
|
viewController?.present(UIAlertController.unknownCurrentPosition(), animated: true, completion: nil)
|
||||||
let alert = UIAlertController(title: L("unknown_current_position"), message: nil, preferredStyle: .alert)
|
return
|
||||||
alert.addAction(UIAlertAction(title: L("ok"), style: .default, handler: nil))
|
|
||||||
viewController?.present(alert, animated: true, completion: nil)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
guard let viewController = viewController else { return }
|
guard let viewController = viewController else { return }
|
||||||
let vc = ActivityViewController.share(forMyPosition: coordinates)
|
let vc = ActivityViewController.share(forMyPosition: coordinates)
|
||||||
@@ -79,8 +76,13 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toggleTrackRecording() {
|
func toggleTrackRecording() {
|
||||||
trackRecorder.processAction(trackRecorder.recordingState == .active ? .stop : .start) { [weak self] in
|
switch trackRecorder.recordingState {
|
||||||
self?.close()
|
case .active:
|
||||||
}
|
break
|
||||||
|
case .inactive:
|
||||||
|
trackRecorder.processAction(.start)
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
MapViewController.shared()?.showTrackRecordingPlacePage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ final class ActionBarViewController: UIViewController {
|
|||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttons: [ActionBarButtonType] = []
|
var buttons: [ActionBarButtonType] = []
|
||||||
|
switch placePageData.objectType {
|
||||||
|
case .POI, .bookmark, .track:
|
||||||
if isRoutePlanning {
|
if isRoutePlanning {
|
||||||
buttons.append(.routeFrom)
|
buttons.append(.routeFrom)
|
||||||
}
|
}
|
||||||
@@ -73,8 +76,13 @@ final class ActionBarViewController: UIViewController {
|
|||||||
if !isRoutePlanning {
|
if !isRoutePlanning {
|
||||||
buttons.append(.routeFrom)
|
buttons.append(.routeFrom)
|
||||||
}
|
}
|
||||||
|
case .trackRecording:
|
||||||
|
break
|
||||||
|
@unknown default:
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
assert(buttons.count > 0)
|
guard !buttons.isEmpty else { return }
|
||||||
visibleButtons.append(buttons[0])
|
visibleButtons.append(buttons[0])
|
||||||
if buttons.count > 1 {
|
if buttons.count > 1 {
|
||||||
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
|
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
|
||||||
@@ -83,21 +91,24 @@ final class ActionBarViewController: UIViewController {
|
|||||||
|
|
||||||
private func configButton2() {
|
private func configButton2() {
|
||||||
var buttons: [ActionBarButtonType] = []
|
var buttons: [ActionBarButtonType] = []
|
||||||
|
switch placePageData.objectType {
|
||||||
|
case .POI, .bookmark:
|
||||||
if canAddStop {
|
if canAddStop {
|
||||||
buttons.append(.routeAddStop)
|
buttons.append(.routeAddStop)
|
||||||
}
|
}
|
||||||
switch placePageData.objectType {
|
|
||||||
case .POI, .bookmark:
|
|
||||||
buttons.append(.bookmark)
|
buttons.append(.bookmark)
|
||||||
case .track:
|
case .track:
|
||||||
|
if canAddStop {
|
||||||
|
buttons.append(.routeAddStop)
|
||||||
|
}
|
||||||
buttons.append(.track)
|
buttons.append(.track)
|
||||||
case .trackRecording:
|
case .trackRecording:
|
||||||
// TODO: implement for track recording
|
buttons.append(.saveTrackRecording)
|
||||||
break
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
assert(buttons.count > 0)
|
assert(buttons.count > 0)
|
||||||
|
|
||||||
visibleButtons.append(buttons[0])
|
visibleButtons.append(buttons[0])
|
||||||
if buttons.count > 1 {
|
if buttons.count > 1 {
|
||||||
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
|
additionalButtons.append(contentsOf: buttons.suffix(from: 1))
|
||||||
@@ -105,7 +116,14 @@ final class ActionBarViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func configButton3() {
|
private func configButton3() {
|
||||||
|
switch placePageData.objectType {
|
||||||
|
case .POI, .bookmark, .track:
|
||||||
visibleButtons.append(.routeTo)
|
visibleButtons.append(.routeTo)
|
||||||
|
case .trackRecording:
|
||||||
|
break
|
||||||
|
@unknown default:
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func configButton4() {
|
private func configButton4() {
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ extension ElevationProfilePresenter: ElevationProfilePresenterProtocol {
|
|||||||
view?.setChartData(ChartPresentationData(chartData, formatter: formatter))
|
view?.setChartData(ChartPresentationData(chartData, formatter: formatter))
|
||||||
view?.reloadDescription()
|
view?.reloadDescription()
|
||||||
|
|
||||||
|
guard !profileData.isTrackRecording else {
|
||||||
|
view?.isChartViewInfoHidden = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
view?.setActivePoint(profileData.activePoint)
|
view?.setActivePoint(profileData.activePoint)
|
||||||
view?.setMyPosition(profileData.myPosition)
|
view?.setMyPosition(profileData.myPosition)
|
||||||
bookmarkManager.setElevationActivePointChanged(profileData.trackId) { [weak self] distance in
|
bookmarkManager.setElevationActivePointChanged(profileData.trackId) { [weak self] distance in
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ extension PlacePageHeaderPresenter: PlacePageHeaderPresenterProtocol {
|
|||||||
view?.isExpandViewHidden = true
|
view?.isExpandViewHidden = true
|
||||||
view?.isShadowViewHidden = false
|
view?.isShadowViewHidden = false
|
||||||
}
|
}
|
||||||
// TODO: (KK) Enable share button for the tracks to share the whole track gpx/kml
|
|
||||||
view?.isShareButtonHidden = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func onClosePress() {
|
func onClosePress() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ protocol PlacePageHeaderViewProtocol: AnyObject {
|
|||||||
var presenter: PlacePageHeaderPresenterProtocol? { get set }
|
var presenter: PlacePageHeaderPresenterProtocol? { get set }
|
||||||
var isExpandViewHidden: Bool { get set }
|
var isExpandViewHidden: Bool { get set }
|
||||||
var isShadowViewHidden: Bool { get set }
|
var isShadowViewHidden: Bool { get set }
|
||||||
var isShareButtonHidden: Bool { get set }
|
|
||||||
|
|
||||||
func setTitle(_ title: String?, secondaryTitle: String?)
|
func setTitle(_ title: String?, secondaryTitle: String?)
|
||||||
func showShareTrackMenu()
|
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?) {
|
func setTitle(_ title: String?, secondaryTitle: String?) {
|
||||||
titleText = title
|
titleText = title
|
||||||
secondaryText = secondaryTitle
|
secondaryText = secondaryTitle
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
case .track:
|
case .track:
|
||||||
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
|
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
|
||||||
case .trackRecording:
|
case .trackRecording:
|
||||||
// TODO: Implement PlacePageTrackRecordingLayout
|
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
|
||||||
fatalError("PlacePageTrackRecordingLayout is not implemented")
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
@@ -34,14 +33,14 @@
|
|||||||
data: data,
|
data: data,
|
||||||
mapViewController: MapViewController.shared()!)
|
mapViewController: MapViewController.shared()!)
|
||||||
let layout: IPlacePageLayout
|
let layout: IPlacePageLayout
|
||||||
|
let storyboard = viewController.storyboard!
|
||||||
switch data.objectType {
|
switch data.objectType {
|
||||||
case .POI, .bookmark:
|
case .POI, .bookmark:
|
||||||
layout = PlacePageCommonLayout(interactor: interactor, storyboard: viewController.storyboard!, data: data)
|
layout = PlacePageCommonLayout(interactor: interactor, storyboard: storyboard, data: data)
|
||||||
case .track:
|
case .track:
|
||||||
layout = PlacePageTrackLayout(interactor: interactor, storyboard: viewController.storyboard!, data: data)
|
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
|
||||||
case .trackRecording:
|
case .trackRecording:
|
||||||
// TODO: Implement PlacePageTrackRecordingLayout
|
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
|
||||||
fatalError("PlacePageTrackRecordingLayout is not implemented")
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,9 +246,19 @@ extension PlacePageInteractor: ActionBarViewControllerDelegate {
|
|||||||
fatalError("More button should've been handled in ActionBarViewContoller")
|
fatalError("More button should've been handled in ActionBarViewContoller")
|
||||||
case .track:
|
case .track:
|
||||||
guard placePageData.trackData != nil else { return }
|
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.
|
// directly here when the track recovery mechanism will be implemented.
|
||||||
showTrackDeletionConfirmationDialog()
|
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:
|
@unknown default:
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
@@ -298,8 +308,8 @@ extension PlacePageInteractor: ElevationProfileViewControllerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateMapPoint(_ point: CLLocationCoordinate2D, distance: Double) {
|
func updateMapPoint(_ point: CLLocationCoordinate2D, distance: Double) {
|
||||||
guard let trackId = placePageData.trackData?.trackId else { return }
|
guard let trackData = placePageData.trackData, trackData.elevationProfileData?.isTrackRecording == false else { return }
|
||||||
BookmarksManager.shared().setElevationActivePoint(point, distance: distance, trackId: trackId)
|
BookmarksManager.shared().setElevationActivePoint(point, distance: distance, trackId: trackData.trackId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +333,12 @@ extension PlacePageInteractor: PlacePageHeaderViewControllerDelegate {
|
|||||||
case .track:
|
case .track:
|
||||||
presenter?.showShareTrackMenu()
|
presenter?.showShareTrackMenu()
|
||||||
default:
|
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,
|
MWMActionBarButtonTypeBookingSearch,
|
||||||
MWMActionBarButtonTypeBookmark,
|
MWMActionBarButtonTypeBookmark,
|
||||||
MWMActionBarButtonTypeTrack,
|
MWMActionBarButtonTypeTrack,
|
||||||
|
MWMActionBarButtonTypeSaveTrackRecording,
|
||||||
MWMActionBarButtonTypeCall,
|
MWMActionBarButtonTypeCall,
|
||||||
MWMActionBarButtonTypeDownload,
|
MWMActionBarButtonTypeDownload,
|
||||||
MWMActionBarButtonTypeMore,
|
MWMActionBarButtonTypeMore,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
|
|||||||
case MWMActionBarButtonTypeBookmark:
|
case MWMActionBarButtonTypeBookmark:
|
||||||
case MWMActionBarButtonTypeTrack:
|
case MWMActionBarButtonTypeTrack:
|
||||||
return L(isSelected ? @"delete" : @"save");
|
return L(isSelected ? @"delete" : @"save");
|
||||||
|
case MWMActionBarButtonTypeSaveTrackRecording:
|
||||||
|
return L(@"save");
|
||||||
case MWMActionBarButtonTypeRouteFrom:
|
case MWMActionBarButtonTypeRouteFrom:
|
||||||
return L(@"p2p_from_here");
|
return L(@"p2p_from_here");
|
||||||
case MWMActionBarButtonTypeRouteTo:
|
case MWMActionBarButtonTypeRouteTo:
|
||||||
@@ -55,6 +57,7 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
|
|||||||
self.label.text = titleForButton(self.type, isSelected);
|
self.label.text = titleForButton(self.type, isSelected);
|
||||||
self.extraBackground.hidden = YES;
|
self.extraBackground.hidden = YES;
|
||||||
self.button.coloring = MWMButtonColoringBlack;
|
self.button.coloring = MWMButtonColoringBlack;
|
||||||
|
[self.button.imageView setContentMode:UIViewContentModeScaleAspectFit];
|
||||||
|
|
||||||
switch (self.type) {
|
switch (self.type) {
|
||||||
case MWMActionBarButtonTypeDownload: {
|
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 setImage:[[UIImage imageNamed:@"ic_route_manager_trash"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||||
self.button.coloring = MWMButtonColoringRed;
|
self.button.coloring = MWMButtonColoringRed;
|
||||||
break;
|
break;
|
||||||
|
case MWMActionBarButtonTypeSaveTrackRecording:
|
||||||
|
[self.button setImage:[UIImage imageNamed:@"ic_placepage_save_track_recording"] forState:UIControlStateNormal];
|
||||||
|
break;
|
||||||
case MWMActionBarButtonTypeRouteFrom:
|
case MWMActionBarButtonTypeRouteFrom:
|
||||||
[self.button setImage:[UIImage imageNamed:@"ic_route_from"] forState:UIControlStateNormal];
|
[self.button setImage:[UIImage imageNamed:@"ic_route_from"] forState:UIControlStateNormal];
|
||||||
break;
|
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