Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
53
iphone/Maps/Common/Common.swift
Normal file
53
iphone/Maps/Common/Common.swift
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import Foundation
|
||||
|
||||
var isiPad: Bool {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return true
|
||||
}
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
func L(_ key: String) -> String { return NSLocalizedString(key, comment: "") }
|
||||
|
||||
func L(_ key: String, languageCode: String) -> String {
|
||||
guard let path = Bundle.main.path(forResource: languageCode, ofType: "lproj"),
|
||||
let bundle = Bundle(path: path) else {
|
||||
LOG(.warning, "Localization bundle not found for language code: \(languageCode)")
|
||||
return L(key)
|
||||
}
|
||||
return NSLocalizedString(key, bundle: bundle, comment: "")
|
||||
}
|
||||
|
||||
func alternative<T>(iPhone: T, iPad: T) -> T { isiPad ? iPad : iPhone }
|
||||
|
||||
func iPadSpecific(_ f: () -> Void) {
|
||||
if isiPad {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func iPhoneSpecific(_ f: () -> Void) {
|
||||
if !isiPad {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func toString(_ cls: AnyClass) -> String {
|
||||
return String(describing: cls)
|
||||
}
|
||||
|
||||
func LOG(_ level: LogLevel,
|
||||
_ message: @autoclosure () -> Any,
|
||||
functionName: StaticString = #function,
|
||||
fileName: StaticString = #file,
|
||||
lineNumber: UInt = #line) {
|
||||
if (Logger.canLog(level)) {
|
||||
let shortFileName = URL(string: "\(fileName)")?.lastPathComponent ?? ""
|
||||
let formattedMessage = "\(shortFileName):\(lineNumber) \(functionName): \(message())"
|
||||
Logger.log(level, message: formattedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
struct Weak<T> where T: AnyObject {
|
||||
weak var value: T?
|
||||
}
|
||||
18
iphone/Maps/Common/Keyboard/MWMKeyboard.h
Normal file
18
iphone/Maps/Common/Keyboard/MWMKeyboard.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import "MWMKeyboardObserver.h"
|
||||
|
||||
@interface MWMKeyboard : NSObject
|
||||
|
||||
+ (void)applicationDidBecomeActive;
|
||||
|
||||
+ (void)addObserver:(id<MWMKeyboardObserver>)observer;
|
||||
+ (void)removeObserver:(id<MWMKeyboardObserver>)observer;
|
||||
|
||||
+ (CGFloat)keyboardHeight;
|
||||
|
||||
- (instancetype)init __attribute__((unavailable("call +manager instead")));
|
||||
- (instancetype)copy __attribute__((unavailable("call +manager instead")));
|
||||
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call +manager instead")));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable("call +manager instead")));
|
||||
+ (instancetype)new __attribute__((unavailable("call +manager instead")));
|
||||
|
||||
@end
|
||||
98
iphone/Maps/Common/Keyboard/MWMKeyboard.m
Normal file
98
iphone/Maps/Common/Keyboard/MWMKeyboard.m
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#import "MWMKeyboard.h"
|
||||
|
||||
@interface MWMKeyboard ()
|
||||
|
||||
@property(nonatomic) NSHashTable *observers;
|
||||
@property(nonatomic) CGFloat keyboardHeight;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMKeyboard
|
||||
|
||||
+ (void)applicationDidBecomeActive {
|
||||
[self manager];
|
||||
}
|
||||
|
||||
+ (MWMKeyboard *)manager {
|
||||
static MWMKeyboard *manager;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[self alloc] initManager];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
- (instancetype)initManager {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_observers = [NSHashTable weakObjectsHashTable];
|
||||
NSNotificationCenter *nc = NSNotificationCenter.defaultCenter;
|
||||
[nc addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
||||
[nc addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
+ (CGFloat)keyboardHeight {
|
||||
return [self manager].keyboardHeight;
|
||||
}
|
||||
|
||||
#pragma mark - Add/Remove Observers
|
||||
|
||||
+ (void)addObserver:(id<MWMKeyboardObserver>)observer {
|
||||
[[self manager].observers addObject:observer];
|
||||
}
|
||||
|
||||
+ (void)removeObserver:(id<MWMKeyboardObserver>)observer {
|
||||
[[self manager].observers removeObject:observer];
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)onKeyboardWillAnimate {
|
||||
for (id<MWMKeyboardObserver> observer in self.observers) {
|
||||
if ([observer respondsToSelector:@selector(onKeyboardWillAnimate)])
|
||||
[observer onKeyboardWillAnimate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onKeyboardAnimation {
|
||||
for (id<MWMKeyboardObserver> observer in self.observers) {
|
||||
[observer onKeyboardAnimation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyboardWillShow:(NSNotification *)notification {
|
||||
[self onKeyboardWillAnimate];
|
||||
CGSize keyboardSize = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
|
||||
self.keyboardHeight = MIN(keyboardSize.height, keyboardSize.width);
|
||||
NSNumber *duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
|
||||
NSNumber *curve = notification.userInfo[UIKeyboardAnimationCurveUserInfoKey];
|
||||
[UIView animateWithDuration:duration.doubleValue
|
||||
delay:0
|
||||
options:curve.integerValue
|
||||
animations:^{
|
||||
[self onKeyboardAnimation];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)keyboardWillHide:(NSNotification *)notification {
|
||||
[self onKeyboardWillAnimate];
|
||||
self.keyboardHeight = 0;
|
||||
NSNumber *duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
|
||||
NSNumber *curve = notification.userInfo[UIKeyboardAnimationCurveUserInfoKey];
|
||||
[UIView animateWithDuration:duration.doubleValue
|
||||
delay:0
|
||||
options:curve.integerValue
|
||||
animations:^{
|
||||
[self onKeyboardAnimation];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
8
iphone/Maps/Common/Keyboard/MWMKeyboardObserver.h
Normal file
8
iphone/Maps/Common/Keyboard/MWMKeyboardObserver.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@protocol MWMKeyboardObserver<NSObject>
|
||||
|
||||
- (void)onKeyboardAnimation;
|
||||
|
||||
@optional
|
||||
- (void)onKeyboardWillAnimate;
|
||||
|
||||
@end
|
||||
10
iphone/Maps/Common/MWMConsts.h
Normal file
10
iphone/Maps/Common/MWMConsts.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
static NSString * const kMapsmeErrorDomain = @"com.mapsme.error";
|
||||
|
||||
static NSTimeInterval const kDefaultAnimationDuration = .3;
|
||||
|
||||
static uint64_t const KB = 1024;
|
||||
static uint64_t const MB = 1024 * 1024;
|
||||
// The last 5% are left for applying diffs.
|
||||
static float const kMaxProgress = 0.95f;
|
||||
static NSString * const kLocaleUsedInSupportEmails = @"en_gb";
|
||||
static NSString * const kTapEventKey = @"$onClick";
|
||||
3
iphone/Maps/Common/MWMMacros.h
Normal file
3
iphone/Maps/Common/MWMMacros.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#define IPAD (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
|
||||
|
||||
#define L(str) NSLocalizedString(str, nil)
|
||||
34
iphone/Maps/Common/WebViewController.h
Normal file
34
iphone/Maps/Common/WebViewController.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#import <WebKit/WebKit.h>
|
||||
#import "MWMViewController.h"
|
||||
#import <CoreApi/MWMTypes.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^WebViewControllerWillLoadBlock)(BOOL, NSDictionary<NSString *, NSString *> * _Nullable);
|
||||
|
||||
@interface WebViewController : MWMViewController <WKNavigationDelegate>
|
||||
|
||||
@property (nonatomic) NSURL * _Nullable m_url;
|
||||
@property (copy, nonatomic) NSString * _Nullable m_htmlText;
|
||||
// Set to YES if external browser should be launched
|
||||
@property (nonatomic) BOOL openInSafari;
|
||||
@property (nonatomic, readonly) WKWebView * webView;
|
||||
|
||||
- (instancetype _Nullable)initWithUrl:(NSURL *)url title:( NSString * _Nullable)title;
|
||||
- (instancetype _Nullable)initWithHtml:(NSString *)htmlText
|
||||
baseUrl:(NSURL * _Nullable)url
|
||||
title:(NSString * _Nullable)title;
|
||||
- (instancetype _Nullable)initWithAuthURL:(NSURL *)url
|
||||
onSuccessAuth:(MWMStringBlock _Nullable)success
|
||||
onFailure:(MWMVoidBlock _Nullable)failure;
|
||||
- (void)willLoadUrl:(WebViewControllerWillLoadBlock)decisionHandler;
|
||||
- (BOOL)shouldAddAccessToken;
|
||||
- (void)forward;
|
||||
- (void)back;
|
||||
- (void)reloadFromOrigin;
|
||||
- (NSString *)configuredHtmlWithText:(NSString *)htmlText;
|
||||
- (void)performURLRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
200
iphone/Maps/Common/WebViewController.m
Normal file
200
iphone/Maps/Common/WebViewController.m
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#import "WebViewController.h"
|
||||
#import <CoreApi/MWMFrameworkHelper.h>
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface WebViewController ()
|
||||
|
||||
@property(copy, nonatomic) MWMVoidBlock onFailure;
|
||||
@property(copy, nonatomic) MWMStringBlock onSuccess;
|
||||
@property(nonatomic) BOOL authorized;
|
||||
@property(nonatomic) WKWebView *webView;
|
||||
@property(nonatomic) BOOL shouldResendHeaders;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WebViewController
|
||||
|
||||
- (id)initWithUrl:(NSURL *)url title:(NSString *)title {
|
||||
self = [super initWithNibName:nil bundle:nil];
|
||||
if (self) {
|
||||
_m_url = url;
|
||||
if (title)
|
||||
self.navigationItem.title = title;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithHtml:(NSString *)htmlText baseUrl:(NSURL *)url title:(NSString *)title {
|
||||
self = [super initWithNibName:nil bundle:nil];
|
||||
if (self) {
|
||||
_m_htmlText = htmlText;
|
||||
_m_url = url;
|
||||
if (title)
|
||||
self.navigationItem.title = title;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)configuredHtmlWithText:(NSString *)htmlText {
|
||||
NSString *html = [htmlText stringByReplacingOccurrencesOfString:@"<body>"
|
||||
withString:@"<body><font face=\"helvetica\" size=\"14pt\">"];
|
||||
html = [html stringByReplacingOccurrencesOfString:@"</body>" withString:@"</font></body>"];
|
||||
return html;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthURL:(NSURL *)url onSuccessAuth:(MWMStringBlock)success onFailure:(MWMVoidBlock)failure {
|
||||
self = [super initWithNibName:nil bundle:nil];
|
||||
if (self) {
|
||||
_m_url = url;
|
||||
_onFailure = failure;
|
||||
_onSuccess = success;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
UIView *view = self.view;
|
||||
view.styleName = @"Background";
|
||||
|
||||
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero];
|
||||
self.webView.backgroundColor = UIColor.clearColor;
|
||||
self.webView.opaque = NO;
|
||||
self.webView.navigationDelegate = self;
|
||||
[view addSubview:self.webView];
|
||||
|
||||
self.webView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.webView.autoresizesSubviews = YES;
|
||||
NSLayoutYAxisAnchor *topAnchor = view.topAnchor;
|
||||
NSLayoutYAxisAnchor *bottomAnchor = view.bottomAnchor;
|
||||
NSLayoutXAxisAnchor *leadingAnchor = view.leadingAnchor;
|
||||
NSLayoutXAxisAnchor *trailingAnchor = view.trailingAnchor;
|
||||
UILayoutGuide *safeAreaLayoutGuide = view.safeAreaLayoutGuide;
|
||||
topAnchor = safeAreaLayoutGuide.topAnchor;
|
||||
bottomAnchor = safeAreaLayoutGuide.bottomAnchor;
|
||||
leadingAnchor = safeAreaLayoutGuide.leadingAnchor;
|
||||
trailingAnchor = safeAreaLayoutGuide.trailingAnchor;
|
||||
|
||||
[self.webView.topAnchor constraintEqualToAnchor:topAnchor].active = YES;
|
||||
[self.webView.bottomAnchor constraintEqualToAnchor:bottomAnchor].active = YES;
|
||||
[self.webView.leadingAnchor constraintEqualToAnchor:leadingAnchor].active = YES;
|
||||
[self.webView.trailingAnchor constraintEqualToAnchor:trailingAnchor].active = YES;
|
||||
|
||||
self.webView.allowsLinkPreview = NO;
|
||||
|
||||
[self performURLRequest];
|
||||
}
|
||||
|
||||
- (void)performURLRequest {
|
||||
__weak __typeof(self) ws = self;
|
||||
[self willLoadUrl:^(BOOL load, NSDictionary<NSString *, NSString *> *headers) {
|
||||
__typeof(self) self = ws;
|
||||
if (load) {
|
||||
if (self.m_htmlText) {
|
||||
[self.webView loadHTMLString:[self configuredHtmlWithText:self.m_htmlText] baseURL:self.m_url];
|
||||
} else {
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.m_url];
|
||||
for (NSString *header in headers.allKeys) {
|
||||
[request setValue:headers[header] forHTTPHeaderField:header];
|
||||
}
|
||||
|
||||
// if (self.shouldAddAccessToken) {
|
||||
// NSString *authHeader = [NSString stringWithFormat:@"Bearer %@", [MWMFrameworkHelper userAccessToken]];
|
||||
// [request setValue:authHeader forHTTPHeaderField:@"Authorization"];
|
||||
// }
|
||||
if ([UIColor isNightMode]) {
|
||||
[request setValue:@"dark" forHTTPHeaderField:@"x-mapsme-theme"];
|
||||
}
|
||||
[self.webView loadRequest:request];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)willLoadUrl:(WebViewControllerWillLoadBlock)decisionHandler {
|
||||
decisionHandler(YES, nil);
|
||||
}
|
||||
|
||||
- (BOOL)shouldAddAccessToken {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[super viewDidDisappear:animated];
|
||||
if (self.isMovingFromParentViewController && !self.authorized && self.onFailure)
|
||||
self.onFailure();
|
||||
}
|
||||
|
||||
- (void)pop {
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView
|
||||
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
||||
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
||||
NSURL *url = navigationAction.request.URL;
|
||||
// about:blank#localAnchor, e.g. in copyright.html.
|
||||
if ([url.scheme isEqualToString:@"about"]) {
|
||||
decisionHandler(WKNavigationActionPolicyAllow);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([url.host isEqualToString:@"localhost"]) {
|
||||
NSString *query = url.query;
|
||||
NSArray<NSString *> *components = [query componentsSeparatedByString:@"="];
|
||||
if (components.count != 2) {
|
||||
NSAssert(false, @"Incorrect query:", query);
|
||||
[self pop];
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
self.authorized = YES;
|
||||
[self pop];
|
||||
self.onSuccess(components[1]);
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.openInSafari && navigationAction.navigationType == WKNavigationTypeLinkActivated &&
|
||||
![url.scheme isEqualToString:@"applewebdata"]) // do not try to open local links in Safari
|
||||
{
|
||||
[UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.shouldResendHeaders) {
|
||||
decisionHandler(WKNavigationActionPolicyAllow);
|
||||
} else {
|
||||
_m_url = url;
|
||||
self.shouldResendHeaders = NO;
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
[self performURLRequest];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forward {
|
||||
[self.webView goForward];
|
||||
}
|
||||
|
||||
- (void)back {
|
||||
[self.webView goBack];
|
||||
}
|
||||
|
||||
- (void)reloadFromOrigin {
|
||||
self.shouldResendHeaders = YES;
|
||||
[self.webView reloadFromOrigin];
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
- (void)webView:(WKWebView *)webView
|
||||
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *_Nullable credential))completionHandler {
|
||||
NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust];
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
Loading…
Add table
Add a link
Reference in a new issue