diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..27bf70574c2297bd84bf4f461e9c378f8489fea5 Binary files /dev/null and b/.DS_Store differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 8f04207db8961e660763b77544f84b7fe19ae0ce..0c30be4d96fde8050959618d673d7dfdd13667e7 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,35 @@ -# Nmo Teams App +# [Traccar Client for iOS](https://www.traccar.org/client) +[](https://itunes.apple.com/app/traccar-client/id843156974) +## Overview -## Getting started +Traccar Client is an iOS GPS tracking application. It can work with Traccar open source server software. -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +## Build -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +Project uses CocoaPods for dependencies management. To build the project you need to download dependencies: ``` -cd existing_repo -git remote add origin https://git.beinmedia.com/aziz97/nmo-teams-app.git -git branch -M main -git push -uf origin main +pod install ``` -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.beinmedia.com/aziz97/nmo-teams-app/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. +## Team -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +- Anton Tananaev ([anton@traccar.org](mailto:anton@traccar.org)) -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. +## License -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + Apache License, Version 2.0 -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -## License -For open source projects, say how it is licensed. + http://www.apache.org/licenses/LICENSE-2.0 -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/TraccarClient.xcodeproj/project.pbxproj b/TraccarClient.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000000000000000000000000000000..495d1676dc11b7c9ca7097ef62cf3fb882d634cb --- /dev/null +++ b/TraccarClient.xcodeproj/project.pbxproj @@ -0,0 +1,920 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 5E394EBE28A9CC7600396F33 /* BatteryStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */; }; + 5E716A271F63A0B100A2DBC3 /* DistanceCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */; }; + 5E716A291F63A45A00A2DBC3 /* RequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A281F63A45A00A2DBC3 /* RequestManager.swift */; }; + 5E716A2B1F63A60800A2DBC3 /* ProtocolFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */; }; + 5E716A2E1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */; }; + 5E716A301F63A9F600A2DBC3 /* RequestManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */; }; + 5EE3CA342998948F002C86E4 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA332998948F002C86E4 /* FirebaseAnalytics */; }; + 5EE3CA362998948F002C86E4 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */; }; + 5EE3CA39299894B9002C86E4 /* InAppSettingsKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA38299894B9002C86E4 /* InAppSettingsKit */; }; + CB4197931F674A3E008F301C /* DatabaseHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197921F674A3E008F301C /* DatabaseHelper.swift */; }; + CB4197951F675518008F301C /* CoreDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197941F675518008F301C /* CoreDataTests.swift */; }; + CB4197971F676B52008F301C /* DatabaseHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197961F676B52008F301C /* DatabaseHelperTests.swift */; }; + CB4197991F67724F008F301C /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197981F67724F008F301C /* NetworkManager.swift */; }; + CB7ED0801F6602CD00A33FCF /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED07F1F6602CD00A33FCF /* Position.swift */; }; + CB7ED0821F661B4F00A33FCF /* PositionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */; }; + CB7ED0841F662BAF00A33FCF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */; }; + CBAA0F7D1F68E14C008BBBBE /* StatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */; }; + CBAA0F7F1F68E807008BBBBE /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */; }; + CBAA0F811F68EB62008BBBBE /* TrackingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F801F68EB62008BBBBE /* TrackingController.swift */; }; + CBCE82EF1B8D253600A7318B /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBCE82EE1B8D253600A7318B /* CoreData.framework */; }; + CBCE82F21B8D265800A7318B /* TraccarClient.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */; }; + CBDEEB8A1B8EB6A4006BC126 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */; }; + CE5899C81B115C9100ED70D2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE5899C71B115C9100ED70D2 /* Images.xcassets */; }; + CED4871C17DB1BF6007FCF57 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871B17DB1BF6007FCF57 /* UIKit.framework */; }; + CED4871E17DB1BF6007FCF57 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871D17DB1BF6007FCF57 /* Foundation.framework */; }; + CED4872017DB1BF6007FCF57 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */; }; + CED4879917DB1DF4007FCF57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4879817DB1DF4007FCF57 /* MessageUI.framework */; }; + CED4879B17DB1E61007FCF57 /* InAppSettings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */; }; + CEDB048717ED50C1000E7EDF /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEDB048617ED50C1000E7EDF /* CoreLocation.framework */; }; + CEF643271B919FFA00195CEA /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF643241B919FFA00195CEA /* LaunchScreen.xib */; }; + CEF6433C1B91AA9400195CEA /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */; }; + CEF6433D1B91AA9400195CEA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CEF643371B91A94600195CEA /* Localizable.strings */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CEE286D41B80839700AE7E83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CED4871017DB1BF6007FCF57 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CED4871717DB1BF6007FCF57; + remoteInfo = TraccarClient; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + CEFA94EF1EA2EE31009B5809 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStatus.swift; sourceTree = "<group>"; }; + 5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistanceCalculator.swift; sourceTree = "<group>"; }; + 5E716A281F63A45A00A2DBC3 /* RequestManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestManager.swift; sourceTree = "<group>"; }; + 5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolFormatter.swift; sourceTree = "<group>"; }; + 5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolFormatterTests.swift; sourceTree = "<group>"; }; + 5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestManagerTests.swift; sourceTree = "<group>"; }; + CB4197921F674A3E008F301C /* DatabaseHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHelper.swift; sourceTree = "<group>"; }; + CB4197941F675518008F301C /* CoreDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataTests.swift; sourceTree = "<group>"; }; + CB4197961F676B52008F301C /* DatabaseHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHelperTests.swift; sourceTree = "<group>"; }; + CB4197981F67724F008F301C /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; }; + CB7ED07F1F6602CD00A33FCF /* Position.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Position.swift; sourceTree = "<group>"; }; + CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PositionProvider.swift; sourceTree = "<group>"; }; + CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; + CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusViewController.swift; sourceTree = "<group>"; }; + CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; }; + CBAA0F801F68EB62008BBBBE /* TrackingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingController.swift; sourceTree = "<group>"; }; + CBCE82EE1B8D253600A7318B /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TraccarClient.xcdatamodel; sourceTree = "<group>"; }; + CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + CE2FA5181CB935C9007F86B4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B80FE1EC71CAE00D1C41D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/MainStoryboard.strings"; sourceTree = "<group>"; }; + CE4B80FF1EC71CAE00D1C41D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; }; + CE4B81001EC71CCF00D1C41D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B81011EC71CCF00D1C41D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE4B81021EC71CFF00D1C41D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B81031EC71CFF00D1C41D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE4B81041EC71D1700D1C41D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B81051EC71D1700D1C41D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE4B81061EC71D3600D1C41D /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B81071EC71D3600D1C41D /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE4B81081EC71D4300D1C41D /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B81091EC71D4300D1C41D /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE4B810A1EC71D4800D1C41D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CE4B810B1EC71D4800D1C41D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; }; + CE5899C71B115C9100ED70D2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; + CED4871817DB1BF6007FCF57 /* TraccarClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TraccarClient.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CED4871B17DB1BF6007FCF57 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + CED4871D17DB1BF6007FCF57 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + CED4872317DB1BF6007FCF57 /* TraccarClient-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TraccarClient-Info.plist"; sourceTree = "<group>"; }; + CED4872917DB1BF6007FCF57 /* TraccarClient-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TraccarClient-Prefix.pch"; sourceTree = "<group>"; }; + CED4879817DB1DF4007FCF57 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; + CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = InAppSettings.bundle; sourceTree = "<group>"; }; + CEDB048617ED50C1000E7EDF /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TraccarClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CEE286D11B80839700AE7E83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + CEECE4B91B91AE2B00EB6F4A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; }; + CEECE4BA1B91AE2C00EB6F4A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; }; + CEECE4BB1B91AE2D00EB6F4A /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; }; + CEF254DD1B905266001DEFD2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CEF254DF1B9052EB001DEFD2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainStoryboard.storyboard; sourceTree = "<group>"; }; + CEF643241B919FFA00195CEA /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = "<group>"; }; + CEF6432A1B91A48B00195CEA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CEF6432C1B91A49400195CEA /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CEF6432E1B91A49700195CEA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CEF643321B91A4BF00195CEA /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/MainStoryboard.strings; sourceTree = "<group>"; }; + CEF643361B91A94600195CEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; }; + CEF643381B91A95700195CEA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; }; + CEF643391B91A98300195CEA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CED4871517DB1BF6007FCF57 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CBDEEB8A1B8EB6A4006BC126 /* SystemConfiguration.framework in Frameworks */, + CBCE82EF1B8D253600A7318B /* CoreData.framework in Frameworks */, + 5EE3CA342998948F002C86E4 /* FirebaseAnalytics in Frameworks */, + CEDB048717ED50C1000E7EDF /* CoreLocation.framework in Frameworks */, + CED4879917DB1DF4007FCF57 /* MessageUI.framework in Frameworks */, + CED4871C17DB1BF6007FCF57 /* UIKit.framework in Frameworks */, + 5EE3CA362998948F002C86E4 /* FirebaseCrashlytics in Frameworks */, + 5EE3CA39299894B9002C86E4 /* InAppSettingsKit in Frameworks */, + CED4871E17DB1BF6007FCF57 /* Foundation.framework in Frameworks */, + CED4872017DB1BF6007FCF57 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEE286CB1B80839600AE7E83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CED4870F17DB1BF6007FCF57 = { + isa = PBXGroup; + children = ( + CED4872117DB1BF6007FCF57 /* TraccarClient */, + CEE286CF1B80839700AE7E83 /* TraccarClientTests */, + CED4871A17DB1BF6007FCF57 /* Frameworks */, + CED4871917DB1BF6007FCF57 /* Products */, + ); + sourceTree = "<group>"; + }; + CED4871917DB1BF6007FCF57 /* Products */ = { + isa = PBXGroup; + children = ( + CED4871817DB1BF6007FCF57 /* TraccarClient.app */, + CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */, + ); + name = Products; + sourceTree = "<group>"; + }; + CED4871A17DB1BF6007FCF57 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */, + CBCE82EE1B8D253600A7318B /* CoreData.framework */, + CEDB048617ED50C1000E7EDF /* CoreLocation.framework */, + CED4879817DB1DF4007FCF57 /* MessageUI.framework */, + CED4871B17DB1BF6007FCF57 /* UIKit.framework */, + CED4871D17DB1BF6007FCF57 /* Foundation.framework */, + CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + CED4872117DB1BF6007FCF57 /* TraccarClient */ = { + isa = PBXGroup; + children = ( + CE5899C71B115C9100ED70D2 /* Images.xcassets */, + CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */, + CEF643241B919FFA00195CEA /* LaunchScreen.xib */, + CEF643371B91A94600195CEA /* Localizable.strings */, + CED4872217DB1BF6007FCF57 /* Supporting Files */, + CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */, + CB4197921F674A3E008F301C /* DatabaseHelper.swift */, + 5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */, + CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */, + CB4197981F67724F008F301C /* NetworkManager.swift */, + CB7ED07F1F6602CD00A33FCF /* Position.swift */, + 5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */, + CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */, + 5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */, + 5E716A281F63A45A00A2DBC3 /* RequestManager.swift */, + CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */, + CBAA0F801F68EB62008BBBBE /* TrackingController.swift */, + ); + path = TraccarClient; + sourceTree = "<group>"; + }; + CED4872217DB1BF6007FCF57 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CED4872317DB1BF6007FCF57 /* TraccarClient-Info.plist */, + CED4872917DB1BF6007FCF57 /* TraccarClient-Prefix.pch */, + CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */, + CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + CEE286CF1B80839700AE7E83 /* TraccarClientTests */ = { + isa = PBXGroup; + children = ( + CEE286D01B80839700AE7E83 /* Supporting Files */, + CB4197941F675518008F301C /* CoreDataTests.swift */, + CB4197961F676B52008F301C /* DatabaseHelperTests.swift */, + 5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */, + 5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */, + ); + path = TraccarClientTests; + sourceTree = "<group>"; + }; + CEE286D01B80839700AE7E83 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CEE286D11B80839700AE7E83 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CED4871717DB1BF6007FCF57 /* TraccarClient */ = { + isa = PBXNativeTarget; + buildConfigurationList = CED4873E17DB1BF6007FCF57 /* Build configuration list for PBXNativeTarget "TraccarClient" */; + buildPhases = ( + CED4871417DB1BF6007FCF57 /* Sources */, + CED4871517DB1BF6007FCF57 /* Frameworks */, + CED4871617DB1BF6007FCF57 /* Resources */, + 5E3175221F692FA600CDF0F5 /* ShellScript */, + CEFA94EF1EA2EE31009B5809 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TraccarClient; + packageProductDependencies = ( + 5EE3CA332998948F002C86E4 /* FirebaseAnalytics */, + 5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */, + 5EE3CA38299894B9002C86E4 /* InAppSettingsKit */, + ); + productName = TraccarClient; + productReference = CED4871817DB1BF6007FCF57 /* TraccarClient.app */; + productType = "com.apple.product-type.application"; + }; + CEE286CD1B80839600AE7E83 /* TraccarClientTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEE286D61B80839700AE7E83 /* Build configuration list for PBXNativeTarget "TraccarClientTests" */; + buildPhases = ( + CEE286CA1B80839600AE7E83 /* Sources */, + CEE286CB1B80839600AE7E83 /* Frameworks */, + CEE286CC1B80839600AE7E83 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CEE286D51B80839700AE7E83 /* PBXTargetDependency */, + ); + name = TraccarClientTests; + productName = TraccarClientTests; + productReference = CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CED4871017DB1BF6007FCF57 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1200; + ORGANIZATIONNAME = Traccar; + TargetAttributes = { + CED4871717DB1BF6007FCF57 = { + LastSwiftMigration = 1100; + }; + CEE286CD1B80839600AE7E83 = { + CreatedOnToolsVersion = 6.4; + LastSwiftMigration = 1100; + TestTargetID = CED4871717DB1BF6007FCF57; + }; + }; + }; + buildConfigurationList = CED4871317DB1BF6007FCF57 /* Build configuration list for PBXProject "TraccarClient" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + de, + es, + fr, + pt, + ru, + "pt-BR", + it, + pl, + nl, + zh, + ko, + ja, + ); + mainGroup = CED4870F17DB1BF6007FCF57; + packageReferences = ( + 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */, + ); + productRefGroup = CED4871917DB1BF6007FCF57 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CED4871717DB1BF6007FCF57 /* TraccarClient */, + CEE286CD1B80839600AE7E83 /* TraccarClientTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CED4871617DB1BF6007FCF57 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEF6433C1B91AA9400195CEA /* MainStoryboard.storyboard in Resources */, + CEF6433D1B91AA9400195CEA /* Localizable.strings in Resources */, + CE5899C81B115C9100ED70D2 /* Images.xcassets in Resources */, + CEF643271B919FFA00195CEA /* LaunchScreen.xib in Resources */, + CED4879B17DB1E61007FCF57 /* InAppSettings.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEE286CC1B80839600AE7E83 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5E3175221F692FA600CDF0F5 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", + "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"${CONFIGURATION}\" = \"Google\" ]; then\ncp -r \"${PROJECT_DIR}/../environment/google-services/traccar-client-ios.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n\"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CED4871417DB1BF6007FCF57 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CB7ED0801F6602CD00A33FCF /* Position.swift in Sources */, + CBAA0F7F1F68E807008BBBBE /* MainViewController.swift in Sources */, + CB7ED0821F661B4F00A33FCF /* PositionProvider.swift in Sources */, + CB4197991F67724F008F301C /* NetworkManager.swift in Sources */, + CB7ED0841F662BAF00A33FCF /* AppDelegate.swift in Sources */, + 5E394EBE28A9CC7600396F33 /* BatteryStatus.swift in Sources */, + 5E716A2B1F63A60800A2DBC3 /* ProtocolFormatter.swift in Sources */, + CBCE82F21B8D265800A7318B /* TraccarClient.xcdatamodeld in Sources */, + CBAA0F7D1F68E14C008BBBBE /* StatusViewController.swift in Sources */, + CBAA0F811F68EB62008BBBBE /* TrackingController.swift in Sources */, + 5E716A271F63A0B100A2DBC3 /* DistanceCalculator.swift in Sources */, + 5E716A291F63A45A00A2DBC3 /* RequestManager.swift in Sources */, + CB4197931F674A3E008F301C /* DatabaseHelper.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEE286CA1B80839600AE7E83 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E716A301F63A9F600A2DBC3 /* RequestManagerTests.swift in Sources */, + CB4197971F676B52008F301C /* DatabaseHelperTests.swift in Sources */, + 5E716A2E1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift in Sources */, + CB4197951F675518008F301C /* CoreDataTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + CEE286D51B80839700AE7E83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CED4871717DB1BF6007FCF57 /* TraccarClient */; + targetProxy = CEE286D41B80839700AE7E83 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CEF254DD1B905266001DEFD2 /* ru */, + CEF254DF1B9052EB001DEFD2 /* Base */, + CEF6432A1B91A48B00195CEA /* de */, + CEF6432C1B91A49400195CEA /* es */, + CEF6432E1B91A49700195CEA /* fr */, + CEF643321B91A4BF00195CEA /* pt */, + CE2FA5181CB935C9007F86B4 /* en */, + CE4B80FE1EC71CAE00D1C41D /* pt-BR */, + CE4B81001EC71CCF00D1C41D /* it */, + CE4B81021EC71CFF00D1C41D /* pl */, + CE4B81041EC71D1700D1C41D /* nl */, + CE4B81061EC71D3600D1C41D /* zh */, + CE4B81081EC71D4300D1C41D /* ko */, + CE4B810A1EC71D4800D1C41D /* ja */, + ); + name = MainStoryboard.storyboard; + sourceTree = "<group>"; + }; + CEF643371B91A94600195CEA /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + CEF643361B91A94600195CEA /* Base */, + CEF643381B91A95700195CEA /* de */, + CEF643391B91A98300195CEA /* ru */, + CEECE4B91B91AE2B00EB6F4A /* es */, + CEECE4BA1B91AE2C00EB6F4A /* fr */, + CEECE4BB1B91AE2D00EB6F4A /* pt */, + CE4B80FF1EC71CAE00D1C41D /* pt-BR */, + CE4B81011EC71CCF00D1C41D /* it */, + CE4B81031EC71CFF00D1C41D /* pl */, + CE4B81051EC71D1700D1C41D /* nl */, + CE4B81071EC71D3600D1C41D /* zh */, + CE4B81091EC71D4300D1C41D /* ko */, + CE4B810B1EC71D4800D1C41D /* ja */, + ); + name = Localizable.strings; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 5E3175231F69305200CDF0F5 /* Google */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEFINES_MODULE = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Google; + }; + 5E3175241F69305200CDF0F5 /* Google */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 58; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch"; + INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 7.0; + OTHER_SWIFT_FLAGS = "-D FIREBASE"; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = app; + }; + name = Google; + }; + 5E3175251F69305200CDF0F5 /* Google */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = TraccarClientTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient"; + }; + name = Google; + }; + CED4873C17DB1BF6007FCF57 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEFINES_MODULE = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CED4873D17DB1BF6007FCF57 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEFINES_MODULE = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CED4873F17DB1BF6007FCF57 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 58; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch"; + INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 7.0; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + CED4874017DB1BF6007FCF57 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 58; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch"; + INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 7.0; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + CEE286D71B80839700AE7E83 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = TraccarClientTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient"; + }; + name = Debug; + }; + CEE286D81B80839700AE7E83 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = HK8L8KQMEZ; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = TraccarClientTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CED4871317DB1BF6007FCF57 /* Build configuration list for PBXProject "TraccarClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CED4873C17DB1BF6007FCF57 /* Debug */, + CED4873D17DB1BF6007FCF57 /* Release */, + 5E3175231F69305200CDF0F5 /* Google */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CED4873E17DB1BF6007FCF57 /* Build configuration list for PBXNativeTarget "TraccarClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CED4873F17DB1BF6007FCF57 /* Debug */, + CED4874017DB1BF6007FCF57 /* Release */, + 5E3175241F69305200CDF0F5 /* Google */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEE286D61B80839700AE7E83 /* Build configuration list for PBXNativeTarget "TraccarClientTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE286D71B80839700AE7E83 /* Debug */, + CEE286D81B80839700AE7E83 /* Release */, + 5E3175251F69305200CDF0F5 /* Google */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 9.0.0; + }; + }; + 5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/futuretap/InAppSettingsKit.git"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 5EE3CA332998948F002C86E4 /* FirebaseAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalytics; + }; + 5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; + 5EE3CA38299894B9002C86E4 /* InAppSettingsKit */ = { + isa = XCSwiftPackageProductDependency; + package = 5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */; + productName = InAppSettingsKit; + }; +/* End XCSwiftPackageProductDependency section */ + +/* Begin XCVersionGroup section */ + CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */, + ); + currentVersion = CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */; + path = TraccarClient.xcdatamodeld; + sourceTree = "<group>"; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = CED4871017DB1BF6007FCF57 /* Project object */; +} diff --git a/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..919434a6254f0e9651f402737811be6634a03e9c --- /dev/null +++ b/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:"> + </FileRef> +</Workspace> diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000000000000000000000000000000..18d981003d68d0546c4804ac2ff47dd97c6e7921 --- /dev/null +++ b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEDidComputeMac32BitWarning</key> + <true/> +</dict> +</plist> diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000000000000000000000000000000000000..a0a0de81c028f65b3a72df38a63d6de08c28d2c3 --- /dev/null +++ b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,122 @@ +{ + "pins" : [ + { + "identity" : "abseil-cpp-swiftpm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git", + "state" : { + "revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1", + "version" : "0.20220203.2" + } + }, + { + "identity" : "boringssl-swiftpm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/boringssl-SwiftPM.git", + "state" : { + "revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab", + "version" : "0.9.1" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050", + "version" : "9.6.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "c1cfde8067668027b23a42c29d11c246152fe046", + "version" : "9.6.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "aae45a320fd0d11811820335b1eabc8753902a40", + "version" : "9.2.5" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b", + "version" : "7.11.5" + } + }, + { + "identity" : "grpc-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/grpc/grpc-ios.git", + "state" : { + "revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6", + "version" : "1.44.3-grpc" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "5ccda3981422a84186387dbb763ba739178b529c", + "version" : "2.3.0" + } + }, + { + "identity" : "inappsettingskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/futuretap/InAppSettingsKit.git", + "state" : { + "branch" : "master", + "revision" : "08ab93cd15759eed534b821c2ea789d97a0fdca0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", + "version" : "1.22.2" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", + "version" : "2.30909.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", + "version" : "2.3.1" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "3c54ab05249f59f2c6641dd2920b8358ea9ed127", + "version" : "1.24.0" + } + } + ], + "version" : 2 +} diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist new file mode 100644 index 0000000000000000000000000000000000000000..5dd5da85fdbd81ad600c193382e3305209b9e392 --- /dev/null +++ b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<array/> +</plist> diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..b2d758c245a287f08b5ba9e4e4628cff98a6159a Binary files /dev/null and b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist b/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..84e35758aec5a66ef5b837c12f92e045efbfda22 --- /dev/null +++ b/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>SchemeUserState</key> + <dict> + <key>Promises (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>2</integer> + </dict> + <key>Promises (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>3</integer> + </dict> + <key>Promises (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>0</integer> + </dict> + <key>TraccarClient.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>0</integer> + </dict> + </dict> +</dict> +</plist> diff --git a/TraccarClient/AppDelegate.swift b/TraccarClient/AppDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..732da69387cb905f29992dd42d94f687961a90db --- /dev/null +++ b/TraccarClient/AppDelegate.swift @@ -0,0 +1,176 @@ +// +// Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import CoreData +import Firebase + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, PositionProviderDelegate { + + var window: UIWindow? + + var managedObjectContext: NSManagedObjectContext? + var managedObjectModel: NSManagedObjectModel? + var persistentStoreCoordinator: NSPersistentStoreCoordinator? + + var trackingController: TrackingController? + var positionProvider: PositionProvider? + + static var instance: AppDelegate { + return UIApplication.shared.delegate as! AppDelegate + } + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { + #if FIREBASE + FirebaseApp.configure() + #endif + + UIDevice.current.isBatteryMonitoringEnabled = true + + let userDefaults = UserDefaults.standard + if userDefaults.string(forKey: "device_id_preference") == nil { + let identifier = "\(Int.random(in: 100000..<1000000))" + userDefaults.setValue(identifier, forKey: "device_id_preference") + } + + registerDefaultsFromSettingsBundle() + + migrateLegacyDefaults() + + let modelUrl = Bundle.main.url(forResource: "TraccarClient", withExtension: "momd") + managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl!) + + persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!) + let storeUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("TraccarClient.sqlite") + let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] + try! persistentStoreCoordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeUrl, options: options) + + managedObjectContext = NSManagedObjectContext.init(concurrencyType: .mainQueueConcurrencyType) + managedObjectContext?.persistentStoreCoordinator = persistentStoreCoordinator + + if userDefaults.bool(forKey: "service_status_preference") { + StatusViewController.addMessage(NSLocalizedString("Service created", comment: "")) + trackingController = TrackingController() + trackingController?.start() + } + + return true + } + + func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { + + let userDefaults = UserDefaults.standard + + switch shortcutItem.type { + case "org.traccar.client.start": + if !userDefaults.bool(forKey: "service_status_preference") { + userDefaults.setValue(true, forKey: "service_status_preference") + StatusViewController.addMessage(NSLocalizedString("Service created", comment: "")) + trackingController = TrackingController() + trackingController?.start() + showToast(message: NSLocalizedString("Service created", comment: "")) + } + case "org.traccar.client.stop": + if userDefaults.bool(forKey: "service_status_preference") { + userDefaults.setValue(false, forKey: "service_status_preference") + StatusViewController.addMessage(NSLocalizedString("Service destroyed", comment: "")) + trackingController?.stop() + trackingController = nil + showToast(message: NSLocalizedString("Service destroyed", comment: "")) + } + case "org.traccar.client.sos": + positionProvider = PositionProvider() + positionProvider?.delegate = self + positionProvider?.startUpdates() + default: + break + } + + completionHandler(true) + } + + func didUpdate(position: Position) { + + positionProvider?.stopUpdates() + positionProvider = nil + + let userDefaults = UserDefaults.standard + + if let request = ProtocolFormatter.formatPostion(position, url: userDefaults.string(forKey: "server_url_preference")!, alarm: "sos") { + RequestManager.sendRequest(request, completionHandler: {(_ success: Bool) -> Void in + if success { + self.showToast(message: NSLocalizedString("Send successfully", comment: "")) + } else { + self.showToast(message: NSLocalizedString("Send failed", comment: "")) + } + }) + } + } + + func showToast(message : String) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + window?.rootViewController?.present(alert, animated: true) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) { + alert.dismiss(animated: true) + } + } + + func applicationWillTerminate(_ application: UIApplication) { + if let context = managedObjectContext { + if context.hasChanges { + try! context.save() + } + } + } + + func registerDefaultsFromSettingsBundle() { + let settingsBundle = Bundle.main.path(forResource: "InAppSettings", ofType: "bundle")! + let finalPath = URL(fileURLWithPath: settingsBundle).appendingPathComponent("Root.plist") + let settingsDictionary = NSDictionary(contentsOf: finalPath) + let preferenceSpecifiers = settingsDictionary?.object(forKey: "PreferenceSpecifiers") as! [NSDictionary] + + var defaults: [String:Any] = [:] + + for item in preferenceSpecifiers { + if let key = item.object(forKey: "Key") as? String { + defaults[key] = item.object(forKey: "DefaultValue") + } + } + + UserDefaults.standard.register(defaults: defaults) + } + + func migrateLegacyDefaults() { + let userDefaults = UserDefaults.standard + if userDefaults.object(forKey: "server_address_preference") != nil { + var urlComponents = URLComponents() + urlComponents.scheme = userDefaults.bool(forKey: "secure_preference") ? "https" : "http" + urlComponents.host = userDefaults.string(forKey: "server_address_preference") + urlComponents.port = userDefaults.integer(forKey: "server_port_preference") + if urlComponents.port == 0 { + urlComponents.port = 5055 + } + + userDefaults.set(urlComponents.string, forKey: "server_url_preference") + + userDefaults.removeObject(forKey: "server_port_preference") + userDefaults.removeObject(forKey: "server_address_preference") + userDefaults.removeObject(forKey: "secure_preference") + } + } + +} diff --git a/TraccarClient/Base.lproj/Localizable.strings b/TraccarClient/Base.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..92a31cc82dd116ea886fa9319439cdd55314ae51 --- /dev/null +++ b/TraccarClient/Base.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar Client"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Send failed"; +"Location update" = "Location update"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/Base.lproj/MainStoryboard.storyboard b/TraccarClient/Base.lproj/MainStoryboard.storyboard new file mode 100644 index 0000000000000000000000000000000000000000..f5ea5bcf7da3a57c47628446d9b50eeb04e6a12e --- /dev/null +++ b/TraccarClient/Base.lproj/MainStoryboard.storyboard @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="4Uy-y2-7bi"> + <device id="retina4_7" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> + <capability name="Named colors" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--Root View Controller--> + <scene sceneID="Ejt-ia-q1N"> + <objects> + <tableViewController id="ZiV-Qa-hnE" customClass="MainViewController" customModule="TraccarClient" customModuleProvider="target" sceneMemberID="viewController"> + <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="KcQ-JP-xCp"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/> + </tableView> + <navigationItem key="navigationItem" title="Root View Controller" id="mBp-pp-ezn"> + <barButtonItem key="rightBarButtonItem" title="Status" id="eKa-7m-jq1"> + <connections> + <segue destination="LmD-5n-dPz" kind="push" id="iTl-2p-q5b"/> + </connections> + </barButtonItem> + </navigationItem> + </tableViewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="68Y-02-qka" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="863.768115942029" y="82.366071428571431"/> + </scene> + <!--Status View Controller--> + <scene sceneID="El5-px-WCe"> + <objects> + <tableViewController id="LmD-5n-dPz" customClass="StatusViewController" customModule="TraccarClient" customModuleProvider="target" sceneMemberID="viewController"> + <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="fdn-aT-LfP"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/> + <prototypes> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="StatusCell" id="bOU-VM-Ej4"> + <rect key="frame" x="0.0" y="55.5" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bOU-VM-Ej4" id="jfy-rh-RFY"> + <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + </tableViewCellContentView> + </tableViewCell> + </prototypes> + <connections> + <outlet property="dataSource" destination="LmD-5n-dPz" id="6o2-Hb-hz7"/> + <outlet property="delegate" destination="LmD-5n-dPz" id="hkG-pc-QSu"/> + </connections> + </tableView> + <navigationItem key="navigationItem" id="mzb-XD-7DL"> + <barButtonItem key="rightBarButtonItem" title="Clear" id="He4-eJ-a31"> + <connections> + <action selector="clear:" destination="LmD-5n-dPz" id="pZK-JC-IGX"/> + </connections> + </barButtonItem> + </navigationItem> + </tableViewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="522-xf-AQn" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="1633" y="82"/> + </scene> + <!--Navigation Controller--> + <scene sceneID="kvI-h8-0j0"> + <objects> + <navigationController definesPresentationContext="YES" id="4Uy-y2-7bi" sceneMemberID="viewController"> + <navigationBar key="navigationBar" contentMode="scaleToFill" id="aeM-3M-Z4b"> + <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="barTintColor" name="Brand"/> + <navigationBarAppearance key="standardAppearance"> + <color key="backgroundColor" name="Brand"/> + <textAttributes key="titleTextAttributes"> + <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </textAttributes> + </navigationBarAppearance> + <navigationBarAppearance key="scrollEdgeAppearance"> + <color key="backgroundColor" name="Brand"/> + <textAttributes key="titleTextAttributes"> + <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </textAttributes> + </navigationBarAppearance> + </navigationBar> + <connections> + <segue destination="ZiV-Qa-hnE" kind="relationship" relationship="rootViewController" id="nGO-vC-KYZ"/> + </connections> + </navigationController> + <placeholder placeholderIdentifier="IBFirstResponder" id="Kiw-gD-Vdj" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="89.855072463768124" y="82.366071428571431"/> + </scene> + </scenes> + <resources> + <namedColor name="Brand"> + <color red="0.1803921568627451" green="0.49019607843137253" blue="0.19607843137254902" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </namedColor> + <systemColor name="groupTableViewBackgroundColor"> + <color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> + </resources> +</document> diff --git a/TraccarClient/BatteryStatus.swift b/TraccarClient/BatteryStatus.swift new file mode 100644 index 0000000000000000000000000000000000000000..70b8cf821d11d62e2f5bb512a71d111cfc16e38b --- /dev/null +++ b/TraccarClient/BatteryStatus.swift @@ -0,0 +1,22 @@ +// +// Copyright 2022 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +struct BatteryStatus { + public let level: Float + public let charging: Bool +} diff --git a/TraccarClient/DatabaseHelper.swift b/TraccarClient/DatabaseHelper.swift new file mode 100644 index 0000000000000000000000000000000000000000..8a06abcd65a23530f1ca0901c3b5e3d348493f34 --- /dev/null +++ b/TraccarClient/DatabaseHelper.swift @@ -0,0 +1,48 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import CoreData +import UIKit + +public class DatabaseHelper: NSObject { + + let managedObjectContext: NSManagedObjectContext + + convenience override init() { + self.init(managedObjectContext: AppDelegate.instance.managedObjectContext!) + } + + public init(managedObjectContext: NSManagedObjectContext) { + self.managedObjectContext = managedObjectContext + super.init() + } + + public func selectPosition() -> Position? { + let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Position") + if let fetchedObjects = try? managedObjectContext.fetch(fetchRequest) { + if fetchedObjects.count > 0 { + return fetchedObjects[0] as? Position + } + } + return nil + } + + public func delete(position: Position) { + managedObjectContext.delete(position) + } + +} diff --git a/TraccarClient/DistanceCalculator.swift b/TraccarClient/DistanceCalculator.swift new file mode 100644 index 0000000000000000000000000000000000000000..747fa9bd6aa3a98810627f7ef75b2b219114dc78 --- /dev/null +++ b/TraccarClient/DistanceCalculator.swift @@ -0,0 +1,33 @@ +// +// Copyright 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class DistanceCalculator: NSObject { + + static let EQUATORIAL_EARTH_RADIUS = 6378.1370 + static let DEG_TO_RAD = Double.pi / 180 + + static func distance(fromLat lat1: Double, fromLon lon1: Double, toLat lat2: Double, toLon lon2: Double) -> Double { + let dlong = (lon2 - lon1) * DEG_TO_RAD + let dlat = (lat2 - lat1) * DEG_TO_RAD + let a = pow(sin(dlat / 2), 2) + cos(lat1 * DEG_TO_RAD) * cos(lat2 * DEG_TO_RAD) * pow(sin(dlong / 2), 2) + let c = 2 * atan2(sqrt(a), sqrt(1 - a)) + let d = EQUATORIAL_EARTH_RADIUS * c + return d * 1000 + } + +} diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000000000000000000000000000000000000..c2fd176693fa7fb61ea3a98bedff3d287d8b26da Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000000000000000000000000000000000000..2b31dd5db33453fa95d6fcf21e3a53c9d8ce61e7 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000000000000000000000000000000000000..b590b7608aadbbd0287265e820ab68ecfb2eef70 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000000000000000000000000000000000000..ff47ff513bd7dd3b14e765f59d6b05a844acbce0 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000000000000000000000000000000000000..c7dc8fd745e0649bf030ba506a678422ecbc12bc Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000000000000000000000000000000000..a352b16e19f007dd26d6e53cd6b49ceccc33826f Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000000000000000000000000000000000000..775d0f13a7a0dd1df91e27ce450b4a00aba9a65f Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000000000000000000000000000000000000..2da8eb5371d8049cae1cdc3d634e3d63fb999b42 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000000000000000000000000000000000000..a02a3585dda02fea50c666c7780b8fc7d1adb4bd Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000000000000000000000000000000000000..9332bbfbebe3a53fd8331ba4594b5513619d9ca2 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad24730ab6915664a8aa18a40d4856a13c59e94 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000000000000000000000000000000000000..f1d873799721bafa570d571928cb82f24243e3c5 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000000000000000000000000000000000000..27d7e8599f82bec5a73d750fa766450e5e123a2e Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000000000000000000000000000000000000..71395ffb8e585642c602bcab0d2ff455ce62f0e1 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000000000000000000000000000000000..8e12676f4f3bcb1190676377db77be2f213f30c9 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3720e633247927765e8dbdcaf1d49366ff0264 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000000000000000000000000000000000..93a8c0dca2cecd5cf724fcd56a88347cb87e9141 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000000000000000000000000000000000000..627736770128d5e760cce2e28ac18357e1f74277 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000000000000000000000000000000000000..c25052d78fdde19b8ec4f62abb6b4f75a30d0bf2 Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png differ diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json b/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..65b74d7ef11fa59fafa829e681ac90906f3ac8b2 --- /dev/null +++ b/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} \ No newline at end of file diff --git a/TraccarClient/Images.xcassets/Brand.colorset/Contents.json b/TraccarClient/Images.xcassets/Brand.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..1b244fc84744e4d0ffe272bcc36cb37a4c669048 --- /dev/null +++ b/TraccarClient/Images.xcassets/Brand.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x32", + "green" : "0x7D", + "red" : "0x2E" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x05", + "green" : "0x50", + "red" : "0x00" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TraccarClient/Images.xcassets/Contents.json b/TraccarClient/Images.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601 --- /dev/null +++ b/TraccarClient/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TraccarClient/InAppSettings.bundle/Root.plist b/TraccarClient/InAppSettings.bundle/Root.plist new file mode 100644 index 0000000000000000000000000000000000000000..97c1a60469121ccff268cec5bda4f91d5fae6ec2 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/Root.plist @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>PreferenceSpecifiers</key> + <array> + <dict> + <key>Type</key> + <string>PSToggleSwitchSpecifier</string> + <key>Title</key> + <string>Service status</string> + <key>Key</key> + <string>service_status_preference</string> + <key>DefaultValue</key> + <false/> + </dict> + <dict> + <key>AutocapitalizationType</key> + <string>None</string> + <key>AutocorrectionType</key> + <string>No</string> + <key>IsSecure</key> + <false/> + <key>Key</key> + <string>device_id_preference</string> + <key>KeyboardType</key> + <string>Alphabet</string> + <key>Title</key> + <string>Device identifier</string> + <key>Type</key> + <string>PSTextFieldSpecifier</string> + </dict> + <dict> + <key>DefaultValue</key> + <string>http://demo.traccar.org:5055</string> + <key>Type</key> + <string>PSTextFieldSpecifier</string> + <key>Title</key> + <string>Server URL</string> + <key>Key</key> + <string>server_url_preference</string> + </dict> + <dict> + <key>Type</key> + <string>PSMultiValueSpecifier</string> + <key>Title</key> + <string>Location accuracy</string> + <key>Key</key> + <string>accuracy_preference</string> + <key>DefaultValue</key> + <string>medium</string> + <key>Titles</key> + <array> + <string>High</string> + <string>Medium</string> + <string>Low</string> + </array> + <key>Values</key> + <array> + <string>high</string> + <string>medium</string> + <string>low</string> + </array> + </dict> + <dict> + <key>KeyboardType</key> + <string>NumberPad</string> + <key>DefaultValue</key> + <string>300</string> + <key>Type</key> + <string>PSTextFieldSpecifier</string> + <key>Title</key> + <string>Frequency</string> + <key>Key</key> + <string>frequency_preference</string> + </dict> + <dict> + <key>KeyboardType</key> + <string>NumberPad</string> + <key>DefaultValue</key> + <string></string> + <key>Type</key> + <string>PSTextFieldSpecifier</string> + <key>Title</key> + <string>Distance</string> + <key>Key</key> + <string>distance_preference</string> + </dict> + <dict> + <key>KeyboardType</key> + <string>NumberPad</string> + <key>DefaultValue</key> + <string></string> + <key>Type</key> + <string>PSTextFieldSpecifier</string> + <key>Title</key> + <string>Angle</string> + <key>Key</key> + <string>angle_preference</string> + </dict> + <dict> + <key>Type</key> + <string>PSToggleSwitchSpecifier</string> + <key>Title</key> + <string>Offline buffering</string> + <key>Key</key> + <string>buffer_preference</string> + <key>DefaultValue</key> + <true/> + </dict> + </array> +</dict> +</plist> diff --git a/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..694cf5f9a611e20a85c25cb1d6adbb07ea5eef3e --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Dienststatus"; +"Device identifier" = "Gerätekennung"; +"Server URL" = "Server URL"; +"Location accuracy" = "Positionsgenauigkeit"; +"High" = "Hoch"; +"Medium" = "Mittel"; +"Low" = "Niedrig"; +"Frequency" = "Frequenz"; +"Distance" = "Entfernung"; +"Angle" = "Winkel"; diff --git a/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..5e8f08c75e9188b4c28a9f5a180d36bfebc596db --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings @@ -0,0 +1,11 @@ +"Service status" = "Service status"; +"Device identifier" = "Device identifier"; +"Server URL" = "Server URL"; +"Location accuracy" = "Location accuracy"; +"High" = "High"; +"Medium" = "Medium"; +"Low" = "Low"; +"Frequency" = "Frequency"; +"Distance" = "Distance"; +"Angle" = "Angle"; +"Offline buffering" = "Offline buffering"; diff --git a/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..41223f6da362df3a98ca1c7bf9b38bd1324c7539 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Estado del servicio"; +"Device identifier" = "Identificador de dispositivo"; +"Server URL" = "URL del servidor"; +"Location accuracy" = "Precisión"; +"High" = "Alto"; +"Medium" = "Medio"; +"Low" = "Bajo"; +"Frequency" = "Frecuencia de rastreo"; +"Distance" = "Distancia"; +"Angle" = "Ãngulo"; diff --git a/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..4e420ca0826c95adcf4f760cdbefba36b25ddfd7 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Statut"; +"Device identifier" = "Identifiant de l\'appareil"; +"Server URL" = "URL du serveur"; +"Location accuracy" = "Precision de la localisation"; +"High" = "Haut"; +"Medium" = "Moyen"; +"Low" = "Bas"; +"Frequency" = "Fréquence"; +"Distance" = "Distance"; +"Angle" = "Angle"; diff --git a/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..64e72479824e1fe5dbbd9aba84801e90059b0836 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Stato del servizio"; +"Device identifier" = "Identificativo dispositivo"; +"Server URL" = "Server URL"; +"Location accuracy" = "Accuratezza posizione"; +"High" = "Alta"; +"Medium" = "Media"; +"Low" = "Bassa"; +"Frequency" = "Frequenza"; +"Distance" = "Distanza"; +"Angle" = "Angolo"; diff --git a/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..21cdbdb5b25b53aad046499e223bf4c2091957b5 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "サービスステータス"; +"Device identifier" = "デãƒã‚¤ã‚¹ ID"; +"Server URL" = "サーãƒãƒ¼ URL"; +"Location accuracy" = "ä½ç½®æƒ…å ±ã®ç²¾åº¦"; +"High" = "高"; +"Medium" = "ä¸"; +"Low" = "低"; +"Frequency" = "周期"; +"Distance" = "è·é›¢"; +"Angle" = "角度"; diff --git a/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..e37f2a6c8dcf54a15da868dd7c3fe837e75255e6 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "서비스ìƒíƒœ"; +"Device identifier" = "장치 ì‹ë³„ìž"; +"Server URL" = "Server URL"; +"Location accuracy" = "Location accuracy"; +"High" = "High"; +"Medium" = "Medium"; +"Low" = "Low"; +"Frequency" = "주기"; +"Distance" = "Distance"; +"Angle" = "Angle"; diff --git a/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..e97f2c3bd3c96e3d8ef51ad5c4401f9fe7502a57 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Server status"; +"Device identifier" = "Toestelidentificatie"; +"Server URL" = "Server URL"; +"Location accuracy" = "Locatienauwkeurigheid"; +"High" = "Hoog"; +"Medium" = "Gemiddeld"; +"Low" = "Laag"; +"Frequency" = "Frequentie"; +"Distance" = "Afstand"; +"Angle" = "Hoek"; diff --git a/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..28edf5dd826f96113832fa125e371c8b7692e6a8 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Status usÅ‚ugi"; +"Device identifier" = "Identyfikator urzÄ…dzenia"; +"Server URL" = "URL Serwera"; +"Location accuracy" = "DokÅ‚adność lokalizacji"; +"High" = "Wysoka"; +"Medium" = "Åšrednia"; +"Low" = "Niska"; +"Frequency" = "CzÄ™stotliwość"; +"Distance" = "Dystans"; +"Angle" = "KÄ…t"; diff --git a/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..f87b852717d00bf18e7d406fc280157c9a11f281 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Status do Serviço"; +"Device identifier" = "Identificador do dispositivo"; +"Server URL" = "URL do Servidor"; +"Location accuracy" = "Precisão de localização"; +"High" = "Alto"; +"Medium" = "Médio"; +"Low" = "Baixo"; +"Frequency" = "Invervalo"; +"Distance" = "Distância"; +"Angle" = "Ângulo"; diff --git a/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..036a8cb58d237b5c10fed984c9988cd1c231e93a --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "Status do Serviço"; +"Device identifier" = "Identificador único"; +"Server URL" = "URL do Servidor"; +"Location accuracy" = "Precisão de localização"; +"High" = "Alto"; +"Medium" = "Médio"; +"Low" = "Baixo"; +"Frequency" = "Invervalo"; +"Distance" = "Distância"; +"Angle" = "Ângulo"; diff --git a/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..6a0beb1c3596dc3fbd107d1d1f741e7f04a8c557 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "СоÑтоÑние ÑервиÑа"; +"Device identifier" = "Идентификатор уÑтройÑтва"; +"Server URL" = "URL Ñервера"; +"Location accuracy" = "ТочноÑть меÑтоположениÑ"; +"High" = "Ð’Ñ‹ÑокаÑ"; +"Medium" = "СреднÑÑ"; +"Low" = "ÐизкаÑ"; +"Frequency" = "ЧаÑтота"; +"Distance" = "РаÑÑтоÑние"; +"Angle" = "Угол"; diff --git a/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..7a8ff6181cecf81b7ddeb4647a1f527689b0ac27 --- /dev/null +++ b/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings @@ -0,0 +1,10 @@ +"Service status" = "定ä½"; +"Device identifier" = "设备编ç "; +"Server URL" = "æœåŠ¡å™¨åœ°å€"; +"Location accuracy" = "ä½ç½®ç²¾åº¦"; +"High" = "高"; +"Medium" = "ä¸"; +"Low" = "低"; +"Frequency" = "定ä½é¢‘率(秒)"; +"Distance" = "è·ç¦»"; +"Angle" = "角度"; diff --git a/TraccarClient/LaunchScreen.xib b/TraccarClient/LaunchScreen.xib new file mode 100644 index 0000000000000000000000000000000000000000..9922a47edd9230eabbdedfede3fbf1537dfcfc5c --- /dev/null +++ b/TraccarClient/LaunchScreen.xib @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES"> + <device id="retina4_7" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/> + <capability name="Named colors" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <objects> + <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> + <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> + <view contentMode="scaleToFill" id="iN0-l3-epB"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jyO-lQ-xCZ"> + <rect key="frame" x="0.0" y="0.0" width="375" height="64"/> + <constraints> + <constraint firstAttribute="height" constant="64" id="TfC-6h-A1k"/> + </constraints> + <color key="barTintColor" name="Brand"/> + </navigationBar> + </subviews> + <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/> + <constraints> + <constraint firstAttribute="trailing" secondItem="jyO-lQ-xCZ" secondAttribute="trailing" id="BVV-Jp-FLs"/> + <constraint firstItem="jyO-lQ-xCZ" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Ra4-bu-wwa"/> + <constraint firstItem="jyO-lQ-xCZ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="frB-PI-Vg3"/> + </constraints> + <point key="canvasLocation" x="499" y="433"/> + </view> + </objects> + <resources> + <namedColor name="Brand"> + <color red="0.2627450980392157" green="0.62745098039215685" blue="0.27843137254901962" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </namedColor> + </resources> +</document> diff --git a/TraccarClient/MainViewController.swift b/TraccarClient/MainViewController.swift new file mode 100644 index 0000000000000000000000000000000000000000..19b59b463c7349a4d86f25af84d2562c83715447 --- /dev/null +++ b/TraccarClient/MainViewController.swift @@ -0,0 +1,80 @@ +// +// Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import InAppSettingsKit + +class MainViewController: IASKAppSettingsViewController { + + var settingsObserver: NSObjectProtocol? + + override func viewDidLoad() { + super.viewDidLoad() + title = NSLocalizedString("Traccar Client", comment: "") + showCreditsFooter = false + neverShowPrivacySettings = true + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + self.settingsObserver = NotificationCenter.default.addObserver(forName: nil, object: nil, queue: nil) { (notification) in + self.didChangeSetting(notification) + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if let observer = self.settingsObserver { + NotificationCenter.default.removeObserver(observer) + } + } + + func showError(_ message: String) { + let alert = UIAlertController(title: nil, message: NSLocalizedString(message, comment: ""), preferredStyle: .alert) + let defaultAction = UIAlertAction(title: "OK", style: .default, handler: {(_ action: UIAlertAction) -> Void in + UserDefaults.standard.setValue(nil, forKey: "service_status_preference") + }) + alert.addAction(defaultAction) + present(alert, animated: true) + } + + func didChangeSetting(_ notification: Notification) { + if let status = notification.userInfo?["service_status_preference"] as? Bool { + if status && AppDelegate.instance.trackingController == nil { + let userDefaults = UserDefaults.standard + let url = userDefaults.string(forKey: "server_url_preference") + let frequency = userDefaults.integer(forKey: "frequency_preference") + + let candidateUrl = NSURL(string: url!) + + if candidateUrl == nil || candidateUrl?.host == nil || (candidateUrl?.scheme != "http" && candidateUrl?.scheme != "https") { + self.showError("Invalid server URL") + } else if frequency <= 0 { + self.showError("Invalid frequency value") + } else { + StatusViewController.addMessage(NSLocalizedString("Service created", comment: "")) + AppDelegate.instance.trackingController = TrackingController() + AppDelegate.instance.trackingController?.start() + } + } else if !status && AppDelegate.instance.trackingController != nil { + StatusViewController.addMessage(NSLocalizedString("Service destroyed", comment: "")) + AppDelegate.instance.trackingController?.stop() + AppDelegate.instance.trackingController = nil + } + } + } + +} diff --git a/TraccarClient/NetworkManager.swift b/TraccarClient/NetworkManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..92e93b10f82326b2e63565c04e4c9628d176d6be --- /dev/null +++ b/TraccarClient/NetworkManager.swift @@ -0,0 +1,82 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import SystemConfiguration + +protocol NetworkManagerDelegate: AnyObject { + func didUpdateNetwork(online: Bool) +} + +class NetworkManager : NSObject { + + weak var delegate: NetworkManagerDelegate? + + //var reachability: SCNetworkReachability + + override init() { + /*var zeroAddress = sockaddr_in() + zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress)) + zeroAddress.sin_family = sa_family_t(AF_INET) + + reachability = withUnsafePointer(to: &zeroAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + SCNetworkReachabilityCreateWithAddress(nil, $0) + } + })!*/ + + super.init() + } + + func online() -> Bool { + /*var flags: SCNetworkReachabilityFlags = [] + SCNetworkReachabilityGetFlags(reachability, &flags) + return NetworkManager.online(for: flags)*/ + return true + } + + func start() { + /*var context = SCNetworkReachabilityContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil) + SCNetworkReachabilitySetCallback(reachability, { (reachability, flags, pointer) in + let networkManager = Unmanaged<NetworkManager>.fromOpaque(pointer!).takeUnretainedValue() + networkManager.delegate?.didUpdateNetwork(online: NetworkManager.online(for: flags)) + }, &context) + SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)*/ + } + + func stop() { + /*SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)*/ + } + + static func online(for flags: SCNetworkReachabilityFlags) -> Bool { + if !flags.contains(.reachable) { + return false + } + if !flags.contains(.connectionRequired) { + return true + } + if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) { + if !flags.contains(.interventionRequired) { + return true + } + } + if flags.contains(.isWWAN) { + return true + } + return false + } + +} diff --git a/TraccarClient/Position.swift b/TraccarClient/Position.swift new file mode 100644 index 0000000000000000000000000000000000000000..694a40c37b459f8fa7a92625bb0d3c859d319f01 --- /dev/null +++ b/TraccarClient/Position.swift @@ -0,0 +1,53 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import CoreData +import CoreLocation + +public class Position: NSManagedObject { + + @NSManaged public var deviceId: String? + @NSManaged public var time: NSDate? + @NSManaged public var latitude: NSNumber? + @NSManaged public var longitude: NSNumber? + @NSManaged public var altitude: NSNumber? + @NSManaged public var speed: NSNumber? + @NSManaged public var course: NSNumber? + @NSManaged public var battery: NSNumber? + @NSManaged public var charging: Bool + @NSManaged public var accuracy: NSNumber? + + public convenience init(managedObjectContext context: NSManagedObjectContext) { + let entityDescription = NSEntityDescription.entity(forEntityName: "Position", in: context) + self.init(entity: entityDescription!, insertInto: context) + } + + public func setLocation(_ location: CLLocation) { + time = location.timestamp as NSDate + latitude = location.coordinate.latitude as NSNumber + longitude = location.coordinate.longitude as NSNumber + altitude = location.altitude as NSNumber + if location.speed >= 0 { + speed = location.speed * 1.94384 as NSNumber // knots from m/s + } + if location.course >= 0 { + course = location.course as NSNumber + } + accuracy = location.horizontalAccuracy as NSNumber + } + +} diff --git a/TraccarClient/PositionProvider.swift b/TraccarClient/PositionProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..a16dc65acc5392d22255c19293fa6ea57f6830de --- /dev/null +++ b/TraccarClient/PositionProvider.swift @@ -0,0 +1,127 @@ +// +// Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import CoreLocation + +protocol PositionProviderDelegate: AnyObject { + func didUpdate(position: Position) +} + +class PositionProvider: NSObject, CLLocationManagerDelegate { + + weak var delegate: PositionProviderDelegate? + + var locationManager: CLLocationManager + var lastLocation: CLLocation? + + var deviceId: String + var interval: Double + var distance: Double + var angle: Double + + var pendingStart = false + + override init() { + let userDefaults = UserDefaults.standard + deviceId = userDefaults.string(forKey: "device_id_preference")! + interval = userDefaults.double(forKey: "frequency_preference") + distance = userDefaults.double(forKey: "distance_preference") + angle = userDefaults.double(forKey: "angle_preference") + + locationManager = CLLocationManager() + + super.init() + + locationManager.delegate = self + + locationManager.pausesLocationUpdatesAutomatically = false + + switch userDefaults.string(forKey: "accuracy_preference") ?? "medium" { + case "high": + locationManager.desiredAccuracy = kCLLocationAccuracyBest + case "low": + locationManager.desiredAccuracy = kCLLocationAccuracyKilometer + default: + locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters + } + + if #available(iOS 9.0, *) { + locationManager.allowsBackgroundLocationUpdates = true + } + } + + func startUpdates() { + switch CLLocationManager.authorizationStatus() { + case .authorizedAlways: + locationManager.startUpdatingLocation() + default: + pendingStart = true + locationManager.requestAlwaysAuthorization() + } + } + + func stopUpdates() { + locationManager.stopUpdatingLocation() + } + + func getBatteryStatus() -> BatteryStatus { + let device = UIDevice.current + if device.batteryState != .unknown { + return BatteryStatus( + level: device.batteryLevel * 100, + charging: device.batteryState == .charging || device.batteryState == .full + ) + } else { + return BatteryStatus(level: 0, charging: true) + } + } + + func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { + switch status { + case .authorizedAlways, .authorizedWhenInUse: + if pendingStart { + pendingStart = false + locationManager.startUpdatingLocation() + } + default: + break + } + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.last { + if lastLocation == nil + || location.timestamp.timeIntervalSince(lastLocation!.timestamp) >= interval + || (distance > 0 && DistanceCalculator.distance(fromLat: location.coordinate.latitude, fromLon: location.coordinate.longitude, toLat: lastLocation!.coordinate.latitude, toLon: lastLocation!.coordinate.longitude) >= distance) + || (angle > 0 && fabs(location.course - lastLocation!.course) >= angle) { + + let position = Position(managedObjectContext: DatabaseHelper().managedObjectContext) + position.deviceId = deviceId + position.setLocation(location) + let batteryStatus = getBatteryStatus() + position.battery = batteryStatus.level as NSNumber + position.charging = batteryStatus.charging + delegate?.didUpdate(position: position) + lastLocation = location + } + } + } + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + } + +} diff --git a/TraccarClient/ProtocolFormatter.swift b/TraccarClient/ProtocolFormatter.swift new file mode 100644 index 0000000000000000000000000000000000000000..8730dfa2929235cf9c237f9c4483e08aac999fdf --- /dev/null +++ b/TraccarClient/ProtocolFormatter.swift @@ -0,0 +1,65 @@ +// +// Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public class ProtocolFormatter: NSObject { + + public static func formatPostion(_ position: Position, url: String, alarm: String? = nil) -> URL? { + var components = URLComponents(string: url) + + var query = String() + + if let deviceId = position.deviceId { + query += "id=\(deviceId)&" + } + if let time = position.time { + query += "timestamp=\(Int(time.timeIntervalSince1970))&" + } + if let latitude = position.latitude { + query += String(format: "lat=%.06f&", latitude.doubleValue) + } + if let longitude = position.longitude { + query += String(format: "lon=%.06f&", longitude.doubleValue) + } + if let speed = position.speed { + query += "speed=\(speed)&" + } + if let course = position.course { + query += "bearing=\(course)&" + } + if let altitude = position.altitude { + query += "altitude=\(altitude)&" + } + if let accuracy = position.accuracy { + query += "accuracy=\(accuracy)&" + } + if let battery = position.battery { + query += "batt=\(battery)&" + } + if position.charging { + query += "charge=true&" + } + if let alarm = alarm { + query += "alarm=\(alarm)&" + } + components?.query = String(query.dropLast()) + + // use queryItems for iOS 8 + return components?.url + } + +} diff --git a/TraccarClient/RequestManager.swift b/TraccarClient/RequestManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..5085462fe5614a6c19a6446c9d29b9f7c7721630 --- /dev/null +++ b/TraccarClient/RequestManager.swift @@ -0,0 +1,29 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public class RequestManager: NSObject { + + public static func sendRequest(_ url: URL, completionHandler handler: @escaping (Bool) -> Void) { + var request = URLRequest(url: url) + request.httpMethod = "POST" + NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: {(response, data, connectionError) -> Void in + handler(data != nil) + }) + } + +} diff --git a/TraccarClient/StatusViewController.swift b/TraccarClient/StatusViewController.swift new file mode 100644 index 0000000000000000000000000000000000000000..95cdd8972728d092d74f6a7648eb53df39ffd109 --- /dev/null +++ b/TraccarClient/StatusViewController.swift @@ -0,0 +1,81 @@ +// +// Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class StatusViewController: UITableViewController { + + static let LIMIT = 20 + static var statusViewController: StatusViewController? + + static var messages = [String]() + + class func addMessage(_ message: String) { + let formatter = DateFormatter() + formatter.dateFormat = "HH:mm - " + + messages.append(formatter.string(from: Date()) + (message)) + + if messages.count > LIMIT { + messages.remove(at: 0) + } + + statusViewController?.tableView.reloadData() + } + + class func clearMessages() { + messages.removeAll() + + statusViewController?.tableView.reloadData() + } + + @IBAction func clear(_ sender: UIBarButtonItem) { + StatusViewController.clearMessages() + } + + override func viewDidLoad() { + tableView.tableFooterView = UIView() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + StatusViewController.statusViewController = self + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + StatusViewController.statusViewController = nil + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return StatusViewController.messages.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellIdentifier: String = "status" + var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) + if cell == nil { + cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier) + } + cell?.textLabel?.text = StatusViewController.messages[indexPath.row] + return cell! + } + +} diff --git a/TraccarClient/TraccarClient-Info.plist b/TraccarClient/TraccarClient-Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..296861d9f5a0ac4d04154ed563f4077d9d035431 --- /dev/null +++ b/TraccarClient/TraccarClient-Info.plist @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>Traccar Client</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>$(MARKETING_VERSION)</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>$(CURRENT_PROJECT_VERSION)</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> + <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> + <string>This is a tracking application and therefore requires access to location services</string> + <key>NSLocationAlwaysUsageDescription</key> + <string>This is a tracking application and therefore requires access to location services</string> + <key>NSLocationWhenInUseUsageDescription</key> + <string>This is a tracking application and therefore requires access to location services</string> + <key>UIApplicationShortcutItems</key> + <array> + <dict> + <key>UIApplicationShortcutItemTitle</key> + <string>Start service</string> + <key>UIApplicationShortcutItemType</key> + <string>org.traccar.client.start</string> + </dict> + <dict> + <key>UIApplicationShortcutItemTitle</key> + <string>Stop service</string> + <key>UIApplicationShortcutItemType</key> + <string>org.traccar.client.stop</string> + </dict> + <dict> + <key>UIApplicationShortcutItemTitle</key> + <string>Send SOS</string> + <key>UIApplicationShortcutItemType</key> + <string>org.traccar.client.sos</string> + </dict> + </array> + <key>UIBackgroundModes</key> + <array> + <string>location</string> + </array> + <key>UILaunchStoryboardName</key> + <string>LaunchScreen</string> + <key>UIMainStoryboardFile</key> + <string>MainStoryboard</string> + <key>UIMainStoryboardFile~ipad</key> + <string>MainStoryboard</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>armv7</string> + </array> + <key>UIStatusBarStyle</key> + <string>UIStatusBarStyleLightContent</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UIViewControllerBasedStatusBarAppearance</key> + <false/> +</dict> +</plist> diff --git a/TraccarClient/TraccarClient-Prefix.pch b/TraccarClient/TraccarClient-Prefix.pch new file mode 100644 index 0000000000000000000000000000000000000000..936fe15b8302fdee7eb11e21fd4ab53afa29c447 --- /dev/null +++ b/TraccarClient/TraccarClient-Prefix.pch @@ -0,0 +1,15 @@ +// +// Prefix header for all source files of the 'TraccarClient' target in the 'TraccarClient' project +// + +#import <Availability.h> + +#ifndef __IPHONE_5_0 +#warning "This project uses features only available in iOS SDK 5.0 and later." +#endif + +#ifdef __OBJC__ + #import <UIKit/UIKit.h> + #import <Foundation/Foundation.h> + #import <CoreData/CoreData.h> +#endif diff --git a/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents b/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents new file mode 100644 index 0000000000000000000000000000000000000000..4960488e64c2d93e2f914f411a0b686c5628f749 --- /dev/null +++ b/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> + <entity name="Position" representedClassName=".Position" syncable="YES"> + <attribute name="accuracy" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/> + <attribute name="altitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="battery" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="charging" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="course" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="deviceId" optional="YES" attributeType="String"/> + <attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="speed" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/> + <attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + </entity> + <elements> + <element name="Position" positionX="-45" positionY="-9" width="128" height="179"/> + </elements> +</model> \ No newline at end of file diff --git a/TraccarClient/TrackingController.swift b/TraccarClient/TrackingController.swift new file mode 100644 index 0000000000000000000000000000000000000000..56a832cb96778287855b1338415bcc0348e3cb96 --- /dev/null +++ b/TraccarClient/TrackingController.swift @@ -0,0 +1,150 @@ +// +// Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import CoreLocation + +class TrackingController: NSObject, PositionProviderDelegate, NetworkManagerDelegate, CLLocationManagerDelegate { + + static let RETRY_DELAY: UInt64 = 30 * 1000; + + var online = false + var waiting = false + var stopped = false + + let positionProvider = PositionProvider() + let locationManager = CLLocationManager() + let databaseHelper = DatabaseHelper() + let networkManager = NetworkManager() + let userDefaults = UserDefaults.standard + + let url: String + let buffer: Bool + + override init() { + online = networkManager.online() + url = userDefaults.string(forKey: "server_url_preference")! + buffer = userDefaults.bool(forKey: "buffer_preference") + + super.init() + + positionProvider.delegate = self + locationManager.delegate = self + networkManager.delegate = self + } + + func start() { + self.stopped = false + if self.online { + read() + } + positionProvider.startUpdates() + locationManager.startMonitoringSignificantLocationChanges() + networkManager.start() + } + + func stop() { + networkManager.stop() + locationManager.stopMonitoringSignificantLocationChanges() + positionProvider.stopUpdates() + self.stopped = true + } + + func didUpdate(position: Position) { + StatusViewController.addMessage(NSLocalizedString("Location update", comment: "")) + if buffer { + write(position) + } else { + send(position) + } + } + + func didUpdateNetwork(online: Bool) { + StatusViewController.addMessage(NSLocalizedString("Connectivity change", comment: "")) + if !self.online && online { + read() + } + self.online = online + } + + // + // State transition examples: + // + // write -> read -> send -> delete -> read + // + // read -> send -> retry -> read -> send + // + + func write(_ position: Position) { + let context = DatabaseHelper().managedObjectContext + if context.hasChanges { + try? context.save() + } + if self.online && self.waiting { + read() + self.waiting = false + } + } + + func read() { + if let position = databaseHelper.selectPosition() { + if (position.deviceId == userDefaults.string(forKey: "device_id_preference")) { + send(position) + } else { + delete(position) + } + } else { + self.waiting = true + } + } + + func delete(_ position: Position) { + databaseHelper.delete(position: position) + read() + } + + func send(_ position: Position) { + if let request = ProtocolFormatter.formatPostion(position, url: url) { + RequestManager.sendRequest(request, completionHandler: {(_ success: Bool) -> Void in + if success { + if self.buffer { + self.delete(position) + } + } else { + StatusViewController.addMessage(NSLocalizedString("Send failed", comment: "")) + if self.buffer { + self.retry() + } + } + }) + } else { + StatusViewController.addMessage(NSLocalizedString("Send failed", comment: "")) + if buffer { + self.retry() + } + } + } + + func retry() { + let deadline = DispatchTime.now() + Double(TrackingController.RETRY_DELAY * NSEC_PER_MSEC) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: deadline, execute: {() -> Void in + if !self.stopped && self.online { + self.read() + } + }) + } + +} diff --git a/TraccarClient/de.lproj/Localizable.strings b/TraccarClient/de.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..7838aa5f4bdcadec3a1d1d8f8fe6b2abdeafb300 --- /dev/null +++ b/TraccarClient/de.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar Client"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Übertragung fehlerhaft"; +"Location update" = "Positionsupdate"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/de.lproj/MainStoryboard.strings b/TraccarClient/de.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..e72108181de932e581214e3accf0509bb7858484 --- /dev/null +++ b/TraccarClient/de.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar Client"; +"eKa-7m-jq1.title" = "Status"; +"gEG-ov-HEk.title" = "Über"; +"He4-eJ-a31.title" = "Leeren"; +"drL-Ag-IYB.text" = "Echtzeit GPS Ortung für Android Geräte. Kompatibel mit Traccar Server und anderen Ortungsservern."; +"2Hh-sN-c9g.text" = "Dieses Programm ist frei und Open Source. Es ist unter Apache Lizenz Version 2.0 lizenziert und ist auf GitHub verfügbar."; +"jF9-Ub-rRP.text" = "Für weitere Informationen besuchen Sie www.traccar.org/client"; diff --git a/TraccarClient/en.lproj/MainStoryboard.strings b/TraccarClient/en.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..0ec39b8859ff6fc622dee3aad35cb0c8434ef83d --- /dev/null +++ b/TraccarClient/en.lproj/MainStoryboard.strings @@ -0,0 +1,3 @@ +"TDj-2j-wEh.text" = "Traccar Client"; +"eKa-7m-jq1.title" = "Status"; +"He4-eJ-a31.title" = "Clear"; diff --git a/TraccarClient/es.lproj/Localizable.strings b/TraccarClient/es.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..ca7946de6ed8c0575ddffeb92eeae883328c564a --- /dev/null +++ b/TraccarClient/es.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Cliente Traccar"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Falló el envÃo"; +"Location update" = "Actualización de localización"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/es.lproj/MainStoryboard.strings b/TraccarClient/es.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..8648329822254ffada43558d6d8930e1331934c5 --- /dev/null +++ b/TraccarClient/es.lproj/MainStoryboard.strings @@ -0,0 +1,8 @@ +"TDj-2j-wEh.text" = "Cliente Traccar"; +"eKa-7m-jq1.title" = "Estado"; +"gEG-ov-HEk.title" = "Acerca de…"; +"He4-eJ-a31.title" = "Limpiar"; +"drL-Ag-IYB.text" = "Rastreador GPS en tiempo real para dispositivos Android. Compatible con Servidor Traccar y otros sistemas de rastreo."; +"2Hh-sN-c9g.text" = "Esta aplición es gratuÃta y de código abierto. El código fuente está bajo la Licencia Apache Versión 2 y disponible en Github."; +"jF9-Ub-rRP.text" = "Para más información visite +www.traccar.org/client"; diff --git a/TraccarClient/fr.lproj/Localizable.strings b/TraccarClient/fr.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..7785509d0524680ec6186c1b03fb27cfe64519b1 --- /dev/null +++ b/TraccarClient/fr.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Client Traccar"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "L’envoi a échoué"; +"Location update" = "Localisation mise à jour"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/fr.lproj/MainStoryboard.strings b/TraccarClient/fr.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..5f4114c74a48d4def25e024863f526a9c427bb1e --- /dev/null +++ b/TraccarClient/fr.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Client Traccar"; +"eKa-7m-jq1.title" = "Etat"; +"gEG-ov-HEk.title" = "A propos"; +"He4-eJ-a31.title" = "Effacer"; +"drL-Ag-IYB.text" = "Suivi GPS en temps réels pour appareils Android. Compatible avec le serveur Traccar ainsi que d\'autres plateformes de suivi."; +"2Hh-sN-c9g.text" = "Cette application open source est gratuite. Le code source est disponible sur GitHub est sous licence Apache Version 2.0."; +"jF9-Ub-rRP.text" = "Pour plus d’information, visitez le site: \nwww.traccar.org/client"; diff --git a/TraccarClient/it.lproj/Localizable.strings b/TraccarClient/it.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..f564adbc86f698e7ff7098da6870cea18f129357 --- /dev/null +++ b/TraccarClient/it.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar Client"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Invio fallito"; +"Location update" = "Aggiornamento posizione"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/it.lproj/MainStoryboard.strings b/TraccarClient/it.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..cf670ea77b5ffa738f90a3203f43b2c2945b45ff --- /dev/null +++ b/TraccarClient/it.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar Client"; +"eKa-7m-jq1.title" = "Stato"; +"gEG-ov-HEk.title" = "Informazioni"; +"He4-eJ-a31.title" = "Pulisci"; +"drL-Ag-IYB.text" = "Real time GPS tracker per dispositivi Android. Compatibile con Traccar Server e altri sistemi di tracking."; +"2Hh-sN-c9g.text" = "Questa applicazione è libera e open source, il codice sorgente è sotto licenza Apache License Version 2.0 e disponibile su GitHub."; +"jF9-Ub-rRP.text" = "Per maggiori informazioni visitate \nwww.traccar.org/client"; diff --git a/TraccarClient/ja.lproj/Localizable.strings b/TraccarClient/ja.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..085c411d114ecb6cd1cabcac7078468b3507424b --- /dev/null +++ b/TraccarClient/ja.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar クライアント"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "é€ä¿¡ã«å¤±æ•—ã—ã¾ã—ãŸ"; +"Location update" = "ä½ç½®æƒ…å ±æ›´æ–°"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/ja.lproj/MainStoryboard.strings b/TraccarClient/ja.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..511a88e6c3e897ba1cc0b051b9263a7071e751a0 --- /dev/null +++ b/TraccarClient/ja.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar クライアント"; +"eKa-7m-jq1.title" = "ステータス"; +"gEG-ov-HEk.title" = "アプリã«ã¤ã„ã¦"; +"He4-eJ-a31.title" = "クリア"; +"drL-Ag-IYB.text" = "Android デãƒã‚¤ã‚¹ç”¨ã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ GPS トラッカーã§ã™ã€‚ Traccar サーãƒãƒ¼ã‚„ä»–ã®ãƒˆãƒ©ãƒƒã‚ングシステムã¨äº’æ›æ€§ãŒã‚りã¾ã™ã€‚"; +"2Hh-sN-c9g.text" = "ã“ã®ã‚¢ãƒ—リケーションã¯ç„¡æ–™ã®ã‚ªãƒ¼ãƒ—ンソースã§ã€ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯ Apache ライセンス ãƒãƒ¼ã‚¸ãƒ§ãƒ³ 2.0 ã§ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã•れã¾ã™ã€‚ GitHub ã§åˆ©ç”¨å¯èƒ½ã§ã™ã€‚"; +"jF9-Ub-rRP.text" = "詳細㯠visit\nwww.traccar.org/client"; diff --git a/TraccarClient/ko.lproj/Localizable.strings b/TraccarClient/ko.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..d323218e704884e709b05e3662e4f325b901a93e --- /dev/null +++ b/TraccarClient/ko.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar ê³ ê°"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "보내기 실패"; +"Location update" = "위치 ìˆ˜ì •"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/ko.lproj/MainStoryboard.strings b/TraccarClient/ko.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..ca3b01ed9b9d53e17cb837de9023bc7a4a1f0599 --- /dev/null +++ b/TraccarClient/ko.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar ê³ ê°"; +"eKa-7m-jq1.title" = "ìƒíƒœ"; +"gEG-ov-HEk.title" = "관하여"; +"He4-eJ-a31.title" = "초기화"; +"drL-Ag-IYB.text" = "안드로ì´ë“œ 장치를위한 실시간 GPS ì¶”ì 기. Traccar 서버 ë° ê¸°íƒ€ ì¶”ì 시스템과 호환ë©ë‹ˆë‹¤."; +"2Hh-sN-c9g.text" = "ì´ ì‘ìš© í”„ë¡œê·¸ëž¨ì€ ë¬´ë£Œì´ë©° 오픈 소스ì´ë©°, 소스 코드는 Apache License Version 2.0ì—서 ì‚¬ìš©ì´ í—ˆê°€ë˜ë©° GitHubì—서 ì‚¬ìš©í• ìˆ˜ 있습니다."; +"jF9-Ub-rRP.text" = "ë” ìžì„¸í•œ ì •ë³´ëŠ” \www.traccar.org/client 를 방문하ì‹ì‹œì˜¤."; diff --git a/TraccarClient/nl.lproj/Localizable.strings b/TraccarClient/nl.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..81fa125a9b3c144b1bf507963e20cad7ba17bf85 --- /dev/null +++ b/TraccarClient/nl.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar client"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Verzenden mislukt"; +"Location update" = "Locatie update"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/nl.lproj/MainStoryboard.strings b/TraccarClient/nl.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..ede2035229c4f0f5259aa37c6721049d7949a83d --- /dev/null +++ b/TraccarClient/nl.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar client"; +"eKa-7m-jq1.title" = "Status"; +"gEG-ov-HEk.title" = "Over ons"; +"He4-eJ-a31.title" = "Wis"; +"drL-Ag-IYB.text" = "Real time GPS tracker voor Android apparaten. Compatibel met de Traccar server en andere tracking systemen."; +"2Hh-sN-c9g.text" = "Deze toepassing is gratis en open source, de broncode is beschikbaar onder de Apache Licentie versie 2.0 op GitHub."; +"jF9-Ub-rRP.text" = "Voor meer informatie bezoek\nwww.traccar.org/client"; diff --git a/TraccarClient/pl.lproj/Localizable.strings b/TraccarClient/pl.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..fee3d1ec4c87413c0ce6073574b283a6f69b2a5c --- /dev/null +++ b/TraccarClient/pl.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar Klient"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "WysÅ‚anie nieudane"; +"Location update" = "Aktualizacja lokalizacji"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/pl.lproj/MainStoryboard.strings b/TraccarClient/pl.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..5f2182d6da436bd8ef6a1b98c942cd87dc910088 --- /dev/null +++ b/TraccarClient/pl.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar Klient"; +"eKa-7m-jq1.title" = "Status"; +"gEG-ov-HEk.title" = "O Traccar"; +"He4-eJ-a31.title" = "Wyczyść"; +"drL-Ag-IYB.text" = "Monitor GPS dla urzÄ…dzeÅ„ Android. Kompatybilny z serwerem Traccar oraz innymi platformami Å›ledzÄ…cymi."; +"2Hh-sN-c9g.text" = "Ta aplikacja jest darmowa i otwartoźródÅ‚owa. Kod źródÅ‚owy jest licencjonowany Apache License Version 2.0 i jest dostÄ™pny na GitHub."; +"jF9-Ub-rRP.text" = "WiÄ™cej informacji - odwiedź\nwww.traccar.org/client"; diff --git a/TraccarClient/pt-BR.lproj/Localizable.strings b/TraccarClient/pt-BR.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..10c227edbb37595503836229d8c7dd798c54a241 --- /dev/null +++ b/TraccarClient/pt-BR.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Cliente Traccar"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Falha no envio"; +"Location update" = "Localização atualizada"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/pt-BR.lproj/MainStoryboard.strings b/TraccarClient/pt-BR.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..061dffa7c9515c7bb6a0925812faf2fd0bc5b74f --- /dev/null +++ b/TraccarClient/pt-BR.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Cliente Traccar"; +"eKa-7m-jq1.title" = "Status"; +"gEG-ov-HEk.title" = "Sobre"; +"He4-eJ-a31.title" = "Limpar"; +"drL-Ag-IYB.text" = "Rastreador GPS em tempo real para dispositivos Android. CompatÃvel com o Traccar Server e outros sistemas de rastreamento."; +"2Hh-sN-c9g.text" = "Esta aplicação é livre e de código aberto, o código fonte é licenciado sob a licença Apache Versão 2.0 e disponÃvel no GitHub."; +"jF9-Ub-rRP.text" = "Para mais informações visite\nwww.traccar.org/client"; diff --git a/TraccarClient/pt.lproj/Localizable.strings b/TraccarClient/pt.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..10c227edbb37595503836229d8c7dd798c54a241 --- /dev/null +++ b/TraccarClient/pt.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Cliente Traccar"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Falha no envio"; +"Location update" = "Localização atualizada"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/pt.lproj/MainStoryboard.strings b/TraccarClient/pt.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..5fb390e5fe16e58cd84dcfb4d1da158e4a05d39c --- /dev/null +++ b/TraccarClient/pt.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Cliente Traccar"; +"eKa-7m-jq1.title" = "Estado"; +"gEG-ov-HEk.title" = "Sobre"; +"He4-eJ-a31.title" = "Limpar"; +"drL-Ag-IYB.text" = "Localizador GPS em tempo real para dispositivos Android. CompatÃvel com servidor Traccar e outros sistemas de localização."; +"2Hh-sN-c9g.text" = "Esta aplicação é livre e de código aberto, o código fonte é licenciado sob a licença Apache Versão 2.0 e disponÃvel no GitHub."; +"jF9-Ub-rRP.text" = "Para mais informações visite \nwww.traccar.org/client"; diff --git a/TraccarClient/ru.lproj/Localizable.strings b/TraccarClient/ru.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..79fa773db4da9c0aad1f94f80289e6e59ca4e260 --- /dev/null +++ b/TraccarClient/ru.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar Клиент"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "Ошибка отправки"; +"Location update" = "Обновление меÑтоположениÑ"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/ru.lproj/MainStoryboard.strings b/TraccarClient/ru.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..39ee6fbe54e8447fced48c3b29e41a4f11d54958 --- /dev/null +++ b/TraccarClient/ru.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar Клиент"; +"eKa-7m-jq1.title" = "СоÑтоÑние"; +"gEG-ov-HEk.title" = "О программе"; +"He4-eJ-a31.title" = "ОчиÑтить"; +"drL-Ag-IYB.text" = "Трекер Ð´Ð»Ñ Android уÑтройÑтв. СовмеÑтим Ñ Traccar Ñервером и другими мониторинговыми ÑиÑтемами."; +"2Hh-sN-c9g.text" = "Ðто приложение раÑпроÑтанÑетÑÑ Ð±ÐµÑплатно, иÑходный код раÑпроÑтанÑетÑÑ Ð¿Ð¾ лицензии Apache License Version 2.0 и доÑтупен на GitHub."; +"jF9-Ub-rRP.text" = "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации поÑетите\nwww.traccar.org/client"; diff --git a/TraccarClient/zh.lproj/Localizable.strings b/TraccarClient/zh.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..a424ae16431f7137e880ff35366f207544ff9633 --- /dev/null +++ b/TraccarClient/zh.lproj/Localizable.strings @@ -0,0 +1,9 @@ +"Traccar Client" = "Traccar定ä½ç«¯"; +"Service created" = "Service created"; +"Service destroyed" = "Service destroyed"; +"Send failed" = "å‘é€å¤±è´¥"; +"Location update" = "ä½ç½®æ›´æ–°"; +"Connectivity change" = "Connectivity change"; +"Invalid server address" = "Invalid server address"; +"Invalid server port" = "Invalid server port"; +"Invalid frequency value" = "Invalid frequency value"; diff --git a/TraccarClient/zh.lproj/MainStoryboard.strings b/TraccarClient/zh.lproj/MainStoryboard.strings new file mode 100644 index 0000000000000000000000000000000000000000..fc16c54c8649def5c385131d984ad8bc7e286a57 --- /dev/null +++ b/TraccarClient/zh.lproj/MainStoryboard.strings @@ -0,0 +1,7 @@ +"TDj-2j-wEh.text" = "Traccar定ä½ç«¯"; +"eKa-7m-jq1.title" = "定ä½çжæ€"; +"gEG-ov-HEk.title" = "关于"; +"He4-eJ-a31.title" = "清除"; +"drL-Ag-IYB.text" = "Android设备的实时GPS跟踪器。兼容TraccaræœåŠ¡å™¨å’Œå…¶ä»–è·Ÿè¸ªç³»ç»Ÿã€‚"; +"2Hh-sN-c9g.text" = "æ¤åº”用å…费且开æºã€‚æºç 许å¯ä¸ºApache V2.0,在GitHub上å‘布。"; +"jF9-Ub-rRP.text" = "更多信æ¯è¯·è®¿é—®www.traccar.org/client"; diff --git a/TraccarClientTests/CoreDataTests.swift b/TraccarClientTests/CoreDataTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..be123bae6964ffb976eb94567c1049da8c29b72e --- /dev/null +++ b/TraccarClientTests/CoreDataTests.swift @@ -0,0 +1,36 @@ +// +// Copyright 2015 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import CoreData + +class CoreDataTests: XCTestCase { + + var managedObjectContext: NSManagedObjectContext? + var managedObjectModel: NSManagedObjectModel? + var persistentStoreCoordinator: NSPersistentStoreCoordinator? + + override func setUp() { + let modelURL = Bundle.main.url(forResource: "TraccarClient", withExtension: "momd") + managedObjectModel = NSManagedObjectModel(contentsOf: modelURL!) + + persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!) + + managedObjectContext = NSManagedObjectContext() + managedObjectContext?.persistentStoreCoordinator = persistentStoreCoordinator + } + +} diff --git a/TraccarClientTests/DatabaseHelperTests.swift b/TraccarClientTests/DatabaseHelperTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..359868db476e26712bbdb22d36cf6716b9e4db12 --- /dev/null +++ b/TraccarClientTests/DatabaseHelperTests.swift @@ -0,0 +1,39 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import TraccarClient + +class DatabaseHelperTests: CoreDataTests { + + func testFormatPosition() { + let databaseHelper = DatabaseHelper(managedObjectContext: managedObjectContext!) + + XCTAssertNil(databaseHelper.selectPosition()) + + let position = Position(managedObjectContext: managedObjectContext!) + position.deviceId = "123456789012345" + position.time = Date(timeIntervalSince1970: 0) as NSDate + + XCTAssertNotNil(databaseHelper.selectPosition()) + + databaseHelper.delete(position: position) + + XCTAssertNil(databaseHelper.selectPosition()) + } + + +} diff --git a/TraccarClientTests/Info.plist b/TraccarClientTests/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..ba72822e8728ef2951005e49b6c27a2f1da6572d --- /dev/null +++ b/TraccarClientTests/Info.plist @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> diff --git a/TraccarClientTests/ProtocolFormatterTests.swift b/TraccarClientTests/ProtocolFormatterTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..1f7bb3f73676810b46832337c997ff6937d7e61d --- /dev/null +++ b/TraccarClientTests/ProtocolFormatterTests.swift @@ -0,0 +1,32 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import TraccarClient + +class ProtocolFormatterTests: CoreDataTests { + + func testFormatPosition() { + let position = Position(managedObjectContext: managedObjectContext!) + position.deviceId = "123456789012345" + position.time = Date(timeIntervalSince1970: 0) as NSDate + + let url = ProtocolFormatter.formatPostion(position, url: "http://localhost:5055") + + XCTAssertEqual("http://localhost:5055?id=123456789012345×tamp=0&lat=0.000000&lon=0.000000&speed=0&bearing=0&altitude=0&accuracy=0&batt=0", url?.absoluteString) + } + +} diff --git a/TraccarClientTests/RequestManagerTests.swift b/TraccarClientTests/RequestManagerTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..432a756962aaabc6c9a8a99886afc1bd5cedb941 --- /dev/null +++ b/TraccarClientTests/RequestManagerTests.swift @@ -0,0 +1,33 @@ +// +// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import TraccarClient + +class RequestManagerTests: XCTestCase { + + func testSendRequest() { + let expectation: XCTestExpectation? = self.expectation(description: "sendRequest") + + RequestManager.sendRequest(URL(string: "http://www.google.com")!, completionHandler: {(success) -> Void in + XCTAssertTrue(success) + expectation?.fulfill() + }) + + waitForExpectations(timeout: 5, handler: nil) + } + +} diff --git a/translate.py b/translate.py new file mode 100755 index 0000000000000000000000000000000000000000..d8a2d1004c62ca3329c648d53e3dbcb1245c9694 --- /dev/null +++ b/translate.py @@ -0,0 +1,85 @@ +#!/usr/bin/python + +import os +import optparse +import urllib2 +import json +import base64 +import re +import codecs +import xml.etree.ElementTree + +parser = optparse.OptionParser() +parser.add_option('-u', '--user', dest='username', help='transifex user login') +parser.add_option('-p', '--password', dest='password', help='transifex user password') + +(options, args) = parser.parse_args() + +if not options.username or not options.password: + parser.error('User name and password are required') + +os.chdir(os.path.dirname(os.path.abspath(__file__)) + '/TraccarClient') + +def request(url): + req = urllib2.Request(url) + auth = base64.encodestring("%s:%s" % (options.username, options.password)).replace("\n", "") + req.add_header("Authorization", "Basic %s" % auth) + return urllib2.urlopen(req) + +mapping_en = {} + +def load_en(): + data = request('https://www.transifex.com/api/2/project/traccar/resource/client/translation/en?file').read().decode('utf-8') + for entry in xml.etree.ElementTree.fromstring(data).findall('string'): + mapping_en[entry.attrib['name']] = entry.text.replace('Android', 'iOS') + +load_en() + +regex = re.compile(r'"([^"]+)" = "([^"]+)";') + +def write_storyboard(code, mapping): + in_file = codecs.open('en.lproj/MainStoryboard.strings', 'r', 'utf-8') + out_file = codecs.open(code + '.lproj/MainStoryboard.strings', 'w', 'utf-8') + for line in in_file: + match = regex.match(line) + if match: + out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n') + out_file.close() + in_file.close() + +def write_strings(code, mapping): + in_file = codecs.open('Base.lproj/Localizable.strings', 'r', 'utf-8') + out_file = codecs.open(code + '.lproj/Localizable.strings', 'w', 'utf-8') + for line in in_file: + match = regex.match(line) + if match: + out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n') + out_file.close() + in_file.close() + +def write_settings(code, mapping): + filename = 'InAppSettings.bundle/' + code + '.lproj/Root.strings' + if not os.path.exists(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + in_file = codecs.open('InAppSettings.bundle/en.lproj/Root.strings', 'r', 'utf-8') + out_file = codecs.open(filename, 'w', 'utf-8') + for line in in_file: + match = regex.match(line) + if match: + out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n') + out_file.close() + in_file.close() + +for directory in os.walk('.'): + match = re.match(r"([a-z]{2}(?:-[A-Z]{2})?)\.lproj", directory[0][2:]) + if match: + code = match.group(1) + if code != 'en': + mapping = {} + data = request('https://www.transifex.com/api/2/project/traccar/resource/client/translation/' + code.replace('-', '_') + '?file').read() + for entry in xml.etree.ElementTree.fromstring(data).findall('string'): + mapping[mapping_en[entry.attrib['name']]] = entry.text + write_storyboard(code, mapping) + write_strings(code, mapping) + write_settings(code, mapping) + print code