Browse Source

initial implementation of a location tracking manager

cyberta 5 years ago
parent
commit
00a3ce38f5

+ 4 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -87,6 +87,7 @@
 		305FE03623A81B4C0053BE90 /* PaddingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305FE03523A81B4C0053BE90 /* PaddingLabel.swift */; };
 		3060119C22DDE24000C1CE6F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; };
 		306011B622E5E7FB00C1CE6F /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 306011B422E5E7FB00C1CE6F /* Localizable.stringsdict */; };
+		307D822E241669C7006D2490 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307D822D241669C7006D2490 /* LocationManager.swift */; };
 		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
@@ -308,6 +309,7 @@
 		306011C722E5E82E00C1CE6F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
 		306011C822E5E83100C1CE6F /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
 		306011C922E5E83500C1CE6F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+		307D822D241669C7006D2490 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
 		3095A350237DD1F700AB07F7 /* MediaPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPicker.swift; sourceTree = "<group>"; };
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
 		30AC265E237F1807002A943F /* AvatarHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHelper.swift; sourceTree = "<group>"; };
@@ -775,6 +777,7 @@
 				302B84C42396627F001C261F /* RelayHelper.swift */,
 				AE1988A423EB2FBA00B4CD5F /* Errors.swift */,
 				AEFBE23023FF09B20045327A /* TypeAlias.swift */,
+				307D822D241669C7006D2490 /* LocationManager.swift */,
 			);
 			path = Helper;
 			sourceTree = "<group>";
@@ -1233,6 +1236,7 @@
 				7092474120B3869500AF8799 /* ContactDetailViewController.swift in Sources */,
 				300C50A1234BDAB800F8AE22 /* TextMediaMessageSizeCalculator.swift in Sources */,
 				30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */,
+				307D822E241669C7006D2490 /* LocationManager.swift in Sources */,
 				305961F12346125100C80F33 /* ContactMessageCell.swift in Sources */,
 				AE851AD0227DF50900ED86F0 /* GroupChatDetailViewController.swift in Sources */,
 				305961D12346125100C80F33 /* Bundle+Extensions.swift in Sources */,

+ 11 - 0
deltachat-ios/Helper/Constants.swift

@@ -11,6 +11,8 @@ struct Constants {
         static let deltachatImapPasswordKey = "__DELTACHAT_IMAP_PASSWORD_KEY__"
     }
 
+
+
     static let defaultShadow = UIImage(color: UIColor(hexString: "ff2b82"), size: CGSize(width: 1, height: 1))
     static let onlineShadow = UIImage(color: UIColor(hexString: "3ed67e"), size: CGSize(width: 1, height: 1))
 
@@ -19,3 +21,12 @@ struct Constants {
     static let defaultCellHeight: CGFloat = 48
     static let defaultHeaderHeight: CGFloat = 20
 }
+
+struct Time {
+    static let twoMinutes = 2 * 60
+    static let fiveMinutes = 5 * 60
+    static let thirtyMinutes = 30 * 6
+    static let oneHour = 60 * 60
+    static let twoHours = 2 * 60 * 60
+    static let sixHours = 6 * 60 * 60
+}

+ 104 - 8
deltachat-ios/Helper/LocationManager.swift

@@ -1,9 +1,105 @@
-//
-//  LocationManager.swift
-//  deltachat-ios
-//
-//  Created by Macci on 09.03.20.
-//  Copyright © 2020 Jonas Reinsch. All rights reserved.
-//
-
 import Foundation
+import CoreLocation
+
+class LocationManager: NSObject, CLLocationManagerDelegate {
+
+    let locationManager: CLLocationManager
+    let dcContext: DcContext
+    var lastLocation: CLLocation?
+
+    init(context: DcContext) {
+        dcContext = context
+        locationManager = CLLocationManager()
+        locationManager.distanceFilter = 50
+        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
+        //locationManager.allowsBackgroundLocationUpdates = true
+        locationManager.pausesLocationUpdatesAutomatically = true
+        //TODO: check which activity Type is needed
+        locationManager.activityType = CLActivityType.other
+        super.init()
+        locationManager.delegate = self
+
+    }
+
+    func shareLocation(chatId: Int, duration: Int) {
+        dcContext.sendLocationsToChat(chatId: chatId, seconds: duration)
+        if duration > 0 {
+            startLocationTracking()
+        } else {
+            stopLocationTracking()
+        }
+    }
+
+    func startLocationTracking() {
+        locationManager.requestAlwaysAuthorization()
+        locationManager.startUpdatingLocation()
+
+    }
+
+    func stopLocationTracking() {
+        locationManager.stopUpdatingLocation()
+
+    }
+
+    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+        logger.debug("LOCATION: didUpdateLocations")
+
+        guard let newLocation = locations.last else {
+            logger.debug("LOCATION: new location is emtpy")
+            return
+        }
+
+        let isBetter = isBetterLocation(newLocation: newLocation, lastLocation: lastLocation)
+        logger.debug("LOCATION: isBetterLocation: \(isBetter)")
+        if isBetter {
+            dcContext.setLocation(latitude: newLocation.coordinate.latitude, longitude: newLocation.coordinate.longitude, accuracy: newLocation.horizontalAccuracy)
+            lastLocation = newLocation
+        }
+    }
+
+    func isBetterLocation(newLocation: CLLocation, lastLocation: CLLocation?) -> Bool {
+        guard let lastLocation = lastLocation else {
+            return !isNewLocationOutdated(newLocation: newLocation) && hasValidAccuracy(newLocation: newLocation)
+        }
+
+        return !isNewLocationOutdated(newLocation: newLocation) &&
+            hasValidAccuracy(newLocation: newLocation) &&
+            (isSignificantlyMoreAccurate(newLocation: newLocation, lastLocation: lastLocation) ||
+            isMoreAccurate(newLocation: newLocation, lastLocation: lastLocation) && hasLocationChanged(newLocation: newLocation, lastLocation: lastLocation) ||
+            hasLocationSignificantlyChanged(newLocation: newLocation, lastLocation: lastLocation))
+    }
+
+    func hasValidAccuracy(newLocation: CLLocation) -> Bool {
+        logger.debug("LOCATION: hasValidAccuracy: \(newLocation.horizontalAccuracy > 0)")
+        return newLocation.horizontalAccuracy > 0
+    }
+
+    func isSignificantlyMoreAccurate(newLocation: CLLocation, lastLocation: CLLocation) -> Bool {
+        logger.debug("LOCATION isSignificantlyMoreAccurate: \(lastLocation.horizontalAccuracy - newLocation.horizontalAccuracy > 25)")
+        return lastLocation.horizontalAccuracy - newLocation.horizontalAccuracy > 25
+    }
+
+    func isMoreAccurate(newLocation: CLLocation, lastLocation: CLLocation) -> Bool {
+        logger.debug("LOCATION: isMoreAccurate \(lastLocation.horizontalAccuracy - newLocation.horizontalAccuracy > 0)")
+        return lastLocation.horizontalAccuracy - newLocation.horizontalAccuracy > 0
+    }
+
+    func hasLocationChanged(newLocation: CLLocation, lastLocation: CLLocation) -> Bool {
+        logger.debug("LOCATION: hasLocationChanged \(newLocation.distance(from: lastLocation) > 10)")
+        return newLocation.distance(from: lastLocation) > 10
+    }
+
+    func hasLocationSignificantlyChanged(newLocation: CLLocation, lastLocation: CLLocation) -> Bool {
+        logger.debug("LOCATION: hasLocationSignificantlyChanged \(newLocation.distance(from: lastLocation) > 30)")
+        return newLocation.distance(from: lastLocation) > 30
+    }
+
+    /**
+        Locations can be cached by iOS, timestamp comparison checks if the location has been tracked within the last 5 minutes
+     */
+    func isNewLocationOutdated(newLocation: CLLocation) -> Bool{
+        let timeDelta = DateUtils.getRelativeTimeInSeconds(timeStamp: Double(newLocation.timestamp.timeIntervalSince1970))
+        return timeDelta < Double(Time.fiveMinutes)
+    }
+    
+}

+ 2 - 2
deltachat-ios/Helper/Utils.swift

@@ -131,7 +131,7 @@ struct Utils {
         }
 
         do {
-            let timestamp = Int(Date().timeIntervalSince1970)
+            let timestamp = Double(Date().timeIntervalSince1970)
             let path = directory.appendingPathComponent("\(timestamp).jpg")
             try data.write(to: path!)
             return path?.relativePath
@@ -178,7 +178,7 @@ class DateUtils {
     static let day: Double = 86400
     static let year: Double = 365 * day
 
-    private static func getRelativeTimeInSeconds(timeStamp: Double) -> Double {
+    static func getRelativeTimeInSeconds(timeStamp: Double) -> Double {
         let unixTime = Double(Date().timeIntervalSince1970)
         return unixTime - timeStamp
     }