diff --git a/CHANGELOG.md b/CHANGELOG.md index dfeed6a5..91ad0df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ OwnTracks iOS App Release Notes =================================== +## OwnTracks 16.3.0 iOS/ipadOS/macOS +* Release Date 2022-09-08 + +** Danish (Dansk) Translation and bug fixes + + [NEW] Danish Translation + [FIX] mark positions triggered by "visits" correctly + +## OwnTracks 16.2.5 iOS/ipadOS/macOS +* Release Date 2022-08-08 + +** Sharing with non-OwnTracks users, Card editing + + [NEW] lock all changes to configuration with by 'locked' setting #707 + [NEW] create/list/delete tours in collaboration with ot-recorder + [NEW] Edit card info and photo + ## OwnTracks 16.1.3 iOS/ipadOS/macOS * Release Date 2022-02-17 diff --git a/OwnTracks/OwnTracks.xcodeproj/project.pbxproj b/OwnTracks/OwnTracks.xcodeproj/project.pbxproj index c00f03f2..137bec7c 100644 --- a/OwnTracks/OwnTracks.xcodeproj/project.pbxproj +++ b/OwnTracks/OwnTracks.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 84929F0B20AAC0D000227D00 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84929F0A20AAC0CF00227D00 /* WebKit.framework */; }; 84930A2C1B3755AF00810C3A /* TabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84930A2B1B3755AF00810C3A /* TabBarController.m */; }; 849B1E061FE911FD0016EF1A /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 849B1E081FE911FD0016EF1A /* MainInterface.storyboard */; }; + 849CE153289CF2FF004B32BF /* ToursStatusCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 849CE152289CF2FF004B32BF /* ToursStatusCell.m */; }; 84A488E21F7BBFD900ADCF98 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84A488E11F7BBFD900ADCF98 /* Media.xcassets */; }; 84AFD3DC2434380700F1E922 /* OwnTracksWrist WatchKit App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 84AFD3DB2434380700F1E922 /* OwnTracksWrist WatchKit App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 84AFD3E22434380700F1E922 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84AFD3E02434380700F1E922 /* Interface.storyboard */; }; @@ -79,6 +80,10 @@ 84B7397A1A65629200B103F4 /* LocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 84B739791A65629200B103F4 /* LocationManager.m */; }; 84BD03AA25D5301F0077B9B8 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84AE8ADB25C165D600C0B0EE /* MapKit.framework */; }; 84BE759827720BD2006CBDCF /* OwnTracksWrist WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 84AFD3EA2434380A00F1E922 /* OwnTracksWrist WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 84C7C02C2885B18E0088A544 /* CreateCardTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C7C02B2885B18E0088A544 /* CreateCardTVC.m */; }; + 84C7C02F2885B1B00088A544 /* CreateTourTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C7C02E2885B1B00088A544 /* CreateTourTVC.m */; }; + 84C7C032289821280088A544 /* ToursTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C7C031289821280088A544 /* ToursTVC.m */; }; + 84C7C0352898FAF90088A544 /* Tours.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C7C0342898FAF90088A544 /* Tours.m */; }; 84CC5A521FEC71850010314C /* OwnTracks-29.png in Resources */ = {isa = PBXBuildFile; fileRef = 84CC5A511FEC71840010314C /* OwnTracks-29.png */; }; 84D3072F1ACD644500FACB9E /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D3072E1ACD644500FACB9E /* NotificationCenter.framework */; }; 84D307351ACD644500FACB9E /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D307341ACD644500FACB9E /* TodayViewController.m */; }; @@ -298,7 +303,6 @@ 847D5C7426121ED400B6DCD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 847D5C7526121ED600B6DCD5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 847D5C7626121F4200B6DCD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 847D5C7F2612227C00B6DCD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Storyboard.strings; sourceTree = ""; }; 847D5C80261224CF00B6DCD5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 847D5C81261224CF00B6DCD5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 847D5C82261224CF00B6DCD5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Interface.strings; sourceTree = ""; }; @@ -311,9 +315,17 @@ 847F8B5719295C65003876D4 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; }; 84856A8B1BD6201800621533 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 848794B119A6234B00DF56EB /* Model 7.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 7.3.xcdatamodel"; sourceTree = ""; }; + 848806F528C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Intents.strings; sourceTree = ""; }; + 848806F628C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Storyboard.strings; sourceTree = ""; }; + 848806F728C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; + 848806F828C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + 848806F928C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; + 848806FA28C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + 848806FB28C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainInterface.strings; sourceTree = ""; }; + 848806FC28C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; + 848806FD28C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Interface.strings; sourceTree = ""; }; + 848806FE28C9E7A500F7DFFD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; 8488CFDE25A45CC3000AFEE2 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; - 8489EDCE2611D97A00A8B2DF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Storyboard.strings; sourceTree = ""; }; - 8489EDD02611D9EF00A8B2DF /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Storyboard.strings; sourceTree = ""; }; 84929F0A20AAC0CF00227D00 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 84930A221B36B8FB00810C3A /* Model 8.1.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 8.1.1.xcdatamodel"; sourceTree = ""; }; 84930A261B36C21B00810C3A /* Model 8.1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 8.1.2.xcdatamodel"; sourceTree = ""; }; @@ -330,6 +342,14 @@ 849B359924A7578000BEC1BB /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Intents.framework; sourceTree = DEVELOPER_DIR; }; 849B3C0F242F9EFC0052EE6D /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.2.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; }; 849B3C10242F9F190052EE6D /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.2.sdk/System/Library/Frameworks/Network.framework; sourceTree = DEVELOPER_DIR; }; + 849CE151289CF2FF004B32BF /* ToursStatusCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToursStatusCell.h; sourceTree = ""; }; + 849CE152289CF2FF004B32BF /* ToursStatusCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToursStatusCell.m; sourceTree = ""; }; + 849CE165289FB836004B32BF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Storyboard.strings; sourceTree = ""; }; + 849CE167289FB86C004B32BF /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Storyboard.strings; sourceTree = ""; }; + 849CE169289FB878004B32BF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Storyboard.strings; sourceTree = ""; }; + 849CE16B289FB8AB004B32BF /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Storyboard.strings; sourceTree = ""; }; + 849CE16D289FB9D5004B32BF /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Storyboard.strings; sourceTree = ""; }; + 849CE16F289FBA06004B32BF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Storyboard.strings; sourceTree = ""; }; 849D457B18CDACDF00201BF3 /* MQTT.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = MQTT.plist; sourceTree = ""; }; 84A173691B428E5C00CCB7D0 /* Model 8.2.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 8.2.1.xcdatamodel"; sourceTree = ""; }; 84A488E11F7BBFD900ADCF98 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; @@ -361,7 +381,6 @@ 84B03514261D9506000F2ECA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; 84B03515261D9506000F2ECA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Intents.strings; sourceTree = ""; }; 84B03516261D9506000F2ECA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; - 84B03517261D9506000F2ECA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Storyboard.strings; sourceTree = ""; }; 84B03518261D9506000F2ECA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Interface.strings; sourceTree = ""; }; 84B03519261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; 84B0351A261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -371,12 +390,10 @@ 84B0351E261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MainInterface.strings; sourceTree = ""; }; 84B0351F261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = ""; }; 84B03520261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; - 84B03521261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Storyboard.strings; sourceTree = ""; }; 84B03522261D95F2000F2ECA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Interface.strings; sourceTree = ""; }; 84B0CDAC239E2EFA00CE9305 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/BackgroundTasks.framework; sourceTree = DEVELOPER_DIR; }; 84B739781A65629200B103F4 /* LocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocationManager.h; sourceTree = ""; }; 84B739791A65629200B103F4 /* LocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocationManager.m; sourceTree = ""; }; - 84BE758C2771EAD9006CBDCF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Storyboard.strings; sourceTree = ""; }; 84BE758D2771EADA006CBDCF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Intents.strings; sourceTree = ""; }; 84BE758E2771EADA006CBDCF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/MainInterface.strings; sourceTree = ""; }; 84BE758F2771EADD006CBDCF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Interface.strings; sourceTree = ""; }; @@ -390,6 +407,14 @@ 84BE75972771EBA2006CBDCF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; 84C38C14193F88C200F56D04 /* Model 7.2.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 7.2.2.xcdatamodel"; sourceTree = ""; }; 84C756A61A89F706003BC8A8 /* Model 7.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 7.4.xcdatamodel"; sourceTree = ""; }; + 84C7C02A2885B18E0088A544 /* CreateCardTVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CreateCardTVC.h; sourceTree = ""; }; + 84C7C02B2885B18E0088A544 /* CreateCardTVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CreateCardTVC.m; sourceTree = ""; }; + 84C7C02D2885B1B00088A544 /* CreateTourTVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CreateTourTVC.h; sourceTree = ""; }; + 84C7C02E2885B1B00088A544 /* CreateTourTVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CreateTourTVC.m; sourceTree = ""; }; + 84C7C030289821280088A544 /* ToursTVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToursTVC.h; sourceTree = ""; }; + 84C7C031289821280088A544 /* ToursTVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToursTVC.m; sourceTree = ""; }; + 84C7C0332898FAF90088A544 /* Tours.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tours.h; sourceTree = ""; }; + 84C7C0342898FAF90088A544 /* Tours.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tours.m; sourceTree = ""; }; 84CC5A511FEC71840010314C /* OwnTracks-29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "OwnTracks-29.png"; sourceTree = ""; }; 84D3072D1ACD644500FACB9E /* OwnTracksToday.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OwnTracksToday.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 84D3072E1ACD644500FACB9E /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; @@ -617,6 +642,8 @@ 8400D9721D83825E00DEA134 /* HTTP.plist */, 8411B88F189FE9CE0019D2D6 /* Settings.h */, 8411B890189FE9CE0019D2D6 /* Settings.m */, + 84C7C0332898FAF90088A544 /* Tours.h */, + 84C7C0342898FAF90088A544 /* Tours.m */, 84664E9B1CC83A38002123EC /* NSBundle+privateLocalization.h */, 84664E9C1CC83A38002123EC /* NSBundle+privateLocalization.m */, 84D6B47B25C844A500B2AC07 /* NSNumber+decimals.h */, @@ -763,6 +790,14 @@ 8411B909189FEC070019D2D6 /* StatusTVC.m */, 845AA1B21B44245100945856 /* CertificatesTVC.h */, 845AA1B31B44245100945856 /* CertificatesTVC.m */, + 84C7C02A2885B18E0088A544 /* CreateCardTVC.h */, + 84C7C02B2885B18E0088A544 /* CreateCardTVC.m */, + 84C7C030289821280088A544 /* ToursTVC.h */, + 84C7C031289821280088A544 /* ToursTVC.m */, + 849CE151289CF2FF004B32BF /* ToursStatusCell.h */, + 849CE152289CF2FF004B32BF /* ToursStatusCell.m */, + 84C7C02D2885B1B00088A544 /* CreateTourTVC.h */, + 84C7C02E2885B1B00088A544 /* CreateTourTVC.m */, ); name = Settings; sourceTree = ""; @@ -1036,6 +1071,7 @@ nl, sv, tr, + da, ); mainGroup = 8411B81F189FDB140019D2D6; productRefGroup = 8411B829189FDB140019D2D6 /* Products */; @@ -1234,8 +1270,10 @@ 8411B83A189FDB140019D2D6 /* main.m in Sources */, 8411B90C189FEC070019D2D6 /* WaypointTVC.m in Sources */, 84DE749824A9E1C600C0DA68 /* Intents.intentdefinition in Sources */, + 84C7C02C2885B18E0088A544 /* CreateCardTVC.m in Sources */, 8411B910189FEC070019D2D6 /* PersonTVC.m in Sources */, 84E402082314140400F1D6E6 /* HistoryTVC.m in Sources */, + 84C7C02F2885B1B00088A544 /* CreateTourTVC.m in Sources */, 846FFA581B419687001482A6 /* RegionTVC.m in Sources */, 8411B912189FEC070019D2D6 /* ViewController.m in Sources */, 84930A2C1B3755AF00810C3A /* TabBarController.m in Sources */, @@ -1261,6 +1299,7 @@ 842E6ED520BEF031001928FA /* Setting+CoreDataClass.m in Sources */, 8411B83E189FDB140019D2D6 /* OwnTracksAppDelegate.m in Sources */, 84DDEAC71BD9F37900B01879 /* SettingsTVC.m in Sources */, + 84C7C032289821280088A544 /* ToursTVC.m in Sources */, 845AA1B41B44245100945856 /* CertificatesTVC.m in Sources */, 84B7397A1A65629200B103F4 /* LocationManager.m in Sources */, 847D5C8C2612554B00B6DCD5 /* ModesTVC.m in Sources */, @@ -1270,6 +1309,8 @@ 8411B88E189FE9940019D2D6 /* Connection.m in Sources */, 84E40205231413D400F1D6E6 /* History+CoreDataProperties.m in Sources */, 84E8A18D1BB94782002518AA /* Region+CoreDataProperties.m in Sources */, + 84C7C0352898FAF90088A544 /* Tours.m in Sources */, + 849CE153289CF2FF004B32BF /* ToursStatusCell.m in Sources */, 8404876B1C5386DD00569C79 /* FeaturedContentVC.m in Sources */, 842E6ECE20BEE96F001928FA /* Region+CoreDataClass.m in Sources */, ); @@ -1359,6 +1400,7 @@ 84B03516261D9506000F2ECA /* nl */, 84B03520261D95F2000F2ECA /* sv */, 84BE75912771EADF006CBDCF /* tr */, + 848806F828C9E7A500F7DFFD /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -1373,6 +1415,7 @@ 84B0350F261D9506000F2ECA /* nl */, 84B03519261D95F2000F2ECA /* sv */, 84BE75942771EAE0006CBDCF /* tr */, + 848806FA28C9E7A500F7DFFD /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -1381,12 +1424,13 @@ isa = PBXVariantGroup; children = ( 843B91731C8A14BF00DBD306 /* Base */, - 8489EDCE2611D97A00A8B2DF /* de */, - 8489EDD02611D9EF00A8B2DF /* pl */, - 847D5C7F2612227C00B6DCD5 /* en */, - 84B03517261D9506000F2ECA /* nl */, - 84B03521261D95F2000F2ECA /* sv */, - 84BE758C2771EAD9006CBDCF /* tr */, + 849CE165289FB836004B32BF /* en */, + 849CE167289FB86C004B32BF /* nl */, + 849CE169289FB878004B32BF /* de */, + 849CE16B289FB8AB004B32BF /* pl */, + 849CE16D289FB9D5004B32BF /* sv */, + 849CE16F289FBA06004B32BF /* tr */, + 848806F628C9E7A500F7DFFD /* da */, ); name = Storyboard.storyboard; sourceTree = ""; @@ -1400,6 +1444,7 @@ 84B03514261D9506000F2ECA /* nl */, 84B0351D261D95F2000F2ECA /* sv */, 84BE75952771EAE2006CBDCF /* tr */, + 848806FE28C9E7A500F7DFFD /* da */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1412,6 +1457,7 @@ 84B03512261D9506000F2ECA /* nl */, 84B0351B261D95F2000F2ECA /* sv */, 84BE75962771EBA2006CBDCF /* tr */, + 848806F928C9E7A500F7DFFD /* da */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1424,6 +1470,7 @@ 84B03510261D9506000F2ECA /* nl */, 84B0351A261D95F2000F2ECA /* sv */, 84BE75972771EBA2006CBDCF /* tr */, + 848806FC28C9E7A500F7DFFD /* da */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1438,6 +1485,7 @@ 84B03511261D9506000F2ECA /* nl */, 84B0351E261D95F2000F2ECA /* sv */, 84BE758E2771EADA006CBDCF /* tr */, + 848806FB28C9E7A500F7DFFD /* da */, ); name = MainInterface.storyboard; sourceTree = ""; @@ -1451,6 +1499,7 @@ 84B03518261D9506000F2ECA /* nl */, 84B03522261D95F2000F2ECA /* sv */, 84BE758F2771EADD006CBDCF /* tr */, + 848806FD28C9E7A500F7DFFD /* da */, ); name = Interface.storyboard; sourceTree = ""; @@ -1465,6 +1514,7 @@ 84B03515261D9506000F2ECA /* nl */, 84B0351F261D95F2000F2ECA /* sv */, 84BE758D2771EADA006CBDCF /* tr */, + 848806F528C9E7A500F7DFFD /* da */, ); name = Intents.intentdefinition; sourceTree = ""; @@ -1496,6 +1546,7 @@ 84B03513261D9506000F2ECA /* nl */, 84B0351C261D95F2000F2ECA /* sv */, 84BE75932771EAE0006CBDCF /* tr */, + 848806F728C9E7A500F7DFFD /* da */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1617,7 +1668,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = NO; DEVELOPMENT_TEAM = C652JFWU23; ENABLE_BITCODE = YES; @@ -1640,7 +1691,7 @@ "$(PROJECT_DIR)", "$(PROJECT_DIR)/OwnTracks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.mqttitude.MQTTitude; "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = maccatalyst.org.mqttitude.MQTTitude; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1668,7 +1719,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = NO; DEVELOPMENT_TEAM = C652JFWU23; ENABLE_BITCODE = YES; @@ -1691,7 +1742,7 @@ "$(PROJECT_DIR)", "$(PROJECT_DIR)/OwnTracks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.mqttitude.MQTTitude; "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = maccatalyst.org.mqttitude.MQTTitude; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1720,7 +1771,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1733,7 +1784,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.mqttitude.MQTTitude.OwnTracksIntents; @@ -1759,7 +1810,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1772,7 +1823,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.mqttitude.MQTTitude.OwnTracksIntents; @@ -1793,12 +1844,12 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist; @@ -1817,12 +1868,12 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist; @@ -1841,14 +1892,14 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; IBSC_MODULE = OwnTracksWrist_WatchKit_Extension; INFOPLIST_FILE = "OwnTracksWrist WatchKit App/Info.plist"; - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist.watchkitapp; @@ -1872,14 +1923,14 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; IBSC_MODULE = OwnTracksWrist_WatchKit_Extension; INFOPLIST_FILE = "OwnTracksWrist WatchKit App/Info.plist"; - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist.watchkitapp; @@ -1903,7 +1954,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1914,7 +1965,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist.watchkitapp.watchkitextension; @@ -1939,7 +1990,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = C652JFWU23; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1950,7 +2001,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.owntracks.OwnTracksWrist.watchkitapp.watchkitextension; @@ -1971,7 +2022,7 @@ CODE_SIGN_ENTITLEMENTS = OwnTracksToday/OwnTracksToday.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -1982,7 +2033,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.mqttitude.MQTTitude.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2002,7 +2053,7 @@ CODE_SIGN_ENTITLEMENTS = OwnTracksToday/OwnTracksToday.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 16.1.3; + CURRENT_PROJECT_VERSION = 16.3.0; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = OwnTracksToday/Info.plist; @@ -2012,7 +2063,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 16.1.3; + MARKETING_VERSION = 16.3.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "org.mqttitude.MQTTitude.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/OwnTracks/OwnTracks/Base.lproj/Localizable.strings b/OwnTracks/OwnTracks/Base.lproj/Localizable.strings index f4211d74..ced9e412 100644 Binary files a/OwnTracks/OwnTracks/Base.lproj/Localizable.strings and b/OwnTracks/OwnTracks/Base.lproj/Localizable.strings differ diff --git a/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info-Mac.plist b/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info-Mac.plist index 6dfaacee..f4826752 100644 --- a/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info-Mac.plist +++ b/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info-Mac.plist @@ -147,6 +147,8 @@ No information of your address book will be uploaded to any server. Your location is used to share and record it on a server of your choice NSMotionUsageDescription OwnTracks delivers your step count, floors climbed and distance walked on request + NSCameraUsageDescription + OwnTracks uses your camera on request to create a Card NSUserActivityTypes NSUserActivityTypeBrowsingWeb diff --git a/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info.plist b/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info.plist index 47c8f024..3a9bcbb6 100644 --- a/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info.plist +++ b/OwnTracks/OwnTracks/Base.lproj/OwnTracks-Info.plist @@ -133,6 +133,8 @@ NSAllowsArbitraryLoads + NSCameraUsageDescription + OwnTracks uses your camera on request to create a Card NSContactsUsageDescription If you allow OwnTracks to access your contacts, you can link your devices to contacts. OwnTracks will then display the contact name and image instead of the device Id. diff --git a/OwnTracks/OwnTracks/Base.lproj/Storyboard.storyboard b/OwnTracks/OwnTracks/Base.lproj/Storyboard.storyboard index 88f80253..40508df6 100644 --- a/OwnTracks/OwnTracks/Base.lproj/Storyboard.storyboard +++ b/OwnTracks/OwnTracks/Base.lproj/Storyboard.storyboard @@ -1,9 +1,9 @@ - + - + @@ -67,6 +67,7 @@ + @@ -87,7 +88,7 @@ - + @@ -255,9 +256,45 @@ - + + + + + + + + + + + + + + + + + + + + + @@ -308,7 +345,7 @@ - + @@ -342,7 +379,7 @@ - + @@ -402,7 +439,7 @@ - + @@ -468,7 +505,7 @@ - + @@ -499,7 +536,7 @@ - + @@ -532,7 +569,7 @@ - + @@ -575,7 +612,7 @@ - + @@ -606,7 +643,7 @@ - + @@ -640,15 +677,15 @@ - - + + - + - - - - - - - - - + + + + + + + + - - + + - + - - - - - - - - - + + + + + + + + - + @@ -797,7 +834,7 @@ - + @@ -849,7 +886,7 @@ - + @@ -883,7 +920,7 @@ - + @@ -920,7 +957,7 @@ - + @@ -954,7 +991,7 @@ - + @@ -988,7 +1025,7 @@ - + @@ -1022,7 +1059,7 @@ - + @@ -1056,7 +1093,7 @@ - + @@ -1093,7 +1130,7 @@ - + @@ -1127,7 +1164,7 @@ - + @@ -1164,7 +1201,7 @@ - + @@ -1201,7 +1238,7 @@ - + @@ -1238,7 +1275,7 @@ - + @@ -1275,7 +1312,7 @@ - + @@ -1311,7 +1348,7 @@ - + @@ -1347,7 +1384,7 @@ - + @@ -1383,7 +1420,7 @@ - + @@ -1419,7 +1456,7 @@ - + @@ -1455,7 +1492,7 @@ - + @@ -1491,7 +1528,7 @@ - + @@ -1527,7 +1564,7 @@ - + @@ -1563,7 +1600,7 @@ - + @@ -1660,6 +1697,8 @@ + + @@ -1871,7 +1910,7 @@ - + @@ -1907,15 +1946,10 @@ - + - - - - - @@ -2243,6 +2277,7 @@ + @@ -2279,7 +2314,7 @@ - + @@ -2555,15 +2590,15 @@ - + - + - + @@ -2604,7 +2639,7 @@ - + @@ -2641,7 +2676,7 @@ - + @@ -2709,7 +2744,7 @@ - + @@ -2848,6 +2883,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2855,7 +3222,7 @@ - + @@ -2949,7 +3316,7 @@ - + @@ -3409,9 +3776,9 @@ - - - + + + @@ -3419,9 +3786,6 @@ - - - diff --git a/OwnTracks/OwnTracks/Connection.m b/OwnTracks/OwnTracks/Connection.m index e8e8f7b7..efe9bef5 100644 --- a/OwnTracks/OwnTracks/Connection.m +++ b/OwnTracks/OwnTracks/Connection.m @@ -390,10 +390,10 @@ - (UInt16)sendData:(NSData *)data } - (void)HTTPerror:(NSString *)message { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - - [delegate.navigationController alert:@"HTTP" - message:message]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + + [ad.navigationController alert:@"HTTP" + message:message]; } - (void)sendHTTP:(NSString *)topic data:(NSData *)data { diff --git a/OwnTracks/OwnTracks/CreateCardTVC.h b/OwnTracks/OwnTracks/CreateCardTVC.h new file mode 100644 index 00000000..cad5de94 --- /dev/null +++ b/OwnTracks/OwnTracks/CreateCardTVC.h @@ -0,0 +1,19 @@ +// +// CreateCardTVC.h +// OwnTracks +// +// Created by Christoph Krey on 18.07.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CreateCardTVC : UITableViewController +@property (weak, nonatomic) IBOutlet UITextField *name; +@property (weak, nonatomic) IBOutlet UIImageView *cardImage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OwnTracks/OwnTracks/CreateCardTVC.m b/OwnTracks/OwnTracks/CreateCardTVC.m new file mode 100644 index 00000000..3de5a443 --- /dev/null +++ b/OwnTracks/OwnTracks/CreateCardTVC.m @@ -0,0 +1,122 @@ +// +// CreateCardTVC.m +// OwnTracks +// +// Created by Christoph Krey on 18.07.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import "CreateCardTVC.h" +#import "CoreData.h" +#import "Settings.h" +#import "OwnTracksAppDelegate.h" +#import "Friend+CoreDataClass.h" + +@interface CreateCardTVC () +@property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton; + +@end + +@implementation CreateCardTVC + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Uncomment the following line to preserve selection between presentations. + // self.clearsSelectionOnViewWillAppear = NO; + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + NSManagedObjectContext *moc = [CoreData sharedInstance].mainMOC; + NSString *topic = [Settings theGeneralTopicInMOC:moc]; + Friend *myself = [Friend existsFriendWithTopic:topic + inManagedObjectContext:moc]; + if (!self.name.text || self.name.text.length == 0) { + self.name.text = myself.name ? myself.name : myself.tid; + } + if (!self.cardImage.image) { + self.cardImage.image = myself.image ? [UIImage imageWithData:myself.image] : nil; + } +} + +#pragma UIImagePickerControllerDelegate + +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { + NSLog(@"imagePickerController imagePickerControllerDidCancel"); + [picker dismissViewControllerAnimated:TRUE + completion:^{ + // + }]; +} + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + NSLog(@"imagePickerController didFinishPickingMediaWithInfo %@", info); + UIImage *editedImage = info[@"UIImagePickerControllerEditedImage"]; + + NSLog(@"editedImage %f, %f, %f", + editedImage.size.width, + editedImage.size.height, + editedImage.scale); + + CGFloat scale = 192.0 / MIN(editedImage.size.width, editedImage.size.height); + CGSize size = CGSizeApplyAffineTransform(editedImage.size, + CGAffineTransformMakeScale(scale, scale)); + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(192.0, 192.0), FALSE, 1.0); + [editedImage drawInRect:CGRectMake((192.0 - size.width) / 2, + (192.0 - size.height) / 2, + size.width, size.height) + ]; + UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + self.cardImage.image = scaledImage; + + NSLog(@"cardImage %f, %f, %f", + self.cardImage.image.size.width, + self.cardImage.image.size.height, + self.cardImage.image.scale); + + [picker dismissViewControllerAnimated:TRUE + completion:^{ + // + }]; + +} +- (IBAction)takePhotoPressed:(UIButton *)sender { + NSLog(@"imagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera %d", [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]); + NSLog(@"imagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera %@", [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera]); + + UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; + imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; + imagePickerController.mediaTypes = @[@"public.image"]; + imagePickerController.allowsEditing = TRUE; + imagePickerController.delegate = self; + [self presentViewController:imagePickerController + animated:TRUE + completion:^{ + // + }]; +} + +- (IBAction)selectPressed:(UIButton *)sender { + NSLog(@"imagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary %d", [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]); + NSLog(@"imagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary %@", [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); + + UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; + imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + imagePickerController.mediaTypes = @[@"public.image"]; + imagePickerController.allowsEditing = TRUE; + imagePickerController.delegate = self; + [self presentViewController:imagePickerController + animated:TRUE + completion:^{ + // + }]; +} + +@end diff --git a/OwnTracks/OwnTracks/CreateTourTVC.h b/OwnTracks/OwnTracks/CreateTourTVC.h new file mode 100644 index 00000000..fd0a0be8 --- /dev/null +++ b/OwnTracks/OwnTracks/CreateTourTVC.h @@ -0,0 +1,20 @@ +// +// CreateTourTVC.h +// OwnTracks +// +// Created by Christoph Krey on 18.07.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CreateTourTVC : UITableViewController +@property (weak, nonatomic) IBOutlet UITextField *label; +@property (weak, nonatomic) IBOutlet UIDatePicker *from; +@property (weak, nonatomic) IBOutlet UIDatePicker *to; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OwnTracks/OwnTracks/CreateTourTVC.m b/OwnTracks/OwnTracks/CreateTourTVC.m new file mode 100644 index 00000000..fc0b0a32 --- /dev/null +++ b/OwnTracks/OwnTracks/CreateTourTVC.m @@ -0,0 +1,49 @@ +// +// CreateTourTVC.m +// OwnTracks +// +// Created by Christoph Krey on 18.07.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import "CreateTourTVC.h" +#import "Tours.h" + +@interface CreateTourTVC () +@property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton; + +@end + +@implementation CreateTourTVC + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Uncomment the following line to preserve selection between presentations. + // self.clearsSelectionOnViewWillAppear = NO; + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + self.from.date = [NSDate now]; + self.to.date = [self.from.date dateByAddingTimeInterval:3600.0]; + self.saveButton.enabled = FALSE; + self.label.delegate = self; +} + +- (IBAction)labelChanged:(UITextField *)sender { + if (self.label.text.length > 0) { + self.saveButton.enabled = TRUE; + } else { + self.saveButton.enabled = FALSE; + } +} + +- (IBAction)tappedOutsideText:(UITapGestureRecognizer *)sender { + [self.label resignFirstResponder]; +} + +@end diff --git a/OwnTracks/OwnTracks/FeaturedContentVC.m b/OwnTracks/OwnTracks/FeaturedContentVC.m index 619b47ae..e4bfc3ff 100644 --- a/OwnTracks/OwnTracks/FeaturedContentVC.m +++ b/OwnTracks/OwnTracks/FeaturedContentVC.m @@ -26,11 +26,11 @@ @implementation FeaturedContentVC - (void)viewDidLoad { [super viewDidLoad]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate addObserver:self - forKeyPath:@"action" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad addObserver:self + forKeyPath:@"action" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; self.UIhtml.UIDelegate = self; self.UIhtml.navigationDelegate = self; } diff --git a/OwnTracks/OwnTracks/FriendsTVC.m b/OwnTracks/OwnTracks/FriendsTVC.m index d53b1356..64eef995 100644 --- a/OwnTracks/OwnTracks/FriendsTVC.m +++ b/OwnTracks/OwnTracks/FriendsTVC.m @@ -43,97 +43,100 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { - (void)viewDidLoad { [super viewDidLoad]; - + [[NSNotificationCenter defaultCenter] addObserverForName:@"reload" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ - self.fetchedResultsController = nil; - }]; - - CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; - switch (status) { - case CNAuthorizationStatusRestricted: { - if (![[NSUserDefaults standardUserDefaults] - boolForKey:@"contactsAuthorization"]) { - - DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusRestricted"); - UIAlertController *ac = - [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"Addressbook Access", - @"Headline in addressbook related error messages") - message:NSLocalizedString(@"has been restricted, possibly due to restrictions such as parental controls.", - @"CNAuthorizationStatusRestricted") - preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *ok = [UIAlertAction - actionWithTitle:NSLocalizedString(@"Continue", - @"Continue button title") - - style:UIAlertActionStyleDefault - handler:nil]; - [ac addAction:ok]; - [self presentViewController:ac animated:TRUE completion:nil]; - [[NSUserDefaults standardUserDefaults] - setBool:TRUE - forKey:@"contactsAuthorization"]; + self.fetchedResultsController = nil; + }]; + + BOOL locked = [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; + if (!locked) { + CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; + switch (status) { + case CNAuthorizationStatusRestricted: { + if (![[NSUserDefaults standardUserDefaults] + boolForKey:@"contactsAuthorization"]) { + + DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusRestricted"); + UIAlertController *ac = + [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"Addressbook Access", + @"Headline in addressbook related error messages") + message:NSLocalizedString(@"has been restricted, possibly due to restrictions such as parental controls.", + @"CNAuthorizationStatusRestricted") + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *ok = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Continue", + @"Continue button title") + + style:UIAlertActionStyleDefault + handler:nil]; + [ac addAction:ok]; + [self presentViewController:ac animated:TRUE completion:nil]; + [[NSUserDefaults standardUserDefaults] + setBool:TRUE + forKey:@"contactsAuthorization"]; + } + break; } - break; - } - - case CNAuthorizationStatusDenied: { - if (![[NSUserDefaults standardUserDefaults] - boolForKey:@"contactsAuthorization"]) { - - DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusDenied"); - UIAlertController *ac = - [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"Addressbook Access", - @"Headline in addressbook related error messages") - message:NSLocalizedString(@"has been denied by user. If you allow OwnTracks to access your contacts, you can link your devices to contacts. OwnTracks will then display the contact name and image instead of the device Id. No information of your address book will be uploaded to any server. Go to Settings/Privacy/Contacts to change", - @"CNAuthorizationStatusDenied") - preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *ok = [UIAlertAction - actionWithTitle:NSLocalizedString(@"Continue", - @"Continue button title") - - style:UIAlertActionStyleDefault - handler:nil]; - [ac addAction:ok]; - [self presentViewController:ac animated:TRUE completion:nil]; + + case CNAuthorizationStatusDenied: { + if (![[NSUserDefaults standardUserDefaults] + boolForKey:@"contactsAuthorization"]) { + + DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusDenied"); + UIAlertController *ac = + [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"Addressbook Access", + @"Headline in addressbook related error messages") + message:NSLocalizedString(@"has been denied by user. If you allow OwnTracks to access your contacts, you can link your devices to contacts. OwnTracks will then display the contact name and image instead of the device Id. No information of your address book will be uploaded to any server. Go to Settings/Privacy/Contacts to change", + @"CNAuthorizationStatusDenied") + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *ok = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Continue", + @"Continue button title") + + style:UIAlertActionStyleDefault + handler:nil]; + [ac addAction:ok]; + [self presentViewController:ac animated:TRUE completion:nil]; + [[NSUserDefaults standardUserDefaults] + setBool:TRUE + forKey:@"contactsAuthorization"]; + } + break; + } + + case CNAuthorizationStatusAuthorized: + DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusAuthorized"); + break; + + case CNAuthorizationStatusNotDetermined: + default: [[NSUserDefaults standardUserDefaults] - setBool:TRUE + setBool:FALSE forKey:@"contactsAuthorization"]; - } - break; + + DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusNotDetermined"); + CNContactStore *contactStore = [[CNContactStore alloc] init]; + [contactStore requestAccessForEntityType:CNEntityTypeContacts + completionHandler:^(BOOL granted, NSError * _Nullable error) { + if (granted) { + DDLogVerbose(@"requestAccessForEntityType granted"); + } else { + DDLogVerbose(@"requestAccessForEntityType denied %@", error); + } + }]; + break; } - - case CNAuthorizationStatusAuthorized: - DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusAuthorized"); - break; - - case CNAuthorizationStatusNotDetermined: - default: - [[NSUserDefaults standardUserDefaults] - setBool:FALSE - forKey:@"contactsAuthorization"]; - - DDLogVerbose(@"CNAuthorizationStatus: CNAuthorizationStatusNotDetermined"); - CNContactStore *contactStore = [[CNContactStore alloc] init]; - [contactStore requestAccessForEntityType:CNEntityTypeContacts - completionHandler:^(BOOL granted, NSError * _Nullable error) { - if (granted) { - DDLogVerbose(@"requestAccessForEntityType granted"); - } else { - DDLogVerbose(@"requestAccessForEntityType denied %@", error); - } - }]; - break; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - + while (!self.fetchedResultsController) { // } @@ -168,7 +171,7 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if (indexPath) { Friend *friend = [self.fetchedResultsController objectAtIndexPath:indexPath]; - + if ([segue.identifier isEqualToString:@"showWaypointFromFriends"]) { if ([segue.destinationViewController respondsToSelector:@selector(setWaypoint:)]) { Waypoint *waypoint = friend.newestWaypoint; @@ -177,7 +180,7 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { } } } - + } } @@ -186,7 +189,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath UITabBarController *tbc; UINavigationController *nc; - + if (self.splitViewController) { UISplitViewController *svc = self.splitViewController; nc = svc.viewControllers[1]; @@ -197,7 +200,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } UIViewController *vc = nc.topViewController; - + if ([vc respondsToSelector:@selector(setCenter:)]) { [vc performSelector:@selector(setCenter:) withObject:friend]; if (tbc) { @@ -241,9 +244,9 @@ - (void)tableView:(UITableView *)tableView forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { NSManagedObjectContext *context = (self.fetchedResultsController).managedObjectContext; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; Friend *friend = [self.fetchedResultsController objectAtIndexPath:indexPath]; - [delegate sendEmpty:friend.topic]; + [ad sendEmpty:friend.topic]; [context deleteObject:friend]; NSError *error = nil; @@ -265,7 +268,7 @@ - (NSFetchedResultsController *)fetchedResultsController { inManagedObjectContext:CoreData.sharedInstance.mainMOC]; fetchRequest.entity = entity; fetchRequest.fetchBatchSize = 20; - + double ignoreStaleLocations = [Settings doubleForKey:@"ignorestalelocations_preference" inMOC:CoreData.sharedInstance.mainMOC]; if (ignoreStaleLocations) { @@ -273,7 +276,7 @@ - (NSFetchedResultsController *)fetchedResultsController { fetchRequest.predicate = [NSPredicate predicateWithFormat:@"lastLocation > %@", [NSDate dateWithTimeIntervalSinceNow:stale]]; } - + NSSortDescriptor *sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey:@"topic" ascending:YES]; NSArray *sortDescriptors = @[sortDescriptor1]; fetchRequest.sortDescriptors = sortDescriptors; @@ -314,13 +317,13 @@ - (void)controller:(NSFetchedResultsController *)controller - (void)didChangeSection:(NSDictionary *)d { NSNumber *type = d[@"type"]; NSNumber *sectionIndex = d[@"sectionIndex"]; - + switch(type.intValue) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex.intValue] withRowAnimation:UITableViewRowAnimationAutomatic]; break; - + case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex.intValue] withRowAnimation:UITableViewRowAnimationAutomatic]; @@ -350,28 +353,28 @@ - (void)didChangeObject:(NSDictionary *)d { NSNumber *type = d[@"type"]; NSIndexPath *indexPath = d[@"indexPath"]; NSIndexPath *newIndexPath = d[@"newIndexPath"]; - + switch(type.intValue) { case NSFetchedResultsChangeInsert: [self.tableView insertRowsAtIndexPaths:@[newIndexPath] - withRowAnimation:UITableViewRowAnimationAutomatic]; + withRowAnimation:UITableViewRowAnimationAutomatic]; break; - + case NSFetchedResultsChangeDelete: [self.tableView deleteRowsAtIndexPaths:@[indexPath] - withRowAnimation:UITableViewRowAnimationAutomatic]; + withRowAnimation:UITableViewRowAnimationAutomatic]; break; - + case NSFetchedResultsChangeUpdate: [self.tableView reloadRowsAtIndexPaths:@[indexPath] - withRowAnimation:UITableViewRowAnimationAutomatic]; + withRowAnimation:UITableViewRowAnimationAutomatic]; break; - + case NSFetchedResultsChangeMove: [self.tableView deleteRowsAtIndexPaths:@[indexPath] - withRowAnimation:UITableViewRowAnimationAutomatic]; + withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView insertRowsAtIndexPaths:@[newIndexPath] - withRowAnimation:UITableViewRowAnimationAutomatic]; + withRowAnimation:UITableViewRowAnimationAutomatic]; break; } } @@ -388,14 +391,14 @@ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPa FriendTableViewCell *friendTableViewCell = (FriendTableViewCell *)cell; Friend *friend = [self.fetchedResultsController objectAtIndexPath:indexPath]; - + friendTableViewCell.name.text = friend.name ? friend.name : friend.tid; FriendAnnotationV *friendAnnotationView = [[FriendAnnotationV alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; friendAnnotationView.personImage = friend.image ? [UIImage imageWithData:friend.image] : nil; friendAnnotationView.me = [friend.topic isEqualToString:[Settings theGeneralTopicInMOC:CoreData.sharedInstance.mainMOC]]; friendAnnotationView.tid = friend.effectiveTid; - + Waypoint *waypoint = friend.newestWaypoint; if (waypoint) { if (waypoint.placemark) { @@ -413,7 +416,7 @@ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPa friendAnnotationView.speed = -1; friendAnnotationView.course = -1; } - + NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]]; @@ -427,7 +430,7 @@ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPa dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle]; } - + friendTableViewCell.image.image = [friendAnnotationView getImage]; } diff --git a/OwnTracks/OwnTracks/LocationManager.m b/OwnTracks/OwnTracks/LocationManager.m index bf72279e..7599eaf9 100644 --- a/OwnTracks/OwnTracks/LocationManager.m +++ b/OwnTracks/OwnTracks/LocationManager.m @@ -36,8 +36,8 @@ @interface PendingRegionEvent : NSObject @implementation PendingRegionEvent + (PendingRegionEvent *)holdDown:(CLRegion *)region - for:(NSTimeInterval)interval - to:(id)to { +for:(NSTimeInterval)interval +to:(id)to { PendingRegionEvent *p = [[PendingRegionEvent alloc] init]; p.region = region; p.holdDownTimer = [NSTimer timerWithTimeInterval:interval @@ -67,7 +67,7 @@ - (instancetype)init { self.manager = [[CLLocationManager alloc] init]; self.manager.delegate = self; - + self.altimeter = [[CMAltimeter alloc] init]; self.insideBeaconRegions = [[NSMutableDictionary alloc] init]; @@ -83,33 +83,33 @@ - (instancetype)init { object:nil queue:nil usingBlock:^(NSNotification *note){ - DDLogVerbose(@"[LocationManager] UIApplicationWillEnterForegroundNotification"); - // - }]; + DDLogVerbose(@"[LocationManager] UIApplicationWillEnterForegroundNotification"); + // + }]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note){ - DDLogVerbose(@"[LocationManager] UIApplicationDidBecomeActiveNotification"); - [self wakeup]; - }]; + DDLogVerbose(@"[LocationManager] UIApplicationDidBecomeActiveNotification"); + [self wakeup]; + }]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note){ - DDLogVerbose(@"[LocationManager] UIApplicationWillResignActiveNotification"); - [self sleep]; - }]; + DDLogVerbose(@"[LocationManager] UIApplicationWillResignActiveNotification"); + [self sleep]; + }]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:nil queue:nil usingBlock:^(NSNotification *note){ - DDLogVerbose(@"[LocationManager] UIApplicationWillTerminateNotification"); - [self stop]; - }]; + DDLogVerbose(@"[LocationManager] UIApplicationWillTerminateNotification"); + [self stop]; + }]; self.sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.org.owntracks.Owntracks"]; [self.sharedUserDefaults addObserver:self forKeyPath:@"monitoring" @@ -118,7 +118,7 @@ - (instancetype)init { [self.sharedUserDefaults addObserver:self forKeyPath:@"sendNow" options:NSKeyValueObservingOptionNew context:nil]; - + return self; } @@ -133,30 +133,30 @@ - (void)observeValueForKeyPath:(NSString *)keyPath self.monitoring = monitoring; } } else if ([keyPath isEqualToString:@"sendNow"]) { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate sendNow:self.location]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad sendNow:self.location]; } } - (void)start { DDLogVerbose(@"start"); [self authorize]; - + CMAuthorizationStatus status = [CMAltimeter authorizationStatus]; BOOL available = [CMAltimeter isRelativeAltitudeAvailable]; DDLogVerbose(@"CMAltimeter status=%ld, available=%d", (long)status, available); - + if (available && (status == CMAuthorizationStatusNotDetermined || status == CMAuthorizationStatusAuthorized)) { DDLogVerbose(@"startRelativeAltitudeUpdatesToQueue"); [self.altimeter startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAltitudeData *altitudeData, NSError *error) { - DDLogVerbose(@"altitudeData %@", altitudeData); - self.altitude = altitudeData; - }]; + DDLogVerbose(@"altitudeData %@", altitudeData); + self.altitude = altitudeData; + }]; } } @@ -203,7 +203,7 @@ - (void)sleep { - (void)stop { DDLogVerbose(@"stop"); - + if ([CMAltimeter isRelativeAltitudeAvailable]) { DDLogVerbose(@"stopRelativeAltitudeUpdates"); [self.altimeter stopRelativeAltitudeUpdates]; @@ -280,11 +280,11 @@ - (void)setMonitoring:(LocationMonitoring)monitoring { _monitoring = monitoring; self.manager.pausesLocationUpdatesAutomatically = NO; self.manager.allowsBackgroundLocationUpdates = TRUE; - + [self.manager stopUpdatingLocation]; [self.manager stopMonitoringVisits]; [self.manager stopMonitoringSignificantLocationChanges]; - + switch (monitoring) { case LocationMonitoringMove: self.manager.distanceFilter = self.minDist > 0 ? self.minDist : kCLDistanceFilterNone; @@ -361,77 +361,87 @@ - (void)locationManager:(CLLocationManager *)manager } - (void)showError { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; switch (status) { case kCLAuthorizationStatusAuthorizedAlways: break; case kCLAuthorizationStatusAuthorizedWhenInUse: - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"App is not allowed to use location services in background", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"App is not allowed to use location services in background", + @"Location Manager error message") + ]; break; case kCLAuthorizationStatusNotDetermined: - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"App is not allowed to use location services yet", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"App is not allowed to use location services yet", + @"Location Manager error message") + ]; break; case kCLAuthorizationStatusDenied: - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"App is not allowed to use location services", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"App is not allowed to use location services", + @"Location Manager error message") + ]; break; case kCLAuthorizationStatusRestricted: - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"App use of location services is restricted", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"App use of location services is restricted", + @"Location Manager error message") + ]; break; default: - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"App use of location services is unclear", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"App use of location services is unclear", + @"Location Manager error message") + ]; break; } if (![CLLocationManager locationServicesEnabled]) { - [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"Location services are not enabled", - @"Location Manager error message") - ]; + [ad.navigationController alert:@"LocationManager" + message: + NSLocalizedString(@"Location services are not enabled", + @"Location Manager error message") + ]; } - + #if 0 if (![CLLocationManager significantLocationChangeMonitoringAvailable]) { [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"Significant location change monitoring not available", - @"Location Manager error message") - ]; + message: + NSLocalizedString(@"Significant location change monitoring not available", + @"Location Manager error message") + ]; } if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) { [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"Circular region monitoring not available", - @"Location Manager error message") - ]; + message: + NSLocalizedString(@"Circular region monitoring not available", + @"Location Manager error message") + ]; } if (![CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]) { [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"iBeacon region monitoring not available", - @"Location Manager error message") - ]; + message: + NSLocalizedString(@"iBeacon region monitoring not available", + @"Location Manager error message") + ]; } if (![CLLocationManager isRangingAvailable]) { [delegate.navigationController alert:@"LocationManager" - message:NSLocalizedString(@"iBeacon ranging not available", - @"Location Manager error message") - ]; + message: + NSLocalizedString(@"iBeacon ranging not available", + @"Location Manager error message") + ]; } if (![CLLocationManager headingAvailable]) { @@ -509,7 +519,7 @@ - (void)locationManager:(CLLocationManager *)manager [[CLBeaconIdentityConstraint alloc] initWithUUID:beaconRegion.UUID]; } [self.manager stopRangingBeaconsSatisfyingConstraint:beaconIdentityConstraint]; - + } } @@ -598,7 +608,7 @@ - (void)locationManager:(CLLocationManager *)manager error:(NSError *)error { DDLogVerbose(@"[LocationManager] didFailRangingBeaconsForConstraint %@ %@ %@", beaconConstraint, error.localizedDescription, error.userInfo); - + } - (void)locationManager:(CLLocationManager *)manager @@ -614,7 +624,7 @@ - (void)locationManager:(CLLocationManager *)manager uuid_t beaconUUID; [rangedBeacon.UUID getUUIDBytes:rangedBeaconUUID]; [beacon.UUID getUUIDBytes:beaconUUID]; - + if (uuid_compare(rangedBeaconUUID, beaconUUID) == 0 && (rangedBeacon.major).intValue == (beacon.major).intValue && (rangedBeacon.minor).intValue == (beacon.minor).intValue) { @@ -636,7 +646,7 @@ - (void)locationManager:(CLLocationManager *)manager } } } - + } /* @@ -683,26 +693,8 @@ - (void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit *)visit { visit.arrivalDate, visit.departureDate); - // CLLocation *location; - // if (visit.departureDate && ![visit.departureDate isEqualToDate:[NSDate distantFuture]]) { - // location = [[CLLocation alloc] initWithCoordinate:visit.coordinate - // altitude:-1.0 - // horizontalAccuracy:visit.horizontalAccuracy - // verticalAccuracy:-1.0 - // timestamp:visit.departureDate]; - // } else if (visit.arrivalDate && ![visit.arrivalDate isEqualToDate:[NSDate distantPast]]) { - // location = [[CLLocation alloc] initWithCoordinate:visit.coordinate - // altitude:-1.0 - // horizontalAccuracy:visit.horizontalAccuracy - // verticalAccuracy:-1.0 - // timestamp:visit.arrivalDate]; - // } - // - // if (location) { - // [self.delegate visitLocation:location]; - // } - if (self.manager.location) { - [self.delegate newLocation:self.manager.location]; + if (manager.location) { + [self.delegate visitLocation:manager.location]; } } diff --git a/OwnTracks/OwnTracks/NavigationController.h b/OwnTracks/OwnTracks/NavigationController.h index 6987f1cf..11ae1dd5 100644 --- a/OwnTracks/OwnTracks/NavigationController.h +++ b/OwnTracks/OwnTracks/NavigationController.h @@ -10,6 +10,7 @@ @interface NavigationController : UINavigationController - (void)alert:(NSString *)title message:(NSString *)message; +- (void)alert:(NSString *)title message:(NSString *)message url:(NSString *)url; - (void)alert:(NSString *)title message:(NSString *)message dismissAfter:(NSTimeInterval)interval; @end diff --git a/OwnTracks/OwnTracks/NavigationController.m b/OwnTracks/OwnTracks/NavigationController.m index c3122826..cfd6d80c 100644 --- a/OwnTracks/OwnTracks/NavigationController.m +++ b/OwnTracks/OwnTracks/NavigationController.m @@ -17,8 +17,8 @@ @interface NavigationController () @implementation NavigationController - (void)viewDidLoad { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - delegate.navigationController = self; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + ad.navigationController = self; self.queuedAlerts = [[NSMutableArray alloc] init]; self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar]; @@ -36,28 +36,28 @@ - (void)viewDidLayoutSubviews { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate addObserver:self - forKeyPath:@"connectionState" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; - [delegate addObserver:self - forKeyPath:@"connectionBuffered" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; - [delegate addObserver:self - forKeyPath:@"processingMessage" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad addObserver:self + forKeyPath:@"connectionState" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; + [ad addObserver:self + forKeyPath:@"connectionBuffered" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; + [ad addObserver:self + forKeyPath:@"processingMessage" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; } - (void)viewWillDisappear:(BOOL)animated { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate removeObserver:self - forKeyPath:@"connectionState"]; - [delegate removeObserver:self - forKeyPath:@"connectionBuffered"]; - + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad removeObserver:self + forKeyPath:@"connectionState"]; + [ad removeObserver:self + forKeyPath:@"connectionBuffered"]; + [super viewWillDisappear:animated]; } @@ -67,10 +67,10 @@ - (void)observeValueForKeyPath:(NSString *)keyPath change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"processingMessage"]) { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - if (delegate.processingMessage) { - [self alert:@"openURL" message:delegate.processingMessage]; - delegate.processingMessage = nil; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + if (ad.processingMessage) { + [self alert:@"openURL" message:ad.processingMessage]; + ad.processingMessage = nil; } } @@ -78,8 +78,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } - (void)updateUI { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - switch ((delegate.connectionState).intValue) { + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + switch ((ad.connectionState).intValue) { case state_connected: self.progressView.progressTintColor = [UIColor colorNamed:@"connectedColor"]; self.progressView.progress = 0.0; @@ -106,6 +106,20 @@ - (void)alert:(NSString *)title [self alert:title message:message dismissAfter:0]; } +- (void)alert:(NSString *)title + message:(NSString *)message + url:(NSString *)url { + [self performSelectorOnMainThread:@selector(alert:) + withObject:@{ + @"title": title, + @"message": message, + @"url": url, + @"interval": [NSNumber numberWithFloat:0.0] + } + waitUntilDone:NO]; + +} + - (void)alert:(NSString *)title message:(NSString *)message dismissAfter:(NSTimeInterval)interval { @@ -137,7 +151,7 @@ - (void)alert:(NSDictionary *)parameters { actionWithTitle:NSLocalizedString(@"Continue", @"Continue button title") - style:UIAlertActionStyleDefault + style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { if (self.queuedAlerts.count > 0) { NSDictionary *parameters = self.queuedAlerts.firstObject; @@ -146,6 +160,26 @@ - (void)alert:(NSDictionary *)parameters { } }]; [ac addAction:ok]; + if (parameters[@"url"]) { + UIAlertAction *open = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Open", + @"Open button title") + + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + NSURL *url = [NSURL URLWithString:parameters[@"url"]]; + [[UIApplication sharedApplication] openURL:url + options:@{} + completionHandler:^(BOOL success) { + if (self.queuedAlerts.count > 0) { + NSDictionary *parameters = self.queuedAlerts.firstObject; + [self.queuedAlerts removeObjectAtIndex:0]; + [self alert:parameters]; + } + }]; + }]; + [ac addAction:open]; + } } [self presentViewController:ac animated:TRUE completion:nil]; diff --git a/OwnTracks/OwnTracks/OwnTracking.m b/OwnTracks/OwnTracks/OwnTracking.m index a1a6d474..e6d7d7a9 100644 --- a/OwnTracks/OwnTracks/OwnTracking.m +++ b/OwnTracks/OwnTracks/OwnTracking.m @@ -251,12 +251,13 @@ - (void)processTransitionMessage:(NSDictionary *)dictionary { maximum:[Settings theMaximumHistoryInMOC:[CoreData sharedInstance].mainMOC]]; [CoreData.sharedInstance sync:CoreData.sharedInstance.queuedMOC]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController alert:NSLocalizedString(@"Friend", - @"Alert message header for friend's messages") - message:notificationMessage - dismissAfter:2.0 - ]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert: + NSLocalizedString(@"Friend", + @"Alert message header for friend's messages") + message:notificationMessage + dismissAfter:2.0 + ]; } } diff --git a/OwnTracks/OwnTracks/OwnTracks.entitlements b/OwnTracks/OwnTracks/OwnTracks.entitlements index 02d7b4cd..0dedc639 100644 --- a/OwnTracks/OwnTracks/OwnTracks.entitlements +++ b/OwnTracks/OwnTracks/OwnTracks.entitlements @@ -12,6 +12,8 @@ group.org.owntracks.Owntracks + com.apple.security.device.camera + com.apple.security.files.user-selected.read-only com.apple.security.network.client diff --git a/OwnTracks/OwnTracks/OwnTracksAppDelegate.m b/OwnTracks/OwnTracks/OwnTracksAppDelegate.m index 6cd8a1d8..61f71f6f 100644 --- a/OwnTracks/OwnTracks/OwnTracksAppDelegate.m +++ b/OwnTracks/OwnTracks/OwnTracksAppDelegate.m @@ -16,6 +16,7 @@ #import "History+CoreDataClass.h" #import "Settings.h" #import "OwnTracking.h" +#import "Tours.h" #import "ConnType.h" #import "NSNumber+decimals.h" @@ -428,29 +429,36 @@ - (BOOL)processNSURL:(NSURL *)url { attributes:nil]; } else { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"OOPS %@ %@", - [NSError errorWithDomain:@"OwnTracks" - code:2 - userInfo:@{@"extension":extension ? extension : @"(null)"}], - url]]; + message: + [NSString stringWithFormat:@"OOPS %@ %@", + [NSError errorWithDomain:@"OwnTracks" + code:2 + userInfo:@{@"extension":extension ? extension : @"(null)"}], + url]]; } } else { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"httpResponse.statusCode %ld %@", - (long)httpResponse.statusCode, - url]]; + message: + [NSString stringWithFormat:@"httpResponse.statusCode %ld %@", + (long)httpResponse.statusCode, + url] + ]; } } else { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"response %@ %@", + message: + [NSString stringWithFormat:@"response %@ %@", response, - url]]; + url] + ]; } } else { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"dataTaskWithRequest %@ %@", - error, - url]]; + message: + [NSString stringWithFormat:@"dataTaskWithRequest %@ %@", + error, + url] + ]; } }]; [dataTask resume]; @@ -462,9 +470,11 @@ - (void)configFromDictionary:(NSDictionary *)json { [CoreData.sharedInstance sync:CoreData.sharedInstance.mainMOC]; if (error) { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"configFromDictionary %@ %@", - error, - json]]; + message: + [NSString stringWithFormat:@"configFromDictionary %@ %@", + error, + json] + ]; } } @@ -473,9 +483,11 @@ - (void)waypointsFromDictionary:(NSDictionary *)json { [CoreData.sharedInstance sync:CoreData.sharedInstance.mainMOC]; if (error) { [self.navigationController alert:@"processNSURL" - message:[NSString stringWithFormat:@"waypointsFromDictionary %@ %@", - error, - json]]; + message: + [NSString stringWithFormat:@"waypointsFromDictionary %@ %@", + error, + json] + ]; } } @@ -563,12 +575,14 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { } if (self.backgroundFetchCheckMessage) { - [self.navigationController alert:@"Background Fetch" message:self.backgroundFetchCheckMessage]; + [self.navigationController alert:@"Background Fetch" + message:self.backgroundFetchCheckMessage]; self.backgroundFetchCheckMessage = nil; } if (self.processingMessage) { - [self.navigationController alert:@"openURL" message:self.processingMessage]; + [self.navigationController alert:@"openURL" + message:self.processingMessage]; self.processingMessage = nil; [self reconnect]; } @@ -577,7 +591,8 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { NSString *message = NSLocalizedString(@"To publish your location userID and deviceID must be set", @"Warning displayed if necessary settings are missing"); - [self.navigationController alert:@"Settings" message:message]; + [self.navigationController alert:@"Settings" + message:message]; } } @@ -1014,6 +1029,11 @@ - (BOOL)handleMessage:(Connection *)connection withObject:dictionary waitUntilDone:NO]; + } else if ([@"response" saveEqual:dictionary[@"action"]]) { + [self performSelectorOnMainThread:@selector(performResponse:) + withObject:dictionary + waitUntilDone:NO]; + } else { DDLogWarn(@"[OwnTracksAppDelegate] unknown action %@", dictionary[@"action"]); } @@ -1095,8 +1115,9 @@ - (void)performAction:(NSDictionary *)dictionary { maximum:[Settings theMaximumHistoryInMOC:[CoreData sharedInstance].mainMOC]]; [CoreData.sharedInstance sync:CoreData.sharedInstance.mainMOC]; - [self.navigationController alert:NSLocalizedString(@"Notification", - @"Alert message header for notification messages") + [self.navigationController alert: + NSLocalizedString(@"Notification", + @"Alert message header for notification messages") message:notificationMessage dismissAfter:2.0 ]; @@ -1115,6 +1136,12 @@ - (void)performAction:(NSDictionary *)dictionary { } } +- (void)performResponse:(NSDictionary *)dictionary { + if ([[Tours sharedInstance] processResponse:dictionary]) { + } else { + } +} + - (void)performSetConfiguration:(NSDictionary *)dictionary { NSDictionary *payload = [NSDictionary saveCopy:dictionary[@"payload"]]; NSDictionary *configuration = [NSDictionary saveCopy:dictionary[@"configuration"]]; @@ -1483,10 +1510,12 @@ - (void)connect { passphrase:[Settings stringForKey:@"passphrase" inMOC:moc]]; if (!certificates) { - [self.navigationController alert:NSLocalizedString(@"TLS Client Certificate", - @"Heading for certificate error message") - message:NSLocalizedString(@"incorrect file or passphrase", - @"certificate error message") + [self.navigationController alert: + NSLocalizedString(@"TLS Client Certificate", + @"Heading for certificate error message") + message: + NSLocalizedString(@"incorrect file or passphrase", + @"certificate error message") ]; } } diff --git a/OwnTracks/OwnTracks/RegionTVC.m b/OwnTracks/OwnTracks/RegionTVC.m index 41d6ea1c..19c6b57d 100644 --- a/OwnTracks/OwnTracks/RegionTVC.m +++ b/OwnTracks/OwnTracks/RegionTVC.m @@ -68,8 +68,8 @@ - (void)viewWillDisappear:(BOOL)animated { self.editRegion.major = @((self.UImajor.text).intValue); self.editRegion.minor = @((self.UIminor.text).intValue); - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate sendRegion:self.editRegion]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad sendRegion:self.editRegion]; if (self.oldRegion) { DDLogVerbose(@"stopMonitoringForRegion %@", self.oldRegion.identifier); [[LocationManager sharedInstance] stopRegion:self.oldRegion]; diff --git a/OwnTracks/OwnTracks/Settings.h b/OwnTracks/OwnTracks/Settings.h index 2b1a3b8c..059e321d 100644 --- a/OwnTracks/OwnTracks/Settings.h +++ b/OwnTracks/OwnTracks/Settings.h @@ -53,6 +53,7 @@ typedef NS_ENUM(int, ConnectionMode) { + (NSString *)theMqttPassInMOC:(NSManagedObjectContext *)context; + (BOOL)theMqttUsePasswordInMOC:(NSManagedObjectContext *)context; + (BOOL)theMqttAuthInMOC:(NSManagedObjectContext *)context; ++ (BOOL)theLockedInMOC:(NSManagedObjectContext *)context; + (int)theMaximumHistoryInMOC:(NSManagedObjectContext *)context; diff --git a/OwnTracks/OwnTracks/Settings.m b/OwnTracks/OwnTracks/Settings.m index 4695c3e8..f9ae21dc 100644 --- a/OwnTracks/OwnTracks/Settings.m +++ b/OwnTracks/OwnTracks/Settings.m @@ -779,6 +779,10 @@ + (BOOL)theMqttUsePasswordInMOC:(NSManagedObjectContext *)context { return [self boolForKey:@"usepassword_preference" inMOC:context]; } ++ (BOOL)theLockedInMOC:(NSManagedObjectContext *)context { + return [self boolForKey:@"locked" inMOC:context]; +} + + (BOOL)theMqttAuthInMOC:(NSManagedObjectContext *)context { return [self boolForKey:@"auth_preference" inMOC:context]; } diff --git a/OwnTracks/OwnTracks/SettingsTVC.m b/OwnTracks/OwnTracks/SettingsTVC.m index 84e9738e..390f5138 100644 --- a/OwnTracks/OwnTracks/SettingsTVC.m +++ b/OwnTracks/OwnTracks/SettingsTVC.m @@ -72,6 +72,8 @@ @interface SettingsTVC () @property (weak, nonatomic) IBOutlet UILabel *UIeffectiveTid; @property (weak, nonatomic) IBOutlet UILabel *UIeffectiveDeviceId; @property (weak, nonatomic) IBOutlet UITextField *UIdowngrade; +@property (weak, nonatomic) IBOutlet UIButton *createCardButton; +@property (weak, nonatomic) IBOutlet UIButton *toursButton; @property (strong, nonatomic) UIDocumentInteractionController *dic; @@ -99,11 +101,11 @@ - (void)viewWillAppear:(BOOL)animated { self.UImonitoring.delegate = self; self.UIdowngrade.delegate = self; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate addObserver:self - forKeyPath:@"configLoad" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad addObserver:self + forKeyPath:@"configLoad" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; [self updated]; } @@ -113,10 +115,10 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField { } - (void)viewWillDisappear:(BOOL)animated { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate removeObserver:self - forKeyPath:@"configLoad" - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad removeObserver:self + forKeyPath:@"configLoad" + context:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"reload" object:nil]; [self reconnect]; [super viewWillDisappear:animated]; @@ -369,8 +371,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } - (void)updated { - BOOL locked = [Settings boolForKey:@"locked" - inMOC:CoreData.sharedInstance.mainMOC]; + BOOL locked = [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; self.title = [NSString stringWithFormat:@"%@%@", NSLocalizedString(@"Settings", @"Settings screen title"), @@ -379,6 +380,9 @@ - (void)updated { @"indicates a locked configuration")] : @""]; + self.createCardButton.enabled = !locked; + self.toursButton.enabled = !locked; + if (self.UIDeviceID) { self.UIDeviceID.text = [Settings stringForKey:@"deviceid_preference" @@ -509,7 +513,7 @@ - (void)updated { DDLogVerbose(@"[Settings] mode is %d", mode); switch (mode) { case CONNECTION_MODE_HTTP: - self.UImodeSwitch.selectedSegmentIndex =1; + self.UImodeSwitch.selectedSegmentIndex = 1; break; case CONNECTION_MODE_MQTT: default: @@ -636,8 +640,7 @@ - (void)updated { } if (self.self.UIlocked) { self.self.UIlocked.on = - [Settings boolForKey:@"locked" - inMOC:CoreData.sharedInstance.mainMOC]; + [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; self.self.UIlocked.enabled = false; } if (self.self.UIsub) { @@ -683,32 +686,20 @@ - (void)updated { self.UIurl.enabled = !locked; } - // if (!self.UIusepolicy) { if (self.UImodeSwitch) { int mode = [Settings intForKey:@"mode" inMOC:CoreData.sharedInstance.mainMOC]; - NSArray *publishPaths = @[ - [NSIndexPath indexPathForRow:3 inSection:0] - ]; - - for (NSIndexPath *indexPath in publishPaths) { - if ([self isRowVisible:indexPath] && (mode != CONNECTION_MODE_MQTT && mode != CONNECTION_MODE_HTTP)) { - [self deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } else if (![self isRowVisible:indexPath] && (mode == CONNECTION_MODE_MQTT || mode == CONNECTION_MODE_HTTP)) { - [self insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } - } - - NSArray *privatePaths = @[ - [NSIndexPath indexPathForRow:5 inSection:0], + // hide MQTT related rows if not MQTT mode + NSArray *mqttPaths = @[ [NSIndexPath indexPathForRow:6 inSection:0], - [NSIndexPath indexPathForRow:7 inSection:0] + [NSIndexPath indexPathForRow:7 inSection:0], + [NSIndexPath indexPathForRow:8 inSection:0] ]; - for (NSIndexPath *indexPath in privatePaths) { + for (NSIndexPath *indexPath in mqttPaths) { if ([self isRowVisible:indexPath] && mode != CONNECTION_MODE_MQTT) { [self deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } else if (![self isRowVisible:indexPath] && mode == CONNECTION_MODE_MQTT) { @@ -732,33 +723,9 @@ - (void)updated { } } - NSArray *secretPaths = @[ - [NSIndexPath indexPathForRow:11 inSection:0] - ]; - for (NSIndexPath *indexPath in secretPaths) { - if ([self isRowVisible:indexPath] && (mode != CONNECTION_MODE_MQTT && mode != CONNECTION_MODE_HTTP)) { - [self deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } else if (![self isRowVisible:indexPath] && (mode == CONNECTION_MODE_MQTT || mode == CONNECTION_MODE_HTTP)) { - [self insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } - } - - NSArray *authPaths = @[ - [NSIndexPath indexPathForRow:8 inSection:0], - [NSIndexPath indexPathForRow:9 inSection:0], - [NSIndexPath indexPathForRow:10 inSection:0] - ]; - - for (NSIndexPath *indexPath in authPaths) { - if ([self isRowVisible:indexPath] && (mode != CONNECTION_MODE_MQTT && mode != CONNECTION_MODE_HTTP)) { - [self deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } else if (![self isRowVisible:indexPath] && (mode == CONNECTION_MODE_MQTT || mode == CONNECTION_MODE_HTTP)) { - [self insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - } - } - + // hide HTTP related rows if not in HTTP mode NSArray *httpPaths = @[ - [NSIndexPath indexPathForRow:12 inSection:0] + [NSIndexPath indexPathForRow:13 inSection:0] ]; for (NSIndexPath *indexPath in httpPaths) { @@ -798,14 +765,14 @@ - (void)updated { - (IBAction)publishSettingsPressed:(UIButton *)sender { [self updateValues]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate dump]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad dump]; } - (IBAction)publishWaypointsPressed:(UIButton *)sender { [self updateValues]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate waypoints]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad waypoints]; } - (IBAction)exportPressed:(UIButton *)sender { @@ -924,6 +891,53 @@ - (IBAction)setNames:(UIStoryboardSegue *)segue { } } +- (IBAction)setCard:(UIStoryboardSegue *)segue { + if ([segue.sourceViewController respondsToSelector:@selector(cardImage)] && + [segue.sourceViewController respondsToSelector:@selector(name)]) { + UITextField *name = [segue.sourceViewController performSelector:@selector(name)]; + UIImageView *cardImage = [segue.sourceViewController performSelector:@selector(cardImage)]; + + NSLog(@"image %f, %f, %f", + cardImage.image.size.width, + cardImage.image.size.height, + cardImage.image.scale); + + NSData *png = UIImagePNGRepresentation(cardImage.image); + NSManagedObjectContext *moc = [CoreData sharedInstance].mainMOC; + NSString *topic = [Settings theGeneralTopicInMOC:moc]; + Friend *myself = [Friend existsFriendWithTopic:topic + inManagedObjectContext:moc]; + + + myself.cardName = name.text; + myself.cardImage = UIImagePNGRepresentation(cardImage.image); + NSString *b64String = [png base64EncodedStringWithOptions:0]; + + NSDictionary *json = @{ + @"_type": @"card", + @"face": b64String, + @"name": name.text, + }; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.connection sendData:[NSJSONSerialization dataWithJSONObject:json + options:NSJSONWritingSortedKeys + error:nil] + topic:[[Settings theGeneralTopicInMOC:moc] stringByAppendingString:@"/info"] + topicAlias:@(0) + qos:[Settings intForKey:@"qos_preference" + inMOC:moc] + retain:YES]; + + [ad.navigationController alert: + NSLocalizedString(@"Card", + @"Header of an alert message regarding a card") + message: + NSLocalizedString(@"set and sent to backend", + @"content of an alert message regarding card")]; + } +} + - (IBAction)touchedOutsideText:(UITapGestureRecognizer *)sender { [self.UIHost resignFirstResponder]; [self.UIPort resignFirstResponder]; @@ -1006,11 +1020,11 @@ - (IBAction)modeSwitchChanged:(UISegmentedControl *)sender { style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate terminateSession]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad terminateSession]; [self updateValues]; [self updated]; - [delegate reconnect]; + [ad reconnect]; }]; [ac addAction:cancel]; @@ -1094,12 +1108,12 @@ - (IBAction)modeChanged:(UITextField *)sender { style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate terminateSession]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad terminateSession]; [self updateValues]; [self updated]; - [delegate reconnect]; + [ad reconnect]; }]; [ac addAction:cancel]; @@ -1124,11 +1138,11 @@ - (IBAction)trashPressed:(UIBarButtonItem *)sender { } - (void)reconnect { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate connectionOff]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad connectionOff]; [[OwnTracking sharedInstance] syncProcessing]; [self updateValues]; - [delegate reconnect]; + [ad reconnect]; } @end diff --git a/OwnTracks/OwnTracks/StatusTVC.m b/OwnTracks/OwnTracks/StatusTVC.m index 931169e2..50f19eb6 100644 --- a/OwnTracks/OwnTracks/StatusTVC.m +++ b/OwnTracks/OwnTracks/StatusTVC.m @@ -28,15 +28,15 @@ @implementation StatusTVC - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate addObserver:self - forKeyPath:@"connectionState" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; - [delegate addObserver:self - forKeyPath:@"connectionBuffered" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad addObserver:self + forKeyPath:@"connectionState" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; + [ad addObserver:self + forKeyPath:@"connectionBuffered" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; [[LocationManager sharedInstance] addObserver:self forKeyPath:@"location" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -44,13 +44,13 @@ - (void)viewWillAppear:(BOOL)animated { } - (void)viewWillDisappear:(BOOL)animated { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate removeObserver:self - forKeyPath:@"connectionState" - context:nil]; - [delegate removeObserver:self - forKeyPath:@"connectionBuffered" - context:nil]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad removeObserver:self + forKeyPath:@"connectionState" + context:nil]; + [ad removeObserver:self + forKeyPath:@"connectionBuffered" + context:nil]; [[LocationManager sharedInstance] removeObserver:self forKeyPath:@"location" context:nil]; @@ -66,7 +66,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } - (void)updatedStatus { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; const NSDictionary *states; states = @{ @@ -86,24 +86,24 @@ - (void)updatedStatus { NSString *stateName = [NSString stringWithFormat:@"%@ (%@)", NSLocalizedString(@"unknown state", @"description connection unknown state"), - delegate.connectionState]; - if (delegate.connectionState) { - stateName = states[delegate.connectionState]; + ad.connectionState]; + if (ad.connectionState) { + stateName = states[ad.connectionState]; if (!stateName) { - stateName = delegate.connectionState.description; + stateName = ad.connectionState.description; } } self.UIstatusField.text = [NSString stringWithFormat:@"%@ %@ %@ %@ %@", stateName, - delegate.connection.lastErrorCode ? - delegate.connection.lastErrorCode.domain : @"", - delegate.connection.lastErrorCode ? - [NSString stringWithFormat:@"%ld", delegate.connection.lastErrorCode.code] : @"", - delegate.connection.lastErrorCode ? - delegate.connection.lastErrorCode.localizedDescription : @"", - delegate.connection.lastErrorCode ? - delegate.connection.lastErrorCode.userInfo : @"" + ad.connection.lastErrorCode ? + ad.connection.lastErrorCode.domain : @"", + ad.connection.lastErrorCode ? + [NSString stringWithFormat:@"%ld", ad.connection.lastErrorCode.code] : @"", + ad.connection.lastErrorCode ? + ad.connection.lastErrorCode.localizedDescription : @"", + ad.connection.lastErrorCode ? + ad.connection.lastErrorCode.userInfo : @"" ]; if ([LocationManager sharedInstance].location) { @@ -119,7 +119,7 @@ - (void)updatedStatus { } if (self.UIparameters) { - self.UIparameters.text = (delegate.connection).parameters; + self.UIparameters.text = (ad.connection).parameters; } self.UIVersion.text = [NSString stringWithFormat:@"%@/%@", @@ -153,14 +153,13 @@ - (NSIndexPath *)tableView:(UITableView *)tableView ]; UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard]; [generalPasteboard setString:locationString]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController - alert:NSLocalizedString(@"Clipboard", - @"Clipboard") - message:NSLocalizedString(@"Location copied to clipboard", - @"Location copied to clipboard") - dismissAfter:1 - ]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert:NSLocalizedString(@"Clipboard", + @"Clipboard") + message:NSLocalizedString(@"Location copied to clipboard", + @"Location copied to clipboard") + dismissAfter:1 + ]; } } return nil; diff --git a/OwnTracks/OwnTracks/TabBarController.m b/OwnTracks/OwnTracks/TabBarController.m index 31ee69e7..106f9c0d 100644 --- a/OwnTracks/OwnTracks/TabBarController.m +++ b/OwnTracks/OwnTracks/TabBarController.m @@ -38,12 +38,12 @@ - (void)viewDidLoad { } } - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate addObserver:self - forKeyPath:@"action" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:nil]; - + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad addObserver:self + forKeyPath:@"action" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; + [[NSNotificationCenter defaultCenter] addObserverForName:@"reload" object:nil @@ -68,12 +68,13 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } - (void)adjust { + NSMutableArray *viewControllers = [[NSMutableArray alloc] initWithArray:self.viewControllers]; if (self.featuredVC) { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - if (delegate.action) { + if (ad.action) { if (![viewControllers containsObject:self.featuredVC]) { [viewControllers insertObject:self.featuredVC atIndex:viewControllers.count]; @@ -104,6 +105,35 @@ - (void)adjust { } } } + + if (self.regionVC) { + if (![Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]) { + if (![viewControllers containsObject:self.regionVC]) { + if ([viewControllers containsObject:self.featuredVC]) { + if ([viewControllers containsObject:self.historyVC]) { + [viewControllers insertObject:self.regionVC + atIndex:viewControllers.count - 2]; + } else { + [viewControllers insertObject:self.regionVC + atIndex:viewControllers.count - 1]; + } + } else { + if ([viewControllers containsObject:self.historyVC]) { + [viewControllers insertObject:self.regionVC + atIndex:viewControllers.count - 1]; + } else { + [viewControllers insertObject:self.regionVC + atIndex:viewControllers.count]; + } + } + } + } else { + if ([viewControllers containsObject:self.regionVC]) { + [viewControllers removeObject:self.regionVC]; + } + } + } + [self setViewControllers:viewControllers animated:TRUE]; } diff --git a/OwnTracks/OwnTracks/Tours.h b/OwnTracks/OwnTracks/Tours.h new file mode 100644 index 00000000..5c736933 --- /dev/null +++ b/OwnTracks/OwnTracks/Tours.h @@ -0,0 +1,41 @@ +// +// Tours.h +// OwnTracks +// +// Created by Christoph Krey on 02.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface Tour: NSObject +@property (strong, nonatomic) NSString *label; +@property (strong, nonatomic) NSString *uuid; +@property (strong, nonatomic) NSString *url; +@property (strong, nonatomic) NSDate *from; +@property (strong, nonatomic) NSDate *to; + +- (instancetype)initFromDictionary:(nullable NSDictionary *)dictionary; +- (NSDictionary *)asDictionary; +- (NSComparisonResult)compare:(Tour *)tour; +@end + +@interface Tours : NSObject +@property (strong, nonatomic, nullable) NSMutableDictionary *response; +@property (strong, nonatomic) NSDate *timestamp; +@property (strong, nonatomic) NSString *message; +@property (strong, nonatomic) NSNumber *activity; + ++ (Tours *)sharedInstance; +- (void)refresh; +- (NSInteger)count; +- (nullable Tour *)tourAtIndex:(NSInteger)index; +- (void)requestTour:(nonnull Tour *)tour; +- (void)addTour:(nonnull Tour *)tour; +- (BOOL)removeTourAtIndex:(NSInteger)index; +- (BOOL)processResponse:(NSDictionary *)dictionary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OwnTracks/OwnTracks/Tours.m b/OwnTracks/OwnTracks/Tours.m new file mode 100644 index 00000000..2d180704 --- /dev/null +++ b/OwnTracks/OwnTracks/Tours.m @@ -0,0 +1,247 @@ +// +// Tours.m +// OwnTracks +// +// Created by Christoph Krey on 02.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import "Tours.h" +#import "CoreData.h" +#import "Settings.h" +#import "OwnTracksAppDelegate.h" + +@implementation Tour +- (instancetype)initFromDictionary:(NSDictionary *)dictionary { + self = [self init]; + if (dictionary && [dictionary isKindOfClass:[NSDictionary class]]) { + self.label = dictionary[@"label"]; + self.uuid = dictionary[@"uuid"]; + self.url = dictionary[@"url"]; + NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init]; + formatter.formatOptions ^= NSISO8601DateFormatWithTimeZone; + self.from = [formatter dateFromString:dictionary[@"from"]]; + self.to = [formatter dateFromString:dictionary[@"to"]]; + } + return self; +} + +- (NSDictionary *)asDictionary { + NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init]; + formatter.formatOptions = NSISO8601DateFormatWithInternetDateTime ^ + NSISO8601DateFormatWithTimeZone; + formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"]; + + NSMutableDictionary *dictionary = [@{ + @"label": self.label, + @"from": [formatter stringFromDate:self.from], + @"to": [formatter stringFromDate:self.to] + } mutableCopy]; + + if (self.uuid) { + dictionary[@"uuid"] = self.uuid; + } + if (self.url) { + dictionary[@"url"] = self.url; + } + return dictionary; +} + +- (NSComparisonResult)compare:(Tour *)tour { + return [self.from compare:tour.from]; +} + +@end + +@interface Tours() +@property (strong, nonatomic) NSMutableArray *array; +@property (strong, nonatomic) NSTimer *tourTimer; +@property (strong, nonatomic) NSTimer *toursTimer; +@end + +@implementation Tours +static Tours *theInstance = nil; + ++ (Tours *)sharedInstance { + if (theInstance == nil) { + theInstance = [[Tours alloc] init]; + } + return theInstance; +} + +- (instancetype)init { + self = [super init]; + self.timestamp = [NSDate distantPast]; + self.array = [[NSMutableArray alloc] init]; + [self refresh]; + return self; +} + +- (void)setResponse:(NSMutableDictionary *)response { + _response = response; + self.array = [[NSMutableArray alloc] init]; + NSArray *array = [response objectForKey:@"tours"]; + if (array && [array isKindOfClass:[NSArray class]]) { + for (NSDictionary *dictionary in array) { + if ([dictionary isKindOfClass:[NSDictionary class]]) { + Tour *tour = [[Tour alloc] initFromDictionary:dictionary]; + [self.array addObject:tour]; + } + } + } + self.array = [[self.array sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; + self.timestamp = [NSDate date]; + [self.toursTimer invalidate]; + self.activity = @(FALSE); + self.message = NSLocalizedString(@"Tour list received", @"Tour list received"); +} + +- (NSInteger)count{ + return self.array.count; +} + +- (Tour *)tourAtIndex:(NSInteger)index { + if (index < self.array.count) { + return self.array[index]; + } else { + return nil; + } +} + +- (void)refresh { + NSManagedObjectContext *moc = [CoreData sharedInstance].mainMOC; + NSString *topic = [Settings theGeneralTopicInMOC:moc]; + + NSDictionary *json = @{ + @"_type": @"request", + @"request": @"tours", + }; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.connection sendData:[NSJSONSerialization dataWithJSONObject:json + options:NSJSONWritingSortedKeys + error:nil] + topic:[topic stringByAppendingString:@"/request"] + topicAlias:@(0) + qos:MQTTQosLevelAtMostOnce + retain:NO]; + self.activity = @(TRUE); + self.message = NSLocalizedString(@"Requesting tour list", @"Requesting tour list"); + if (self.toursTimer && self.toursTimer.isValid) { + [self.toursTimer invalidate]; + } + self.toursTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 + repeats:FALSE + block:^(NSTimer * _Nonnull timer) { + self.activity = @(FALSE); + self.message = NSLocalizedString(@"Tour list request timed out", @"Tour list request timed out"); + }]; +} + +- (void)addTour:(Tour *)tour { + [self.array addObject:tour]; + self.array = [[self.array sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; + self.timestamp = [NSDate date]; + [self.tourTimer invalidate]; + self.activity = @(FALSE); + self.message = NSLocalizedString(@"Tour created", @"Tour created"); +} + +- (void)requestTour:(Tour *)tour { + NSManagedObjectContext *moc = [CoreData sharedInstance].mainMOC; + NSString *topic = [Settings theGeneralTopicInMOC:moc]; + + NSDictionary *json = @{ + @"_type": @"request", + @"request": @"tour", + @"tour": tour.asDictionary + }; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.connection sendData:[NSJSONSerialization dataWithJSONObject:json + options:NSJSONWritingSortedKeys + error:nil] + topic:[topic stringByAppendingString:@"/request"] + topicAlias:@(0) + qos:MQTTQosLevelAtMostOnce + retain:NO]; + self.activity = @(TRUE); + self.message = NSLocalizedString(@"Requesting tour", @"Requesting tour"); + if (self.tourTimer && self.tourTimer.isValid) { + [self.tourTimer invalidate]; + } + self.tourTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 + repeats:FALSE + block:^(NSTimer * _Nonnull timer) { + self.activity = @(FALSE); + self.message = NSLocalizedString(@"Tour request timed out", @"Tour request timed out"); + }]; +} + +- (BOOL)removeTourAtIndex:(NSInteger)index { + Tour *tour = [self tourAtIndex:index]; + if (!tour) { + return FALSE; + } + + if (tour.uuid) { + NSManagedObjectContext *moc = [CoreData sharedInstance].mainMOC; + NSString *topic = [Settings theGeneralTopicInMOC:moc]; + + NSDictionary *json = @{ + @"_type": @"request", + @"request": @"untour", + @"uuid": tour.uuid, + }; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.connection sendData:[NSJSONSerialization dataWithJSONObject:json + options:NSJSONWritingSortedKeys + error:nil] + topic:[topic stringByAppendingString:@"/request"] + topicAlias:@(0) + qos:MQTTQosLevelAtMostOnce + retain:NO]; + self.activity = @(FALSE); + self.message = NSLocalizedString(@"Tour deleted", @"Tour deleted"); + } + + [self.array removeObjectAtIndex:index]; + self.timestamp = [NSDate date]; + return TRUE; +} + +- (BOOL)processResponse:(NSDictionary *)dictionary { + NSString *request = dictionary[@"request"]; + if ([request isEqualToString:@"tour"]) { + NSNumber *status = dictionary[@"status"]; + if (status.integerValue == 200) { + Tour *tour = [[Tour alloc] initFromDictionary:dictionary[@"tour"]]; + [[Tours sharedInstance] addTour:tour]; + + UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard]; + [generalPasteboard setString:tour.url]; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + + [ad.navigationController alert: + NSLocalizedString(@"Response", + @"Alert message header for Request Response") + message: + [NSString stringWithFormat:@"%@ %ld %@\n", + NSLocalizedString(@"URL copied to Clipboard", + @"URL copied to Clipboard"), + (long)status.integerValue, + tour.url] + url:tour.url + ]; + } + return TRUE; + } else if ([request isEqual:@"tours"]) { + self.response = [dictionary mutableCopy]; + return TRUE; + } else { + return FALSE; + } +} +@end diff --git a/OwnTracks/OwnTracks/ToursStatusCell.h b/OwnTracks/OwnTracks/ToursStatusCell.h new file mode 100644 index 00000000..83ca70e1 --- /dev/null +++ b/OwnTracks/OwnTracks/ToursStatusCell.h @@ -0,0 +1,19 @@ +// +// ToursStatusCell.h +// OwnTracks +// +// Created by Christoph Krey on 05.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ToursStatusCell : UITableViewCell +@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activity; +@property (weak, nonatomic) IBOutlet UILabel *label; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OwnTracks/OwnTracks/ToursStatusCell.m b/OwnTracks/OwnTracks/ToursStatusCell.m new file mode 100644 index 00000000..90142f72 --- /dev/null +++ b/OwnTracks/OwnTracks/ToursStatusCell.m @@ -0,0 +1,24 @@ +// +// ToursStatusCell.m +// OwnTracks +// +// Created by Christoph Krey on 05.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import "ToursStatusCell.h" + +@implementation ToursStatusCell + +- (void)awakeFromNib { + [super awakeFromNib]; + // Initialization code +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +@end diff --git a/OwnTracks/OwnTracks/ToursTVC.h b/OwnTracks/OwnTracks/ToursTVC.h new file mode 100644 index 00000000..5e16f1fb --- /dev/null +++ b/OwnTracks/OwnTracks/ToursTVC.h @@ -0,0 +1,17 @@ +// +// ToursTVC.h +// OwnTracks +// +// Created by Christoph Krey on 01.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import +#import "OwnTracksEditTVC.h" + +NS_ASSUME_NONNULL_BEGIN +@interface ToursTVC : OwnTracksEditTVC + +@end + +NS_ASSUME_NONNULL_END diff --git a/OwnTracks/OwnTracks/ToursTVC.m b/OwnTracks/OwnTracks/ToursTVC.m new file mode 100644 index 00000000..24cb84d3 --- /dev/null +++ b/OwnTracks/OwnTracks/ToursTVC.m @@ -0,0 +1,245 @@ +// +// ToursTVC.m +// OwnTracks +// +// Created by Christoph Krey on 01.08.22. +// Copyright © 2022 OwnTracks. All rights reserved. +// + +#import "ToursTVC.h" +#import "Tours.h" +#import "OwnTracksAppDelegate.h" +#import "CreateTourTVC.h" +#import "ToursStatusCell.h" + +@interface ToursTVC () +@property (strong, nonatomic) UIRefreshControl *refreshControl; +@end + +@implementation ToursTVC +@dynamic refreshControl; + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Uncomment the following line to preserve selection between presentations. + // self.clearsSelectionOnViewWillAppear = NO; + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem; + + self.emptyText = NSLocalizedString(@"No or empty tour list received from backend", + @"No or empty tour list received from backend"); + self.refreshControl = [[UIRefreshControl alloc] init]; + self.refreshControl.attributedTitle = + [[NSAttributedString alloc] + initWithString: NSLocalizedString(@"Fetching tour list from backend", + @"Fetching tour list from backend")]; + [self.refreshControl addTarget:self + action:@selector(refresh) + forControlEvents:UIControlEventValueChanged]; + + [self.tableView addSubview:self.refreshControl]; +} + +- (IBAction)refreshPressed:(UIBarButtonItem *)sender { + [[Tours sharedInstance] refresh]; +} + +- (void)refresh { + [[Tours sharedInstance] refresh]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + Tours *tours = [Tours sharedInstance]; + [tours addObserver:self + forKeyPath:@"timestamp" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; + [tours addObserver:self + forKeyPath:@"message" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:nil]; +} + +- (void)viewWillDisappear:(BOOL)animated { + Tours *tours = [Tours sharedInstance]; + [tours removeObserver:self + forKeyPath:@"message" + context:nil]; + [tours removeObserver:self + forKeyPath:@"timestamp" + context:nil]; + [super viewWillDisappear:animated]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + [self performSelectorOnMainThread:@selector(update) + withObject:nil + waitUntilDone:NO]; +} + +- (void)update { + [self.refreshControl endRefreshing]; + [self.tableView reloadData]; +} + +- (IBAction)tourSaved:(UIStoryboardSegue *)segue { + if ([segue.sourceViewController isKindOfClass:[CreateTourTVC class]]) { + CreateTourTVC *createTourTVC = (CreateTourTVC *)segue.sourceViewController; + Tour *tour = [[Tour alloc] init]; + tour.label = createTourTVC.label.text; + tour.from = createTourTVC.from.date; + tour.to = createTourTVC.to.date; + + [[Tours sharedInstance] requestTour:tour]; + } +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 2; +} + +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + Tours *tours = [Tours sharedInstance]; + if (tours.count == 0) { + [self empty]; + } else { + [self nonempty]; + } + + if (section == 0) { + return tours.count; + } else { + return 1; + } + +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TourCell" forIndexPath:indexPath]; + + Tour *tour = [[Tours sharedInstance] tourAtIndex:indexPath.row]; + cell.textLabel.text = [NSString stringWithFormat:@"%@", + tour.label]; + + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateStyle = NSDateFormatterShortStyle; + formatter.timeStyle = NSDateFormatterShortStyle; + + cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ - %@", + [formatter stringFromDate:tour.from], + [formatter stringFromDate:tour.to]]; + return cell; + + } else { + ToursStatusCell *statusCell = [tableView dequeueReusableCellWithIdentifier:@"ToursStatusCell" forIndexPath:indexPath]; + + if ([[Tours sharedInstance].activity boolValue]) { + [statusCell.activity startAnimating]; + } else { + [statusCell.activity stopAnimating]; + } + statusCell.label.text = [Tours sharedInstance].message; + return statusCell; + } +} + +- (BOOL)tableView:(UITableView *)tableView +canEditRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + return YES; + } else { + return NO; + } +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + [[Tours sharedInstance] removeTourAtIndex:indexPath.row]; + [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; + } else if (editingStyle == UITableViewCellEditingStyleInsert) { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } +} + +/* +// Override to support rearranging the table view. +- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { +} +*/ + +/* +// Override to support conditional rearranging of the table view. +- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { + // Return NO if you do not want the item to be re-orderable. + return YES; +} +*/ + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +- (NSIndexPath *)tableView:(UITableView *)tableView + willSelectRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + Tour *tour = [[Tours sharedInstance] tourAtIndex:indexPath.row]; + if (tour.uuid) { + return indexPath; + } else { + return nil; + } + } else { + return nil; + } +} + +- (void)tableView:(UITableView *)tableView +didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + Tour *tour = [[Tours sharedInstance] tourAtIndex:indexPath.row]; + if (tour.uuid) { + UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard]; + [generalPasteboard setString:tour.url]; + + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert: + NSLocalizedString(@"Copied", + @"Alert message header for copy") + message: + [NSString stringWithFormat:@"%@ %@\n", + NSLocalizedString(@"URL copied to Clipboard", + @"URL copied to Clipboard"), + tour.url] + url:tour.url + ]; + } + [tableView deselectRowAtIndexPath:indexPath animated:TRUE]; +} + +- (NSString *)tableView:(UITableView *)tableView +titleForHeaderInSection:(NSInteger)section { + if (section == 0) { + return NSLocalizedString(@"Tours", @"Tour list header"); + } else { + return NSLocalizedString(@"Tours status", @"Tours status header"); + } +} + +@end diff --git a/OwnTracks/OwnTracks/ViewController.m b/OwnTracks/OwnTracks/ViewController.m index dd93bdcd..3a37be77 100644 --- a/OwnTracks/OwnTracks/ViewController.m +++ b/OwnTracks/OwnTracks/ViewController.m @@ -37,6 +37,7 @@ @interface ViewController () @property (strong, nonatomic) UISegmentedControl *modes; @property (strong, nonatomic) UISegmentedControl *mapMode; @property (strong, nonatomic) MKUserTrackingButton *trackingButton; +@property (weak, nonatomic) IBOutlet UIBarButtonItem *askForMapButton; @property (nonatomic) BOOL warning; @end @@ -214,6 +215,9 @@ - (IBAction)modesChanged:(UISegmentedControl *)segmentedControl { } - (void)updateMoveButton { + BOOL locked = [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; + self.modes.enabled = !locked; + switch ([LocationManager sharedInstance].monitoring) { case LocationMonitoringMove: self.modes.selectedSegmentIndex = 3; @@ -279,6 +283,10 @@ - (void)reloaded { } - (NSInteger)noMap { + BOOL locked = [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; + self.askForMapButton.enabled = !locked; + + NSInteger noMap = [[NSUserDefaults standardUserDefaults] integerForKey:@"noMap"]; @@ -425,13 +433,14 @@ - (void)viewDidAppear:(BOOL)animated { if (!self.warning && ![Setting existsSettingWithKey:@"mode" inMOC:CoreData.sharedInstance.mainMOC]) { self.warning = TRUE; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController alert: + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert: NSLocalizedString(@"Setup", @"Header of an alert message regarding missing setup") - message: + message: NSLocalizedString(@"You need to setup your own OwnTracks server and edit your configuration for full privacy protection. Detailed info on https://owntracks.org/booklet", - @"Text explaining the Setup")]; + @"Text explaining the Setup") + ]; } if (self.noMap == 0) { @@ -816,7 +825,7 @@ - (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend } - (IBAction)actionPressed:(UIBarButtonItem *)sender { - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; BOOL validIds = [Settings validIdsInMOC:CoreData.sharedInstance.mainMOC]; int ignoreInaccurateLocations = [Settings intForKey:@"ignoreinaccuratelocations_preference" inMOC:CoreData.sharedInstance.mainMOC]; @@ -829,7 +838,7 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { NSString *message = NSLocalizedString(@"To publish your location userID and deviceID must be set", @"Warning displayed if necessary settings are missing"); - [delegate.navigationController alert:@"Settings" message:message]; + [ad.navigationController alert:@"Settings" message:message]; return; } @@ -838,7 +847,7 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { (location.coordinate.latitude == 0.0 && location.coordinate.longitude == 0.0) ) { - [delegate.navigationController alert: + [ad.navigationController alert: NSLocalizedString(@"Location", @"Header of an alert message regarding a location") message: @@ -849,7 +858,7 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { } if (ignoreInaccurateLocations != 0 && location.horizontalAccuracy > ignoreInaccurateLocations) { - [delegate.navigationController alert: + [ad.navigationController alert: NSLocalizedString(@"Location", @"Header of an alert message regarding a location") message: @@ -859,8 +868,8 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { return; } - if ([delegate sendNow:location]) { - [delegate.navigationController alert: + if ([ad sendNow:location]) { + [ad.navigationController alert: NSLocalizedString(@"Location", @"Header of an alert message regarding a location") message: @@ -869,7 +878,7 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { dismissAfter:1 ]; } else { - [delegate.navigationController alert: + [ad.navigationController alert: NSLocalizedString(@"Location", @"Header of an alert message regarding a location") message: @@ -879,6 +888,10 @@ - (IBAction)actionPressed:(UIBarButtonItem *)sender { } - (IBAction)longPress:(UILongPressGestureRecognizer *)sender { + if ([Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]) { + return; + } + if (sender.state == UIGestureRecognizerStateBegan) { Friend *friend = [Friend friendWithTopic:[Settings theGeneralTopicInMOC:CoreData.sharedInstance.mainMOC] inManagedObjectContext:CoreData.sharedInstance.mainMOC]; @@ -897,13 +910,15 @@ - (IBAction)longPress:(UILongPressGestureRecognizer *)sender { context:CoreData.sharedInstance.mainMOC]; [CoreData.sharedInstance sync:CoreData.sharedInstance.mainMOC]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController alert:NSLocalizedString(@"Region", - @"Header of an alert message regarding circular region") - message:NSLocalizedString(@"created at center of map", - @"content of an alert message regarding circular region") - dismissAfter:1 - ]; + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert: + NSLocalizedString(@"Region", + @"Header of an alert message regarding circular region") + message: + NSLocalizedString(@"created at center of map", + @"content of an alert message regarding circular region") + dismissAfter:1 + ]; } } diff --git a/OwnTracks/OwnTracks/WaypointTVC.m b/OwnTracks/OwnTracks/WaypointTVC.m index 427b4497..55283b52 100644 --- a/OwnTracks/OwnTracks/WaypointTVC.m +++ b/OwnTracks/OwnTracks/WaypointTVC.m @@ -26,6 +26,7 @@ @interface WaypointTVC () @property (weak, nonatomic) IBOutlet UITextField *UIinfo; @property (weak, nonatomic) IBOutlet UITextField *UIcreatedAt; @property (weak, nonatomic) IBOutlet UITextField *UIbatterylevel; +@property (weak, nonatomic) IBOutlet UIBarButtonItem *bookmarkButton; @property (nonatomic) BOOL needsUpdate; @property (strong, nonatomic) CLRegion *oldRegion; @@ -46,9 +47,11 @@ - (IBAction)setPerson:(UIStoryboardSegue *)segue { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + + BOOL locked = [Settings theLockedInMOC:CoreData.sharedInstance.mainMOC]; CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; - if (status != CNAuthorizationStatusAuthorized) { + if (locked || status != CNAuthorizationStatusAuthorized) { [self.navigationItem setRightBarButtonItem:nil]; } @@ -116,23 +119,22 @@ - (NSIndexPath *)tableView:(UITableView *)tableView NSString *locationString = (self.waypoint).shortCoordinateText; UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard]; [generalPasteboard setString:locationString]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController alert:NSLocalizedString(@"Clipboard", - @"Clipboard") - message:NSLocalizedString(@"Location copied to clipboard", - @"Location copied to clipboard") - dismissAfter:1 + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert:NSLocalizedString(@"Clipboard", + @"Clipboard") + message:NSLocalizedString(@"Location copied to clipboard", + @"Location copied to clipboard") + dismissAfter:1 ]; } else if (indexPath.section == 1 && indexPath.row == 6) { UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard]; [generalPasteboard setString:(self.waypoint).belongsTo.topic]; - OwnTracksAppDelegate *delegate = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; - [delegate.navigationController - alert:NSLocalizedString(@"Clipboard", - @"Clipboard") - message:NSLocalizedString(@"Topic copied to clipboard", - @"Topic copied to clipboard") - dismissAfter:1 + OwnTracksAppDelegate *ad = (OwnTracksAppDelegate *)[UIApplication sharedApplication].delegate; + [ad.navigationController alert:NSLocalizedString(@"Clipboard", + @"Clipboard") + message:NSLocalizedString(@"Topic copied to clipboard", + @"Topic copied to clipboard") + dismissAfter:1 ]; } diff --git a/OwnTracks/OwnTracks/da.lproj/InfoPlist.strings b/OwnTracks/OwnTracks/da.lproj/InfoPlist.strings new file mode 100644 index 00000000..3c879038 --- /dev/null +++ b/OwnTracks/OwnTracks/da.lproj/InfoPlist.strings @@ -0,0 +1,39 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "OwnTracks"; + +/* Bundle name */ +"CFBundleName" = "OwnTracks"; + +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "OwnTracks bruger dit kamera når anmodet om at oprette et kort"; + +/* Privacy - Contacts Usage Description */ +"NSContactsUsageDescription" = "Hvis du tillader at OwnTracks tilgår dine kontakter, kan du linke dine enheder til kontakter. Derefter vil OwnTracks vise kontaktnavn og -billede i stedet for device id. Ingen information fra din addressebog vil blive sendt til servere."; + +/* Privacy - Location Always and When In Use Usage Description */ +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Din position er brugt til at dele og gemme på en server som du vælger"; + +/* Privacy - Location Always Usage Description */ +"NSLocationAlwaysUsageDescription" = "Din position er brugt til at dele og gemme på en server som du vælger"; + +/* Privacy - Location Usage Description */ +"NSLocationUsageDescription" = "Din position er brugt til at dele og gemme på en server som du vælger"; + +/* Privacy - Location When In Use Usage Description */ +"NSLocationWhenInUseUsageDescription" = "Din position er brugt til at dele og gemme på en server som du vælger"; + +/* Privacy - Motion Usage Description */ +"NSMotionUsageDescription" = "OwnTracks leverer dit antal skridt, etager bestiget og tilbagelagt distance ved anmodning"; + +/* (No Comment) */ +"OwnTracks CER File" = "OwnTracks CER-fil"; + +/* (No Comment) */ +"OwnTracks Configuration File" = "OwnTracks Konfigurationsfil"; + +/* (No Comment) */ +"OwnTracks PKCS12 File" = "OwnTracks PKCS12 Fil"; + +/* (No Comment) */ +"OwnTracks Waypoints File" = "OwnTracks rutepunkter-fil"; + diff --git a/OwnTracks/OwnTracks/da.lproj/Intents.strings b/OwnTracks/OwnTracks/da.lproj/Intents.strings new file mode 100644 index 00000000..3f9299ba --- /dev/null +++ b/OwnTracks/OwnTracks/da.lproj/Intents.strings @@ -0,0 +1,63 @@ +/* (No Comment) */ +"5EuzDa" = "manuel"; + +/* (No Comment) */ +"B33bKe" = "stille"; + +/* (No Comment) */ +"EJYIEL" = "Send nuværende position"; + +/* (No Comment) */ +"EqYRFW" = "bevægelse"; + +/* (No Comment) */ +"FYfTj6" = "Skit overvågningstilstand"; + +/* (No Comment) */ +"GJDEib-5EuzDa" = "Lige for at være sikker, du gik efter 'manuel'?"; + +/* (No Comment) */ +"GJDEib-B33bKe" = "Lige for at være sikker, du gik efter 'stille'?"; + +/* (No Comment) */ +"GJDEib-EqYRFW" = "Lige for at være sikker, du gik efter 'bevægelse'?"; + +/* (No Comment) */ +"GJDEib-l7Rl2N" = "Lige for at være sikker, du gik efter 'betydelig'?"; + +/* (No Comment) */ +"JylJeF" = "Skit overvågningstilstand"; + +/* (No Comment) */ +"l7Rl2N" = "betydelig"; + +/* (No Comment) */ +"MfQBKX" = "Overvågningstilstand"; + +/* (No Comment) */ +"Ogyrko" = "Skit din overvågningstilstand"; + +/* (No Comment) */ +"P5jx7T-5EuzDa" = "Der er ${count} muligheder som matcher 'manuel'."; + +/* (No Comment) */ +"P5jx7T-B33bKe" = "Der er ${count} muligheder som matcher 'stille'."; + +/* (No Comment) */ +"P5jx7T-EqYRFW" = "Der er ${count} muligheder som matcher 'bevægelse'."; + +/* (No Comment) */ +"P5jx7T-l7Rl2N" = "Der er ${count} muligheder som matcher 'betydelig'."; + +/* (No Comment) */ +"PKS8bh" = "Overvågning"; + +/* (No Comment) */ +"u3pDrc" = "Send Nu"; + +/* (No Comment) */ +"vuIozC" = "Skift Overvågning"; + +/* (No Comment) */ +"WvwXPJ" = "Send din position med det samme"; + diff --git a/OwnTracks/OwnTracks/da.lproj/Localizable.strings b/OwnTracks/OwnTracks/da.lproj/Localizable.strings new file mode 100644 index 00000000..5f24eb67 --- /dev/null +++ b/OwnTracks/OwnTracks/da.lproj/Localizable.strings @@ -0,0 +1,360 @@ +/* New featured content indicator */ +"!" = "!"; + +/* Short for degrees celsius as in 20° */ +"°" = "°"; + +/* Short for deviation plus/minus */ +"±" = "±"; + +/* Short for altitude as in ✈︎1000m */ +"✈︎" = "✈︎"; + +/* name of an unknown or hidden region */ +"a region" = "en region"; + +/* Address resolver disabled */ +"Address resolver disabled" = "Adressebestemmer slået fra"; + +/* reverseGeocodeLocation error */ +"Address resolver failed" = "Adressebestemmer fejlede"; + +/* Headline in addressbook related error messages */ +"Addressbook Access" = "Adgang til Adressebog"; + +/* Location Manager error message */ +"App is not allowed to use location services" = "App er ikke tilladt at bruge lokationstjenester"; + +/* Location Manager error message */ +"App is not allowed to use location services in background" = "App er ikke tilladt at bruge lokationstjenester i baggrunden"; + +/* Location Manager error message */ +"App is not allowed to use location services yet" = "App er ikke tilladt at bruge lokationstjenester endnu"; + +/* Location Manager error message */ +"App use of location services is restricted" = "Apps brug af lokationstjenester er begrænset"; + +/* Location Manager error message */ +"App use of location services is unclear" = "Apps brug af lokationstjenester er usikker"; + +/* Display after processing beacon QR code */ +"Beacon QR successfully processed" = "Beacon QR succesfuldt indlæst"; + +/* Cancel button title */ +"Cancel" = "Fortryd"; + +/* Header of an alert message regarding a card */ +"Card" = "Kort"; + +/* Location Manager error message */ +"Circular region monitoring not available" = "Cirkulær regionsovervågning ikke tilgængelig"; + +/* Clipboard */ +"Clipboard" = "Udklipsholder"; + +/* description connection closed state */ +"closed" = "lukket"; + +/* description connection closing state */ +"closing" = "lukker"; + +/* description connection connected state */ +"connected" = "forbundet"; + +/* description connection connected state */ +"connecting" = "forbinder"; + +/* Continue button title */ +"Continue" = "Fortsæt"; + +/* content of an alert message regarding circular region */ +"created at center of map" = "oprettet i midten af kortet"; + +/* Message map interaction */ +"Do you want the map to allow interaction? If you choose yes, the map provider may analyze your tile requests" = "Vil du tillade interaktion på kortet? Hvis du vælger ja, kan kortudbyderen muligvis analysere dine anmodninger"; + +/* Message Revgeo */ +"Do you want to resolve adresses? If you choose yes, the geocoding provider may analyze your requests" = "Vil du bestemme adresser? Hvis du vælger ja, kan geokoderen muligvis analysere dine anmodninger"; + +/* reverseGeocodeLocation text */ +"due to rate limit or off-line" = "grundet hastighedsbegrænsning eller offline"; + +/* Display when entering region (region name follows) */ +"Entering" = "Bevæger sig ind i"; + +/* friend enters region verb */ +"enters" = "bevæger sig ind i"; + +/* description connection error state */ +"error" = "fejl"; + +/* Display when file processing fails */ +"Error processing file" = "Fejl under indslæsning af fil"; + +/* Export */ +"Export" = "Eksportér"; + +/* Fetching sharing list from backend */ +"Fetching tour list from backend" = "Henter tur-liste fra serveren"; + +/* Display when file processing succeeds (filename follows) */ +"File" = "Fil"; + +/* Display after trying to open a file */ +"file open error" = "fejl ved åbning af fil"; + +/* Fly */ +"Fly" = "Fly"; + +/* Alert message header for friend's messages */ +"Friend" = "Ven"; + +/* CNAuthorizationStatusDenied */ +"has been denied by user. If you allow OwnTracks to access your contacts, you can link your devices to contacts. OwnTracks will then display the contact name and image instead of the device Id. No information of your address book will be uploaded to any server. Go to Settings/Privacy/Contacts to change" = "er blevet nægtet af bruger. Hvis du tillader at OwnTracks tilgår dine kontakter, kan du linke dine enheder til kontakter. Derefter vil OwnTracks vise kontaktnavn og -billede i stedet for device id. Ingen information fra din addressebog vil blive sendt til servere. Gå til Indstillinger/Privatlivs/Kontakter for at ændre"; + +/* CNAuthorizationStatusRestricted */ +"has been restricted, possibly due to restrictions such as parental controls." = "er blevet begrænset, mulgivis på grund af begrænsninger som forældrekontrol."; + +/* Hyb */ +"Hyb" = "Hyb"; + +/* HybFly */ +"HybFly" = "HybFly"; + +/* Location Manager error message */ +"iBeacon ranging not available" = "iBeacon-måling er ikke tilgængelig"; + +/* Location Manager error message */ +"iBeacon region monitoring not available" = "iBeacon-regionsovervågning er ikke tilgængelig"; + +/* description connection idle state */ +"idle" = "ledig"; + +/* Warning displayed if location is inaccurate or old */ +"Inaccurate or old location information" = "Upræcis eller forældet positionsdata"; + +/* certificate error message */ +"incorrect file or passphrase" = "forkert fil eller passphrase"; + +/* Display for incorrect inline config */ +"Inline Configuration incorrect" = "Inline Konfiguration ugyldig"; + +/* Display for incorrectly encoded inline config */ +"Inline Configuration incorrectly encoded" = "Inline Konfiguration ugyldigt kodet"; + +/* Display for config without parameters */ +"Inline Configuration missing parameters" = "Inline Konfiguration mangler parametre"; + +/* Display after processing inline config */ +"Inline Configuration successfully processed" = "Inline Konfiguration succesfuldt indlæst"; + +/* Short for kilometers as in 120km */ +"km" = "km"; + +/* Short for kilometers per hour as in 120km/h */ +"km/h" = "kom/h"; + +/* friend leaves region verb */ +"leaves" = "forlader"; + +/* Display when leaving region (region name follows) */ +"Leaving" = "Forlader"; + +/* Header of an alert message regarding a location */ +"Location" = "Position"; + +/* Location copied to clipboard */ +"Location copied to clipboard" = "Position kopieret til udklipsholder"; + +/* Location Manager error message */ +"Location services are not enabled" = "Lokationstjenester er ikke slået til"; + +/* indicates a locked configuration */ +"locked" = "låst"; + +/* Short for meters */ +"m" = "m"; + +/* content of an alert message regarging missing Mac Catalyst Export */ +"Mac Catalyst does not support export yet" = "Mac Catalyst understøtter ikke eksport endnu"; + +/* Manual */ +"Manual" = "Manuel"; + +/* Title map interaction */ +"Map Interaction" = "Kortinteraktion"; + +/* Alert header for mode change warning */ +"Mode change" = "Tilstandsændring"; + +/* Move */ +"Move" = "Bevægelse"; + +/* Mute */ +"Mute" = "Mute"; + +/* No button title */ +"No" = "Nej"; + +/* dummy for missing content */ +"no content available" = "intet indhold tilgængeligt"; + +/* No location available indication */ +"No location available" = "Ingen placering tilgængelig"; + +/* No location recorded indication */ +"No location recorded" = "Ingen placering gemt"; + +/* No or empty tour list received from backend */ +"No or empty tour list received from backend" = "Ingen eller tom liste modtaget fra serveren"; + +/* Display after trying to process a file */ +"no url specified" = "ingen URL specificeret"; + +/* Alert message header for notification messages */ +"Notification" = "Notifikation"; + +/* temporary display while opening URL */ +"opening URL" = "åbner URL"; + +/* Alert content for mode change warning */ +"Please be aware your stored waypoints and locations will be deleted on this device for privacy reasons. Please backup before." = "Vær venligst opmærksom på at dine gemte rutepunkter og positioner will blive slettet på denne enhed for dit privatlivs skyld. Lav en backup først."; + +/* Alert header regarding protocol input */ +"Protocol invalid" = "Protokol ugyldig"; + +/* Alert content regarding protocol input */ +"Protocol may be 3 for MQTT V3.1 or 4 for MQTT V3.1.1 or 5 for MQTT V5" = "Protkoll kan være 3 for MQTT V3.1 eller 4 for MQTT V3.1.1 eller 5 for MQTT V5"; + +/* content of an alert message regarding user publish */ +"publish queued on user request" = "offentliggørelse sat i gang efter brugeranmodning"; + +/* Quiet */ +"Quiet" = "Stille"; + +/* Header of an alert message regarding circular region */ +"Region" = "Region"; + +/* Requesting tour */ +"Requesting tour" = "Anmoder tur"; + +/* Requesting tour list */ +"Requesting tour list" = "Anmoder liste over ture"; + +/* temporary display while resolving address */ +"resolving..." = "bestemmer..."; + +/* Alert message header for Request Response */ +"Response" = "Respons"; + +/* Title Revgeo */ +"Reverse Geocoding Address Resolution" = "Omvent Geokoder Adressebestemmelse"; + +/* Sat */ +"Sat" = "Sat"; + +/* User Activity (Siri) Send location now */ +"Send location now" = "Send position nu"; + +/* content of an alert message regarding card */ +"set and sent to backend" = "vælg og send til serveren"; + +/* Settings screen title */ +"Settings" = "Indstillinger"; + +/* Header of an alert message regarding missing setup */ +"Setup" = "Opsætning"; + +/* MQTT Description Text */ +"Setup your own OwnTracks server for full privacy protection. More Info on https://owntracks.org/booklet" = "Opsæt din egen OwnTracks server for fuld privatlivsbeskyttelse. Mere Info på https://owntracks.org/booklet"; + +/* Significant */ +"Significant" = "Betydelig"; + +/* Location Manager error message */ +"Significant location change monitoring not available" = "Betydelig ændring overvågningstilstand er ikke tilgængelig"; + +/* HTTP Description Text */ +"Similar to MQTT mode, except data transmission uses HTTP, not MQTT." = "Ligner MQTT-tilstanden, bortset fra at data overføres med HTTP, ikke MQTT."; + +/* Std */ +"Std" = "Std"; + +/* Display when file processing succeeds */ +"successfully processed" = "succesfuldt indlæst"; + +/* Table is empty */ +"Table is empty" = "Tabel er tom"; + +/* Heading for certificate error message */ +"TLS Client Certificate" = "TLS Klientcertifikat"; + +/* Warning displayed if necessary settings are missing */ +"To publish your location userID and deviceID must be set" = "For at offentliggøre din position skal UserID og DeviceID være defineret"; + +/* Topic copied to clipboard */ +"Topic copied to clipboard" = "Emne kopieret til udklipsholder"; + +/* Tour created */ +"Tour created" = "Tur oprettet"; + +/* Tour deleted */ +"Tour deleted" = "Tur slettet"; + +/* Tour list received */ +"Tour list received" = "Tur-liste modtaget"; + +/* Tour list request timed out */ +"Tour list request timed out" = "Tur-liste-anmodning tog for lang tid"; + +/* Tour request timed out */ +"Tour request timed out" = "Tur-anmodning tog for lang tid"; + +/* Tours list header */ +"Tours" = "Ture"; + +/* Tours status header */ +"Tours status" = "Ture-status"; + +/* Tours status header */ +"Tours Status" = "Ture-Status"; + +/* Alert header regarding TrackerID input */ +"TrackerID invalid" = "TrackerID ugyldigt"; + +/* Alert content regarding TrackerID input */ +"TrackerID may be empty or up to 2 characters long" = "TrackerID kan være tomt eller op til to tegn langt"; + +/* Alert content regarding TrackerID input */ +"TrackerID may contain alphanumeric characters only" = "TrackerID kan kun indeholde alfanumeriske tegn"; + +/* Display after entering an unknown scheme in url */ +"unknown scheme in url" = "ukendt skema i url"; + +/* description connection unknown state */ +"unknown state" = "ukendt tilstand"; + +/* Display for unknown url path */ +"unknown url path" = "ukendt url-sti"; + +/* URL copied to Clipboard */ +"URL copied to Clipboard" = "URL kopieret til udklipsholder"; + +/* webView didFailLoadWithError display */ +"webView didFailLoadWithError" = "webView didFailLoadWithError"; + +/* Please Note Text */ +"When switching between modes, all OwnTracks data will be deleted for privacy reasons." = "Når der skiftes mellem tilstande, slettes alt OwnTracks data for privatliv."; + +/* Yes button title */ +"Yes" = "Yes"; + +/* You cannot use background fetch */ +"You cannot use background fetch" = "Du kan ikke bruge baggrundsfetch"; + +/* You did disable background fetch */ +"You did disable background fetch" = "Du slog baggrundsfetch fra"; + +/* Text explaining the Setup */ +"You need to setup your own OwnTracks server and edit your configuration for full privacy protection. Detailed info on https://owntracks.org/booklet" = "Du skal opsætte din egen OwnTracks server og redigere din konfiguration for fuld privatlivsbeskyttelse. Detaljeret info på https://owntracks.org/booklet"; + diff --git a/OwnTracks/OwnTracks/da.lproj/Storyboard.strings b/OwnTracks/OwnTracks/da.lproj/Storyboard.strings new file mode 100644 index 00000000..ea10c168 --- /dev/null +++ b/OwnTracks/OwnTracks/da.lproj/Storyboard.strings @@ -0,0 +1,516 @@ +/* Class = "UITextField"; placeholder = "Radius"; ObjectID = "0WN-ST-XQq"; Note = "Don't translate. Technical Term"; */ +"0WN-ST-XQq.placeholder" = "Radius"; + +/* Class = "UILabel"; text = "Parameters"; ObjectID = "1Lf-oi-0xo"; */ +"1Lf-oi-0xo.text" = "Parametre"; + +/* Class = "UILabel"; text = "Street\n1\n2\n3\n4"; ObjectID = "1PK-JM-nD2"; */ +"1PK-JM-nD2.text" = "Gade\n1\n2\n3\n4"; + +/* Class = "UILabel"; text = "maxHistory"; ObjectID = "2F5-PF-efV"; Note = "Don't translate, technical term"; */ +"2F5-PF-efV.text" = "maxHistory"; + +/* Class = "UITextField"; placeholder = "secret"; ObjectID = "2FK-gv-e75"; */ +"2FK-gv-e75.placeholder" = "secret"; + +/* Class = "UILabel"; text = "Mode"; ObjectID = "3ad-LO-zcn"; */ +"3ad-LO-zcn.text" = "Tilstand"; + +/* Class = "UILabel"; text = "pubRetain"; ObjectID = "3Hs-lP-khD"; Note = "Don't translate, technical term"; */ +"3Hs-lP-khD.text" = "pubRetain"; + +/* Class = "UINavigationItem"; title = "Friends"; ObjectID = "03k-Ca-PSS"; */ +"03k-Ca-PSS.title" = "Venner"; + +/* Class = "UILabel"; text = "locked"; ObjectID = "3K5-oQ-oxQ"; Note = "Don't translate, technical term"; */ +"3K5-oQ-oxQ.text" = "locked"; + +/* Class = "UITextField"; placeholder = "Password"; ObjectID = "4OY-Dm-bnz"; */ +"4OY-Dm-bnz.placeholder" = "Adgangskode"; + +/* Class = "UILabel"; text = "Title"; ObjectID = "5df-yk-myT"; */ +"5df-yk-myT.text" = "Titel"; + +/* Class = "UITextField"; placeholder = "days"; ObjectID = "5Nq-by-zFb"; */ +"5Nq-by-zFb.placeholder" = "dage"; + +/* Class = "UILabel"; text = "11:30 PM"; ObjectID = "6AN-cF-srI"; Note = "Don't translate. Placeholder"; */ +"6AN-cF-srI.text" = "11:30"; + +/* Class = "UITableViewSection"; headerTitle = "Client Certificate"; ObjectID = "6aR-4g-W4D"; */ +"6aR-4g-W4D.headerTitle" = "Klientcertifikat"; + +/* Class = "UILabel"; text = "Filename"; ObjectID = "7ie-NC-EFG"; Note = "Don't translate, technical term"; */ +"7ie-NC-EFG.text" = "Filnavn"; + +/* Class = "UINavigationItem"; title = "Tours"; ObjectID = "7Qj-vi-EaF"; */ +"7Qj-vi-EaF.title" = "Ture"; + +/* Class = "UIButton"; normalTitle = "Create Card"; ObjectID = "7rX-Bq-DGb"; */ +"7rX-Bq-DGb.normalTitle" = "Opret Kort"; + +/* Class = "UISegmentedControl"; 7W2-Mn-fUG.segmentTitles[0] = "MQTT"; ObjectID = "7W2-Mn-fUG"; Note = "Don't translate, technical term"; */ +"7W2-Mn-fUG.segmentTitles[0]" = "MQTT"; + +/* Class = "UISegmentedControl"; 7W2-Mn-fUG.segmentTitles[1] = "HTTP"; ObjectID = "7W2-Mn-fUG"; Note = "Don't translate, technical term"; */ +"7W2-Mn-fUG.segmentTitles[1]" = "HTTP"; + +/* Class = "UITableViewSection"; headerTitle = "Circular Region"; ObjectID = "7xg-sB-qXa"; */ +"7xg-sB-qXa.headerTitle" = "Cirkulær Region"; + +/* Class = "UINavigationItem"; title = "Waypoint"; ObjectID = "8DB-l4-Dop"; */ +"8DB-l4-Dop.title" = "Rutepunkt"; + +/* Class = "UINavigationItem"; title = "History"; ObjectID = "9IF-HL-6Bf"; */ +"9IF-HL-6Bf.title" = "Historik"; + +/* Class = "UITableViewSection"; headerTitle = "Info"; ObjectID = "9tZ-tQ-x72"; */ +"9tZ-tQ-x72.headerTitle" = "Info"; + +/* Class = "UIButton"; normalTitle = "Export Waypoints"; ObjectID = "54d-E6-cWX"; */ +"54d-E6-cWX.normalTitle" = "Eksportér Rutepunkter"; + +/* Class = "UITextField"; placeholder = "willTopic"; ObjectID = "79P-sU-en0"; Note = "Placeholder. Do not translate"; */ +"79P-sU-en0.placeholder" = "willTopic"; + +/* Class = "UILabel"; text = "keepAlive"; ObjectID = "222-vu-d4y"; Note = "Don't translate, technical term"; */ +"222-vu-d4y.text" = "keepAlive"; + +/* Class = "UITextField"; placeholder = ""; ObjectID = "572-Up-z9B"; Note = "Placeholder. Do not translate"; */ +"572-Up-z9B.placeholder" = ""; + +/* Class = "UITableViewSection"; headerTitle = "Please note"; ObjectID = "a09-rn-TZE"; */ +"a09-rn-TZE.headerTitle" = "Læg venligst mærke til"; + +/* Class = "UILabel"; text = "Host"; ObjectID = "a19-FN-Mwf"; */ +"a19-FN-Mwf.text" = "Vært"; + +/* Class = "UITableViewSection"; headerTitle = "HTTP Mode"; ObjectID = "Ac4-3H-YRL"; */ +"Ac4-3H-YRL.headerTitle" = "HTTP-tilstand"; + +/* Class = "UINavigationItem"; title = "Featured"; ObjectID = "AdM-qz-URl"; */ +"AdM-qz-URl.title" = "Fremhævet"; + +/* Class = "UILabel"; text = "Street\n1\n2\n3\n4"; ObjectID = "apt-Il-Ih4"; Note = "Don't Translate, Placeholder"; */ +"apt-Il-Ih4.text" = "Gade\n1\n2\n3\n4"; + +/* Class = "UILabel"; text = "Allow Untrusted Certificates"; ObjectID = "aVN-vG-YjO"; Note = "Don't translate, technical term"; */ +"aVN-vG-YjO.text" = "Tillad Utroværdige Certifikater"; + +/* Class = "UINavigationItem"; title = "Credits and License"; ObjectID = "BPo-EV-Jgg"; */ +"BPo-EV-Jgg.title" = "Anerkendelser og Licens"; + +/* Class = "UILabel"; text = "Passphrase"; ObjectID = "BTX-XN-kzE"; Note = "Don't translate, technical term"; */ +"BTX-XN-kzE.text" = "Passphrase"; + +/* Class = "UILabel"; text = "Password"; ObjectID = "C1o-h4-z6h"; */ +"C1o-h4-z6h.text" = "Adgangskode"; + +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "cBn-p7-eqG"; Note = "Placeholder. Do not translate"; */ +"cBn-p7-eqG.placeholder" = "qos"; + +/* Class = "UILabel"; text = "cmd"; ObjectID = "cTs-Rr-JTs"; Note = "Don't translate, technical term"; */ +"cTs-Rr-JTs.text" = "cmd"; + +/* Class = "UILabel"; text = "Latitude"; ObjectID = "Cuh-Tm-IcV"; */ +"Cuh-Tm-IcV.text" = "Breddegrad"; + +/* Class = "UILabel"; text = "extendedData"; ObjectID = "cxz-nO-ptK"; Note = "Don't translate, technical term"; */ +"cxz-nO-ptK.text" = "extendedData"; + +/* Class = "UILabel"; text = "Topic"; ObjectID = "Dgw-5m-uvy"; */ +"Dgw-5m-uvy.text" = "Emne"; + +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "dhG-pz-V9R"; */ +"dhG-pz-V9R.title" = "Element"; + +/* Class = "UITableViewSection"; headerTitle = "Beacon Region"; ObjectID = "dPT-PC-ShQ"; */ +"dPT-PC-ShQ.headerTitle" = "Beacon Region"; + +/* Class = "UITextField"; placeholder = "trackerid"; ObjectID = "dUg-TH-IQ0"; Note = "Don't translate, technical term"; */ +"dUg-TH-IQ0.placeholder" = "trackerid"; + +/* Class = "UITextView"; text = "Copyright © 2013-2022 by Christoph Krey, https://owntracks.org"; ObjectID = "dZg-CC-7yD"; */ +"dZg-CC-7yD.text" = "Copyright © 2013-2022 af Christoph Krey, https://owntracks.org"; + +/* Class = "UILabel"; text = "ranging"; ObjectID = "DZx-uw-XhQ"; Note = "Don't translate, technical term"; */ +"DZx-uw-XhQ.text" = "ranging"; + +/* Class = "UITextField"; placeholder = "deviceid"; ObjectID = "e0q-hc-0z2"; Note = "Placeholder. Do not translate"; */ +"e0q-hc-0z2.placeholder" = "deviceid"; + +/* Class = "UITabBarController"; title = "Tab Bar"; ObjectID = "e04-OU-xZ4"; */ +"e04-OU-xZ4.title" = "Fanebar"; + +/* Class = "UIButton"; normalTitle = "Documentation"; ObjectID = "ePs-kX-VDJ"; */ +"ePs-kX-VDJ.normalTitle" = "Dokumentation"; + +/* Class = "UIBarButtonItem"; title = "±2000m"; ObjectID = "ere-0U-4rD"; Note = "Placeholder. Do not transalate"; */ +"ere-0U-4rD.title" = "±2000m"; + +/* Class = "UILabel"; text = "Port"; ObjectID = "EWI-7o-7Jo"; */ +"EWI-7o-7Jo.text" = "Port"; + +/* Class = "UILabel"; text = "clientId"; ObjectID = "fHY-ry-yB3"; Note = "Don't translate, technical term"; */ +"fHY-ry-yB3.text" = "clientid"; + +/* Class = "UIButton"; normalTitle = "Talk to us"; ObjectID = "Fng-qL-diO"; */ +"Fng-qL-diO.normalTitle" = "Snak med os"; + +/* Class = "UITextField"; placeholder = "uuid"; ObjectID = "fPO-vc-4UX"; */ +"fPO-vc-4UX.placeholder" = "uuid"; + +/* Class = "UILabel"; text = "Credits and Licence"; ObjectID = "FYQ-ba-bf8"; */ +"FYQ-ba-bf8.text" = "Anerkendelser og Licens"; + +/* Class = "UILabel"; text = "Version"; ObjectID = "g84-aA-XOI"; */ +"g84-aA-XOI.text" = "Version"; + +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "giY-cI-jfy"; Note = "Placeholder. Do not translate"; */ +"giY-cI-jfy.placeholder" = "qos"; + +/* Class = "UITableViewSection"; headerTitle = "OwnTracks -Your location companion"; ObjectID = "Gjb-Ud-8a8"; */ +"Gjb-Ud-8a8.headerTitle" = "OwnTracks -Din positionspartner"; + +/* Class = "UITextField"; placeholder = ""; ObjectID = "gkb-3O-jAy"; Note = "Placeholder. Don't translate."; */ +"gkb-3O-jAy.placeholder" = ""; + +/* Class = "UILabel"; text = "pubTopicBase"; ObjectID = "gkR-ph-8W7"; Note = "Don't translate, technical term"; */ +"gkR-ph-8W7.text" = "pubTopicBase"; + +/* Class = "UINavigationItem"; title = "Regions"; ObjectID = "GM9-KK-8vY"; */ +"GM9-KK-8vY.title" = "Regioner"; + +/* Class = "UILabel"; text = "locatorDisplacement"; ObjectID = "gtX-x4-Vwl"; Note = "Don't translate, technical term"; */ +"gtX-x4-Vwl.text" = "locatorDisplacement"; + +/* Class = "UITextField"; placeholder = "Name for new tour"; ObjectID = "guB-Ga-J3e"; */ +"guB-Ga-J3e.placeholder" = "Navn for ny tur"; + +/* Class = "UINavigationItem"; title = "Certificates"; ObjectID = "GWL-01-fh1"; Note = "Don't translate, technical term"; */ +"GWL-01-fh1.title" = "Certifikater"; + +/* Class = "UILabel"; text = "Batterylevel"; ObjectID = "gXA-0p-FKt"; */ +"gXA-0p-FKt.text" = "Batteriniveau"; + +/* Class = "UILabel"; text = "effective pubTopic"; ObjectID = "H7d-L2-cbZ"; Note = "Don't translate, technical term"; */ +"H7d-L2-cbZ.text" = "effective pubTopic"; + +/* Class = "UITabBarItem"; title = "Regions"; ObjectID = "HB5-wo-JZx"; */ +"HB5-wo-JZx.title" = "Regioner"; + +/* Class = "UIButton"; normalTitle = "Navigate"; ObjectID = "hcc-3n-8aC"; */ +"hcc-3n-8aC.normalTitle" = "Navigér"; + +/* Class = "UITabBarItem"; title = "Map"; ObjectID = "HCO-pB-vNN"; */ +"HCO-pB-vNN.title" = "Kort"; + +/* Class = "UITextField"; placeholder = ""; ObjectID = "hdf-7C-N25"; Note = "Placeholder, do not translate"; */ +"hdf-7C-N25.placeholder" = ""; + +/* Class = "UILabel"; text = "effective deviceId"; ObjectID = "hE7-sf-UPA"; Note = "Don't translate, technical term"; */ +"hE7-sf-UPA.text" = "effective deviceId"; + +/* Class = "UINavigationItem"; title = "Region"; ObjectID = "Hfg-cd-JM1"; */ +"Hfg-cd-JM1.title" = "Region"; + +/* Class = "UINavigationItem"; title = "Status Info"; ObjectID = "hFq-9F-EAc"; */ +"hFq-9F-EAc.title" = "Status Info"; + +/* Class = "UILabel"; text = "Info"; ObjectID = "hLa-Yd-Jmn"; */ +"hLa-Yd-Jmn.text" = "Info"; + +/* Class = "UITextField"; placeholder = "pubTopicBase"; ObjectID = "hNb-lH-EGK"; Note = "Placeholder. Do not translate"; */ +"hNb-lH-EGK.placeholder" = "pubTopicBase"; + +/* Class = "UILabel"; text = "effective clientId"; ObjectID = "hQX-dc-Yix"; Note = "Don't translate, technical term"; */ +"hQX-dc-Yix.text" = "effective clientId"; + +/* Class = "UITextView"; text = ""; ObjectID = "HQZ-wy-aC5"; Note = "Don't translate. Placeholder!"; */ +"HQZ-wy-aC5.text" = ""; + +/* Class = "UITextField"; placeholder = "timestamp"; ObjectID = "HVH-Pt-DXp"; */ +"HVH-Pt-DXp.placeholder" = "tidspunkt"; + +/* Class = "UILabel"; text = "locatorInterval"; ObjectID = "hY6-k2-neH"; Note = "Don't translate, technical term"; */ +"hY6-k2-neH.text" = "locatorInterval"; + +/* Class = "UILabel"; text = "Label"; ObjectID = "I19-GX-iMA"; */ +"I19-GX-iMA.text" = "Etiket"; + +/* Class = "UITextField"; placeholder = "info"; ObjectID = "Ikm-SR-MCD"; */ +"Ikm-SR-MCD.placeholder" = "info"; + +/* Class = "UIButton"; normalTitle = "Select from Photo Library"; ObjectID = "IL1-c9-0c1"; */ +"IL1-c9-0c1.normalTitle" = "Vælg fra Fotogalleri"; + +/* Class = "UITextField"; placeholder = "clientId"; ObjectID = "IsX-6T-YbC"; Note = "Placeholder. Do not translate"; */ +"IsX-6T-YbC.placeholder" = "clientId"; + +/* Class = "UILabel"; text = "willTopic"; ObjectID = "iuh-ye-xuh"; Note = "Don't translate, technical term"; */ +"iuh-ye-xuh.text" = "willTopic"; + +/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "jaH-UC-XYI"; */ +"jaH-UC-XYI.title" = "Indstillinger"; + +/* Class = "UILabel"; text = "subQos"; ObjectID = "jH3-JK-ef6"; Note = "Don't translate, technical term"; */ +"jH3-JK-ef6.text" = "subQos"; + +/* Class = "UITextField"; placeholder = "Meters"; ObjectID = "jqJ-cr-0tT"; Note = "Don't translate, technical term"; */ +"jqJ-cr-0tT.placeholder" = "Meter"; + +/* Class = "UITextField"; placeholder = "client certificate file"; ObjectID = "JSF-BR-9Nb"; Note = "Don't translate, technical term"; */ +"JSF-BR-9Nb.placeholder" = "client certificate file"; + +/* Class = "UILabel"; text = "sub"; ObjectID = "Jw3-DE-n5i"; Note = "Don't translate, technical term"; */ +"Jw3-DE-n5i.text" = "sub"; + +/* Class = "UILabel"; text = "effective willTopic"; ObjectID = "jwg-hb-6ed"; Note = "Don't translate, technical term"; */ +"jwg-hb-6ed.text" = "effective willTopic"; + +/* Class = "UITextField"; placeholder = ""; ObjectID = "jwK-F1-3iE"; Note = "Placeholder. Don't translate."; */ +"jwK-F1-3iE.placeholder" = ""; + +/* Class = "UITextField"; placeholder = "longitude"; ObjectID = "JZL-1C-khg"; */ +"JZL-1C-khg.placeholder" = "længdegrad"; + +/* Class = "UILabel"; text = "Name"; ObjectID = "K3J-wU-omB"; Note = "Don't Translate. Placeholder"; */ +"K3J-wU-omB.text" = "Navn"; + +/* Class = "UITextField"; placeholder = "passphrase"; ObjectID = "KhB-Z4-umi"; Note = "Don't translate, technical term"; */ +"KhB-Z4-umi.placeholder" = "passphrase"; + +/* Class = "UINavigationItem"; title = "Persons"; ObjectID = "KOf-PY-JcB"; */ +"KOf-PY-JcB.title" = "Personer"; + +/* Class = "UILabel"; text = "Title"; ObjectID = "Kvr-J3-4fw"; */ +"Kvr-J3-4fw.text" = "Titel"; + +/* Class = "UILabel"; text = "Activity"; ObjectID = "L2M-p0-CtV"; */ +"L2M-p0-CtV.text" = "Aktivitet"; + +/* Class = "UITextField"; placeholder = "mode"; ObjectID = "LEn-Jc-PvC"; Note = "Placeholder. Do not translate"; */ +"LEn-Jc-PvC.placeholder" = "tilstand"; + +/* Class = "UITextField"; placeholder = "latitude"; ObjectID = "Lmo-Kp-uCi"; */ +"Lmo-Kp-uCi.placeholder" = "breddegrad"; + +/* Class = "UILabel"; text = "Secret encryption key"; ObjectID = "lQx-Zr-z69"; */ +"lQx-Zr-z69.text" = "Hemmelig krypteringsnøgle"; + +/* Class = "UITextField"; placeholder = "number"; ObjectID = "lTh-wG-5YG"; */ +"lTh-wG-5YG.placeholder" = "nummer"; + +/* Class = "UITextField"; placeholder = "Meters"; ObjectID = "M0H-SF-pCO"; */ +"M0H-SF-pCO.placeholder" = "Meter"; + +/* Class = "UITextView"; text = "Uses the following cocoapods (http://cocoapods.org): CocoaLumberjack, ABStaticTableViewController, libsodium (libsodium.org)\n\n"; ObjectID = "Moe-Di-frm"; */ +"Moe-Di-frm.text" = "Bruger de følgende cocoapods (http://cocoapods.org): CocoaLumberjack, ABStaticTableViewController, libsodium (libsodium.org)\n\n"; + +/* Class = "UITabBarItem"; title = "Friends"; ObjectID = "msF-bI-hEJ"; */ +"msF-bI-hEJ.title" = "Venner"; + +/* Class = "UINavigationItem"; title = "Modes"; ObjectID = "nJk-gq-XUi"; */ +"nJk-gq-XUi.title" = "Tilstande"; + +/* Class = "UILabel"; text = "positions"; ObjectID = "NkN-Ib-izC"; Note = "Don't translate, technical term"; */ +"NkN-Ib-izC.text" = "positioner"; + +/* Class = "UITableViewSection"; headerTitle = "Expert Mode"; ObjectID = "NRQ-7X-SLM"; */ +"NRQ-7X-SLM.headerTitle" = "Eksperttilstand"; + +/* Class = "UINavigationItem"; title = "TLS"; ObjectID = "NS8-pg-IlP"; Note = "Don't translate, technical term"; */ +"NS8-pg-IlP.title" = "TLS"; + +/* Class = "UITextField"; placeholder = "timestamp"; ObjectID = "nwd-8O-YcR"; */ +"nwd-8O-YcR.placeholder" = "tidspunkt"; + +/* Class = "UILabel"; text = "Name"; ObjectID = "o8t-NB-IV6"; */ +"o8t-NB-IV6.text" = "Navn"; + +/* Class = "UITextField"; placeholder = "Seconds"; ObjectID = "oBs-Zn-P4m"; */ +"oBs-Zn-P4m.placeholder" = "Sekunder"; + +/* Class = "UINavigationItem"; title = "Map"; ObjectID = "OeB-EW-2jx"; */ +"OeB-EW-2jx.title" = "Kort"; + +/* Class = "UILabel"; text = "Longitude"; ObjectID = "OgN-Bg-AeZ"; */ +"OgN-Bg-AeZ.text" = "Længdegrad"; + +/* Class = "UILabel"; text = "Subtitle"; ObjectID = "OIH-xB-QZe"; */ +"OIH-xB-QZe.text" = "Undertitel"; + +/* Class = "UITextField"; placeholder = "userid"; ObjectID = "Oiq-Kb-qLx"; */ +"Oiq-Kb-qLx.placeholder" = "userid"; + +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "PBp-Kc-CwF"; Note = "Placeholder. Do not translate"; */ +"PBp-Kc-CwF.placeholder" = "qos"; + +/* Class = "UITextField"; placeholder = "url"; ObjectID = "pD2-Zj-Toq"; */ +"pD2-Zj-Toq.placeholder" = "url"; + +/* Class = "UILabel"; text = "URL"; ObjectID = "pHe-yA-a8f"; */ +"pHe-yA-a8f.text" = "URL"; + +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "pRh-Nl-A5i"; */ +"pRh-Nl-A5i.title" = "Element"; + +/* Class = "UILabel"; text = "To"; ObjectID = "pud-EK-OTL"; */ +"pud-EK-OTL.text" = "Til"; + +/* Class = "UILabel"; text = "Radius for Region (m)"; ObjectID = "Q3S-EG-XKZ"; */ +"Q3S-EG-XKZ.text" = "Radius for Region (m)"; + +/* Class = "UILabel"; text = "Websockets"; ObjectID = "qg7-Fn-2Qr"; Note = "Don't translate, technical term"; */ +"qg7-Fn-2Qr.text" = "Websockets"; + +/* Class = "UILabel"; text = "UUID"; ObjectID = "Qpu-nC-kBF"; */ +"Qpu-nC-kBF.text" = "UUID"; + +/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "R0X-4j-vxu"; */ +"R0X-4j-vxu.normalTitle" = "Indstillinger"; + +/* Class = "UILabel"; text = "effective tid"; ObjectID = "rdL-4M-Ugx"; Note = "Don't translate, technical term"; */ +"rdL-4M-Ugx.text" = "effective id"; + +/* Class = "UITextField"; placeholder = ""; ObjectID = "RfS-uy-gRR"; Note = "Placeholder. Don't translate."; */ +"RfS-uy-gRR.placeholder" = ""; + +/* Class = "UINavigationItem"; title = "Create Card"; ObjectID = "rJ2-2y-TWr"; */ +"rJ2-2y-TWr.title" = "Opret Kort"; + +/* Class = "UIButton"; normalTitle = "Publish Waypoints"; ObjectID = "rMe-Is-0BE"; */ +"rMe-Is-0BE.normalTitle" = "Offentliggør Rutepunkter"; + +/* Class = "UIButton"; configuration.title = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.configuration.title" = "Brug Kamera"; + +/* Class = "UIButton"; normalTitle = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.normalTitle" = "Brug Kamera"; + +/* Class = "UILabel"; text = "Subtitle"; ObjectID = "rx4-uC-NeB"; */ +"rx4-uC-NeB.text" = "Undertitel"; + +/* Class = "UILabel"; text = "Status"; ObjectID = "Rxv-hL-MC1"; Note = "Placeholder. Do not translate!"; */ +"Rxv-hL-MC1.text" = "Status"; + +/* Class = "UITextField"; placeholder = "name"; ObjectID = "s1I-Ff-fYw"; */ +"s1I-Ff-fYw.placeholder" = "navn"; + +/* Class = "UILabel"; text = "TrackerID"; ObjectID = "SaB-mS-8Nw"; Note = "Don't translate, technical term"; */ +"SaB-mS-8Nw.text" = "TrackerID"; + +/* Class = "UILabel"; text = "UserID"; ObjectID = "SAr-Vi-xHD"; */ +"SAr-Vi-xHD.text" = "UserID"; + +/* Class = "UILabel"; text = "downgrade"; ObjectID = "Sbl-15-Idv"; Note = "Don't translate, technical term"; */ +"Sbl-15-Idv.text" = "downgrade"; + +/* Class = "UILabel"; text = "Distance"; ObjectID = "SCe-XW-vBZ"; */ +"SCe-XW-vBZ.text" = "Afstand"; + +/* Class = "UINavigationItem"; title = "Create Tour"; ObjectID = "SR5-Yo-l2L"; */ +"SR5-Yo-l2L.title" = "Opret Tur"; + +/* Class = "UITextView"; text = ""; ObjectID = "src-OY-ZUR"; Note = "Don't translate. Placeholder"; */ +"src-OY-ZUR.text" = ""; + +/* Class = "UITextField"; placeholder = "subTopic"; ObjectID = "suj-kA-G3U"; Note = "Placeholder. Do not translate"; */ +"suj-kA-G3U.placeholder" = "subTopic"; + +/* Class = "UILabel"; text = "From"; ObjectID = "SZe-bR-3x5"; */ +"SZe-bR-3x5.text" = "Fra"; + +/* Class = "UILabel"; text = "willQos"; ObjectID = "t48-sP-9SU"; Note = "Don't translate, technical term"; */ +"t48-sP-9SU.text" = "willQos"; + +/* Class = "UIButton"; normalTitle = "Tours"; ObjectID = "tiK-11-lia"; */ +"tiK-11-lia.normalTitle" = "Ture"; + +/* Class = "UITableViewSection"; headerTitle = "Connection"; ObjectID = "TPY-zx-JfY"; */ +"TPY-zx-JfY.headerTitle" = "Forbindelse"; + +/* Class = "UITextField"; placeholder = "host"; ObjectID = "tQs-PK-sbV"; Note = "Don't translate, technical term"; */ +"tQs-PK-sbV.placeholder" = "vært"; + +/* Class = "UITableViewSection"; headerTitle = "Location"; ObjectID = "uci-TJ-YaO"; */ +"uci-TJ-YaO.headerTitle" = "Position"; + +/* Class = "UILabel"; text = "willRetain"; ObjectID = "udF-FX-N41"; Note = "Don't translate, technical term"; */ +"udF-FX-N41.text" = "willRetain"; + +/* Class = "UILabel"; text = "DeviceID"; ObjectID = "uJc-pU-O6W"; Note = "Don't translate, technical term"; */ +"uJc-pU-O6W.text" = "DeviceID"; + +/* Class = "UILabel"; text = "Authentication"; ObjectID = "UjS-PQ-pwa"; */ +"UjS-PQ-pwa.text" = "Godkendelse"; + +/* Class = "UILabel"; text = "Subtitle"; ObjectID = "Urx-Dz-nZk"; */ +"Urx-Dz-nZk.text" = "Undertitel"; + +/* Class = "UILabel"; text = "monitoring"; ObjectID = "vhk-Yh-d0n"; Note = "Don't translate, technical term"; */ +"vhk-Yh-d0n.text" = "overvågning"; + +/* Class = "UILabel"; text = "ignoreStaleLocations"; ObjectID = "vHW-Iv-6Q1"; Note = "Don't translate, technical term"; */ +"vHW-Iv-6Q1.text" = "ignoreStaleLocations"; + +/* Class = "UITableViewSection"; headerTitle = "MQTT Mode"; ObjectID = "vi2-IZ-KJt"; */ +"vi2-IZ-KJt.headerTitle" = "MQTT-Tilstand"; + +/* Class = "UILabel"; text = "TLS"; ObjectID = "VQE-Wz-JiS"; Note = "Technical termin. Do not translate"; */ +"VQE-Wz-JiS.text" = "TLS"; + +/* Class = "UILabel"; text = "allowRemoteLocation"; ObjectID = "VqT-ix-RdG"; Note = "Don't translate, technical term"; */ +"VqT-ix-RdG.text" = "allowRemoteLocation"; + +/* Class = "UILabel"; text = "effective subTopic"; ObjectID = "vUl-tT-Aw3"; Note = "Don't translate, technical term"; */ +"vUl-tT-Aw3.text" = "effective subTopic"; + +/* Class = "UILabel"; text = "cleanSession"; ObjectID = "VYy-2S-U3O"; Note = "Don't translate, technical term"; */ +"VYy-2S-U3O.text" = "cleanSession"; + +/* Class = "UILabel"; text = "subTopic"; ObjectID = "w8s-ld-3gB"; Note = "Don't translate, technical term"; */ +"w8s-ld-3gB.text" = "subTopic"; + +/* Class = "UITextField"; placeholder = "topic"; ObjectID = "wKK-9Q-BMU"; */ +"wKK-9Q-BMU.placeholder" = "emne"; + +/* Class = "UILabel"; text = "Name"; ObjectID = "WPX-rf-Obi"; */ +"WPX-rf-Obi.text" = "Navn"; + +/* Class = "UILabel"; text = "Timestamp"; ObjectID = "x2d-ex-SAA"; */ +"x2d-ex-SAA.text" = "Tidspunkt"; + +/* Class = "UILabel"; text = "pubQos"; ObjectID = "x67-7L-vnt"; Note = "Don't translate, technical term"; */ +"x67-7L-vnt.text" = "pubQos"; + +/* Class = "UITextField"; placeholder = "downgrade%"; ObjectID = "XkO-CT-wUS"; Note = "Placeholder. Do not translate"; */ +"XkO-CT-wUS.placeholder" = "downgrade%"; + +/* Class = "UILabel"; text = "Coordinates"; ObjectID = "Ym9-bx-HAr"; */ +"Ym9-bx-HAr.text" = "Koordinater"; + +/* Class = "UILabel"; text = "ignoreInaccurateLocations"; ObjectID = "yni-yE-tcr"; Note = "Don't translate, technical term"; */ +"yni-yE-tcr.text" = "ignoreInaccurateLocations"; + +/* Class = "UITextField"; placeholder = "number"; ObjectID = "yny-2e-dpz"; */ +"yny-2e-dpz.placeholder" = "nummer"; + +/* Class = "UIButton"; normalTitle = "Publish Settings"; ObjectID = "YQ6-jA-Rsh"; */ +"YQ6-jA-Rsh.normalTitle" = "Offentliggør Indstillinger"; + +/* Class = "UIButton"; normalTitle = "Export Settings"; ObjectID = "ysB-cv-35Y"; */ +"ysB-cv-35Y.normalTitle" = "Eksportér indstillinger"; + +/* Class = "UILabel"; text = "Coordinates"; ObjectID = "Yx0-Kk-yOQ"; */ +"Yx0-Kk-yOQ.text" = "Koordinater"; + +/* Class = "UILabel"; text = "Title"; ObjectID = "z87-2B-BRC"; */ +"z87-2B-BRC.text" = "Titel"; + +/* Class = "UITextField"; placeholder = "seconds"; ObjectID = "ZT5-mN-Tt5"; */ +"ZT5-mN-Tt5.placeholder" = "sekunder"; + +/* Class = "UITextField"; placeholder = "port"; ObjectID = "ZW3-WS-Wb7"; Note = "Don't translate, technical term"; */ +"ZW3-WS-Wb7.placeholder" = "port"; + diff --git a/OwnTracks/OwnTracks/de.lproj/InfoPlist.strings b/OwnTracks/OwnTracks/de.lproj/InfoPlist.strings index 4252ed32..6be1f800 100644 --- a/OwnTracks/OwnTracks/de.lproj/InfoPlist.strings +++ b/OwnTracks/OwnTracks/de.lproj/InfoPlist.strings @@ -4,6 +4,9 @@ /* Bundle name */ "CFBundleName" = "OwnTracks"; +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "OwnTracks nutzt die Kamera um eine Visitenkarte zu erstellen"; + /* Privacy - Contacts Usage Description */ "NSContactsUsageDescription" = "Wenn Sie OwnTracks Zugriff auf Ihr Adressbuch geben, können Sie Geräte Kontakten zuordnen. OwnTracks zeigt dann an Stelle der Gerätekennung den Namen und das Bild des Kontaktes an. Keinerlei Informationen aus Ihrem Adressbuch werden auf irgendeinen Server übertragen."; diff --git a/OwnTracks/OwnTracks/de.lproj/Localizable.strings b/OwnTracks/OwnTracks/de.lproj/Localizable.strings index 1b6f4e76..8a296252 100644 --- a/OwnTracks/OwnTracks/de.lproj/Localizable.strings +++ b/OwnTracks/OwnTracks/de.lproj/Localizable.strings @@ -43,6 +43,9 @@ /* Cancel button title */ "Cancel" = "Abbrechen"; +/* Header of an alert message regarding a card */ +"Card" = "Visitenkarte"; + /* Location Manager error message */ "Circular region monitoring not available" = "Überwachung kreisförmiger Regionen nicht verfügbar"; @@ -91,6 +94,9 @@ /* Export */ "Export" = "Export"; +/* Fetching sharing list from backend */ +"Fetching tour list from backend" = "Tourenliste Anfrage läuft"; + /* Display when file processing succeeds (filename follows) */ "File" = "Datei"; @@ -199,6 +205,9 @@ /* No location recorded indication */ "No location recorded" = "Keine Position verfügbar\n"; +/* No or empty tour list received from backend */ +"No or empty tour list received from backend" = "Tourenliste nicht vorhanden oder leer"; + /* Display after trying to process a file */ "no url specified" = "keine URL angegeben"; @@ -226,9 +235,18 @@ /* Header of an alert message regarding circular region */ "Region" = "Region"; +/* Requesting tour */ +"Requesting tour" = "Tour angefragt"; + +/* Requesting tour list */ +"Requesting tour list" = "Tourenliste angefragt"; + /* temporary display while resolving address */ "resolving..." = "Adressauflösung..."; +/* Alert message header for Request Response */ +"Response" = "Antwort"; + /* Title Revgeo */ "Reverse Geocoding Address Resolution" = "Umgekehrte Adressauflösung"; @@ -238,6 +256,9 @@ /* User Activity (Siri) Send location now */ "Send location now" = "Sende Position sofort"; +/* content of an alert message regarding card */ +"set and sent to backend" = "gesetzt und gesendet"; + /* Settings screen title */ "Settings" = "Einstellungen"; @@ -274,6 +295,30 @@ /* Topic copied to clipboard */ "Topic copied to clipboard" = "Topic wurde in die Zwischenablage kopiert"; +/* Tour created */ +"Tour created" = "Tour angelegt"; + +/* Tour deleted */ +"Tour deleted" = "Tour gelöscht"; + +/* Tour list received */ +"Tour list received" = "Tourenliste empfangen"; + +/* Tour list request timed out */ +"Tour list request timed out" = "Tourenliste Anfrage ohne Antwort"; + +/* Tour request timed out */ +"Tour request timed out" = "Tour Anfrage ohne Antwort"; + +/* Tours list header */ +"Tours" = "Touren"; + +/* Tours status header */ +"Tours status" = "Touren Status"; + +/* Tours status header */ +"Tours Status" = "Status Touren"; + /* Alert header regarding TrackerID input */ "TrackerID invalid" = "TrackerID ungültig"; @@ -292,6 +337,9 @@ /* Display for unknown url path */ "unknown url path" = "unbekannter URL Pfad"; +/* URL copied to Clipboard */ +"URL copied to Clipboard" = "URL in die Zwischenablage kopiert"; + /* webView didFailLoadWithError display */ "webView didFailLoadWithError" = "webView didFailLoadWithError"; @@ -310,3 +358,5 @@ /* Text explaining the Setup */ "You need to setup your own OwnTracks server and edit your configuration for full privacy protection. Detailed info on https://owntracks.org/booklet" = "Konfigurieren Sie Ihren OwnTracks Server für vollständigen Datenschutz. Mehr Information hier: https://owntracks.org/booklet"; +/* Alert message header for copy */ +"Copied" = "Kopiert"; diff --git a/OwnTracks/OwnTracks/de.lproj/Storyboard.strings b/OwnTracks/OwnTracks/de.lproj/Storyboard.strings index 15995724..9d7c60d5 100644 --- a/OwnTracks/OwnTracks/de.lproj/Storyboard.strings +++ b/OwnTracks/OwnTracks/de.lproj/Storyboard.strings @@ -28,6 +28,9 @@ /* Class = "UITextField"; placeholder = "Password"; ObjectID = "4OY-Dm-bnz"; */ "4OY-Dm-bnz.placeholder" = "password"; +/* Class = "UILabel"; text = "Title"; ObjectID = "5df-yk-myT"; */ +"5df-yk-myT.text" = "Titel"; + /* Class = "UITextField"; placeholder = "days"; ObjectID = "5Nq-by-zFb"; */ "5Nq-by-zFb.placeholder" = "Tage"; @@ -40,6 +43,12 @@ /* Class = "UILabel"; text = "Filename"; ObjectID = "7ie-NC-EFG"; Note = "Don't translate, technical term"; */ "7ie-NC-EFG.text" = "Filename"; +/* Class = "UINavigationItem"; title = "Tours"; ObjectID = "7Qj-vi-EaF"; */ +"7Qj-vi-EaF.title" = "Touren"; + +/* Class = "UIButton"; normalTitle = "Create Card"; ObjectID = "7rX-Bq-DGb"; */ +"7rX-Bq-DGb.normalTitle" = "Erstelle Visitenkarte"; + /* Class = "UISegmentedControl"; 7W2-Mn-fUG.segmentTitles[0] = "MQTT"; ObjectID = "7W2-Mn-fUG"; Note = "Don't translate, technical term"; */ "7W2-Mn-fUG.segmentTitles[0]" = "MQTT"; @@ -67,6 +76,9 @@ /* Class = "UILabel"; text = "keepAlive"; ObjectID = "222-vu-d4y"; Note = "Don't translate, technical term"; */ "222-vu-d4y.text" = "keepAlive"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "572-Up-z9B"; Note = "Placeholder. Do not translate"; */ +"572-Up-z9B.placeholder" = ""; + /* Class = "UITableViewSection"; headerTitle = "Please note"; ObjectID = "a09-rn-TZE"; */ "a09-rn-TZE.headerTitle" = "Bitte beachten Sie"; @@ -121,6 +133,9 @@ /* Class = "UITextField"; placeholder = "trackerid"; ObjectID = "dUg-TH-IQ0"; Note = "Don't translate, technical term"; */ "dUg-TH-IQ0.placeholder" = "trackerid"; +/* Class = "UITextView"; text = "Copyright © 2013-2022 by Christoph Krey, https://owntracks.org"; ObjectID = "dZg-CC-7yD"; */ +"dZg-CC-7yD.text" = "Copyright © 2013-2022 by Christoph Krey, https://owntracks.org"; + /* Class = "UILabel"; text = "ranging"; ObjectID = "DZx-uw-XhQ"; Note = "Don't translate, technical term"; */ "DZx-uw-XhQ.text" = "ranging"; @@ -160,6 +175,9 @@ /* Class = "UITableViewSection"; headerTitle = "OwnTracks -Your location companion"; ObjectID = "Gjb-Ud-8a8"; */ "Gjb-Ud-8a8.headerTitle" = "OwnTracks -Your location companion"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "gkb-3O-jAy"; Note = "Placeholder. Don't translate."; */ +"gkb-3O-jAy.placeholder" = ""; + /* Class = "UILabel"; text = "pubTopicBase"; ObjectID = "gkR-ph-8W7"; Note = "Don't translate, technical term"; */ "gkR-ph-8W7.text" = "pubTopicBase"; @@ -169,6 +187,9 @@ /* Class = "UILabel"; text = "locatorDisplacement"; ObjectID = "gtX-x4-Vwl"; Note = "Don't translate, technical term"; */ "gtX-x4-Vwl.text" = "locatorDisplacement"; +/* Class = "UITextField"; placeholder = "Name for new tour"; ObjectID = "guB-Ga-J3e"; */ +"guB-Ga-J3e.placeholder" = "Name für neue Tour"; + /* Class = "UINavigationItem"; title = "Certificates"; ObjectID = "GWL-01-fh1"; Note = "Don't translate, technical term"; */ "GWL-01-fh1.title" = "Certificates"; @@ -187,6 +208,9 @@ /* Class = "UITabBarItem"; title = "Map"; ObjectID = "HCO-pB-vNN"; */ "HCO-pB-vNN.title" = "Karte"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "hdf-7C-N25"; Note = "Placeholder, do not translate"; */ +"hdf-7C-N25.placeholder" = ""; + /* Class = "UILabel"; text = "effective deviceId"; ObjectID = "hE7-sf-UPA"; Note = "Don't translate, technical term"; */ "hE7-sf-UPA.text" = "effective deviceId"; @@ -214,9 +238,15 @@ /* Class = "UILabel"; text = "locatorInterval"; ObjectID = "hY6-k2-neH"; Note = "Don't translate, technical term"; */ "hY6-k2-neH.text" = "locatorInterval"; +/* Class = "UILabel"; text = "Label"; ObjectID = "I19-GX-iMA"; */ +"I19-GX-iMA.text" = "Titel"; + /* Class = "UITextField"; placeholder = "info"; ObjectID = "Ikm-SR-MCD"; */ "Ikm-SR-MCD.placeholder" = "info"; +/* Class = "UIButton"; normalTitle = "Select from Photo Library"; ObjectID = "IL1-c9-0c1"; */ +"IL1-c9-0c1.normalTitle" = "Aus dem Album auswählen"; + /* Class = "UITextField"; placeholder = "clientId"; ObjectID = "IsX-6T-YbC"; Note = "Placeholder. Do not translate"; */ "IsX-6T-YbC.placeholder" = "clientId"; @@ -241,6 +271,9 @@ /* Class = "UILabel"; text = "effective willTopic"; ObjectID = "jwg-hb-6ed"; Note = "Don't translate, technical term"; */ "jwg-hb-6ed.text" = "effektives willTopic"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "jwK-F1-3iE"; Note = "Placeholder. Don't translate."; */ +"jwK-F1-3iE.placeholder" = "coordinate"; + /* Class = "UITextField"; placeholder = "longitude"; ObjectID = "JZL-1C-khg"; */ "JZL-1C-khg.placeholder" = "longitude"; @@ -256,6 +289,12 @@ /* Class = "UILabel"; text = "Title"; ObjectID = "Kvr-J3-4fw"; */ "Kvr-J3-4fw.text" = "Title"; +/* Class = "UILabel"; text = "Activity"; ObjectID = "L2M-p0-CtV"; */ +"L2M-p0-CtV.text" = "Aktivität"; + +/* Class = "UITextField"; placeholder = "mode"; ObjectID = "LEn-Jc-PvC"; Note = "Placeholder. Do not translate"; */ +"LEn-Jc-PvC.placeholder" = "Meter"; + /* Class = "UITextField"; placeholder = "latitude"; ObjectID = "Lmo-Kp-uCi"; */ "Lmo-Kp-uCi.placeholder" = "latitude"; @@ -295,6 +334,9 @@ /* Class = "UILabel"; text = "Proto"; ObjectID = "NZB-F6-F8c"; */ "NZB-F6-F8c.text" = "Proto"; +/* Class = "UILabel"; text = "Name"; ObjectID = "o8t-NB-IV6"; */ +"o8t-NB-IV6.text" = "Name"; + /* Class = "UITextField"; placeholder = "Seconds"; ObjectID = "oBs-Zn-P4m"; */ "oBs-Zn-P4m.placeholder" = "Sekunden"; @@ -322,6 +364,12 @@ /* Class = "UITextField"; placeholder = "proto"; ObjectID = "pn1-hA-OmO"; */ "pn1-hA-OmO.placeholder" = "proto"; +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "pRh-Nl-A5i"; */ +"pRh-Nl-A5i.title" = "Eintrag"; + +/* Class = "UILabel"; text = "To"; ObjectID = "pud-EK-OTL"; */ +"pud-EK-OTL.text" = "Bis"; + /* Class = "UILabel"; text = "Radius for Region (m)"; ObjectID = "Q3S-EG-XKZ"; */ "Q3S-EG-XKZ.text" = "Radius für Region (m)"; @@ -340,9 +388,21 @@ /* Class = "UITextField"; placeholder = ""; ObjectID = "RfS-uy-gRR"; Note = "Placeholder. Don't translate."; */ "RfS-uy-gRR.placeholder" = ""; +/* Class = "UINavigationItem"; title = "Create Card"; ObjectID = "rJ2-2y-TWr"; */ +"rJ2-2y-TWr.title" = "Erstelle Visitenkarte"; + /* Class = "UIButton"; normalTitle = "Publish Waypoints"; ObjectID = "rMe-Is-0BE"; */ "rMe-Is-0BE.normalTitle" = "Sende Regionen"; +/* Class = "UIButton"; configuration.title = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.configuration.title" = "Kamera nutzen"; + +/* Class = "UIButton"; normalTitle = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.normalTitle" = "Kamera nutzen"; + +/* Class = "UILabel"; text = "Subtitle"; ObjectID = "rx4-uC-NeB"; */ +"rx4-uC-NeB.text" = "Untertitel"; + /* Class = "UILabel"; text = "Status"; ObjectID = "Rxv-hL-MC1"; Note = "Placeholder. Do not translate!"; */ "Rxv-hL-MC1.text" = "Status"; @@ -355,15 +415,24 @@ /* Class = "UILabel"; text = "UserID"; ObjectID = "SAr-Vi-xHD"; */ "SAr-Vi-xHD.text" = "UserID"; +/* Class = "UILabel"; text = "downgrade"; ObjectID = "Sbl-15-Idv"; Note = "Don't translate, technical term"; */ +"Sbl-15-Idv.text" = "zurückstufen"; + /* Class = "UILabel"; text = "Distance"; ObjectID = "SCe-XW-vBZ"; */ "SCe-XW-vBZ.text" = "Entfernung"; +/* Class = "UINavigationItem"; title = "Create Tour"; ObjectID = "SR5-Yo-l2L"; */ +"SR5-Yo-l2L.title" = "Erstelle Tour"; + /* Class = "UITextView"; text = ""; ObjectID = "src-OY-ZUR"; Note = "Don't translate. Placeholder"; */ "src-OY-ZUR.text" = "state"; /* Class = "UITextField"; placeholder = "subTopic"; ObjectID = "suj-kA-G3U"; Note = "Placeholder. Do not translate"; */ "suj-kA-G3U.placeholder" = "subTopic"; +/* Class = "UILabel"; text = "From"; ObjectID = "SZe-bR-3x5"; */ +"SZe-bR-3x5.text" = "Von"; + /* Class = "UILabel"; text = "Major"; ObjectID = "sZM-l3-N2o"; */ "sZM-l3-N2o.text" = "Major"; @@ -373,6 +442,9 @@ /* Class = "UITextField"; placeholder = "minor"; ObjectID = "Tc6-JR-qOB"; */ "Tc6-JR-qOB.placeholder" = "minor"; +/* Class = "UIButton"; normalTitle = "Tours"; ObjectID = "tiK-11-lia"; */ +"tiK-11-lia.normalTitle" = "Touren"; + /* Class = "UITableViewSection"; headerTitle = "Connection"; ObjectID = "TPY-zx-JfY"; */ "TPY-zx-JfY.headerTitle" = "Connection"; @@ -433,6 +505,9 @@ /* Class = "UILabel"; text = "pubQos"; ObjectID = "x67-7L-vnt"; Note = "Don't translate, technical term"; */ "x67-7L-vnt.text" = "pubQos"; +/* Class = "UITextField"; placeholder = "downgrade%"; ObjectID = "XkO-CT-wUS"; Note = "Placeholder. Do not translate"; */ +"XkO-CT-wUS.placeholder" = "zurückstufen bei %"; + /* Class = "UILabel"; text = "Coordinates"; ObjectID = "Ym9-bx-HAr"; */ "Ym9-bx-HAr.text" = "Koordinaten"; diff --git a/OwnTracks/OwnTracks/en.lproj/Localizable.strings b/OwnTracks/OwnTracks/en.lproj/Localizable.strings index 9accdca8..c7faf469 100644 Binary files a/OwnTracks/OwnTracks/en.lproj/Localizable.strings and b/OwnTracks/OwnTracks/en.lproj/Localizable.strings differ diff --git a/OwnTracks/OwnTracks/en.lproj/Storyboard.strings b/OwnTracks/OwnTracks/en.lproj/Storyboard.strings index a8955cf8..39fae028 100644 --- a/OwnTracks/OwnTracks/en.lproj/Storyboard.strings +++ b/OwnTracks/OwnTracks/en.lproj/Storyboard.strings @@ -17,7 +17,7 @@ /* Class = "UILabel"; text = "maxHistory"; ObjectID = "2F5-PF-efV"; Note = "Don't translate, technical term"; */ "2F5-PF-efV.text" = "maxHistory"; -/* Class = "UITextField"; placeholder = "secret"; ObjectID = "2FK-gv-e75"; Note = "Don't translate, technical term"; */ +/* Class = "UITextField"; placeholder = "secret"; ObjectID = "2FK-gv-e75"; */ "2FK-gv-e75.placeholder" = "secret"; /* Class = "UILabel"; text = "pubRetain"; ObjectID = "3Hs-lP-khD"; Note = "Don't translate, technical term"; */ @@ -26,7 +26,7 @@ /* Class = "UILabel"; text = "locked"; ObjectID = "3K5-oQ-oxQ"; Note = "Don't translate, technical term"; */ "3K5-oQ-oxQ.text" = "locked"; -/* Class = "UILabel"; text = "Mode"; ObjectID = "3ad-LO-zcn"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "Mode"; ObjectID = "3ad-LO-zcn"; */ "3ad-LO-zcn.text" = "Mode"; /* Class = "UITextField"; placeholder = "Password"; ObjectID = "4OY-Dm-bnz"; */ @@ -35,18 +35,27 @@ /* Class = "UIButton"; normalTitle = "Export Waypoints"; ObjectID = "54d-E6-cWX"; */ "54d-E6-cWX.normalTitle" = "Export Waypoints"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "572-Up-z9B"; Note = "Placeholder. Do not translate"; */ +"572-Up-z9B.placeholder" = ""; + /* Class = "UITextField"; placeholder = "days"; ObjectID = "5Nq-by-zFb"; */ "5Nq-by-zFb.placeholder" = "days"; +/* Class = "UILabel"; text = "Title"; ObjectID = "5df-yk-myT"; */ +"5df-yk-myT.text" = "Title"; + /* Class = "UILabel"; text = "11:30 PM"; ObjectID = "6AN-cF-srI"; Note = "Don't translate. Placeholder"; */ "6AN-cF-srI.text" = "11:30 PM"; /* Class = "UITableViewSection"; headerTitle = "Client Certificate"; ObjectID = "6aR-4g-W4D"; */ "6aR-4g-W4D.headerTitle" = "Client Certificate"; -/* Class = "UITextField"; placeholder = "willTopic"; ObjectID = "79P-sU-en0"; Note = "Don't translate, technical term"; */ +/* Class = "UITextField"; placeholder = "willTopic"; ObjectID = "79P-sU-en0"; Note = "Placeholder. Do not translate"; */ "79P-sU-en0.placeholder" = "willTopic"; +/* Class = "UINavigationItem"; title = "Tours"; ObjectID = "7Qj-vi-EaF"; */ +"7Qj-vi-EaF.title" = "Tours"; + /* Class = "UISegmentedControl"; 7W2-Mn-fUG.segmentTitles[0] = "MQTT"; ObjectID = "7W2-Mn-fUG"; Note = "Don't translate, technical term"; */ "7W2-Mn-fUG.segmentTitles[0]" = "MQTT"; @@ -56,6 +65,9 @@ /* Class = "UILabel"; text = "Filename"; ObjectID = "7ie-NC-EFG"; Note = "Don't translate, technical term"; */ "7ie-NC-EFG.text" = "Filename"; +/* Class = "UIButton"; normalTitle = "Create Card"; ObjectID = "7rX-Bq-DGb"; */ +"7rX-Bq-DGb.normalTitle" = "Create Card"; + /* Class = "UITableViewSection"; headerTitle = "Circular Region"; ObjectID = "7xg-sB-qXa"; */ "7xg-sB-qXa.headerTitle" = "Circular Region"; @@ -80,7 +92,7 @@ /* Class = "UILabel"; text = "Passphrase"; ObjectID = "BTX-XN-kzE"; Note = "Don't translate, technical term"; */ "BTX-XN-kzE.text" = "Passphrase"; -/* Class = "UILabel"; text = "Password"; ObjectID = "C1o-h4-z6h"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "Password"; ObjectID = "C1o-h4-z6h"; */ "C1o-h4-z6h.text" = "Password"; /* Class = "UILabel"; text = "Latitude"; ObjectID = "Cuh-Tm-IcV"; */ @@ -92,7 +104,7 @@ /* Class = "UILabel"; text = "Topic"; ObjectID = "Dgw-5m-uvy"; */ "Dgw-5m-uvy.text" = "Topic"; -/* Class = "UILabel"; text = "Port"; ObjectID = "EWI-7o-7Jo"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "Port"; ObjectID = "EWI-7o-7Jo"; */ "EWI-7o-7Jo.text" = "Port"; /* Class = "UILabel"; text = "Credits and Licence"; ObjectID = "FYQ-ba-bf8"; */ @@ -128,10 +140,16 @@ /* Class = "UINavigationItem"; title = "Region"; ObjectID = "Hfg-cd-JM1"; */ "Hfg-cd-JM1.title" = "Region"; +/* Class = "UILabel"; text = "Label"; ObjectID = "I19-GX-iMA"; */ +"I19-GX-iMA.text" = "Label"; + +/* Class = "UIButton"; normalTitle = "Select from Photo Library"; ObjectID = "IL1-c9-0c1"; */ +"IL1-c9-0c1.normalTitle" = "Select from Photo Library"; + /* Class = "UITextField"; placeholder = "info"; ObjectID = "Ikm-SR-MCD"; */ "Ikm-SR-MCD.placeholder" = "info"; -/* Class = "UITextField"; placeholder = "clientId"; ObjectID = "IsX-6T-YbC"; */ +/* Class = "UITextField"; placeholder = "clientId"; ObjectID = "IsX-6T-YbC"; Note = "Placeholder. Do not translate"; */ "IsX-6T-YbC.placeholder" = "clientId"; /* Class = "UITextField"; placeholder = "client certificate file"; ObjectID = "JSF-BR-9Nb"; Note = "Don't translate, technical term"; */ @@ -155,8 +173,11 @@ /* Class = "UILabel"; text = "Title"; ObjectID = "Kvr-J3-4fw"; */ "Kvr-J3-4fw.text" = "Title"; -/* Class = "UITextField"; placeholder = "Meters"; ObjectID = "LEn-Jc-PvC"; */ -"LEn-Jc-PvC.placeholder" = "Meters"; +/* Class = "UILabel"; text = "Activity"; ObjectID = "L2M-p0-CtV"; */ +"L2M-p0-CtV.text" = "Activity"; + +/* Class = "UITextField"; placeholder = "mode"; ObjectID = "LEn-Jc-PvC"; Note = "Placeholder. Do not translate"; */ +"LEn-Jc-PvC.placeholder" = "mode"; /* Class = "UITextField"; placeholder = "latitude"; ObjectID = "Lmo-Kp-uCi"; */ "Lmo-Kp-uCi.placeholder" = "latitude"; @@ -188,10 +209,10 @@ /* Class = "UILabel"; text = "Longitude"; ObjectID = "OgN-Bg-AeZ"; */ "OgN-Bg-AeZ.text" = "Longitude"; -/* Class = "UITextField"; placeholder = "userid"; ObjectID = "Oiq-Kb-qLx"; Note = "Don't translate, technical term"; */ +/* Class = "UITextField"; placeholder = "userid"; ObjectID = "Oiq-Kb-qLx"; */ "Oiq-Kb-qLx.placeholder" = "userid"; -/* Class = "UITextField"; placeholder = "qos"; ObjectID = "PBp-Kc-CwF"; */ +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "PBp-Kc-CwF"; Note = "Placeholder. Do not translate"; */ "PBp-Kc-CwF.placeholder" = "qos"; /* Class = "UILabel"; text = "Radius for Region (m)"; ObjectID = "Q3S-EG-XKZ"; */ @@ -206,22 +227,40 @@ /* Class = "UITextField"; placeholder = ""; ObjectID = "RfS-uy-gRR"; Note = "Placeholder. Don't translate."; */ "RfS-uy-gRR.placeholder" = ""; +/* Class = "UIButton"; configuration.title = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.configuration.title" = "Use Camera"; + +/* Class = "UIButton"; normalTitle = "Use Camera"; ObjectID = "Rvi-2b-Hb0"; */ +"Rvi-2b-Hb0.normalTitle" = "Use Camera"; + /* Class = "UILabel"; text = "Status"; ObjectID = "Rxv-hL-MC1"; Note = "Placeholder. Do not translate!"; */ "Rxv-hL-MC1.text" = "Status"; -/* Class = "UILabel"; text = "UserID"; ObjectID = "SAr-Vi-xHD"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "UserID"; ObjectID = "SAr-Vi-xHD"; */ "SAr-Vi-xHD.text" = "UserID"; +/* Class = "UILabel"; text = "Distance"; ObjectID = "SCe-XW-vBZ"; */ +"SCe-XW-vBZ.text" = "Distance"; + +/* Class = "UINavigationItem"; title = "Create Tour"; ObjectID = "SR5-Yo-l2L"; */ +"SR5-Yo-l2L.title" = "Create Tour"; + +/* Class = "UILabel"; text = "From"; ObjectID = "SZe-bR-3x5"; */ +"SZe-bR-3x5.text" = "From"; + /* Class = "UILabel"; text = "TrackerID"; ObjectID = "SaB-mS-8Nw"; Note = "Don't translate, technical term"; */ "SaB-mS-8Nw.text" = "TrackerID"; +/* Class = "UILabel"; text = "downgrade"; ObjectID = "Sbl-15-Idv"; Note = "Don't translate, technical term"; */ +"Sbl-15-Idv.text" = "downgrade"; + /* Class = "UITableViewSection"; headerTitle = "Connection"; ObjectID = "TPY-zx-JfY"; */ "TPY-zx-JfY.headerTitle" = "Connection"; /* Class = "UITextField"; placeholder = "minor"; ObjectID = "Tc6-JR-qOB"; */ "Tc6-JR-qOB.placeholder" = "minor"; -/* Class = "UILabel"; text = "Authentication"; ObjectID = "UjS-PQ-pwa"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "Authentication"; ObjectID = "UjS-PQ-pwa"; */ "UjS-PQ-pwa.text" = "Authentication"; /* Class = "UITextField"; placeholder = "major"; ObjectID = "UqU-7l-HX1"; */ @@ -230,7 +269,7 @@ /* Class = "UILabel"; text = "Subtitle"; ObjectID = "Urx-Dz-nZk"; */ "Urx-Dz-nZk.text" = "Subtitle"; -/* Class = "UILabel"; text = "TLS"; ObjectID = "VQE-Wz-JiS"; */ +/* Class = "UILabel"; text = "TLS"; ObjectID = "VQE-Wz-JiS"; Note = "Technical termin. Do not translate"; */ "VQE-Wz-JiS.text" = "TLS"; /* Class = "UILabel"; text = "cleanSession"; ObjectID = "VYy-2S-U3O"; Note = "Don't translate, technical term"; */ @@ -242,6 +281,9 @@ /* Class = "UILabel"; text = "Name"; ObjectID = "WPX-rf-Obi"; */ "WPX-rf-Obi.text" = "Name"; +/* Class = "UITextField"; placeholder = "downgrade%"; ObjectID = "XkO-CT-wUS"; Note = "Placeholder. Do not translate"; */ +"XkO-CT-wUS.placeholder" = "downgrade%"; + /* Class = "UIButton"; normalTitle = "Publish Settings"; ObjectID = "YQ6-jA-Rsh"; */ "YQ6-jA-Rsh.normalTitle" = "Publish Settings"; @@ -251,9 +293,6 @@ /* Class = "UILabel"; text = "Coordinates"; ObjectID = "Yx0-Kk-yOQ"; */ "Yx0-Kk-yOQ.text" = "Coordinates"; -/* Class = "UILabel"; text = "Distance"; ObjectID = "SCe-XW-vBZ"; */ -"SCe-XW-vBZ.text" = "Distance"; - /* Class = "UITextField"; placeholder = "seconds"; ObjectID = "ZT5-mN-Tt5"; */ "ZT5-mN-Tt5.placeholder" = "seconds"; @@ -263,7 +302,7 @@ /* Class = "UITableViewSection"; headerTitle = "Please note"; ObjectID = "a09-rn-TZE"; */ "a09-rn-TZE.headerTitle" = "Please note"; -/* Class = "UILabel"; text = "Host"; ObjectID = "a19-FN-Mwf"; Note = "Don't translate, technical term"; */ +/* Class = "UILabel"; text = "Host"; ObjectID = "a19-FN-Mwf"; */ "a19-FN-Mwf.text" = "Host"; /* Class = "UILabel"; text = "Allow Untrusted Certificates"; ObjectID = "aVN-vG-YjO"; Note = "Don't translate, technical term"; */ @@ -272,7 +311,7 @@ /* Class = "UILabel"; text = "Street\n1\n2\n3\n4"; ObjectID = "apt-Il-Ih4"; Note = "Don't Translate, Placeholder"; */ "apt-Il-Ih4.text" = "Street\n1\n2\n3\n4"; -/* Class = "UITextField"; placeholder = "qos"; ObjectID = "cBn-p7-eqG"; */ +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "cBn-p7-eqG"; Note = "Placeholder. Do not translate"; */ "cBn-p7-eqG.placeholder" = "qos"; /* Class = "UILabel"; text = "cmd"; ObjectID = "cTs-Rr-JTs"; Note = "Don't translate, technical term"; */ @@ -299,7 +338,7 @@ /* Class = "UITabBarController"; title = "Tab Bar"; ObjectID = "e04-OU-xZ4"; */ "e04-OU-xZ4.title" = "Tab Bar"; -/* Class = "UITextField"; placeholder = "deviceid"; ObjectID = "e0q-hc-0z2"; */ +/* Class = "UITextField"; placeholder = "deviceid"; ObjectID = "e0q-hc-0z2"; Note = "Placeholder. Do not translate"; */ "e0q-hc-0z2.placeholder" = "deviceid"; /* Class = "UIButton"; normalTitle = "Documentation"; ObjectID = "ePs-kX-VDJ"; */ @@ -317,15 +356,24 @@ /* Class = "UILabel"; text = "Version"; ObjectID = "g84-aA-XOI"; */ "g84-aA-XOI.text" = "Version"; -/* Class = "UITextField"; placeholder = "qos"; ObjectID = "giY-cI-jfy"; */ +/* Class = "UILabel"; text = "Batterylevel"; ObjectID = "gXA-0p-FKt"; */ +"gXA-0p-FKt.text" = "Batterylevel"; + +/* Class = "UITextField"; placeholder = "qos"; ObjectID = "giY-cI-jfy"; Note = "Placeholder. Do not translate"; */ "giY-cI-jfy.placeholder" = "qos"; /* Class = "UILabel"; text = "pubTopicBase"; ObjectID = "gkR-ph-8W7"; Note = "Don't translate, technical term"; */ "gkR-ph-8W7.text" = "pubTopicBase"; +/* Class = "UITextField"; placeholder = ""; ObjectID = "gkb-3O-jAy"; Note = "Placeholder. Don't translate."; */ +"gkb-3O-jAy.placeholder" = ""; + /* Class = "UILabel"; text = "locatorDisplacement"; ObjectID = "gtX-x4-Vwl"; Note = "Don't translate, technical term"; */ "gtX-x4-Vwl.text" = "locatorDisplacement"; +/* Class = "UITextField"; placeholder = "Name for new tour"; ObjectID = "guB-Ga-J3e"; */ +"guB-Ga-J3e.placeholder" = "Name for new tour"; + /* Class = "UILabel"; text = "effective deviceId"; ObjectID = "hE7-sf-UPA"; Note = "Don't translate, technical term"; */ "hE7-sf-UPA.text" = "effective deviceId"; @@ -335,7 +383,7 @@ /* Class = "UILabel"; text = "Info"; ObjectID = "hLa-Yd-Jmn"; */ "hLa-Yd-Jmn.text" = "Info"; -/* Class = "UITextField"; placeholder = "pubTopicBase"; ObjectID = "hNb-lH-EGK"; */ +/* Class = "UITextField"; placeholder = "pubTopicBase"; ObjectID = "hNb-lH-EGK"; Note = "Placeholder. Do not translate"; */ "hNb-lH-EGK.placeholder" = "pubTopicBase"; /* Class = "UILabel"; text = "effective clientId"; ObjectID = "hQX-dc-Yix"; Note = "Don't translate, technical term"; */ @@ -347,7 +395,7 @@ /* Class = "UIButton"; normalTitle = "Navigate"; ObjectID = "hcc-3n-8aC"; */ "hcc-3n-8aC.normalTitle" = "Navigate"; -/* Class = "UITextField"; placeholder = ""; ObjectID = "hdf-7C-N25"; */ +/* Class = "UITextField"; placeholder = ""; ObjectID = "hdf-7C-N25"; Note = "Placeholder, do not translate"; */ "hdf-7C-N25.placeholder" = ""; /* Class = "UILabel"; text = "willTopic"; ObjectID = "iuh-ye-xuh"; Note = "Don't translate, technical term"; */ @@ -362,16 +410,13 @@ /* Class = "UITextField"; placeholder = "Meters"; ObjectID = "jqJ-cr-0tT"; Note = "Don't translate, technical term"; */ "jqJ-cr-0tT.placeholder" = "Meters"; -/* Class = "UITextField"; placeholder = ""; ObjectID = "jwK-F1-3iE"; Note = "Don't Translate. Placeholder"; */ +/* Class = "UITextField"; placeholder = ""; ObjectID = "jwK-F1-3iE"; Note = "Placeholder. Don't translate."; */ "jwK-F1-3iE.placeholder" = ""; -/* Class = "UITextField"; placeholder = ""; ObjectID = "gkb-3O-jAy"; Note = "Don't Translate. Placeholder"; */ -"gkb-3O-jAy.placeholder" = "