Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
159
iphone/Maps/Core/DeepLink/DeepLinkHandler.swift
Normal file
159
iphone/Maps/Core/DeepLink/DeepLinkHandler.swift
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
@objc @objcMembers class DeepLinkHandler: NSObject {
|
||||
static let shared = DeepLinkHandler()
|
||||
|
||||
private(set) var isLaunchedByDeeplink = false
|
||||
private(set) var isLaunchedByUniversalLink = false
|
||||
private(set) var url: URL?
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ options: [UIApplication.LaunchOptionsKey : Any]? = nil) {
|
||||
if let launchDeeplink = options?[UIApplication.LaunchOptionsKey.url] as? URL {
|
||||
isLaunchedByDeeplink = true
|
||||
url = launchDeeplink
|
||||
}
|
||||
}
|
||||
|
||||
func applicationDidOpenUrl(_ url: URL) -> Bool {
|
||||
// File reading should be processed synchronously to avoid permission issues (the Files app will close the file for reading when the application:openURL:options returns).
|
||||
if url.isFileURL {
|
||||
return handleFileImport(url: url)
|
||||
}
|
||||
|
||||
// On the cold start, isLaunchedByDeeplink is set and handleDeepLink() call is delayed
|
||||
// until the map view will be fully initialized.
|
||||
guard !isLaunchedByDeeplink else { return true }
|
||||
|
||||
// On the hot start, link can be processed immediately.
|
||||
self.url = url
|
||||
return handleDeepLink(url: url)
|
||||
}
|
||||
|
||||
func applicationDidReceiveUniversalLink(_ universalLink: URL) -> Bool {
|
||||
// Convert http(s)://comaps.at/ENCODEDCOORDS/NAME to cm://ENCODEDCOORDS/NAME
|
||||
self.url = URL(string: universalLink.absoluteString
|
||||
.replacingOccurrences(of: "http://comaps.at", with: "cm:/")
|
||||
.replacingOccurrences(of: "https://comaps.at", with: "cm:/"))
|
||||
isLaunchedByUniversalLink = true
|
||||
return handleDeepLink(url: self.url!)
|
||||
}
|
||||
|
||||
func reset() {
|
||||
isLaunchedByDeeplink = false
|
||||
isLaunchedByUniversalLink = false
|
||||
url = nil
|
||||
}
|
||||
|
||||
func getBackUrl() -> String? {
|
||||
guard let urlString = url?.absoluteString else { return nil }
|
||||
guard let url = URLComponents(string: urlString) else { return nil }
|
||||
return (url.queryItems?.first(where: { $0.name == "backurl" })?.value ?? nil)
|
||||
}
|
||||
|
||||
func getInAppFeatureHighlightData() -> DeepLinkInAppFeatureHighlightData? {
|
||||
guard (isLaunchedByUniversalLink || isLaunchedByDeeplink), let url else { return nil }
|
||||
reset()
|
||||
return DeepLinkInAppFeatureHighlightData(DeepLinkParser.parseAndSetApiURL(url))
|
||||
}
|
||||
|
||||
func handleDeepLinkAndReset() -> Bool {
|
||||
if let url {
|
||||
let result = handleDeepLink(url: url)
|
||||
reset()
|
||||
return result
|
||||
}
|
||||
LOG(.error, "handleDeepLink is called with nil URL")
|
||||
return false
|
||||
}
|
||||
|
||||
private func handleFileImport(url: URL) -> Bool {
|
||||
LOG(.info, "handleFileImport: \(url)")
|
||||
let fileCoordinator = NSFileCoordinator()
|
||||
var error: NSError?
|
||||
fileCoordinator.coordinate(readingItemAt: url, options: [], error: &error) { fileURL in
|
||||
DeepLinkParser.addBookmarksFile(fileURL)
|
||||
}
|
||||
if let error {
|
||||
LOG(.error, "Failed to read file: \(error)")
|
||||
}
|
||||
reset()
|
||||
return true
|
||||
}
|
||||
|
||||
private func handleDeepLink(url: URL) -> Bool {
|
||||
LOG(.info, "handleDeepLink: \(url)")
|
||||
// TODO(AB): Rewrite API so iOS and Android will call only one C++ method to clear/set API state.
|
||||
// This call is also required for DeepLinkParser.showMap, and it also clears old API points...
|
||||
let urlType = DeepLinkParser.parseAndSetApiURL(url)
|
||||
LOG(.info, "URL type: \(urlType)")
|
||||
switch urlType {
|
||||
case .route:
|
||||
if let adapter = DeepLinkRouteStrategyAdapter(url) {
|
||||
MWMRouter.buildApiRoute(with: adapter.type, start: adapter.p1, finish: adapter.p2)
|
||||
MapsAppDelegate.theApp().showMap()
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
case .map:
|
||||
DeepLinkParser.executeMapApiRequest()
|
||||
MapsAppDelegate.theApp().showMap()
|
||||
return true
|
||||
case .search:
|
||||
let sd = DeepLinkSearchData();
|
||||
let kSearchInViewportZoom: Int32 = 16;
|
||||
// Set viewport only when cll parameter was provided in url.
|
||||
// Equator and Prime Meridian are perfectly valid separately.
|
||||
if (sd.hasValidCenterLatLon()) {
|
||||
MapViewController.setViewport(sd.centerLat, lon: sd.centerLon, zoomLevel: kSearchInViewportZoom)
|
||||
// Need to update viewport for search API manually because Drape engine
|
||||
// will not notify subscribers when search view is shown.
|
||||
if (!sd.isSearchOnMap) {
|
||||
sd.onViewportChanged(kSearchInViewportZoom)
|
||||
}
|
||||
}
|
||||
let searchQuery = SearchQuery(sd.query, locale: sd.locale, source: .deeplink)
|
||||
if (sd.isSearchOnMap) {
|
||||
MWMMapViewControlsManager.manager()?.search(onMap: searchQuery)
|
||||
} else {
|
||||
MWMMapViewControlsManager.manager()?.search(searchQuery)
|
||||
}
|
||||
return true
|
||||
case .menu:
|
||||
MapsAppDelegate.theApp().mapViewController.openMenu()
|
||||
return true
|
||||
case .settings:
|
||||
MapsAppDelegate.theApp().mapViewController.openSettings()
|
||||
return true
|
||||
case .crosshair:
|
||||
// Not supported on iOS.
|
||||
return false;
|
||||
case .oAuth2:
|
||||
var components = url.absoluteString.components(separatedBy: "cm://oauth2/osm/callback?code=")
|
||||
components.removeAll { component in
|
||||
component.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
if let code = components.first {
|
||||
Task(priority: .userInitiated) {
|
||||
await Profile.saveAuthorizationToken(from: code)
|
||||
DispatchQueue.main.sync {
|
||||
NotificationCenter.default.post(name: SafariView.dismissNotificationName, object: nil)
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .incorrect:
|
||||
if url.absoluteString.starts(with: "cm://oauth2/osm/callback") {
|
||||
NotificationCenter.default.post(name: SafariView.dismissNotificationName, object: nil)
|
||||
}
|
||||
// Invalid URL or API parameters.
|
||||
return false;
|
||||
@unknown default:
|
||||
LOG(.critical, "Unknown URL type: \(urlType)")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "MWMRouterType.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MWMRoutePoint;
|
||||
@interface DeepLinkRouteStrategyAdapter : NSObject
|
||||
|
||||
@property(nonatomic, readonly) MWMRoutePoint* p1;
|
||||
@property(nonatomic, readonly) MWMRoutePoint* p2;
|
||||
@property(nonatomic, readonly) MWMRouterType type;
|
||||
|
||||
- (nullable instancetype)init:(NSURL*)url;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#import "DeepLinkRouteStrategyAdapter.h"
|
||||
#import <CoreApi/Framework.h>
|
||||
#import "MWMCoreRouterType.h"
|
||||
#import "MWMRoutePoint+CPP.h"
|
||||
|
||||
@implementation DeepLinkRouteStrategyAdapter
|
||||
|
||||
- (instancetype)init:(NSURL *)url {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
auto const parsedData = GetFramework().GetParsedRoutingData();
|
||||
auto const points = parsedData.m_points;
|
||||
|
||||
if (points.size() == 2) {
|
||||
_p1 = [[MWMRoutePoint alloc] initWithURLSchemeRoutePoint:points.front()
|
||||
type:MWMRoutePointTypeStart
|
||||
intermediateIndex:0];
|
||||
_p2 = [[MWMRoutePoint alloc] initWithURLSchemeRoutePoint:points.back()
|
||||
type:MWMRoutePointTypeFinish
|
||||
intermediateIndex:0];
|
||||
_type = routerType(parsedData.m_type);
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Add table
Add a link
Reference in a new issue