Сравнить коммиты
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
88cc37e96e | ||
|
|
b80fde0b6d | ||
|
|
f734d0256f | ||
|
|
1dcc26b47b | ||
|
|
5580f0b6bf | ||
|
|
ddf1f0fbe0 | ||
|
|
235e58d3e2 | ||
|
|
9cdb10db14 | ||
|
|
745a8523ef | ||
|
|
a2b2c215da | ||
|
|
6d1c7b47e1 | ||
|
|
10bc96caf9 | ||
|
|
0873aa7302 | ||
|
|
37d3584f77 | ||
|
|
404886f80e | ||
|
|
a68b489681 | ||
|
|
04e5b01cc7 | ||
|
|
5f059645a1 | ||
|
|
8b985526dd | ||
|
|
5e8ea134e0 | ||
|
|
ec85edd56f | ||
|
|
c5ab10919e | ||
|
|
3e8ba9a603 | ||
|
|
826d638ea3 | ||
|
|
1b687bd075 | ||
|
|
42d5584d8d | ||
|
|
ff88682e40 | ||
|
|
307e692459 | ||
|
|
b7ec644ecf | ||
|
|
842baa2aec | ||
|
|
26bc92cf8e | ||
|
|
6dadf5e7c3 | ||
|
|
39e9c171c4 | ||
|
|
bcb5985cef | ||
|
|
eb868056bf | ||
|
|
94e094e648 | ||
|
|
25c0434e4c | ||
|
|
3afcfa17d0 | ||
|
|
02263d5fd9 | ||
|
|
a884b2f3d7 | ||
|
|
6910790fa7 | ||
|
|
6cab1a0b36 | ||
|
|
edd4f670fe | ||
|
|
298d7becb5 | ||
|
|
9a2e3f9126 | ||
|
|
d3f7ee0c8b | ||
|
|
8ae486dbc8 | ||
|
|
02295f5fcf | ||
|
|
958eb77c51 | ||
|
|
0a3a8381a5 | ||
|
|
6009dba342 | ||
|
|
59085f668c | ||
|
|
1cb5326e07 | ||
|
|
28befc6180 | ||
|
|
e83ef80fa3 | ||
|
|
b26a1bd133 | ||
|
|
0bbee716a6 | ||
|
|
52e3b839ae | ||
|
|
cfe04eb0a6 | ||
|
|
c243912c55 | ||
|
|
914a157afc | ||
|
|
454ce6be21 | ||
|
|
e8a1e015d4 | ||
|
|
ceb15c1378 | ||
|
|
d25c745013 | ||
|
|
c24df5b246 | ||
|
|
283d0467b4 | ||
|
|
83bf1daa84 | ||
|
|
96d61c4c66 | ||
|
|
3978a3d8d6 | ||
|
|
78018c3038 | ||
|
|
19b7c4da21 | ||
|
|
a3d43294ce | ||
|
|
330dc27f0f | ||
|
|
f052be83c3 | ||
|
|
c77d4b477b | ||
|
|
eb7de9eb98 | ||
|
|
3a786b14b7 | ||
|
|
6bad9786ec | ||
|
|
259aaf2000 | ||
|
|
aa00a33f5b | ||
|
|
ab86076b9b | ||
|
|
fed2a73999 | ||
|
|
42c210763c | ||
|
|
faa05dea5a | ||
|
|
453edfa97e | ||
|
|
4c6c79b6a2 | ||
|
|
93456bf7fb | ||
|
|
a2ff7fcb38 | ||
|
|
7015f75edb | ||
|
|
636ceaf027 | ||
|
|
350b800184 | ||
|
|
7ecba2cffd | ||
|
|
5d8f0dfb83 | ||
|
|
f42ecf9f09 | ||
|
|
556856ab5c | ||
|
|
f9f37ead08 | ||
|
|
b3ee99140e | ||
|
|
85f1e09308 | ||
|
|
20fd20f425 | ||
|
|
1ae7b27683 | ||
|
|
f5b12b9e74 | ||
|
|
d2f653034d | ||
|
|
ab5aec9721 | ||
|
|
1b0559c98a | ||
|
|
2e0e7ea453 | ||
|
|
ac6d677ff4 | ||
|
|
cbfe90757d | ||
|
|
a9f4390ae4 | ||
|
|
8a938701c8 | ||
|
|
2319834c5b | ||
|
|
1b93619e64 | ||
|
|
2a36e1aa6d | ||
|
|
64687b26c2 | ||
|
|
dcb60ab75e | ||
|
|
0b4c4cccc6 | ||
|
|
ef5bfff70a | ||
|
|
855efc7acb | ||
|
|
0ae47e65d4 | ||
|
|
e8d5e5c99b | ||
|
|
c3980780d8 | ||
|
|
3c11216334 | ||
|
|
bcde8388d6 | ||
|
|
7f18915514 | ||
|
|
42ad669fca | ||
|
|
3e2b508ea7 | ||
|
|
fd4d616812 | ||
|
|
4f6571e572 | ||
|
|
d3025b437b | ||
|
|
1e861ef0ac | ||
|
|
43afc57114 | ||
|
|
d47783808f | ||
|
|
3ef2272227 | ||
|
|
3625ee42db | ||
|
|
385826c3c1 | ||
|
|
1426926c8e | ||
|
|
4f680d33c9 | ||
|
|
2dad912da5 | ||
|
|
290a7d80eb | ||
|
|
44228feb2f | ||
|
|
d887bc4119 | ||
|
|
c892a20104 | ||
|
|
2719b57f7c | ||
|
|
cc6c8a23f6 | ||
|
|
1e82cbbbf6 | ||
|
|
4b8fde36d7 | ||
|
|
b8cd56439a | ||
|
|
edb1f696df | ||
|
|
56d2f2ef53 | ||
|
|
59e6a5ae65 | ||
|
|
319fab647b | ||
|
|
42429f3496 | ||
|
|
841d3e581c | ||
|
|
dac43ccbc1 | ||
|
|
3c9ae512bd | ||
|
|
d3e600d81d | ||
|
|
e0296a9c65 | ||
|
|
e51f590e19 | ||
|
|
6e3812320c | ||
|
|
c45c8ea797 | ||
|
|
ed0aa53aa3 | ||
|
|
f88891be2b | ||
|
|
3f83684d02 | ||
|
|
a9d772bfda | ||
|
|
2bc3c78a91 | ||
|
|
8fab55552d | ||
|
|
979963f6a2 | ||
|
|
70d6dfec4e | ||
|
|
43e108de8c | ||
|
|
fedce0e26b | ||
|
|
e24d725feb | ||
|
|
3198bee2f2 | ||
|
|
92e0fb3ad5 | ||
|
|
f7b0df1c41 | ||
|
|
8a26c7e5ba | ||
|
|
27c98b910b | ||
|
|
360958d250 | ||
|
|
4d8d15d615 | ||
|
|
7c49bb78f3 | ||
|
|
e87c3c7783 | ||
|
|
5428530dbd | ||
|
|
3d5682d1b1 | ||
|
|
8f91c0e0ea | ||
|
|
965feb9b04 | ||
|
|
8f8a5e3f55 | ||
|
|
77154a81af | ||
|
|
8d94b8d011 | ||
|
|
ab37fd478a | ||
|
|
79662bad35 | ||
|
|
90373ff8fb | ||
|
|
f2e7ae7c21 | ||
|
|
e0c5340d8b | ||
|
|
90fffa9873 | ||
|
|
db51adaf86 | ||
|
|
7eb6876491 | ||
|
|
5db3407e88 | ||
|
|
ff4fe90c6d | ||
|
|
62a2599765 | ||
|
|
149eee530a | ||
|
|
6e398d12fe | ||
|
|
10e573017b | ||
|
|
f5f8d41e41 | ||
|
|
839b180890 | ||
|
|
e2a26297c9 | ||
|
|
4d33d8ff9e | ||
|
|
7e923954b8 | ||
|
|
f0b0650531 | ||
|
|
3eb655040d | ||
|
|
96a75f02a7 | ||
|
|
d632c7cdfa | ||
|
|
9bae6e554f | ||
|
|
faf23e7926 | ||
|
|
54331ffe93 | ||
|
|
c19c17f091 | ||
|
|
2bd5bf540f | ||
|
|
3d89cfe649 | ||
|
|
e2e51b3048 | ||
|
|
43d9775aa0 | ||
|
|
e5bddc5aa6 | ||
|
|
3b1ba21a76 | ||
|
|
26467740fa | ||
|
|
a8d6f0b82d | ||
|
|
94acc71f8c | ||
|
|
fe4e798eb8 | ||
|
|
8b72fd5522 | ||
|
|
4a8b66694c | ||
|
|
c717da9c6a | ||
|
|
969cbc6cf4 | ||
|
|
a04689350a | ||
|
|
87e798b459 | ||
|
|
5a99be4005 | ||
|
|
255598b7e8 | ||
|
|
6c29113278 | ||
|
|
a861b71d02 | ||
|
|
370e9778b4 | ||
|
|
efdaf69d07 | ||
|
|
3dbdaeb555 | ||
|
|
806a31cd92 | ||
|
|
fa4654c969 | ||
|
|
624eefc309 | ||
|
|
7ab99da14c | ||
|
|
532a1f4249 | ||
|
|
c3986ac172 | ||
|
|
fae01757b6 | ||
|
|
4f1af00383 | ||
|
|
4c4192394f | ||
|
|
3d602aaf16 | ||
|
|
acf935dcfd | ||
|
|
bb49b11bf4 | ||
|
|
b72b7dfa6f | ||
|
|
c4d4e9ff40 | ||
|
|
bcfa577e91 | ||
|
|
ac1ca8df2f | ||
|
|
e0dfc8fe66 | ||
|
|
7dc7f32110 | ||
|
|
6bffc7bbc1 | ||
|
|
aaa6092501 | ||
|
|
8cd3601a4f | ||
|
|
088adcaa24 | ||
|
|
0365837e4d | ||
|
|
f952e16c00 | ||
|
|
b7fb6445ef | ||
|
|
bc3f47e296 | ||
|
|
c60f749a67 | ||
|
|
821f24ce2b | ||
|
|
62c713e97a | ||
|
|
ab587d465e | ||
|
|
f82e89409d | ||
|
|
7e6d4f5822 | ||
|
|
b8a099189e | ||
|
|
e961df85d5 | ||
|
|
44648cf142 | ||
|
|
d916bf432e | ||
|
|
09a62e639e | ||
|
|
d1912bd430 | ||
|
|
052320f981 | ||
|
|
140a3ebece | ||
|
|
603e7bb3e0 | ||
|
|
f81db810ff | ||
|
|
1ca71f80c6 | ||
|
|
5d101b6f6e | ||
|
|
dc949e0152 | ||
|
|
96d9127a4a | ||
|
|
2194290935 | ||
|
|
78bf7f3169 | ||
|
|
e2d0329635 | ||
|
|
a64da65c6f | ||
|
|
8a3d86c173 | ||
|
|
c78cac2da5 | ||
|
|
5f7a1a8bfd | ||
|
|
16f743e90a | ||
|
|
429dda1e37 | ||
|
|
0ac173c043 | ||
|
|
9ae90d2edc | ||
|
|
27a99a6af1 | ||
|
|
5ac0610bd5 | ||
|
|
b109de92c4 | ||
|
|
be0d556ed6 | ||
|
|
1861d9ca34 | ||
|
|
c0cc6e8e55 | ||
|
|
1cc3a2d9d1 | ||
|
|
9263b4e08b | ||
|
|
77213ecc91 | ||
|
|
3214944d69 | ||
|
|
3e786e474d | ||
|
|
0850ac09b0 | ||
|
|
50e6c00bd0 | ||
|
|
0e4918ba22 | ||
|
|
8027c5be9f | ||
|
|
6084bfdfbe | ||
|
|
b560577d96 | ||
|
|
92137c83bd | ||
|
|
3617700351 | ||
|
|
c803eb7bff | ||
|
|
7fe765d124 | ||
|
|
3235229d1e | ||
|
|
25e035e59d | ||
|
|
b63bf4779e | ||
|
|
2cf1213535 | ||
|
|
03266a8c78 | ||
|
|
3782fd0003 | ||
|
|
fc83e7525a | ||
|
|
596ede019c | ||
|
|
2593ec7d2e | ||
|
|
14894c294e | ||
|
|
fe9132e46a | ||
|
|
26bc00f5fd | ||
|
|
d5e2a3155c | ||
|
|
d49bc6ad28 | ||
|
|
c008c5f65e | ||
|
|
e4f167f17b | ||
|
|
346ad93ca0 | ||
|
|
3f4663c570 | ||
|
|
7396e9c436 | ||
|
|
59037d5fb1 | ||
|
|
5591bec9e9 | ||
|
|
e4151ac228 | ||
|
|
630cf8eb86 | ||
|
|
222497d6ff | ||
|
|
0f9645120b | ||
|
|
9ffc0aa773 | ||
|
|
9d93c1d001 | ||
|
|
ac6f648062 | ||
|
|
06c741e6b5 | ||
|
|
ceaf4d526c | ||
|
|
6d4d0d74cd | ||
|
|
0186b8fc6e | ||
|
|
16c634b9f5 | ||
|
|
4aac812c8a | ||
|
|
9a2388011e | ||
|
|
cd2277c3fd | ||
|
|
88d4ff69b7 | ||
|
|
4e7082bed6 | ||
|
|
d5224546c5 | ||
|
|
29a0cd03ae | ||
|
|
56f4534485 | ||
|
|
82ce44b64d | ||
|
|
4f94c51e11 | ||
|
|
63b7566296 | ||
|
|
af48a89874 | ||
|
|
2266bab317 | ||
|
|
01060cf454 | ||
|
|
1d6da0c9d9 | ||
|
|
be4f842a79 | ||
|
|
f611b1b597 | ||
|
|
30412eb7b8 | ||
|
|
d2399a5fed | ||
|
|
026c3e1534 | ||
|
|
6d0db55b60 |
@@ -0,0 +1,47 @@
|
||||
# Contributing to Traccar
|
||||
|
||||
Please make sure you read this guide before commenting on any issue or creating a new issue or pull request.
|
||||
|
||||
Before asking anything, please search for an answer in:
|
||||
|
||||
- [Traccar documentation](https://www.traccar.org/documentation/)
|
||||
- [Traccar forums](https://www.traccar.org/forums/)
|
||||
- Relevant repositories (see below)
|
||||
- [Google Search](https://www.google.com/)
|
||||
|
||||
GitHub issues should be used ONLY for feature requests, code discussions and bug reports. For general discussions please use [Traccar forums](https://www.traccar.org/forums/).
|
||||
|
||||
There are multiple Traccar projects. If you create a new issue you MUST do it in the relevant repository:
|
||||
|
||||
- [Traccar Server](https://github.com/tananaev/traccar/issues)
|
||||
- [Traccar Web Interface](https://github.com/tananaev/traccar-web/issues)
|
||||
- [Traccar Client for Android](https://github.com/tananaev/traccar-client-android/issues)
|
||||
- [Traccar Client for iOS](https://github.com/tananaev/traccar-client-ios/issues)
|
||||
- [Traccar Manager for Android](https://github.com/tananaev/traccar-manager-android/issues)
|
||||
- [Traccar Manager for iOS](https://github.com/tananaev/traccar-manager-ios/issues)
|
||||
|
||||
If you are not sure where your issue belongs to, please use Traccar Server main repository.
|
||||
|
||||
If you want to discuss something that applies to both Android and iOS apps, please use Android repository.
|
||||
|
||||
## Bug Reports
|
||||
|
||||
Before creating a bug report make sure that you have tested latest official release with default configuration.
|
||||
|
||||
Only create a bug report issue if you are confident that there is a problem in Traccar software.
|
||||
|
||||
Provide as much details as possible, including log fragments, operating system and hardware information.
|
||||
|
||||
## Feature Requests
|
||||
|
||||
Before creating a feature request make sure that the feature or modification that you are requesting is not yet implemented.
|
||||
|
||||
Search reposiroty to ensure that there is no existing issues for your request. If there is, add a new comment on that issue.
|
||||
|
||||
Provide as much details as possible, including use case for your feature and any benefits that you can think of.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
If you want to contribute some code to Traccar, it is recommended to discuss your solution with maintainers before starting any work.
|
||||
|
||||
Any code that you want to contribute must be of high quality and follow existing code patterns and styles.
|
||||
@@ -0,0 +1 @@
|
||||
custom: https://www.traccar.org/donate/
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,4 +1,7 @@
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
.idea/
|
||||
local.properties
|
||||
*.iml
|
||||
build/
|
||||
.DS_Store
|
||||
google-services.json
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
traccar-client-android
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="DEFAULT_COMPILER" value="Javac" />
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
</project>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/traccar-client" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/traccar-client/traccar-client.iml" filepath="$PROJECT_DIR$/traccar-client/traccar-client.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/traccar-client-android.iml" filepath="$PROJECT_DIR$/traccar-client-android.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<state>
|
||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||
</state>
|
||||
</component>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,27 @@
|
||||
# [Traccar Client for Android](https://www.traccar.org/client)
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=org.traccar.client) [](https://f-droid.org/repository/browse/?fdid=org.traccar.client)
|
||||
|
||||
## Overview
|
||||
|
||||
Traccar Client is an Android GPS tracking application. It can work with Traccar open source server software.
|
||||
|
||||
## Team
|
||||
|
||||
- Anton Tananaev ([anton@traccar.org](mailto:anton@traccar.org))
|
||||
|
||||
## License
|
||||
|
||||
Apache License, Version 2.0
|
||||
|
||||
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.
|
||||
@@ -1,27 +0,0 @@
|
||||
traccar-client
|
||||
|
||||
Web page - http://www.traccar.org
|
||||
|
||||
Author - Anton Tananaev (anton.tananaev@gmail.com)
|
||||
|
||||
SUMMARY:
|
||||
|
||||
Traccar Client is Android GPS tracking application. In can work with
|
||||
Traccar open source server software.
|
||||
|
||||
LICENSE:
|
||||
|
||||
Apache License, Version 2.0
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'org.traccar.client'
|
||||
buildConfigField 'boolean', 'HIDDEN_APP', 'false'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 33
|
||||
versionCode 81
|
||||
versionName '7.0'
|
||||
multiDexEnabled true
|
||||
}
|
||||
namespace 'org.traccar.client'
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += ['META-INF/DEPENDENCIES']
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions 'default'
|
||||
productFlavors {
|
||||
regular {
|
||||
isDefault = true
|
||||
}
|
||||
google
|
||||
hidden {
|
||||
applicationId 'org.traccar.client.hidden'
|
||||
buildConfigField 'boolean', 'HIDDEN_APP', 'true'
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
lint {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.material:material:1.6.1' // wait for preference-ktx update
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.github.judemanutd:autostarter:1.1.0'
|
||||
implementation('dev.doubledot.doki:library:0.0.1@aar') {
|
||||
transitive = true
|
||||
}
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||
googleImplementation platform('com.google.firebase:firebase-bom:28.2.1')
|
||||
googleImplementation 'com.google.firebase:firebase-core'
|
||||
googleImplementation 'com.google.firebase:firebase-analytics'
|
||||
googleImplementation 'com.google.firebase:firebase-crashlytics'
|
||||
googleImplementation 'com.google.android.gms:play-services-location:21.0.1'
|
||||
googleImplementation 'com.google.android.play:core:1.10.3'
|
||||
}
|
||||
|
||||
if (getGradle().getStartParameter().getTaskRequests().toString().contains('Google')) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
task copyJson(type: Copy) {
|
||||
from '../../environment/google-services'
|
||||
into '.'
|
||||
include 'traccar-client-android.json'
|
||||
rename('traccar-client-android.json', 'google-services.json')
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
preBuild.dependsOn copyJson
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:name="org.traccar.client.GoogleMainApplication"
|
||||
tools:replace="android:name"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2017 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.play.core.review.ReviewInfo
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
import com.google.android.play.core.tasks.Task
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
|
||||
class GoogleMainApplication : MainApplication() {
|
||||
|
||||
private var firebaseAnalytics: FirebaseAnalytics? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
firebaseAnalytics = FirebaseAnalytics.getInstance(this)
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(TrackingService.ACTION_STARTED)
|
||||
filter.addAction(TrackingService.ACTION_STOPPED)
|
||||
registerReceiver(ServiceReceiver(), filter)
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
override fun handleRatingFlow(activity: Activity) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val ratingShown = preferences.getBoolean(KEY_RATING_SHOWN, false)
|
||||
val totalDuration = preferences.getLong(ServiceReceiver.KEY_DURATION, 0)
|
||||
if (!ratingShown && totalDuration > RATING_THRESHOLD) {
|
||||
val reviewManager = ReviewManagerFactory.create(activity)
|
||||
reviewManager.requestReviewFlow().addOnCompleteListener { infoTask: Task<ReviewInfo?> ->
|
||||
if (infoTask.isSuccessful) {
|
||||
val flow = reviewManager.launchReviewFlow(activity, infoTask.result)
|
||||
flow.addOnCompleteListener { preferences.edit().putBoolean(KEY_RATING_SHOWN, true).apply() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_RATING_SHOWN = "ratingShown"
|
||||
private const val RATING_THRESHOLD = -24 * 60 * 60 * 1000L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2019 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import com.google.android.gms.location.LocationCallback
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
import com.google.android.gms.location.LocationResult
|
||||
import com.google.android.gms.location.LocationServices
|
||||
|
||||
class GooglePositionProvider(context: Context, listener: PositionListener) : PositionProvider(context, listener) {
|
||||
|
||||
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
|
||||
|
||||
@Suppress("DEPRECATION", "MissingPermission")
|
||||
override fun startUpdates() {
|
||||
val locationRequest = LocationRequest()
|
||||
locationRequest.priority = getPriority(preferences.getString(MainFragment.KEY_ACCURACY,"medium"))
|
||||
locationRequest.interval = if (distance > 0 || angle > 0) MINIMUM_INTERVAL else interval
|
||||
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
|
||||
}
|
||||
|
||||
override fun stopUpdates() {
|
||||
fusedLocationClient.removeLocationUpdates(locationCallback)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun requestSingleLocation() {
|
||||
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
|
||||
if (location != null) {
|
||||
listener.onPositionUpdate(Position(deviceId, location, getBatteryStatus(context)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val locationCallback: LocationCallback = object : LocationCallback() {
|
||||
override fun onLocationResult(locationResult: LocationResult) {
|
||||
for (location in locationResult.locations) {
|
||||
processLocation(location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPriority(accuracy: String?): Int {
|
||||
return when (accuracy) {
|
||||
"high" -> LocationRequest.PRIORITY_HIGH_ACCURACY
|
||||
"low" -> LocationRequest.PRIORITY_LOW_POWER
|
||||
else -> LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.Context
|
||||
import org.traccar.client.PositionProvider.PositionListener
|
||||
|
||||
object PositionProviderFactory {
|
||||
|
||||
fun create(context: Context, listener: PositionListener): PositionProvider {
|
||||
return GooglePositionProvider(context, listener)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2020 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
class ServiceReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (TrackingService.ACTION_STARTED == intent.action) {
|
||||
startTime = System.currentTimeMillis()
|
||||
} else if (startTime > 0) {
|
||||
updateTime(context, System.currentTimeMillis() - startTime)
|
||||
startTime = 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTime(context: Context, duration: Long) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val totalDuration = preferences.getLong(KEY_DURATION, 0)
|
||||
preferences.edit().putLong(KEY_DURATION, totalDuration + duration).apply()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_DURATION = "serviceTime"
|
||||
private var startTime: Long = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
||||
|
||||
<application
|
||||
android:label="@string/hidden_app_name"
|
||||
tools:replace="android:label">
|
||||
|
||||
<activity
|
||||
android:name="org.traccar.client.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" android:host="www.traccar.org" android:pathPrefix="/hidden" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name="org.traccar.client.DialLaunchReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.Context
|
||||
import org.traccar.client.PositionProvider.PositionListener
|
||||
|
||||
object PositionProviderFactory {
|
||||
|
||||
fun create(context: Context, listener: PositionListener): PositionProvider {
|
||||
return AndroidPositionProvider(context, listener)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333">
|
||||
<group android:scaleX="0.58"
|
||||
android:scaleY="0.58"
|
||||
android:translateX="5.04"
|
||||
android:translateY="5.04">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM17.25,12c0,0.23 -0.02,0.46 -0.05,0.68l1.48,1.16c0.13,0.11 0.17,0.3 0.08,0.45l-1.4,2.42c-0.09,0.15 -0.27,0.21 -0.43,0.15l-1.74,-0.7c-0.36,0.28 -0.76,0.51 -1.18,0.69l-0.26,1.85c-0.03,0.17 -0.18,0.3 -0.35,0.3h-2.8c-0.17,0 -0.32,-0.13 -0.35,-0.29l-0.26,-1.85c-0.43,-0.18 -0.82,-0.41 -1.18,-0.69l-1.74,0.7c-0.16,0.06 -0.34,0 -0.43,-0.15l-1.4,-2.42c-0.09,-0.15 -0.05,-0.34 0.08,-0.45l1.48,-1.16c-0.03,-0.23 -0.05,-0.46 -0.05,-0.69 0,-0.23 0.02,-0.46 0.05,-0.68l-1.48,-1.16c-0.13,-0.11 -0.17,-0.3 -0.08,-0.45l1.4,-2.42c0.09,-0.15 0.27,-0.21 0.43,-0.15l1.74,0.7c0.36,-0.28 0.76,-0.51 1.18,-0.69l0.26,-1.85c0.03,-0.17 0.18,-0.3 0.35,-0.3h2.8c0.17,0 0.32,0.13 0.35,0.29l0.26,1.85c0.43,0.18 0.82,0.41 1.18,0.69l1.74,-0.7c0.16,-0.06 0.34,0 0.43,0.15l1.4,2.42c0.09,0.15 0.05,0.34 -0.08,0.45l-1.48,1.16c0.03,0.23 0.05,0.46 0.05,0.69z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
После Ширина: | Высота: | Размер: 3.5 KiB |
|
После Ширина: | Высота: | Размер: 2.2 KiB |
|
После Ширина: | Высота: | Размер: 5.1 KiB |
|
После Ширина: | Высота: | Размер: 8.2 KiB |
|
После Ширина: | Высота: | Размер: 12 KiB |
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
|
||||
<uses-permission android:name="oppo.permission.OPPO_COMPONENT_SAFE" />
|
||||
<uses-permission android:name="com.huawei.permission.external_app_settings.USE_COMPONENT" />
|
||||
|
||||
<uses-feature android:name="android.hardware.location.network" />
|
||||
<uses-feature android:name="android.hardware.location.gps" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".MainApplication"
|
||||
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
||||
|
||||
<meta-data
|
||||
android:name="google_analytics_adid_collection_enabled"
|
||||
android:value="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask" />
|
||||
|
||||
<activity-alias
|
||||
android:name=".Launcher"
|
||||
android:exported="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name=".StatusActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ShortcutActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/menu_shortcuts">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".TrackingService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location" />
|
||||
|
||||
<service
|
||||
android:name=".TrackingService$HideNotificationService"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".AutostartReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
После Ширина: | Высота: | Размер: 21 KiB |
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2019 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
|
||||
class AndroidPositionProvider(context: Context, listener: PositionListener) : PositionProvider(context, listener), LocationListener {
|
||||
|
||||
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private val provider = getProvider(preferences.getString(MainFragment.KEY_ACCURACY, "medium"))
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun startUpdates() {
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
provider, if (distance > 0 || angle > 0) MINIMUM_INTERVAL else interval, 0f, this)
|
||||
} catch (e: RuntimeException) {
|
||||
listener.onPositionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopUpdates() {
|
||||
locationManager.removeUpdates(this)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "MissingPermission")
|
||||
override fun requestSingleLocation() {
|
||||
try {
|
||||
val location = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
|
||||
if (location != null) {
|
||||
listener.onPositionUpdate(Position(deviceId, location, getBatteryStatus(context)))
|
||||
} else {
|
||||
locationManager.requestSingleUpdate(provider, object : LocationListener {
|
||||
override fun onLocationChanged(location: Location) {
|
||||
listener.onPositionUpdate(Position(deviceId, location, getBatteryStatus(context)))
|
||||
}
|
||||
|
||||
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
|
||||
override fun onProviderEnabled(provider: String) {}
|
||||
override fun onProviderDisabled(provider: String) {}
|
||||
}, Looper.myLooper())
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
listener.onPositionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
processLocation(location)
|
||||
}
|
||||
|
||||
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
|
||||
override fun onProviderEnabled(provider: String) {}
|
||||
override fun onProviderDisabled(provider: String) {}
|
||||
|
||||
private fun getProvider(accuracy: String?): String {
|
||||
return when (accuracy) {
|
||||
"high" -> LocationManager.GPS_PROVIDER
|
||||
"low" -> LocationManager.PASSIVE_PROVIDER
|
||||
else -> LocationManager.NETWORK_PROVIDER
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
class AutostartReceiver : WakefulBroadcastReceiver() {
|
||||
|
||||
@Suppress("UnsafeProtectedBroadcastReceiver")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (sharedPreferences.getBoolean(MainFragment.KEY_STATUS, false)) {
|
||||
startWakefulForegroundService(context, Intent(context, TrackingService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.judemanutd.autostarter.AutoStartPermissionHelper
|
||||
|
||||
class BatteryOptimizationHelper {
|
||||
|
||||
private fun showDialog(context: Context, onSuccess: () -> Unit) {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder.setMessage(context.getString(R.string.request_exception))
|
||||
builder.setPositiveButton(android.R.string.ok) { _, _ -> onSuccess() }
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
builder.show()
|
||||
}
|
||||
|
||||
private fun requestVendorException(context: Context) {
|
||||
val vendorIntentList = listOf(
|
||||
Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
|
||||
Intent().setComponent(ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")),
|
||||
Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
|
||||
Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
|
||||
Intent().setComponent(ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
|
||||
Intent().setComponent(ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")),
|
||||
Intent().setComponent(ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")),
|
||||
Intent().setComponent(ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity")),
|
||||
)
|
||||
for (vendorIntent in vendorIntentList) {
|
||||
if (context.packageManager.resolveActivity(vendorIntent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
|
||||
try {
|
||||
context.startActivity(vendorIntent)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestException(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (!sharedPreferences.getBoolean(KEY_EXCEPTION_REQUESTED, false)) {
|
||||
sharedPreferences.edit().putBoolean(KEY_EXCEPTION_REQUESTED, true).apply()
|
||||
val powerManager = context.getSystemService(PowerManager::class.java)
|
||||
if (!powerManager.isIgnoringBatteryOptimizations(context.packageName)) {
|
||||
showDialog(context) {
|
||||
try {
|
||||
context.startActivity(Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
requestVendorException(context)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
} else if (!sharedPreferences.getBoolean(KEY_AUTOSTART_REQUESTED, false)) {
|
||||
sharedPreferences.edit().putBoolean(KEY_AUTOSTART_REQUESTED, true).apply()
|
||||
try {
|
||||
if (AutoStartPermissionHelper.getInstance().getAutoStartPermission(context)) {
|
||||
return true
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_EXCEPTION_REQUESTED = "exceptionRequested"
|
||||
private const val KEY_AUTOSTART_REQUESTED = "autostartRequested"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
data class BatteryStatus(
|
||||
val level: Double = 0.0,
|
||||
val charging: Boolean = false,
|
||||
)
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "StaticFieldLeak")
|
||||
package org.traccar.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.SQLException
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import android.os.AsyncTask
|
||||
import java.sql.Date
|
||||
|
||||
class DatabaseHelper(context: Context?) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||
|
||||
interface DatabaseHandler<T> {
|
||||
fun onComplete(success: Boolean, result: T)
|
||||
}
|
||||
|
||||
private abstract class DatabaseAsyncTask<T>(val handler: DatabaseHandler<T?>) : AsyncTask<Unit, Unit, T?>() {
|
||||
|
||||
private var error: RuntimeException? = null
|
||||
|
||||
override fun doInBackground(vararg params: Unit): T? {
|
||||
return try {
|
||||
executeMethod()
|
||||
} catch (error: RuntimeException) {
|
||||
this.error = error
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun executeMethod(): T
|
||||
|
||||
override fun onPostExecute(result: T?) {
|
||||
handler.onComplete(error == null, result)
|
||||
}
|
||||
}
|
||||
|
||||
private val db: SQLiteDatabase = writableDatabase
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE position (" +
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
"deviceId TEXT," +
|
||||
"time INTEGER," +
|
||||
"latitude REAL," +
|
||||
"longitude REAL," +
|
||||
"altitude REAL," +
|
||||
"speed REAL," +
|
||||
"course REAL," +
|
||||
"accuracy REAL," +
|
||||
"battery REAL," +
|
||||
"charging INTEGER," +
|
||||
"mock INTEGER)"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("DROP TABLE IF EXISTS position;")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("DROP TABLE IF EXISTS position;")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
fun insertPosition(position: Position) {
|
||||
val values = ContentValues()
|
||||
values.put("deviceId", position.deviceId)
|
||||
values.put("time", position.time.time)
|
||||
values.put("latitude", position.latitude)
|
||||
values.put("longitude", position.longitude)
|
||||
values.put("altitude", position.altitude)
|
||||
values.put("speed", position.speed)
|
||||
values.put("course", position.course)
|
||||
values.put("accuracy", position.accuracy)
|
||||
values.put("battery", position.battery)
|
||||
values.put("charging", if (position.charging) 1 else 0)
|
||||
values.put("mock", if (position.mock) 1 else 0)
|
||||
db.insertOrThrow("position", null, values)
|
||||
}
|
||||
|
||||
fun insertPositionAsync(position: Position, handler: DatabaseHandler<Unit?>) {
|
||||
object : DatabaseAsyncTask<Unit>(handler) {
|
||||
override fun executeMethod() {
|
||||
insertPosition(position)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
fun selectPosition(): Position? {
|
||||
db.rawQuery("SELECT * FROM position ORDER BY id LIMIT 1", null).use { cursor ->
|
||||
if (cursor.count > 0) {
|
||||
cursor.moveToFirst()
|
||||
return Position(
|
||||
id = cursor.getLong(cursor.getColumnIndex("id")),
|
||||
deviceId = cursor.getString(cursor.getColumnIndex("deviceId")),
|
||||
time = Date(cursor.getLong(cursor.getColumnIndex("time"))),
|
||||
latitude = cursor.getDouble(cursor.getColumnIndex("latitude")),
|
||||
longitude = cursor.getDouble(cursor.getColumnIndex("longitude")),
|
||||
altitude = cursor.getDouble(cursor.getColumnIndex("altitude")),
|
||||
speed = cursor.getDouble(cursor.getColumnIndex("speed")),
|
||||
course = cursor.getDouble(cursor.getColumnIndex("course")),
|
||||
accuracy = cursor.getDouble(cursor.getColumnIndex("accuracy")),
|
||||
battery = cursor.getDouble(cursor.getColumnIndex("battery")),
|
||||
charging = cursor.getInt(cursor.getColumnIndex("charging")) > 0,
|
||||
mock = cursor.getInt(cursor.getColumnIndex("mock")) > 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun selectPositionAsync(handler: DatabaseHandler<Position?>) {
|
||||
object : DatabaseAsyncTask<Position?>(handler) {
|
||||
override fun executeMethod(): Position? {
|
||||
return selectPosition()
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
fun deletePosition(id: Long) {
|
||||
if (db.delete("position", "id = ?", arrayOf(id.toString())) != 1) {
|
||||
throw SQLException()
|
||||
}
|
||||
}
|
||||
|
||||
fun deletePositionAsync(id: Long, handler: DatabaseHandler<Unit?>) {
|
||||
object : DatabaseAsyncTask<Unit>(handler) {
|
||||
override fun executeMethod() {
|
||||
deletePosition(id)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DATABASE_VERSION = 4
|
||||
const val DATABASE_NAME = "traccar.db"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2015 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class DialLaunchReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
|
||||
if (phoneNumber == LAUNCHER_NUMBER) {
|
||||
resultData = null
|
||||
val appIntent = Intent(context, MainActivity::class.java)
|
||||
appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(appIntent)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LAUNCHER_NUMBER = "8722227" // TRACCAR
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2017 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.main)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import android.annotation.TargetApi
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Notification
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.app.Activity
|
||||
|
||||
open class MainApplication : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
System.setProperty("http.keepAliveDuration", (30 * 60 * 1000).toString())
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
registerChannel()
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private fun registerChannel() {
|
||||
val channel = NotificationChannel(
|
||||
PRIMARY_CHANNEL, getString(R.string.channel_default), NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
channel.lightColor = Color.GREEN
|
||||
channel.lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
open fun handleRatingFlow(activity: Activity) {}
|
||||
|
||||
companion object {
|
||||
const val PRIMARY_CHANNEL = "default"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright 2012 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.webkit.URLUtil
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.EditTextPreferenceDialogFragmentCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.TwoStatePreference
|
||||
import dev.doubledot.doki.ui.DokiActivity
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {
|
||||
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private lateinit var alarmManager: AlarmManager
|
||||
private lateinit var alarmIntent: PendingIntent
|
||||
private var requestingPermissions: Boolean = false
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
if (BuildConfig.HIDDEN_APP && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
removeLauncherIcon()
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
initPreferences()
|
||||
|
||||
findPreference<Preference>(KEY_DEVICE)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
newValue != null && newValue != ""
|
||||
}
|
||||
findPreference<Preference>(KEY_URL)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
newValue != null && validateServerURL(newValue.toString())
|
||||
}
|
||||
findPreference<Preference>(KEY_INTERVAL)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
newValue != null && (newValue as String).toInt() > 0
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.w(TAG, e)
|
||||
false
|
||||
}
|
||||
}
|
||||
val numberValidationListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
newValue != null && (newValue as String).toInt() >= 0
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.w(TAG, e)
|
||||
false
|
||||
}
|
||||
}
|
||||
findPreference<Preference>(KEY_DISTANCE)?.onPreferenceChangeListener = numberValidationListener
|
||||
findPreference<Preference>(KEY_ANGLE)?.onPreferenceChangeListener = numberValidationListener
|
||||
|
||||
alarmManager = requireActivity().getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
val originalIntent = Intent(activity, AutostartReceiver::class.java)
|
||||
originalIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
||||
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
alarmIntent = PendingIntent.getBroadcast(activity, 0, originalIntent, flags)
|
||||
|
||||
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
||||
startTrackingService(checkPermission = true, initialPermission = false)
|
||||
}
|
||||
}
|
||||
|
||||
class NumericEditTextPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
val editText = view.findViewById<EditText>(android.R.id.edit)
|
||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
super.onBindDialogView(view)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(key: String?): NumericEditTextPreferenceDialogFragment {
|
||||
val fragment = NumericEditTextPreferenceDialogFragment()
|
||||
val bundle = Bundle()
|
||||
bundle.putString(ARG_KEY, key)
|
||||
fragment.arguments = bundle
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||
if (listOf(KEY_INTERVAL, KEY_DISTANCE, KEY_ANGLE).contains(preference.key)) {
|
||||
val f: EditTextPreferenceDialogFragmentCompat =
|
||||
NumericEditTextPreferenceDialogFragment.newInstance(preference.key)
|
||||
f.setTargetFragment(this, 0)
|
||||
f.show(requireFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG")
|
||||
} else {
|
||||
super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeLauncherIcon() {
|
||||
val className = MainActivity::class.java.canonicalName!!.replace(".MainActivity", ".Launcher")
|
||||
val componentName = ComponentName(requireActivity().packageName, className)
|
||||
val packageManager = requireActivity().packageManager
|
||||
if (packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
|
||||
packageManager.setComponentEnabledSetting(
|
||||
componentName,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
val builder = AlertDialog.Builder(requireActivity())
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
builder.setMessage(getString(R.string.hidden_alert))
|
||||
builder.setPositiveButton(android.R.string.ok, null)
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (requestingPermissions) {
|
||||
requestingPermissions = BatteryOptimizationHelper().requestException(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
private fun setPreferencesEnabled(enabled: Boolean) {
|
||||
findPreference<Preference>(KEY_DEVICE)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_URL)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_INTERVAL)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_DISTANCE)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_ANGLE)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_ACCURACY)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_BUFFER)?.isEnabled = enabled
|
||||
findPreference<Preference>(KEY_WAKELOCK)?.isEnabled = enabled
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
if (key == KEY_STATUS) {
|
||||
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
||||
startTrackingService(checkPermission = true, initialPermission = false)
|
||||
} else {
|
||||
stopTrackingService()
|
||||
}
|
||||
(requireActivity().application as MainApplication).handleRatingFlow(requireActivity())
|
||||
} else if (key == KEY_DEVICE) {
|
||||
findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.main, menu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.status) {
|
||||
startActivity(Intent(activity, StatusActivity::class.java))
|
||||
return true
|
||||
} else if (item.itemId == R.id.info) {
|
||||
DokiActivity.start(requireContext())
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun initPreferences() {
|
||||
PreferenceManager.setDefaultValues(requireActivity(), R.xml.preferences, false)
|
||||
if (!sharedPreferences.contains(KEY_DEVICE)) {
|
||||
val id = (Random().nextInt(900000) + 100000).toString()
|
||||
sharedPreferences.edit().putString(KEY_DEVICE, id).apply()
|
||||
findPreference<EditTextPreference>(KEY_DEVICE)?.text = id
|
||||
}
|
||||
findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
|
||||
}
|
||||
|
||||
private fun showBackgroundLocationDialog(context: Context, onSuccess: () -> Unit) {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
val option = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
context.packageManager.backgroundPermissionOptionLabel
|
||||
} else {
|
||||
context.getString(R.string.request_background_option)
|
||||
}
|
||||
builder.setMessage(context.getString(R.string.request_background, option))
|
||||
builder.setPositiveButton(android.R.string.ok) { _, _ -> onSuccess() }
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
builder.show()
|
||||
}
|
||||
|
||||
private fun startTrackingService(checkPermission: Boolean, initialPermission: Boolean) {
|
||||
var permission = initialPermission
|
||||
if (checkPermission) {
|
||||
val requiredPermissions: MutableSet<String> = HashSet()
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
requiredPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
permission = requiredPermissions.isEmpty()
|
||||
if (!permission) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(requiredPermissions.toTypedArray(), PERMISSIONS_REQUEST_LOCATION)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (permission) {
|
||||
setPreferencesEnabled(false)
|
||||
ContextCompat.startForegroundService(requireContext(), Intent(activity, TrackingService::class.java))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
alarmManager.setInexactRepeating(
|
||||
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||
ALARM_MANAGER_INTERVAL.toLong(), ALARM_MANAGER_INTERVAL.toLong(), alarmIntent
|
||||
)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestingPermissions = true
|
||||
showBackgroundLocationDialog(requireContext()) {
|
||||
requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), PERMISSIONS_REQUEST_BACKGROUND_LOCATION)
|
||||
}
|
||||
} else {
|
||||
requestingPermissions = BatteryOptimizationHelper().requestException(requireContext())
|
||||
}
|
||||
} else {
|
||||
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply()
|
||||
val preference = findPreference<TwoStatePreference>(KEY_STATUS)
|
||||
preference?.isChecked = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopTrackingService() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
alarmManager.cancel(alarmIntent)
|
||||
}
|
||||
requireActivity().stopService(Intent(activity, TrackingService::class.java))
|
||||
setPreferencesEnabled(true)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
|
||||
var granted = true
|
||||
for (result in grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||
granted = false
|
||||
break
|
||||
}
|
||||
}
|
||||
startTrackingService(false, granted)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateServerURL(userUrl: String): Boolean {
|
||||
val port = Uri.parse(userUrl).port
|
||||
if (
|
||||
URLUtil.isValidUrl(userUrl) &&
|
||||
(port == -1 || port in 1..65535) &&
|
||||
(URLUtil.isHttpUrl(userUrl) || URLUtil.isHttpsUrl(userUrl))
|
||||
) {
|
||||
return true
|
||||
}
|
||||
Toast.makeText(activity, R.string.error_msg_invalid_url, Toast.LENGTH_LONG).show()
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = MainFragment::class.java.simpleName
|
||||
private const val ALARM_MANAGER_INTERVAL = 15000
|
||||
const val KEY_DEVICE = "id"
|
||||
const val KEY_URL = "url"
|
||||
const val KEY_INTERVAL = "interval"
|
||||
const val KEY_DISTANCE = "distance"
|
||||
const val KEY_ANGLE = "angle"
|
||||
const val KEY_ACCURACY = "accuracy"
|
||||
const val KEY_STATUS = "status"
|
||||
const val KEY_BUFFER = "buffer"
|
||||
const val KEY_WAKELOCK = "wakelock"
|
||||
private const val PERMISSIONS_REQUEST_LOCATION = 2
|
||||
private const val PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 3
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 - 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.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
import android.util.Log
|
||||
|
||||
class NetworkManager(private val context: Context, private val handler: NetworkHandler?) : BroadcastReceiver() {
|
||||
|
||||
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
interface NetworkHandler {
|
||||
fun onNetworkUpdate(isOnline: Boolean)
|
||||
}
|
||||
|
||||
val isOnline: Boolean
|
||||
get() {
|
||||
val activeNetwork = connectivityManager.activeNetworkInfo
|
||||
return activeNetwork != null && activeNetwork.isConnectedOrConnecting
|
||||
}
|
||||
|
||||
fun start() {
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
|
||||
context.registerReceiver(this, filter)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION && handler != null) {
|
||||
val isOnline = isOnline
|
||||
Log.i(TAG, "network " + if (isOnline) "on" else "off")
|
||||
handler.onNetworkUpdate(isOnline)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = NetworkManager::class.java.simpleName
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import java.util.*
|
||||
|
||||
data class Position(
|
||||
val id: Long = 0,
|
||||
val deviceId: String,
|
||||
val time: Date,
|
||||
val latitude: Double = 0.0,
|
||||
val longitude: Double = 0.0,
|
||||
val altitude: Double = 0.0,
|
||||
val speed: Double = 0.0,
|
||||
val course: Double = 0.0,
|
||||
val accuracy: Double = 0.0,
|
||||
val battery: Double = 0.0,
|
||||
val charging: Boolean = false,
|
||||
val mock: Boolean = false,
|
||||
) {
|
||||
|
||||
constructor(deviceId: String, location: Location, battery: BatteryStatus) : this(
|
||||
deviceId = deviceId,
|
||||
time = Date(location.time.correctRollover()),
|
||||
latitude = location.latitude,
|
||||
longitude = location.longitude,
|
||||
altitude = location.altitude,
|
||||
speed = location.speed * 1.943844, // speed in knots
|
||||
course = location.bearing.toDouble(),
|
||||
accuracy = if (location.provider != null && location.provider != LocationManager.GPS_PROVIDER) {
|
||||
location.accuracy.toDouble()
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
battery = battery.level,
|
||||
charging = battery.charging,
|
||||
mock = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
location.isMock
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
@Suppress("DEPRECATION")
|
||||
location.isFromMockProvider
|
||||
} else {
|
||||
false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private const val rolloverDate = 1554508800000L // April 6, 2019
|
||||
private const val rolloverOffset = 619315200000L // 1024 weeks
|
||||
|
||||
private fun Long.correctRollover(): Long {
|
||||
return if (this < rolloverDate) this + rolloverOffset else this
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.SharedPreferences
|
||||
import android.location.Location
|
||||
import android.os.BatteryManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import kotlin.math.abs
|
||||
|
||||
abstract class PositionProvider(
|
||||
protected val context: Context,
|
||||
protected val listener: PositionListener,
|
||||
) {
|
||||
|
||||
interface PositionListener {
|
||||
fun onPositionUpdate(position: Position)
|
||||
fun onPositionError(error: Throwable)
|
||||
}
|
||||
|
||||
protected var preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
protected var deviceId = preferences.getString(MainFragment.KEY_DEVICE, "undefined")!!
|
||||
protected var interval = preferences.getString(MainFragment.KEY_INTERVAL, "600")!!.toLong() * 1000
|
||||
protected var distance: Double = preferences.getString(MainFragment.KEY_DISTANCE, "0")!!.toInt().toDouble()
|
||||
protected var angle: Double = preferences.getString(MainFragment.KEY_ANGLE, "0")!!.toInt().toDouble()
|
||||
private var lastLocation: Location? = null
|
||||
|
||||
abstract fun startUpdates()
|
||||
abstract fun stopUpdates()
|
||||
abstract fun requestSingleLocation()
|
||||
|
||||
protected fun processLocation(location: Location?) {
|
||||
val lastLocation = this.lastLocation
|
||||
if (location != null &&
|
||||
(lastLocation == null || location.time - lastLocation!!.time >= interval || distance > 0
|
||||
&& location.distanceTo(lastLocation) >= distance || angle > 0
|
||||
&& abs(location.bearing - lastLocation!!.bearing) >= angle)
|
||||
) {
|
||||
Log.i(TAG, "location new")
|
||||
this.lastLocation = location
|
||||
listener.onPositionUpdate(Position(deviceId, location, getBatteryStatus(context)))
|
||||
} else {
|
||||
Log.i(TAG, if (location != null) "location ignored" else "location nil")
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getBatteryStatus(context: Context): BatteryStatus {
|
||||
val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
if (batteryIntent != null) {
|
||||
val level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
|
||||
val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1)
|
||||
val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
|
||||
return BatteryStatus(
|
||||
level = level * 100.0 / scale,
|
||||
charging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL,
|
||||
)
|
||||
}
|
||||
return BatteryStatus()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = PositionProvider::class.java.simpleName
|
||||
const val MINIMUM_INTERVAL: Long = 1000
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
object ProtocolFormatter {
|
||||
|
||||
fun formatRequest(url: String, position: Position, alarm: String? = null): String {
|
||||
val serverUrl = Uri.parse(url)
|
||||
val builder = serverUrl.buildUpon()
|
||||
.appendQueryParameter("id", position.deviceId)
|
||||
.appendQueryParameter("timestamp", (position.time.time / 1000).toString())
|
||||
.appendQueryParameter("lat", position.latitude.toString())
|
||||
.appendQueryParameter("lon", position.longitude.toString())
|
||||
.appendQueryParameter("speed", position.speed.toString())
|
||||
.appendQueryParameter("bearing", position.course.toString())
|
||||
.appendQueryParameter("altitude", position.altitude.toString())
|
||||
.appendQueryParameter("accuracy", position.accuracy.toString())
|
||||
.appendQueryParameter("batt", position.battery.toString())
|
||||
if (position.charging) {
|
||||
builder.appendQueryParameter("charge", position.charging.toString())
|
||||
}
|
||||
if (position.mock) {
|
||||
builder.appendQueryParameter("mock", position.mock.toString())
|
||||
}
|
||||
if (alarm != null) {
|
||||
builder.appendQueryParameter("alarm", alarm)
|
||||
}
|
||||
return builder.build().toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2015 - 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.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
package org.traccar.client
|
||||
|
||||
import android.os.AsyncTask
|
||||
import android.util.Log
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
object RequestManager {
|
||||
|
||||
private const val TIMEOUT = 15 * 1000
|
||||
|
||||
fun sendRequest(request: String?): Boolean {
|
||||
var inputStream: InputStream? = null
|
||||
return try {
|
||||
val url = URL(request)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.readTimeout = TIMEOUT
|
||||
connection.connectTimeout = TIMEOUT
|
||||
connection.requestMethod = "POST"
|
||||
connection.connect()
|
||||
inputStream = connection.inputStream
|
||||
while (inputStream.read() != -1) {}
|
||||
true
|
||||
} catch (error: IOException) {
|
||||
false
|
||||
} finally {
|
||||
try {
|
||||
inputStream?.close()
|
||||
} catch (secondError: IOException) {
|
||||
Log.w(RequestManager::class.java.simpleName, secondError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRequestAsync(request: String, handler: RequestHandler) {
|
||||
RequestAsyncTask(handler).execute(request)
|
||||
}
|
||||
|
||||
interface RequestHandler {
|
||||
fun onComplete(success: Boolean)
|
||||
}
|
||||
|
||||
private class RequestAsyncTask(private val handler: RequestHandler) : AsyncTask<String, Unit, Boolean>() {
|
||||
|
||||
override fun doInBackground(vararg request: String): Boolean {
|
||||
return sendRequest(request[0])
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: Boolean) {
|
||||
handler.onComplete(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ListView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.traccar.client.PositionProvider.PositionListener
|
||||
import org.traccar.client.ProtocolFormatter.formatRequest
|
||||
import org.traccar.client.RequestManager.RequestHandler
|
||||
import org.traccar.client.RequestManager.sendRequestAsync
|
||||
|
||||
class ShortcutActivity : AppCompatActivity() {
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (!executeAction(intent)) {
|
||||
setContentView(R.layout.list)
|
||||
val items = arrayOf(
|
||||
getString(R.string.shortcut_start),
|
||||
getString(R.string.shortcut_stop),
|
||||
getString(R.string.shortcut_sos)
|
||||
)
|
||||
val listView = findViewById<ListView>(android.R.id.list)
|
||||
listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
|
||||
listView.onItemClickListener = OnItemClickListener { _, _, position, _ ->
|
||||
when (position) {
|
||||
0 -> setShortcutResult(items[position], R.mipmap.ic_start, ACTION_START)
|
||||
1 -> setShortcutResult(items[position], R.mipmap.ic_stop, ACTION_STOP)
|
||||
2 -> setShortcutResult(items[position], R.mipmap.ic_sos, ACTION_SOS)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
executeAction(intent)
|
||||
}
|
||||
|
||||
private fun setShortcutResult(label: String, @DrawableRes iconResId: Int, action: String) {
|
||||
val intent = Intent(Intent.ACTION_DEFAULT, null, this, ShortcutActivity::class.java)
|
||||
intent.putExtra(EXTRA_ACTION, action)
|
||||
val shortcut = ShortcutInfoCompat.Builder(this, action)
|
||||
.setShortLabel(label)
|
||||
.setIcon(IconCompat.createWithResource(this, iconResId))
|
||||
.setIntent(intent)
|
||||
.build()
|
||||
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
|
||||
}
|
||||
|
||||
private fun sendAlarm() {
|
||||
PositionProviderFactory.create(this, object : PositionListener {
|
||||
override fun onPositionUpdate(position: Position) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this@ShortcutActivity)
|
||||
val request = formatRequest(preferences.getString(MainFragment.KEY_URL, null)!!, position, ALARM_SOS)
|
||||
sendRequestAsync(request, object : RequestHandler {
|
||||
override fun onComplete(success: Boolean) {
|
||||
if (success) {
|
||||
Toast.makeText(this@ShortcutActivity, R.string.status_send_success, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this@ShortcutActivity, R.string.status_send_fail, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onPositionError(error: Throwable) {
|
||||
Toast.makeText(this@ShortcutActivity, error.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}).requestSingleLocation()
|
||||
}
|
||||
|
||||
private fun executeAction(intent: Intent): Boolean {
|
||||
val action: String? = if (intent.hasExtra("shortcutAction")) {
|
||||
if (intent.getBooleanExtra("shortcutAction", false)) ACTION_START else ACTION_STOP
|
||||
} else {
|
||||
intent.getStringExtra(EXTRA_ACTION)
|
||||
}
|
||||
if (action != null) {
|
||||
when (action) {
|
||||
ACTION_START -> {
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(MainFragment.KEY_STATUS, true).apply()
|
||||
ContextCompat.startForegroundService(this, Intent(this, TrackingService::class.java))
|
||||
Toast.makeText(this, R.string.status_service_create, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
ACTION_STOP -> {
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(MainFragment.KEY_STATUS, false).apply()
|
||||
stopService(Intent(this, TrackingService::class.java))
|
||||
Toast.makeText(this, R.string.status_service_destroy, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
ACTION_SOS -> {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
sendAlarm()
|
||||
} else {
|
||||
Toast.makeText(this, R.string.status_send_fail, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
return action != null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_ACTION = "action"
|
||||
const val ACTION_START = "start"
|
||||
const val ACTION_STOP = "stop"
|
||||
const val ACTION_SOS = "sos"
|
||||
private const val ALARM_SOS = "sos"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.widget.ArrayAdapter
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.ListView
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
class StatusActivity : AppCompatActivity() {
|
||||
|
||||
private var adapter: ArrayAdapter<String>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.list)
|
||||
adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages)
|
||||
val listView = findViewById<ListView>(android.R.id.list)
|
||||
listView.adapter = adapter
|
||||
adapter?.let { adapters.add(it) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
adapters.remove(adapter)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val inflater = menuInflater
|
||||
inflater.inflate(R.menu.status, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.clear) {
|
||||
clearMessages()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LIMIT = 20
|
||||
private val messages = LinkedList<String>()
|
||||
private val adapters: MutableSet<ArrayAdapter<String>> = HashSet()
|
||||
|
||||
private fun notifyAdapters() {
|
||||
for (adapter in adapters) {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun addMessage(originalMessage: String) {
|
||||
var message = originalMessage
|
||||
val format = DateFormat.getTimeInstance(DateFormat.MEDIUM)
|
||||
message = format.format(Date()) + " - " + message
|
||||
messages.add(message)
|
||||
while (messages.size > LIMIT) {
|
||||
messages.removeFirst()
|
||||
}
|
||||
notifyAdapters()
|
||||
}
|
||||
|
||||
fun clearMessages() {
|
||||
messages.clear()
|
||||
notifyAdapters()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2015 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.Context
|
||||
import org.traccar.client.ProtocolFormatter.formatRequest
|
||||
import org.traccar.client.RequestManager.sendRequestAsync
|
||||
import org.traccar.client.PositionProvider.PositionListener
|
||||
import org.traccar.client.NetworkManager.NetworkHandler
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import org.traccar.client.DatabaseHelper.DatabaseHandler
|
||||
import org.traccar.client.RequestManager.RequestHandler
|
||||
|
||||
class TrackingController(private val context: Context) : PositionListener, NetworkHandler {
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
private val positionProvider = PositionProviderFactory.create(context, this)
|
||||
private val databaseHelper = DatabaseHelper(context)
|
||||
private val networkManager = NetworkManager(context, this)
|
||||
|
||||
private val url: String = preferences.getString(MainFragment.KEY_URL, context.getString(R.string.settings_url_default_value))!!
|
||||
private val buffer: Boolean = preferences.getBoolean(MainFragment.KEY_BUFFER, true)
|
||||
|
||||
private var isOnline = networkManager.isOnline
|
||||
private var isWaiting = false
|
||||
|
||||
fun start() {
|
||||
if (isOnline) {
|
||||
read()
|
||||
}
|
||||
try {
|
||||
positionProvider.startUpdates()
|
||||
} catch (e: SecurityException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
networkManager.start()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
networkManager.stop()
|
||||
try {
|
||||
positionProvider.stopUpdates()
|
||||
} catch (e: SecurityException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
override fun onPositionUpdate(position: Position) {
|
||||
StatusActivity.addMessage(context.getString(R.string.status_location_update))
|
||||
if (buffer) {
|
||||
write(position)
|
||||
} else {
|
||||
send(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPositionError(error: Throwable) {}
|
||||
override fun onNetworkUpdate(isOnline: Boolean) {
|
||||
val message = if (isOnline) R.string.status_network_online else R.string.status_network_offline
|
||||
StatusActivity.addMessage(context.getString(message))
|
||||
if (!this.isOnline && isOnline) {
|
||||
read()
|
||||
}
|
||||
this.isOnline = isOnline
|
||||
}
|
||||
|
||||
//
|
||||
// State transition examples:
|
||||
//
|
||||
// write -> read -> send -> delete -> read
|
||||
//
|
||||
// read -> send -> retry -> read -> send
|
||||
//
|
||||
|
||||
private fun log(action: String, position: Position?) {
|
||||
var formattedAction: String = action
|
||||
if (position != null) {
|
||||
formattedAction +=
|
||||
" (id:" + position.id +
|
||||
" time:" + position.time.time / 1000 +
|
||||
" lat:" + position.latitude +
|
||||
" lon:" + position.longitude + ")"
|
||||
}
|
||||
Log.d(TAG, formattedAction)
|
||||
}
|
||||
|
||||
private fun write(position: Position) {
|
||||
log("write", position)
|
||||
databaseHelper.insertPositionAsync(position, object : DatabaseHandler<Unit?> {
|
||||
override fun onComplete(success: Boolean, result: Unit?) {
|
||||
if (success) {
|
||||
if (isOnline && isWaiting) {
|
||||
read()
|
||||
isWaiting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun read() {
|
||||
log("read", null)
|
||||
databaseHelper.selectPositionAsync(object : DatabaseHandler<Position?> {
|
||||
override fun onComplete(success: Boolean, result: Position?) {
|
||||
if (success) {
|
||||
if (result != null) {
|
||||
if (result.deviceId == preferences.getString(MainFragment.KEY_DEVICE, null)) {
|
||||
send(result)
|
||||
} else {
|
||||
delete(result)
|
||||
}
|
||||
} else {
|
||||
isWaiting = true
|
||||
}
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun delete(position: Position) {
|
||||
log("delete", position)
|
||||
databaseHelper.deletePositionAsync(position.id, object : DatabaseHandler<Unit?> {
|
||||
override fun onComplete(success: Boolean, result: Unit?) {
|
||||
if (success) {
|
||||
read()
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun send(position: Position) {
|
||||
log("send", position)
|
||||
val request = formatRequest(url, position)
|
||||
sendRequestAsync(request, object : RequestHandler {
|
||||
override fun onComplete(success: Boolean) {
|
||||
if (success) {
|
||||
if (buffer) {
|
||||
delete(position)
|
||||
}
|
||||
} else {
|
||||
StatusActivity.addMessage(context.getString(R.string.status_send_fail))
|
||||
if (buffer) {
|
||||
retry()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun retry() {
|
||||
log("retry", null)
|
||||
handler.postDelayed({
|
||||
if (isOnline) {
|
||||
read()
|
||||
}
|
||||
}, RETRY_DELAY.toLong())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = TrackingController::class.java.simpleName
|
||||
private const val RETRY_DELAY = 30 * 1000
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2012 - 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import android.os.PowerManager.WakeLock
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
class TrackingService : Service() {
|
||||
|
||||
private var wakeLock: WakeLock? = null
|
||||
private var trackingController: TrackingController? = null
|
||||
|
||||
class HideNotificationService : Service() {
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
startForeground(NOTIFICATION_ID, createNotification(this))
|
||||
stopForeground(true)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
stopSelfResult(startId)
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
override fun onCreate() {
|
||||
startForeground(NOTIFICATION_ID, createNotification(this))
|
||||
Log.i(TAG, "service create")
|
||||
sendBroadcast(Intent(ACTION_STARTED))
|
||||
StatusActivity.addMessage(getString(R.string.status_service_create))
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(MainFragment.KEY_WAKELOCK, true)) {
|
||||
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, javaClass.name)
|
||||
wakeLock?.acquire()
|
||||
}
|
||||
trackingController = TrackingController(this)
|
||||
trackingController?.start()
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
ContextCompat.startForegroundService(this, Intent(this, HideNotificationService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
WakefulBroadcastReceiver.completeWakefulIntent(intent)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
stopForeground(true)
|
||||
Log.i(TAG, "service destroy")
|
||||
sendBroadcast(Intent(ACTION_STOPPED))
|
||||
StatusActivity.addMessage(getString(R.string.status_service_destroy))
|
||||
if (wakeLock?.isHeld == true) {
|
||||
wakeLock?.release()
|
||||
}
|
||||
trackingController?.stop()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val ACTION_STARTED = "org.traccar.action.SERVICE_STARTED"
|
||||
const val ACTION_STOPPED = "org.traccar.action.SERVICE_STOPPED"
|
||||
private val TAG = TrackingService::class.java.simpleName
|
||||
private const val NOTIFICATION_ID = 1
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
private fun createNotification(context: Context): Notification {
|
||||
val builder = NotificationCompat.Builder(context, MainApplication.PRIMARY_CHANNEL)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
val intent: Intent
|
||||
if (!BuildConfig.HIDDEN_APP) {
|
||||
intent = Intent(context, MainActivity::class.java)
|
||||
builder
|
||||
.setContentTitle(context.getString(R.string.settings_status_on_summary))
|
||||
.setTicker(context.getString(R.string.settings_status_on_summary))
|
||||
.color = ContextCompat.getColor(context, R.color.primary_dark)
|
||||
} else {
|
||||
intent = Intent(Settings.ACTION_SETTINGS)
|
||||
}
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, flags))
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
package org.traccar.client
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import android.os.PowerManager.WakeLock
|
||||
import android.content.Intent
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.os.PowerManager
|
||||
|
||||
abstract class WakefulBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid"
|
||||
private val activeWakeLocks = SparseArray<WakeLock>()
|
||||
private var nextId = 1
|
||||
|
||||
fun startWakefulForegroundService(context: Context, intent: Intent) {
|
||||
synchronized(activeWakeLocks) {
|
||||
val id = nextId
|
||||
nextId += 1
|
||||
if (nextId <= 0) {
|
||||
nextId = 1
|
||||
}
|
||||
intent.putExtra(EXTRA_WAKE_LOCK_ID, id)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
val wakeLock = powerManager.newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK,
|
||||
WakefulBroadcastReceiver::class.java.simpleName
|
||||
)
|
||||
wakeLock.setReferenceCounted(false)
|
||||
wakeLock.acquire((60 * 1000).toLong())
|
||||
activeWakeLocks.put(id, wakeLock)
|
||||
}
|
||||
}
|
||||
|
||||
fun completeWakefulIntent(intent: Intent?): Boolean {
|
||||
val id = intent?.getIntExtra(EXTRA_WAKE_LOCK_ID, 0) ?: 0
|
||||
if (id == 0) {
|
||||
return false
|
||||
}
|
||||
synchronized(activeWakeLocks) {
|
||||
val wakeLock = activeWakeLocks[id]
|
||||
if (wakeLock != null) {
|
||||
wakeLock.release()
|
||||
activeWakeLocks.remove(id)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<group android:scaleX="0.71"
|
||||
android:scaleY="0.71"
|
||||
android:translateX="9.28"
|
||||
android:translateY="9.28">
|
||||
<path
|
||||
android:pathData="m32.459,10.617c-3.783,-0.085 -7.636,0.834 -11.152,2.865 -10.228,5.906 -13.731,18.983 -7.826,29.211 5.906,10.228 18.983,13.731 29.211,7.826 10.228,-5.906 13.733,-18.983 7.828,-29.211 -3.875,-6.712 -10.839,-10.529 -18.061,-10.691zM40.778,19.38c1.737,1.331 3.242,2.997 4.404,5.01 1.162,2.013 1.854,4.15 2.137,6.319l-3.804,0.547c-0.223,-1.679 -0.73,-3.405 -1.629,-4.964 -0.9,-1.559 -2.139,-2.86 -3.482,-3.892zM36.188,25.461c0.948,0.734 1.766,1.632 2.402,2.734 0.636,1.102 1.003,2.258 1.165,3.446l-3.803,0.549c-0.101,-0.699 -0.284,-1.445 -0.658,-2.093 -0.374,-0.648 -0.928,-1.179 -1.483,-1.615l2.377,-3.019zM21.601,29.215 L25.407,35.806 29.213,42.398a7.611,7.611 0,0 1,-7.612 0,7.611 7.611,0 0,1 -3.806,-6.592 7.611,7.611 0,0 1,3.806 -6.591zM30.306,31.049a1.903,1.903 0,0 1,1.693 0.951,1.903 1.903,0 0,1 -0.697,2.599 1.903,1.903 0,0 1,-2.599 -0.695,1.903 1.903,0 0,1 0.697,-2.601 1.903,1.903 0,0 1,0.906 -0.254z"
|
||||
android:strokeWidth=".84379"
|
||||
android:fillColor="#2e7d32"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#2E7D32">
|
||||
<group android:scaleX="0.58"
|
||||
android:scaleY="0.58"
|
||||
android:translateX="5.04"
|
||||
android:translateY="5.04">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#2E7D32">
|
||||
<group android:scaleX="0.58"
|
||||
android:scaleY="0.58"
|
||||
android:translateX="5.04"
|
||||
android:translateY="5.04">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM9.5,16.5v-9l7,4.5L9.5,16.5z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="72.0"
|
||||
android:viewportWidth="72.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M36,0A36,36 0,0 0,0 36A36,36 0,0 0,36 72A36,36 0,0 0,72 36A36,36 0,0 0,36 0zM50.51,14.66C53.41,16.89 55.93,19.68 57.88,23.05C59.83,26.42 60.99,30 61.46,33.63L55.09,34.55C54.72,31.74 53.87,28.85 52.36,26.24C50.85,23.63 48.78,21.45 46.53,19.72L50.51,14.66zM42.82,24.85C44.4,26.08 45.77,27.58 46.84,29.43C47.91,31.27 48.52,33.21 48.79,35.2L42.42,36.12C42.25,34.95 41.95,33.7 41.32,32.61C40.69,31.53 39.77,30.64 38.84,29.91L42.82,24.85zM18.38,31.13L24.76,42.17L31.13,53.21A12.75,12.75 37.68,0 1,18.38 53.21A12.75,12.75 37.68,0 1,12.01 42.17A12.75,12.75 37.68,0 1,18.38 31.13zM32.96,34.21A3.19,3.19 60,0 1,35.8 35.8A3.19,3.19 60,0 1,34.63 40.15A3.19,3.19 60,0 1,30.28 38.99A3.19,3.19 60,0 1,31.45 34.63A3.19,3.19 60,0 1,32.96 34.21z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#2E7D32">
|
||||
<group android:scaleX="0.58"
|
||||
android:scaleY="0.58"
|
||||
android:translateX="5.04"
|
||||
android:translateY="5.04">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM11,16H9V8h2V16zM15,16h-2V8h2V16z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ListView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:name="org.traccar.client.MainFragment" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/status"
|
||||
android:title="@string/menu_status"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/info"
|
||||
android:title="@string/menu_info"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:title="@string/menu_clear"
|
||||
app:showAsAction="always" />
|
||||
|
||||
</menu>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@drawable/ic_sos_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_sos_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@drawable/ic_start_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_start_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@drawable/ic_stop_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_stop_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
После Ширина: | Высота: | Размер: 4.0 KiB |
|
После Ширина: | Высота: | Размер: 3.6 KiB |
|
После Ширина: | Высота: | Размер: 3.7 KiB |
|
После Ширина: | Высота: | Размер: 3.6 KiB |
|
После Ширина: | Высота: | Размер: 2.4 KiB |
|
После Ширина: | Высота: | Размер: 1.9 KiB |
|
После Ширина: | Высота: | Размер: 2.0 KiB |
|
После Ширина: | Высота: | Размер: 1.9 KiB |
|
После Ширина: | Высота: | Размер: 5.7 KiB |
|
После Ширина: | Высота: | Размер: 5.1 KiB |
|
После Ширина: | Высота: | Размер: 5.3 KiB |
|
После Ширина: | Высота: | Размер: 5.1 KiB |
|
После Ширина: | Высота: | Размер: 9.3 KiB |
|
После Ширина: | Высота: | Размер: 8.2 KiB |
|
После Ширина: | Высота: | Размер: 8.5 KiB |
|
После Ширина: | Высота: | Размер: 8.2 KiB |
|
После Ширина: | Высота: | Размер: 13 KiB |
|
После Ширина: | Высота: | Размер: 12 KiB |
|
После Ширина: | Высота: | Размер: 12 KiB |
|
После Ширина: | Высота: | Размер: 12 KiB |
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar-kliënt</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Begin diens</string>
|
||||
<string name="shortcut_stop">Stop diens</string>
|
||||
<string name="shortcut_sos">Stuur SOS</string>
|
||||
<string name="channel_default">Primêre Kanaal</string>
|
||||
<string name="settings_id_title">Toestel-identifiseerder</string>
|
||||
<string name="settings_url_title">Bediener-URL</string>
|
||||
<string name="settings_url_summary">Volg bediener-URL</string>
|
||||
<string name="settings_interval_title">Frekwensie</string>
|
||||
<string name="settings_interval_summary">Rapporteer interval in sekondes</string>
|
||||
<string name="settings_distance_title">Afstand</string>
|
||||
<string name="settings_distance_summary">Rapporteer afstand in meter</string>
|
||||
<string name="settings_angle_title">Hoek</string>
|
||||
<string name="settings_angle_summary">Rapporteringshoek in grade</string>
|
||||
<string name="settings_status_title">Diensstatus</string>
|
||||
<string name="settings_status_off">Begin</string>
|
||||
<string name="settings_status_on">Stop</string>
|
||||
<string name="settings_status_off_summary">Diens gestop</string>
|
||||
<string name="settings_status_on_summary">Diens loop</string>
|
||||
<string name="settings_buffer">Vanlyn buffer</string>
|
||||
<string name="settings_buffer_off_summary">Buffer af</string>
|
||||
<string name="settings_buffer_on_summary">Buffer aan</string>
|
||||
<string name="settings_accuracy_title">Ligging akkuraatheid</string>
|
||||
<string name="settings_accuracy_summary">Verlangde ligging akkuraatheid</string>
|
||||
<string name="settings_accuracy_high">Hoog</string>
|
||||
<string name="settings_accuracy_medium">Medium</string>
|
||||
<string name="settings_accuracy_low">Laag</string>
|
||||
<string name="settings_foreground_title">Voorgrond diens</string>
|
||||
<string name="settings_foreground_summary">Verhoog diensprioriteit</string>
|
||||
<string name="menu_status">Status</string>
|
||||
<string name="menu_shortcuts">Voeg kortpaaie by</string>
|
||||
<string name="menu_clear">Skoonmaak</string>
|
||||
<string name="status_service_create">Diens het begin</string>
|
||||
<string name="status_service_destroy">Diens het gestop</string>
|
||||
<string name="status_send_success">Stuur suksesvol</string>
|
||||
<string name="status_send_fail">Stuur misluk</string>
|
||||
<string name="status_location_update">Ligging opdateer</string>
|
||||
<string name="status_network_online">Netwerk aanlyn</string>
|
||||
<string name="status_network_offline">Netwerk vanlyn</string>
|
||||
<string name="hidden_app_name">Toestelinstellings</string>
|
||||
<string name="hidden_alert">Die toep is versteek. Skakel 8722227 (TRACCAR) om dit weer oop te maak.</string>
|
||||
<string name="error_msg_invalid_url">Voer \'n geldige http: // of https: // URL in</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">عميل Traccar</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">بدء الخدمة</string>
|
||||
<string name="shortcut_stop">توقف الخدمة</string>
|
||||
<string name="shortcut_sos">ارسال استغاثة</string>
|
||||
<string name="channel_default">القناة الاساسية</string>
|
||||
<string name="settings_id_title">معرف الجهاز</string>
|
||||
<string name="settings_url_title">رابط الخادم</string>
|
||||
<string name="settings_url_summary">رابط خادم التتبع</string>
|
||||
<string name="settings_interval_title">تردد</string>
|
||||
<string name="settings_interval_summary">فترة التقرير بالثواني</string>
|
||||
<string name="settings_distance_title">المسافة</string>
|
||||
<string name="settings_distance_summary">مسافة التقرير بالميتر</string>
|
||||
<string name="settings_angle_title">الزاوية</string>
|
||||
<string name="settings_angle_summary">زاوية التقرير بالدرجات</string>
|
||||
<string name="settings_status_title">حالة الخدمة</string>
|
||||
<string name="settings_status_off">بدء</string>
|
||||
<string name="settings_status_on">إيقاف</string>
|
||||
<string name="settings_status_off_summary">الخدمة توقفت</string>
|
||||
<string name="settings_status_on_summary">الخدمة تعمل</string>
|
||||
<string name="settings_buffer">التخزين المؤقت دون اتصال</string>
|
||||
<string name="settings_buffer_off_summary">التخزين المؤقت غير مفعل</string>
|
||||
<string name="settings_buffer_on_summary">التخزين المؤقت مفعل</string>
|
||||
<string name="settings_wakelock">قفل الإيقاظ</string>
|
||||
<string name="settings_wakelock_off_summary">قفل الإيقاظ غير مفعل</string>
|
||||
<string name="settings_wakelock_on_summary">قفل الإيقاظ مفعل</string>
|
||||
<string name="settings_accuracy_title">دقة الموقع</string>
|
||||
<string name="settings_accuracy_summary">دقة الموقع المطلوبة</string>
|
||||
<string name="settings_accuracy_high">مرتفع</string>
|
||||
<string name="settings_accuracy_medium">متوسط</string>
|
||||
<string name="settings_accuracy_low">منخفض</string>
|
||||
<string name="settings_foreground_title">الخدمة الأمامية</string>
|
||||
<string name="settings_foreground_summary">زيادة أولوية الخدمة</string>
|
||||
<string name="menu_status">الحالة</string>
|
||||
<string name="menu_shortcuts">إضافة اختصارات</string>
|
||||
<string name="menu_clear">مسح</string>
|
||||
<string name="status_service_create">بدأت الخدمة</string>
|
||||
<string name="status_service_destroy">توقفت الخدمة</string>
|
||||
<string name="status_send_success">تم الإرسال بنجاح</string>
|
||||
<string name="status_send_fail">فشل الإرسال</string>
|
||||
<string name="status_location_update">تحديث الموقع</string>
|
||||
<string name="status_network_online">متصل بالشبكة</string>
|
||||
<string name="status_network_offline">غير متصل بالشبكة</string>
|
||||
<string name="hidden_app_name">إعدادات الجهاز</string>
|
||||
<string name="hidden_alert">تم إخفاء التطبيق. لفتحه مجددا رجاءا اتصل بـ8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">الرجاء إدخال رابط صحيح http:// أو https://</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar müştəri</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">xidmətə başla</string>
|
||||
<string name="shortcut_stop">xidmət dayandırmaq</string>
|
||||
<string name="shortcut_sos">sos göndərin</string>
|
||||
<string name="channel_default">əsas kanal</string>
|
||||
<string name="settings_id_title">Qurğu identifikatoru</string>
|
||||
<string name="settings_url_title">Server URL</string>
|
||||
<string name="settings_url_summary">Tracking server URL</string>
|
||||
<string name="settings_interval_title">Frequency</string>
|
||||
<string name="settings_interval_summary">Saniyə cədvəlləri bildirir</string>
|
||||
<string name="settings_distance_title">Məsafə</string>
|
||||
<string name="settings_distance_summary">Metr ölçən məsafə</string>
|
||||
<string name="settings_angle_title">Angle</string>
|
||||
<string name="settings_angle_summary">Dərəcə dərəcəsi hesabat</string>
|
||||
<string name="settings_status_title">Xidmət vəziyyəti</string>
|
||||
<string name="settings_status_off">Başlamaq</string>
|
||||
<string name="settings_status_on">Dur</string>
|
||||
<string name="settings_status_off_summary">Xidmət dayandı</string>
|
||||
<string name="settings_status_on_summary">Xidmət davam edir</string>
|
||||
<string name="settings_accuracy_title">Yer dəqiqliyi</string>
|
||||
<string name="settings_accuracy_summary">İstədiyiniz yer dəqiqliyi</string>
|
||||
<string name="settings_accuracy_high">Yüksək</string>
|
||||
<string name="settings_accuracy_medium">Orta</string>
|
||||
<string name="settings_accuracy_low">Aşağı</string>
|
||||
<string name="settings_foreground_title">Ön xidməti</string>
|
||||
<string name="settings_foreground_summary">Xidmət prioritetini artırın</string>
|
||||
<string name="menu_status">vəziyyət</string>
|
||||
<string name="menu_shortcuts">Qısa yollar əlavə edin</string>
|
||||
<string name="menu_clear">Aydındır</string>
|
||||
<string name="status_service_create">Xidmət başladı</string>
|
||||
<string name="status_service_destroy">Xidmət dayandı</string>
|
||||
<string name="status_send_success">Uğurla göndərin</string>
|
||||
<string name="status_send_fail">Göndərmə uğursuz oldu</string>
|
||||
<string name="status_location_update">Yer yeniləmə</string>
|
||||
<string name="status_network_online">Şəbəkə online</string>
|
||||
<string name="status_network_offline">Şəbəkə offline</string>
|
||||
<string name="hidden_app_name">Qurğu Ayarları</string>
|
||||
<string name="hidden_alert">The app has been hidden. To open it again please dial 8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">Please enter a valid http:// or https:// URL</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar клиент</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Стартирай услугата</string>
|
||||
<string name="shortcut_stop">Спри услугата</string>
|
||||
<string name="shortcut_sos">Изпрати SOS</string>
|
||||
<string name="channel_default">Основен канал</string>
|
||||
<string name="settings_id_title">Идентификатор</string>
|
||||
<string name="settings_url_title">Адрес на сървъра</string>
|
||||
<string name="settings_url_summary">Адрес на проследяващият сървър</string>
|
||||
<string name="settings_interval_title">Честота</string>
|
||||
<string name="settings_interval_summary">Докладван интервал в сек.</string>
|
||||
<string name="settings_distance_title">Разстояние</string>
|
||||
<string name="settings_distance_summary">Докладвано разстояние в метри</string>
|
||||
<string name="settings_angle_title">Ъгъл</string>
|
||||
<string name="settings_angle_summary">Докладван ъгъл в градуси</string>
|
||||
<string name="settings_status_title">Състояние на услугата</string>
|
||||
<string name="settings_status_off">Старт</string>
|
||||
<string name="settings_status_on">Стоп</string>
|
||||
<string name="settings_status_off_summary">Услугата е спряна</string>
|
||||
<string name="settings_status_on_summary">Услугата работи</string>
|
||||
<string name="settings_accuracy_title">Точност на местоположението</string>
|
||||
<string name="settings_accuracy_summary">Желана точност на местоположението</string>
|
||||
<string name="settings_accuracy_high">Висока</string>
|
||||
<string name="settings_accuracy_medium">Средна</string>
|
||||
<string name="settings_accuracy_low">Ниска</string>
|
||||
<string name="settings_foreground_title">Foreground service</string>
|
||||
<string name="settings_foreground_summary">Увеличаване на приоритета на услугата</string>
|
||||
<string name="menu_status">Състояние</string>
|
||||
<string name="menu_shortcuts">Добави пряк път</string>
|
||||
<string name="menu_clear">Изтрий</string>
|
||||
<string name="status_service_create">Услугата стартира</string>
|
||||
<string name="status_service_destroy">Услугата спряна</string>
|
||||
<string name="status_send_success">Успешно изпращане</string>
|
||||
<string name="status_send_fail">Грешка в изпращането</string>
|
||||
<string name="status_location_update">Местоположението е обновено</string>
|
||||
<string name="status_network_online">Има връзка с мрежата</string>
|
||||
<string name="status_network_offline">Няма връзка с мрежа</string>
|
||||
<string name="hidden_app_name">Настройки</string>
|
||||
<string name="hidden_alert">Приложението е скрито. За да го отворите отново, моля наберете 8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">Моля въведете валиден адрес</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar ক্লায়েন্ট</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="settings_id_title">ডিভাইস সনাক্তকারী</string>
|
||||
<string name="settings_url_title">সার্ভার URL</string>
|
||||
<string name="settings_url_summary">ট্র্যাকিং সার্ভার URL</string>
|
||||
<string name="settings_interval_title">ফ্রিকোয়েন্সি</string>
|
||||
<string name="settings_interval_summary">রিপোর্টিং বিরতি - সেকেন্ডে</string>
|
||||
<string name="settings_distance_title">দূরত্ব</string>
|
||||
<string name="settings_distance_summary">রিপোর্টিং বিরতি - মিটারে</string>
|
||||
<string name="settings_angle_title">অ্যাঙ্গেল</string>
|
||||
<string name="settings_angle_summary">রিপোর্টিং অ্যাঙ্গেল - ডিগ্রিতে</string>
|
||||
<string name="settings_status_title">সার্ভিসের অবস্থা</string>
|
||||
<string name="settings_status_off">চালু</string>
|
||||
<string name="settings_status_on">বন্ধ</string>
|
||||
<string name="settings_status_off_summary">সার্ভিস বন্ধ হয়েছে</string>
|
||||
<string name="settings_status_on_summary">সার্ভিস চলমান</string>
|
||||
<string name="settings_foreground_title">ফোরগ্রাউন্ড সার্ভিস</string>
|
||||
<string name="settings_foreground_summary">সার্ভিসের পূর্ববর্তিতা বৃদ্ধি</string>
|
||||
<string name="menu_status">অবস্থা</string>
|
||||
<string name="menu_shortcuts">শর্টকাট যুক্ত করা</string>
|
||||
<string name="menu_clear">পরিষ্কার</string>
|
||||
<string name="status_send_success">সফলভাবে পাঠান হয়েছে</string>
|
||||
<string name="status_send_fail">পাঠান ব্যর্থ হয়েছে</string>
|
||||
<string name="status_location_update">অবস্থান আপডেট</string>
|
||||
<string name="hidden_app_name">ডিভাইস সেটিংস</string>
|
||||
<string name="hidden_alert">অ্যাপ্লিকেশনটি লুকানো আছে। আবার খুলতে ডায়াল করুন 8722227 (TRACCAR)।</string>
|
||||
<string name="error_msg_invalid_url">দয়া করে একটি ভ্যালিড http:// বা https:// URL লিখুন</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar Client</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Inicia servei</string>
|
||||
<string name="shortcut_stop">Atura servei</string>
|
||||
<string name="shortcut_sos">Envia SOS</string>
|
||||
<string name="channel_default">Canal principal</string>
|
||||
<string name="settings_id_title">Id dispositiu</string>
|
||||
<string name="settings_url_title">URL Servidor</string>
|
||||
<string name="settings_url_summary">URL del servidor de seguiment</string>
|
||||
<string name="settings_interval_title">Interval</string>
|
||||
<string name="settings_interval_summary">Interval d\'enviament en segons</string>
|
||||
<string name="settings_distance_title">Distància</string>
|
||||
<string name="settings_distance_summary">Distància en metres per informar</string>
|
||||
<string name="settings_angle_title">Angle</string>
|
||||
<string name="settings_angle_summary">Graus de diferència per informar</string>
|
||||
<string name="settings_status_title">Estat del servei</string>
|
||||
<string name="settings_status_off">Aturat</string>
|
||||
<string name="settings_status_on">Inciat</string>
|
||||
<string name="settings_status_off_summary">Servei aturat</string>
|
||||
<string name="settings_status_on_summary">Servei iniciat</string>
|
||||
<string name="settings_buffer">Buffer</string>
|
||||
<string name="settings_buffer_off_summary">Buffer desactivat</string>
|
||||
<string name="settings_buffer_on_summary">Buffer activat</string>
|
||||
<string name="settings_wakelock">Bloca despertar</string>
|
||||
<string name="settings_wakelock_off_summary">Bloca despertar desactivat</string>
|
||||
<string name="settings_wakelock_on_summary">Bloca despertar activat</string>
|
||||
<string name="settings_accuracy_title">Precisió</string>
|
||||
<string name="settings_accuracy_summary">Precisió</string>
|
||||
<string name="settings_accuracy_high">Precisió alta</string>
|
||||
<string name="settings_accuracy_medium">Precisió mitjana</string>
|
||||
<string name="settings_accuracy_low">Precisió baixa</string>
|
||||
<string name="settings_foreground_title">Primer pla</string>
|
||||
<string name="settings_foreground_summary">En primer pla</string>
|
||||
<string name="menu_status">Estat</string>
|
||||
<string name="menu_info">Informació</string>
|
||||
<string name="menu_shortcuts">Dreceres</string>
|
||||
<string name="menu_clear">Netejar</string>
|
||||
<string name="status_service_create">Servei iniciat</string>
|
||||
<string name="status_service_destroy">Servei aturat</string>
|
||||
<string name="status_send_success">Enviat correctament</string>
|
||||
<string name="status_send_fail">Error a l\'enviar</string>
|
||||
<string name="status_location_update">Actualització de localització</string>
|
||||
<string name="status_network_online">Xarxa en línia</string>
|
||||
<string name="status_network_offline">Xarxa fora de línia</string>
|
||||
<string name="hidden_app_name">Configuració del dispositiu</string>
|
||||
<string name="hidden_alert">Aplicació ocultada. Per obrir-la de nou escriviu 8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">Adreça URL incorrecte</string>
|
||||
<string name="request_exception">Per obtenir contínuament les dades de localització, desactivi la optimització de la bateria per aquesta aplicació</string>
|
||||
<string name="request_background">Per obtenir contínuament les dades de localització, activi el permís \"%s\".</string>
|
||||
<string name="request_background_option">Permetre tot el temps</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar klient</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Spustit službu</string>
|
||||
<string name="shortcut_stop">Zastavit službu</string>
|
||||
<string name="shortcut_sos">Odeslat SOS</string>
|
||||
<string name="channel_default">Primární kanál</string>
|
||||
<string name="settings_id_title">Identifikátor zařízení</string>
|
||||
<string name="settings_url_title">URL serveru</string>
|
||||
<string name="settings_url_summary">URL sledovacího serveru</string>
|
||||
<string name="settings_interval_title">Frekvence</string>
|
||||
<string name="settings_interval_summary">Reportuji interval v sekundách</string>
|
||||
<string name="settings_distance_title">Vzdálenost</string>
|
||||
<string name="settings_distance_summary">Reportuji vzdálenost v metrech</string>
|
||||
<string name="settings_angle_title">Úhel</string>
|
||||
<string name="settings_angle_summary">Reportuji úhel ve stupních</string>
|
||||
<string name="settings_status_title">Stav služby</string>
|
||||
<string name="settings_status_off">Start</string>
|
||||
<string name="settings_status_on">Stop</string>
|
||||
<string name="settings_status_off_summary">Zastaveno</string>
|
||||
<string name="settings_status_on_summary">Spuštěno</string>
|
||||
<string name="settings_buffer">Ukládání do vyrovnávací paměti</string>
|
||||
<string name="settings_buffer_off_summary">Vyrovnávací pamět vypnuta</string>
|
||||
<string name="settings_buffer_on_summary">Vyrovnávací pamět zapnuta</string>
|
||||
<string name="settings_wakelock">Zámek probuzení</string>
|
||||
<string name="settings_wakelock_off_summary">Zámek probuzení vypnut</string>
|
||||
<string name="settings_wakelock_on_summary">Zámek probuzení zapnut</string>
|
||||
<string name="settings_accuracy_title">Přesnost umístění</string>
|
||||
<string name="settings_accuracy_summary">Požadovaná přesnost umístění</string>
|
||||
<string name="settings_accuracy_high">Vysoká</string>
|
||||
<string name="settings_accuracy_medium">Střední</string>
|
||||
<string name="settings_accuracy_low">Nízká</string>
|
||||
<string name="settings_foreground_title">Popředí služba</string>
|
||||
<string name="settings_foreground_summary">Zvýšit prioritu služba</string>
|
||||
<string name="menu_status">Stav</string>
|
||||
<string name="menu_info">Informace</string>
|
||||
<string name="menu_shortcuts">Přidat zástupce</string>
|
||||
<string name="menu_clear">Vyčistit</string>
|
||||
<string name="status_service_create">Služba spuštěna</string>
|
||||
<string name="status_service_destroy">Služba zastavena</string>
|
||||
<string name="status_send_success">Úspěšně odesláno</string>
|
||||
<string name="status_send_fail">Odesílání selhalo</string>
|
||||
<string name="status_location_update">Aktualizace pozice</string>
|
||||
<string name="status_network_online">Síť online</string>
|
||||
<string name="status_network_offline">Síť offile</string>
|
||||
<string name="hidden_app_name">Nastavení zařízení</string>
|
||||
<string name="hidden_alert">Aplikace byla skryta. Pro znovuotevření vytočte 8722227 (TRACCAR)</string>
|
||||
<string name="error_msg_invalid_url">Prosím vložte platnou http:// nebo https:// URL</string>
|
||||
<string name="request_exception">Chcete-li průběžně shromažďovat údaje o poloze, vypněte optimalizaci baterie pro aplikaci.</string>
|
||||
<string name="request_background">Chcete-li průběžně shromažďovat údaje o poloze, zapněte \"%s\" oprávnění.</string>
|
||||
<string name="request_background_option">Povolit po celou dobu</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar klient</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Start tjenesten</string>
|
||||
<string name="shortcut_stop">Stop tjenesten</string>
|
||||
<string name="shortcut_sos">Send SOS</string>
|
||||
<string name="channel_default">Primær kanal</string>
|
||||
<string name="settings_id_title">Enheds ID</string>
|
||||
<string name="settings_url_title">Server Adresse</string>
|
||||
<string name="settings_url_summary">Tracking server adresse</string>
|
||||
<string name="settings_interval_title">Periode mellem registreringer</string>
|
||||
<string name="settings_interval_summary">Angiv periode i sekunder</string>
|
||||
<string name="settings_distance_title">Afstand mellem registreringer</string>
|
||||
<string name="settings_distance_summary">Angiv afstand i meter</string>
|
||||
<string name="settings_angle_title">Retningsændring som udløser ny registrering</string>
|
||||
<string name="settings_angle_summary">Angiv retningsændring i grader</string>
|
||||
<string name="settings_status_title">Status af tjenesten</string>
|
||||
<string name="settings_status_off">Start</string>
|
||||
<string name="settings_status_on">Stop</string>
|
||||
<string name="settings_status_off_summary">Tjenesten stoppet</string>
|
||||
<string name="settings_status_on_summary">Tjenesten er aktiv</string>
|
||||
<string name="settings_buffer">Offline buffering</string>
|
||||
<string name="settings_buffer_off_summary">buffering off</string>
|
||||
<string name="settings_buffer_on_summary">buffering on</string>
|
||||
<string name="settings_wakelock">Vågelås</string>
|
||||
<string name="settings_wakelock_off_summary">Vågelås off</string>
|
||||
<string name="settings_wakelock_on_summary">Vågelås on</string>
|
||||
<string name="settings_accuracy_title">Placeringsnøjagtighed</string>
|
||||
<string name="settings_accuracy_summary">Ønsket placeringsnøjagtighed</string>
|
||||
<string name="settings_accuracy_high">Høj</string>
|
||||
<string name="settings_accuracy_medium">Mellem</string>
|
||||
<string name="settings_accuracy_low">Lav</string>
|
||||
<string name="settings_foreground_title">Forgrunds tjeneste</string>
|
||||
<string name="settings_foreground_summary">Øg tjeneste prioritet</string>
|
||||
<string name="menu_status">Status</string>
|
||||
<string name="menu_shortcuts">Tilføj genveje</string>
|
||||
<string name="menu_clear">Slet</string>
|
||||
<string name="status_service_create">Tjenesten kører</string>
|
||||
<string name="status_service_destroy">Tjenesten er stoppet</string>
|
||||
<string name="status_send_success">Send succesfuld</string>
|
||||
<string name="status_send_fail">Send fejlede</string>
|
||||
<string name="status_location_update">Placering opdateret</string>
|
||||
<string name="status_network_online">Netværk online</string>
|
||||
<string name="status_network_offline">Netværk offline</string>
|
||||
<string name="hidden_app_name">Enheds indstillinger</string>
|
||||
<string name="hidden_alert">Appen er skjult. For at åbne appen igen skal du ringe til 8722227 (TRACCAR)</string>
|
||||
<string name="error_msg_invalid_url">Indtast gyldig http:// eller https:// adresse</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar Client</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Dienst starten</string>
|
||||
<string name="shortcut_stop">Dienst stoppen</string>
|
||||
<string name="shortcut_sos">SOS senden</string>
|
||||
<string name="channel_default">Primärkanal</string>
|
||||
<string name="settings_id_title">Gerätekennung</string>
|
||||
<string name="settings_url_title">Server URL</string>
|
||||
<string name="settings_url_summary">Tracking Server URL</string>
|
||||
<string name="settings_interval_title">Frequenz</string>
|
||||
<string name="settings_interval_summary">Meldung nach Zeit in Sekunden</string>
|
||||
<string name="settings_distance_title">Entfernung</string>
|
||||
<string name="settings_distance_summary">Meldung nach Entfernung in Metern</string>
|
||||
<string name="settings_angle_title">Winkel</string>
|
||||
<string name="settings_angle_summary">Meldung nach Winkel in Grad</string>
|
||||
<string name="settings_status_title">Dienststatus</string>
|
||||
<string name="settings_status_off">Start</string>
|
||||
<string name="settings_status_on">Stop</string>
|
||||
<string name="settings_status_off_summary">Dienst inaktiv</string>
|
||||
<string name="settings_status_on_summary">Dienst aktiv</string>
|
||||
<string name="settings_buffer">Offline puffern</string>
|
||||
<string name="settings_buffer_off_summary">Puffern aus</string>
|
||||
<string name="settings_buffer_on_summary">Puffern ein</string>
|
||||
<string name="settings_accuracy_title">Positionsgenauigkeit</string>
|
||||
<string name="settings_accuracy_summary">Erwünschte Positionsgenauigkeit</string>
|
||||
<string name="settings_accuracy_high">Hoch</string>
|
||||
<string name="settings_accuracy_medium">Mittel</string>
|
||||
<string name="settings_accuracy_low">Niedrig</string>
|
||||
<string name="settings_foreground_title">Vordergrund Service</string>
|
||||
<string name="settings_foreground_summary">Servicepriorität erhöhen</string>
|
||||
<string name="menu_status">Status</string>
|
||||
<string name="menu_shortcuts">Verknüpfung hinzufügen</string>
|
||||
<string name="menu_clear">Leeren</string>
|
||||
<string name="status_service_create">Dienst gestartet</string>
|
||||
<string name="status_service_destroy">Dienst gestoppt</string>
|
||||
<string name="status_send_success">Erfolgreich gesendet</string>
|
||||
<string name="status_send_fail">Übertragung fehlerhaft</string>
|
||||
<string name="status_location_update">Positionsupdate</string>
|
||||
<string name="status_network_online">Netzwerk online</string>
|
||||
<string name="status_network_offline">Netzwerk offline</string>
|
||||
<string name="hidden_app_name">Geräteeinstellungen</string>
|
||||
<string name="hidden_alert">Die App wurde versteckt. Um sie wieder zu öffnen, wähle 8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">Bitte eine gültige http:// oder https:// URL eingeben</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Εφαρμογή Traccar</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Εκκίνηση υπηρεσίας</string>
|
||||
<string name="shortcut_stop">Διακοπή υπηρεσίας</string>
|
||||
<string name="shortcut_sos">Αποστολή SOS</string>
|
||||
<string name="channel_default">Κύριο κανάλι</string>
|
||||
<string name="settings_id_title">Αναγνωριστικό συσκευής</string>
|
||||
<string name="settings_url_title">Διεύθυνση διακομιστή</string>
|
||||
<string name="settings_url_summary">Διεύθυνση διακομιστή παρακολούθησης</string>
|
||||
<string name="settings_interval_title">Συχνότητα</string>
|
||||
<string name="settings_interval_summary">Διάστημα αναφορών σε δευτερόλεπτα</string>
|
||||
<string name="settings_distance_title">Απόσταση</string>
|
||||
<string name="settings_distance_summary">Απόσταση αναφορών σε μέτρα</string>
|
||||
<string name="settings_angle_title">Γωνία</string>
|
||||
<string name="settings_angle_summary">Γωνία αναφορών σε μοίρες</string>
|
||||
<string name="settings_status_title">Κατάσταση υπηρεσίας</string>
|
||||
<string name="settings_status_off">Εκκίνηση</string>
|
||||
<string name="settings_status_on">Παύση</string>
|
||||
<string name="settings_status_off_summary">Η υπηρεσία σταμάτησε</string>
|
||||
<string name="settings_status_on_summary">Η υπηρεσία εκτελείται</string>
|
||||
<string name="settings_buffer">Εκτός σύνδεσης ενδιάμεση αποθήκευση</string>
|
||||
<string name="settings_buffer_off_summary">Ενδιάμεση αποθήκευση απενεργοποιημένη</string>
|
||||
<string name="settings_buffer_on_summary">Ενδιάμεση αποθήκευση ενεργοποιημένη</string>
|
||||
<string name="settings_wakelock">Κλείδωμα αφύπνισης</string>
|
||||
<string name="settings_wakelock_off_summary">Κλείδωμα αφύπνισης απενεργοποιημένο</string>
|
||||
<string name="settings_wakelock_on_summary">Κλείδωμα αφύπνισης ενεργοποιημένο</string>
|
||||
<string name="settings_accuracy_title">Ακρίβεια θέσης</string>
|
||||
<string name="settings_accuracy_summary">Επιθυμητή ακρίβεια θέσης</string>
|
||||
<string name="settings_accuracy_high">Υψηλή</string>
|
||||
<string name="settings_accuracy_medium">Μέση</string>
|
||||
<string name="settings_accuracy_low">Χαμηλή</string>
|
||||
<string name="settings_foreground_title">Υπηρεσία προσκηνίου</string>
|
||||
<string name="settings_foreground_summary">Αύξηση προτεραιότητας της υπηρεσίας</string>
|
||||
<string name="menu_status">Κατάσταση</string>
|
||||
<string name="menu_info">Πληροφορίες</string>
|
||||
<string name="menu_shortcuts">Προσθήκη συντόμευσης</string>
|
||||
<string name="menu_clear">Καθαρισμός</string>
|
||||
<string name="status_service_create">Η υπηρεσία εκκινήθηκε</string>
|
||||
<string name="status_service_destroy">Η υπηρεσία διακόπηκε</string>
|
||||
<string name="status_send_success">Απεστάλη επιτυχώς</string>
|
||||
<string name="status_send_fail">Η αποστολή απέτυχε</string>
|
||||
<string name="status_location_update">Ενημέρωση τοποθεσίας</string>
|
||||
<string name="status_network_online">Ενεργό δίκτυο</string>
|
||||
<string name="status_network_offline">Ανενεργό δίκτυο</string>
|
||||
<string name="hidden_app_name">Ρυθμίσεις συσκευής</string>
|
||||
<string name="hidden_alert">Η εφαρμογή αποκρύφτηκε. Για να προβληθεί καλέστε 8722227 (TRACCAR)</string>
|
||||
<string name="error_msg_invalid_url">Παρακαλώ εισάγετε μία έγκυρη http:// ή https:// διεύθυνση</string>
|
||||
<string name="request_exception">Για να συλλέγετε συνεχώς δεδομένα τοποθεσίας απενεργοποιήστε την βελτιστοποίηση μπαταρίας για την εφαρμογή.</string>
|
||||
<string name="request_background">Για να συλλέγετε συνεχώς δεδομένα τοποθεσίας ενεργοποιήστε την επιλογή \"%s\".</string>
|
||||
<string name="request_background_option">Να επιτρέπεται όλη την ώρα</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar Client</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Iniciar servicio</string>
|
||||
<string name="shortcut_stop">Detener servicio</string>
|
||||
<string name="shortcut_sos">Enviar SOS</string>
|
||||
<string name="channel_default">Canal primario</string>
|
||||
<string name="settings_id_title">Identificador de dispositivo</string>
|
||||
<string name="settings_url_title">URL del servidor</string>
|
||||
<string name="settings_url_summary">URL del servidor de seguimiento</string>
|
||||
<string name="settings_interval_title">Frecuencia de rastreo</string>
|
||||
<string name="settings_interval_summary">Intervalo para reportes en segundos</string>
|
||||
<string name="settings_distance_title">Distancia</string>
|
||||
<string name="settings_distance_summary">Distancia para reportes en metros</string>
|
||||
<string name="settings_angle_title">Ángulo</string>
|
||||
<string name="settings_angle_summary">Ángulo para reportes en grados</string>
|
||||
<string name="settings_status_title">Estado del servicio</string>
|
||||
<string name="settings_status_off">Activar</string>
|
||||
<string name="settings_status_on">Desactivar</string>
|
||||
<string name="settings_status_off_summary">Servicio detenido</string>
|
||||
<string name="settings_status_on_summary">Servicio en ejecución</string>
|
||||
<string name="settings_buffer">Búfer fuera de línea</string>
|
||||
<string name="settings_buffer_off_summary">Búfer activado</string>
|
||||
<string name="settings_buffer_on_summary">Búfer desactivado</string>
|
||||
<string name="settings_wakelock">Estado despierto forzado</string>
|
||||
<string name="settings_wakelock_off_summary">Estado despierto forzado activado</string>
|
||||
<string name="settings_wakelock_on_summary">Estado despierto forzado desactivado</string>
|
||||
<string name="settings_accuracy_title">Precisión de posición</string>
|
||||
<string name="settings_accuracy_summary">Precisión de posición deseada</string>
|
||||
<string name="settings_accuracy_high">Alta</string>
|
||||
<string name="settings_accuracy_medium">Media</string>
|
||||
<string name="settings_accuracy_low">Baja</string>
|
||||
<string name="settings_foreground_title">Servicio en primer plano</string>
|
||||
<string name="settings_foreground_summary">Aumentar prioridad del servicio</string>
|
||||
<string name="menu_status">Estado</string>
|
||||
<string name="menu_info">Info</string>
|
||||
<string name="menu_shortcuts">Añadir acceso directo</string>
|
||||
<string name="menu_clear">Limpiar</string>
|
||||
<string name="status_service_create">Servicio iniciado</string>
|
||||
<string name="status_service_destroy">Servicio detenido</string>
|
||||
<string name="status_send_success">Enviado correctamente</string>
|
||||
<string name="status_send_fail">Fallo en el envío</string>
|
||||
<string name="status_location_update">Actualización de localización</string>
|
||||
<string name="status_network_online">Red en línea</string>
|
||||
<string name="status_network_offline">Red fuera de línea</string>
|
||||
<string name="hidden_app_name">Ajustes del dispositivo</string>
|
||||
<string name="hidden_alert">La aplicación se ha ocultado. Para abrirla de nuevo por favor marque 8722227 (TRACCAR).</string>
|
||||
<string name="error_msg_invalid_url">Por favor, introduzca una URL válida http:// ó https:// </string>
|
||||
<string name="request_exception">Para recoger continuamente los datos de localización, desactiva la optimización de la batería de la aplicación.</string>
|
||||
<string name="request_background">Para recoger continuamente los datos de localización, por favor, activa el permiso \"%s\".</string>
|
||||
<string name="request_background_option">Permitir todo el tiempo</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">ردیاب Traccar</string>
|
||||
<string name="app_logo">ردیاب Traccar</string>
|
||||
<string name="shortcut_start">شروع کار</string>
|
||||
<string name="shortcut_stop">توقف کار</string>
|
||||
<string name="shortcut_sos">ارسال پیام اضطراری</string>
|
||||
<string name="channel_default">شبکه اولیه</string>
|
||||
<string name="settings_id_title">کد هویت دستگاه</string>
|
||||
<string name="settings_url_title">مسیر آدرس سرور</string>
|
||||
<string name="settings_url_summary">مسیر آدرس سرور مسیریاب</string>
|
||||
<string name="settings_interval_title">تاخیر ارسال</string>
|
||||
<string name="settings_interval_summary">فاصله گزارش دهی بر حسب ثانیه</string>
|
||||
<string name="settings_distance_title">فاصله</string>
|
||||
<string name="settings_distance_summary">گزارش فاصله / متر</string>
|
||||
<string name="settings_angle_title">زاویه</string>
|
||||
<string name="settings_angle_summary">گزارش زاویه / درجه</string>
|
||||
<string name="settings_status_title">وضعیت سرویس</string>
|
||||
<string name="settings_status_off">شروع</string>
|
||||
<string name="settings_status_on">توقف</string>
|
||||
<string name="settings_status_off_summary">سرویس متوقف است</string>
|
||||
<string name="settings_status_on_summary">سرویس در حال کار است</string>
|
||||
<string name="settings_accuracy_title">دقت مکان</string>
|
||||
<string name="settings_accuracy_summary">دقت مکان دلخواه</string>
|
||||
<string name="settings_accuracy_high">زیاد</string>
|
||||
<string name="settings_accuracy_medium">متوسط</string>
|
||||
<string name="settings_accuracy_low">کم</string>
|
||||
<string name="settings_foreground_title">سرویس پیش زمینه</string>
|
||||
<string name="settings_foreground_summary">افزایش اولویت سرویس</string>
|
||||
<string name="menu_status">وضعیت</string>
|
||||
<string name="menu_shortcuts">ایجاد میانبر</string>
|
||||
<string name="menu_clear">پاک کردن</string>
|
||||
<string name="status_service_create">کار شروع شد</string>
|
||||
<string name="status_service_destroy">کار متوقف شد</string>
|
||||
<string name="status_send_success">ارسال موفق</string>
|
||||
<string name="status_send_fail">ارسال ناموفق</string>
|
||||
<string name="status_location_update">به روز رسانی مکان</string>
|
||||
<string name="status_network_online">شبکه آنلاین</string>
|
||||
<string name="status_network_offline">شبکه آفلاین</string>
|
||||
<string name="hidden_app_name">تنظیمات دستگاه</string>
|
||||
<string name="hidden_alert">نرم افزار مخفی است . جهت نمایش آن با این شماره تماس بگیرید.
|
||||
8722227 (Traccar)</string>
|
||||
<string name="error_msg_invalid_url">لطفا آدرس معتبر که با http:// یا https:// شروع میشود را وارد نمایید</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Traccar Client</string>
|
||||
<string name="app_logo">Traccar</string>
|
||||
<string name="shortcut_start">Käynnistä palvelu</string>
|
||||
<string name="shortcut_stop">Pysäytä palvelu</string>
|
||||
<string name="shortcut_sos">Lähetä SOS-viesti</string>
|
||||
<string name="channel_default">Ensisijainen kanava</string>
|
||||
<string name="settings_id_title">Laitteen ID</string>
|
||||
<string name="settings_url_title">Palvelimen URL</string>
|
||||
<string name="settings_url_summary">Paikannuspalvelimen URL-osoite</string>
|
||||
<string name="settings_interval_title">Taajuus</string>
|
||||
<string name="settings_interval_summary">Raportointiväli sekunneissa</string>
|
||||
<string name="settings_distance_title">Matka</string>
|
||||
<string name="settings_distance_summary">Raportointimatka metreissä</string>
|
||||
<string name="settings_angle_title">Kulma</string>
|
||||
<string name="settings_angle_summary">Raportointikulma asteissa</string>
|
||||
<string name="settings_status_title">Palvelun tila</string>
|
||||
<string name="settings_status_off">Aloita</string>
|
||||
<string name="settings_status_on">Pysäytä</string>
|
||||
<string name="settings_status_off_summary">Palvelu pysäytetty</string>
|
||||
<string name="settings_status_on_summary">Palvelu käynnissä</string>
|
||||
<string name="settings_buffer">Offline-puskurointi</string>
|
||||
<string name="settings_buffer_off_summary">Puskurointi pois päältä</string>
|
||||
<string name="settings_buffer_on_summary">Puskurointi päälle</string>
|
||||
<string name="settings_wakelock">Lepotilalukko</string>
|
||||
<string name="settings_wakelock_off_summary">Lepotilalukko pois päältä</string>
|
||||
<string name="settings_wakelock_on_summary">Lepotilalukko päällä</string>
|
||||
<string name="settings_accuracy_title">Sijainnin tarkkuus</string>
|
||||
<string name="settings_accuracy_summary">Haluttu sijainnin tarkkuus</string>
|
||||
<string name="settings_accuracy_high">Korkea</string>
|
||||
<string name="settings_accuracy_medium">Keskitasoinen</string>
|
||||
<string name="settings_accuracy_low">Matala</string>
|
||||
<string name="settings_foreground_title">Edustapalvelu</string>
|
||||
<string name="settings_foreground_summary">Lisää palvelun prioriteettia</string>
|
||||
<string name="menu_status">Tila</string>
|
||||
<string name="menu_info">Tiedot</string>
|
||||
<string name="menu_shortcuts">Lisää pikakuvakkeet</string>
|
||||
<string name="menu_clear">Tyhjennä</string>
|
||||
<string name="status_service_create">Palvelu käynnistynyt</string>
|
||||
<string name="status_service_destroy">Palvelu pysähtyi</string>
|
||||
<string name="status_send_success">Lähetys onnistui</string>
|
||||
<string name="status_send_fail">Lähetys epäonnistui</string>
|
||||
<string name="status_location_update">Sijainnin päivitys</string>
|
||||
<string name="status_network_online">Yhdistetty verkkoon</string>
|
||||
<string name="status_network_offline">Verkkoyhteys katkennut</string>
|
||||
<string name="hidden_app_name">Laitteen asetukset</string>
|
||||
<string name="hidden_alert">Sovellus on piilotettu. Näppäile 8722227 (TRACCAR) avataksesi sovelluksen uudelleen.</string>
|
||||
<string name="error_msg_invalid_url">Syötä kelvollinen http:// tai https:// URL-osoite</string>
|
||||
<string name="request_exception">Kerätäksesi jatkuvasti sijaintitietoa, poista akun optimointi käytöstä tältä sovellukselta.</string>
|
||||
<string name="request_background">Kerätäksesi jatkuvasti sijaintitietoa, salli \"%s\" käyttöoikeus.</string>
|
||||
<string name="request_background_option">Salli kokoajan</string>
|
||||
</resources>
|
||||