Сравнить коммиты
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
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 |
@@ -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,7 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Do you want to ask a question? Traccar official forum is the best place for getting support:
|
||||||
|
|
||||||
|
https://www.traccar.org/forums/
|
||||||
|
|
||||||
|
-->
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
.gradle
|
.gradle
|
||||||
.idea/
|
.idea/
|
||||||
local.properties
|
local.properties
|
||||||
traccar-client-android.iml
|
*.iml
|
||||||
app/app.iml
|
|
||||||
build/
|
build/
|
||||||
app/build/
|
.DS_Store
|
||||||
|
google-services.json
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
# [Traccar Client for Android](https://www.traccar.org/client)
|
# [Traccar Client for Android](https://www.traccar.org/client)
|
||||||
|
|
||||||
## Contacts
|
[](https://play.google.com/store/apps/details?id=org.traccar.client) [](https://f-droid.org/repository/browse/?fdid=org.traccar.client)
|
||||||
|
|
||||||
Author - Anton Tananaev ([anton.tananaev@gmail.com](mailto:anton.tananaev@gmail.com))
|
|
||||||
|
|
||||||
Website - [https://www.traccar.org/client](https://www.traccar.org/client)
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Traccar Client is an Android GPS tracking application. It can work with Traccar open source server software.
|
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
|
## License
|
||||||
|
|
||||||
Apache License, Version 2.0
|
Apache License, Version 2.0
|
||||||
|
|||||||
@@ -1,54 +1,54 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 27
|
||||||
buildToolsVersion '23.0.1'
|
buildToolsVersion '27.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 3
|
applicationId "org.traccar.client"
|
||||||
targetSdkVersion 23
|
buildConfigField "boolean", "HIDDEN_APP", "false"
|
||||||
versionCode 27
|
minSdkVersion 15
|
||||||
versionName '3.7'
|
targetSdkVersion 27
|
||||||
|
versionCode 46
|
||||||
|
versionName '5.9'
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
lintOptions {
|
||||||
release {
|
checkReleaseBuilds false
|
||||||
storeFile file('../../android.keystore')
|
|
||||||
keyAlias 'tananaev'
|
|
||||||
storePassword ''
|
|
||||||
keyPassword ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "default"
|
||||||
productFlavors {
|
productFlavors {
|
||||||
regular {
|
regular
|
||||||
buildConfigField "boolean", "HIDDEN_APP", "false"
|
google
|
||||||
}
|
|
||||||
hidden {
|
hidden {
|
||||||
|
applicationId "org.traccar.client.hidden"
|
||||||
buildConfigField "boolean", "HIDDEN_APP", "true"
|
buildConfigField "boolean", "HIDDEN_APP", "true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile 'com.android.support:design:27.1.0'
|
||||||
|
compile 'com.mapzen.android:lost:3.0.4'
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'org.robolectric:robolectric:3.0'
|
testCompile 'org.robolectric:robolectric:3.6.1'
|
||||||
|
googleCompile 'com.google.firebase:firebase-core:11.8.0'
|
||||||
|
googleCompile 'com.crashlytics.sdk.android:crashlytics:2.9.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
task requestPasswords << {
|
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Google")) {
|
||||||
android.signingConfigs.release.storePassword = new String(System.console().readPassword('\nStore password: '))
|
apply plugin: 'com.google.gms.google-services'
|
||||||
android.signingConfigs.release.keyPassword = new String(System.console().readPassword('Key password: '))
|
apply plugin: 'io.fabric'
|
||||||
}
|
|
||||||
|
|
||||||
tasks.whenTaskAdded { theTask ->
|
task copyJson(type: Copy) {
|
||||||
if (theTask.name.equals('packageRegularRelease') || theTask.name.equals('packageHiddenRelease')) {
|
from '../../traccar-shared/google-services'
|
||||||
theTask.dependsOn 'requestPasswords'
|
into '.'
|
||||||
|
include 'traccar-client-android.json'
|
||||||
|
rename('traccar-client-android.json', 'google-services.json')
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
preBuild.dependsOn copyJson
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
|
*
|
||||||
|
* 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 com.google.firebase.analytics.FirebaseAnalytics;
|
||||||
|
|
||||||
|
public class GoogleMainApplication extends MainApplication {
|
||||||
|
|
||||||
|
private FirebaseAnalytics firebaseAnalytics;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,9 +6,19 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="@string/hidden_app_name"
|
android:label="@string/hidden_app_name"
|
||||||
tools:replace="label">
|
tools:replace="android:label">
|
||||||
|
|
||||||
<receiver android:name=".DialLaunchReceiver" >
|
<activity
|
||||||
|
android:name="org.traccar.client.MainActivity">
|
||||||
|
<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" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
|
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -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,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
|
До Ширина: | Высота: | Размер: 5.5 KiB После Ширина: | Высота: | Размер: 3.3 KiB |
|
После Ширина: | Высота: | Размер: 2.0 KiB |
|
До Ширина: | Высота: | Размер: 3.3 KiB После Ширина: | Высота: | Размер: 2.1 KiB |
|
После Ширина: | Высота: | Размер: 1.2 KiB |
|
До Ширина: | Высота: | Размер: 8.0 KiB После Ширина: | Высота: | Размер: 4.7 KiB |
|
После Ширина: | Высота: | Размер: 2.5 KiB |
|
До Ширина: | Высота: | Размер: 14 KiB После Ширина: | Высота: | Размер: 7.6 KiB |
|
После Ширина: | Высота: | Размер: 4.7 KiB |
|
До Ширина: | Высота: | Размер: 20 KiB После Ширина: | Высота: | Размер: 11 KiB |
|
После Ширина: | Высота: | Размер: 4.4 KiB |
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.traccar.client">
|
package="org.traccar.client">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
@@ -8,14 +9,18 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/TraccarTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
android:name=".MainApplication">
|
||||||
|
|
||||||
<activity android:name=".MainActivity" android:launchMode="singleTask" />
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".Launcher"
|
android:name=".Launcher"
|
||||||
@@ -24,12 +29,24 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcuts" />
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<activity android:name=".StatusActivity"/>
|
<activity android:name=".StatusActivity"/>
|
||||||
|
|
||||||
<activity android:name=".AboutActivity"/>
|
<activity android:name=".AboutActivity"/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ShortcutActivity"
|
||||||
|
android:label="@string/menu_shortcuts">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<service android:name=".TrackingService" />
|
<service android:name=".TrackingService" />
|
||||||
|
|
||||||
<service android:name=".TrackingService$HideNotificationService" />
|
<service android:name=".TrackingService$HideNotificationService" />
|
||||||
@@ -38,6 +55,9 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
|
После Ширина: | Высота: | Размер: 48 KiB |
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* 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 android.support.v4.content;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.SparseArray;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper for the common pattern of implementing a {@link BroadcastReceiver}
|
|
||||||
* that receives a device wakeup event and then passes the work off
|
|
||||||
* to a {@link android.app.Service}, while ensuring that the
|
|
||||||
* device does not go back to sleep during the transition.
|
|
||||||
*
|
|
||||||
* <p>This class takes care of creating and managing a partial wake lock
|
|
||||||
* for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
|
|
||||||
* permission to use it.</p>
|
|
||||||
*
|
|
||||||
* <h3>Example</h3>
|
|
||||||
*
|
|
||||||
* <p>A {@link WakefulBroadcastReceiver} uses the method
|
|
||||||
* {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
|
|
||||||
* to start the service that does the work. This method is comparable to
|
|
||||||
* {@link android.content.Context#startService startService()}, except that
|
|
||||||
* the {@link WakefulBroadcastReceiver} is holding a wake lock when the service
|
|
||||||
* starts. The intent that is passed with
|
|
||||||
* {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
|
|
||||||
* holds an extra identifying the wake lock.</p>
|
|
||||||
*
|
|
||||||
* {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulReceiver.java complete}
|
|
||||||
*
|
|
||||||
* <p>The service (in this example, an {@link android.app.IntentService}) does
|
|
||||||
* some work. When it is finished, it releases the wake lock by calling
|
|
||||||
* {@link WakefulBroadcastReceiver#completeWakefulIntent
|
|
||||||
* completeWakefulIntent(intent)}. The intent it passes as a parameter
|
|
||||||
* is the same intent that the {@link WakefulBroadcastReceiver} originally
|
|
||||||
* passed in.</p>
|
|
||||||
*
|
|
||||||
* {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulService.java complete}
|
|
||||||
*/
|
|
||||||
public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
|
|
||||||
private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";
|
|
||||||
|
|
||||||
private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks
|
|
||||||
= new SparseArray<PowerManager.WakeLock>();
|
|
||||||
private static int mNextId = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do a {@link android.content.Context#startService(android.content.Intent)
|
|
||||||
* Context.startService}, but holding a wake lock while the service starts.
|
|
||||||
* This will modify the Intent to hold an extra identifying the wake lock;
|
|
||||||
* when the service receives it in {@link android.app.Service#onStartCommand
|
|
||||||
* Service.onStartCommand}, it should pass back the Intent it receives there to
|
|
||||||
* {@link #completeWakefulIntent(android.content.Intent)} in order to release
|
|
||||||
* the wake lock.
|
|
||||||
*
|
|
||||||
* @param context The Context in which it operate.
|
|
||||||
* @param intent The Intent with which to start the service, as per
|
|
||||||
* {@link android.content.Context#startService(android.content.Intent)
|
|
||||||
* Context.startService}.
|
|
||||||
*/
|
|
||||||
public static ComponentName startWakefulService(Context context, Intent intent) {
|
|
||||||
synchronized (mActiveWakeLocks) {
|
|
||||||
int id = mNextId;
|
|
||||||
mNextId++;
|
|
||||||
if (mNextId <= 0) {
|
|
||||||
mNextId = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
|
|
||||||
ComponentName comp = context.startService(intent);
|
|
||||||
if (comp == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
|
||||||
"wake:" + comp.flattenToShortString());
|
|
||||||
wl.setReferenceCounted(false);
|
|
||||||
wl.acquire(60*1000);
|
|
||||||
mActiveWakeLocks.put(id, wl);
|
|
||||||
return comp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finish the execution from a previous {@link #startWakefulService}. Any wake lock
|
|
||||||
* that was being held will now be released.
|
|
||||||
*
|
|
||||||
* @param intent The Intent as originally generated by {@link #startWakefulService}.
|
|
||||||
* @return Returns true if the intent is associated with a wake lock that is
|
|
||||||
* now released; returns false if there was no wake lock specified for it.
|
|
||||||
*/
|
|
||||||
public static boolean completeWakefulIntent(Intent intent) {
|
|
||||||
final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
|
|
||||||
if (id == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
synchronized (mActiveWakeLocks) {
|
|
||||||
PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
|
|
||||||
if (wl != null) {
|
|
||||||
wl.release();
|
|
||||||
mActiveWakeLocks.remove(id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// We return true whether or not we actually found the wake lock
|
|
||||||
// the return code is defined to indicate whether the Intent contained
|
|
||||||
// an identifier for a wake lock that it was supposed to match.
|
|
||||||
// We just log a warning here if there is no wake lock found, which could
|
|
||||||
// happen for example if this function is called twice on the same
|
|
||||||
// intent or the process is killed and restarted before processing the intent.
|
|
||||||
Log.w("WakefulBroadcastReceive", "No active wake lock id #" + id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,22 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class AboutActivity extends Activity {
|
public class AboutActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.about);
|
setContentView(R.layout.about);
|
||||||
|
|
||||||
TextView title = (TextView) findViewById(R.id.title);
|
TextView title = findViewById(R.id.title);
|
||||||
try {
|
try {
|
||||||
title.setText(title.getText() + " " + getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName);
|
title.setText(title.getText() + " " + getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName);
|
||||||
} catch (NameNotFoundException e) {
|
} catch (NameNotFoundException e) {
|
||||||
|
Log.w(AboutActivity.class.getSimpleName(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2013 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,15 +19,14 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.content.WakefulBroadcastReceiver;
|
|
||||||
|
|
||||||
public class AutostartReceiver extends WakefulBroadcastReceiver {
|
public class AutostartReceiver extends WakefulBroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
if (sharedPreferences.getBoolean(MainActivity.KEY_STATUS, false)) {
|
if (sharedPreferences.getBoolean(MainFragment.KEY_STATUS, false)) {
|
||||||
startWakefulService(context, new Intent(context, TrackingService.class));
|
startWakefulForegroundService(context, new Intent(context, TrackingService.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import java.util.Date;
|
|||||||
|
|
||||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public static final int DATABASE_VERSION = 1;
|
public static final int DATABASE_VERSION = 2;
|
||||||
public static final String DATABASE_NAME = "traccar.db";
|
public static final String DATABASE_NAME = "traccar.db";
|
||||||
|
|
||||||
public interface DatabaseHandler<T> {
|
public interface DatabaseHandler<T> {
|
||||||
@@ -79,6 +79,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||||||
"altitude REAL," +
|
"altitude REAL," +
|
||||||
"speed REAL," +
|
"speed REAL," +
|
||||||
"course REAL," +
|
"course REAL," +
|
||||||
|
"accuracy REAL," +
|
||||||
"battery REAL)");
|
"battery REAL)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||||||
values.put("altitude", position.getAltitude());
|
values.put("altitude", position.getAltitude());
|
||||||
values.put("speed", position.getSpeed());
|
values.put("speed", position.getSpeed());
|
||||||
values.put("course", position.getCourse());
|
values.put("course", position.getCourse());
|
||||||
|
values.put("accuracy", position.getAccuracy());
|
||||||
values.put("battery", position.getBattery());
|
values.put("battery", position.getBattery());
|
||||||
|
|
||||||
db.insertOrThrow("position", null, values);
|
db.insertOrThrow("position", null, values);
|
||||||
@@ -129,6 +131,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||||||
position.setAltitude(cursor.getDouble(cursor.getColumnIndex("altitude")));
|
position.setAltitude(cursor.getDouble(cursor.getColumnIndex("altitude")));
|
||||||
position.setSpeed(cursor.getDouble(cursor.getColumnIndex("speed")));
|
position.setSpeed(cursor.getDouble(cursor.getColumnIndex("speed")));
|
||||||
position.setCourse(cursor.getDouble(cursor.getColumnIndex("course")));
|
position.setCourse(cursor.getDouble(cursor.getColumnIndex("course")));
|
||||||
|
position.setAccuracy(cursor.getDouble(cursor.getColumnIndex("accuracy")));
|
||||||
position.setBattery(cursor.getDouble(cursor.getColumnIndex("battery")));
|
position.setBattery(cursor.getDouble(cursor.getColumnIndex("battery")));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Anton Tananaev (anton@traccar.org)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.traccar.client;
|
||||||
|
|
||||||
|
public final class DistanceCalculator {
|
||||||
|
|
||||||
|
private static final double EQUATORIAL_EARTH_RADIUS = 6378.1370;
|
||||||
|
private static final double DEG_TO_RAD = Math.PI / 180;
|
||||||
|
|
||||||
|
public static double distance(double lat1, double lon1, double lat2, double lon2) {
|
||||||
|
double dlong = (lon2 - lon1) * DEG_TO_RAD;
|
||||||
|
double dlat = (lat2 - lat1) * DEG_TO_RAD;
|
||||||
|
double a = Math.pow(Math.sin(dlat / 2), 2)
|
||||||
|
+ Math.cos(lat1 * DEG_TO_RAD) * Math.cos(lat2 * DEG_TO_RAD) * Math.pow(Math.sin(dlong / 2), 2);
|
||||||
|
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
double d = EQUATORIAL_EARTH_RADIUS * c;
|
||||||
|
return d * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,191 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.CheckBoxPreference;
|
import android.support.annotation.Nullable;
|
||||||
import android.preference.EditTextPreference;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.preference.PreferenceScreen;
|
|
||||||
import android.preference.TwoStatePreference;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
public class MainActivity extends AppCompatActivity {
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public class MainActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
|
|
||||||
|
|
||||||
public static final String KEY_DEVICE = "id";
|
|
||||||
public static final String KEY_ADDRESS = "address";
|
|
||||||
public static final String KEY_PORT = "port";
|
|
||||||
public static final String KEY_INTERVAL = "interval";
|
|
||||||
public static final String KEY_PROVIDER = "provider";
|
|
||||||
public static final String KEY_STATUS = "status";
|
|
||||||
|
|
||||||
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
|
|
||||||
|
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
if (BuildConfig.HIDDEN_APP) {
|
getFragmentManager().beginTransaction().replace(android.R.id.content, new MainFragment()).commit();
|
||||||
removeLauncherIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
|
||||||
initPreferences();
|
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
|
||||||
startTrackingService(true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeLauncherIcon() {
|
|
||||||
ComponentName componentName = new ComponentName(getPackageName(), getPackageName() + ".Launcher");
|
|
||||||
PackageManager packageManager = getPackageManager();
|
|
||||||
if (packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
|
|
||||||
packageManager.setComponentEnabledSetting(
|
|
||||||
componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
|
||||||
builder.setMessage(getString(R.string.hidden_alert));
|
|
||||||
builder.setPositiveButton(android.R.string.ok, null);
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasPermission(String permission) {
|
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPreferencesEnabled(boolean enabled) {
|
|
||||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
|
||||||
preferenceScreen.findPreference(KEY_DEVICE).setEnabled(enabled);
|
|
||||||
preferenceScreen.findPreference(KEY_ADDRESS).setEnabled(enabled);
|
|
||||||
preferenceScreen.findPreference(KEY_PORT).setEnabled(enabled);
|
|
||||||
preferenceScreen.findPreference(KEY_INTERVAL).setEnabled(enabled);
|
|
||||||
preferenceScreen.findPreference(KEY_PROVIDER).setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
||||||
if (key.equals(KEY_STATUS)) {
|
|
||||||
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
|
||||||
startTrackingService(true, false);
|
|
||||||
} else {
|
|
||||||
stopTrackingService();
|
|
||||||
}
|
|
||||||
} else if (key.equals(KEY_DEVICE)) {
|
|
||||||
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.main, menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.status) {
|
|
||||||
startActivity(new Intent(this, StatusActivity.class));
|
|
||||||
return true;
|
|
||||||
} else if (item.getItemId() == R.id.about) {
|
|
||||||
startActivity(new Intent(this, AboutActivity.class));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initPreferences() {
|
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
|
||||||
|
|
||||||
if (!sharedPreferences.contains(KEY_DEVICE)) {
|
|
||||||
String id = String.valueOf(new Random().nextInt(900000) + 100000);
|
|
||||||
sharedPreferences.edit().putString(KEY_DEVICE, id).commit();
|
|
||||||
((EditTextPreference) findPreference(KEY_DEVICE)).setText(id);
|
|
||||||
}
|
|
||||||
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startTrackingService(boolean checkPermission, boolean permission) {
|
|
||||||
if (checkPermission) {
|
|
||||||
Set<String> missingPermissions = new HashSet<String>();
|
|
||||||
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
|
||||||
missingPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
|
|
||||||
}
|
|
||||||
if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
|
|
||||||
missingPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
|
|
||||||
}
|
|
||||||
if (missingPermissions.isEmpty()) {
|
|
||||||
permission = true;
|
|
||||||
} else {
|
|
||||||
requestPermissions(missingPermissions.toArray(new String[missingPermissions.size()]), PERMISSIONS_REQUEST_LOCATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permission) {
|
|
||||||
setPreferencesEnabled(false);
|
|
||||||
startService(new Intent(this, TrackingService.class));
|
|
||||||
} else {
|
|
||||||
sharedPreferences.edit().putBoolean(KEY_STATUS, false).commit();
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
TwoStatePreference preference = (TwoStatePreference) findPreference(KEY_STATUS);
|
|
||||||
preference.setChecked(false);
|
|
||||||
} else {
|
|
||||||
CheckBoxPreference preference = (CheckBoxPreference) findPreference(KEY_STATUS);
|
|
||||||
preference.setChecked(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopTrackingService() {
|
|
||||||
stopService(new Intent(this, TrackingService.class));
|
|
||||||
setPreferencesEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
||||||
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
|
|
||||||
startTrackingService(false, grantResults[0] == PackageManager.PERMISSION_GRANTED &&
|
|
||||||
(permissions.length < 2 || grantResults[1] == PackageManager.PERMISSION_GRANTED));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
|
*
|
||||||
|
* 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.TargetApi;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
public class MainApplication extends Application {
|
||||||
|
|
||||||
|
public static final String PRIMARY_CHANNEL = "default";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
System.setProperty("http.keepAliveDuration", String.valueOf(30 * 60 * 1000));
|
||||||
|
|
||||||
|
migrateLegacyPreferences(PreferenceManager.getDefaultSharedPreferences(this));
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
registerChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
|
private void registerChannel() {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
PRIMARY_CHANNEL, getString(R.string.channel_default), NotificationManager.IMPORTANCE_MIN);
|
||||||
|
channel.setLightColor(Color.GREEN);
|
||||||
|
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
|
||||||
|
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void migrateLegacyPreferences(SharedPreferences preferences) {
|
||||||
|
String port = preferences.getString("port", null);
|
||||||
|
if (port != null) {
|
||||||
|
String host = preferences.getString("address", getString(R.string.settings_url_default_value));
|
||||||
|
String scheme = preferences.getBoolean("secure", false) ? "https" : "http";
|
||||||
|
|
||||||
|
Uri.Builder builder = new Uri.Builder();
|
||||||
|
builder.scheme(scheme).encodedAuthority(host + ":" + port).build();
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putString(MainFragment.KEY_URL, builder.toString());
|
||||||
|
|
||||||
|
editor.remove("port");
|
||||||
|
editor.remove("address");
|
||||||
|
editor.remove("secure");
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
|
*
|
||||||
|
* 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.app.AlarmManager;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
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.preference.CheckBoxPreference;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.preference.TwoStatePreference;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.webkit.URLUtil;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class MainFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
private static final String TAG = MainFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int ALARM_MANAGER_INTERVAL = 15000;
|
||||||
|
|
||||||
|
public static final String KEY_DEVICE = "id";
|
||||||
|
public static final String KEY_URL = "url";
|
||||||
|
public static final String KEY_INTERVAL = "interval";
|
||||||
|
public static final String KEY_DISTANCE = "distance";
|
||||||
|
public static final String KEY_ANGLE = "angle";
|
||||||
|
public static final String KEY_ACCURACY = "accuracy";
|
||||||
|
public static final String KEY_STATUS = "status";
|
||||||
|
|
||||||
|
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
private AlarmManager alarmManager;
|
||||||
|
private PendingIntent alarmIntent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (BuildConfig.HIDDEN_APP) {
|
||||||
|
removeLauncherIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
|
initPreferences();
|
||||||
|
|
||||||
|
findPreference(KEY_DEVICE).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
return newValue != null && !newValue.equals("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
findPreference(KEY_URL).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
return (newValue != null) && validateServerURL(newValue.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findPreference(KEY_INTERVAL).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
if (newValue != null) {
|
||||||
|
try {
|
||||||
|
int value = Integer.parseInt((String) newValue);
|
||||||
|
return value > 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Preference.OnPreferenceChangeListener numberValidationListener = new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
if (newValue != null) {
|
||||||
|
try {
|
||||||
|
int value = Integer.parseInt((String) newValue);
|
||||||
|
return value >= 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
findPreference(KEY_DISTANCE).setOnPreferenceChangeListener(numberValidationListener);
|
||||||
|
findPreference(KEY_ANGLE).setOnPreferenceChangeListener(numberValidationListener);
|
||||||
|
|
||||||
|
alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||||
|
alarmIntent = PendingIntent.getBroadcast(getActivity(), 0, new Intent(getActivity(), AutostartReceiver.class), 0);
|
||||||
|
|
||||||
|
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
||||||
|
startTrackingService(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeLauncherIcon() {
|
||||||
|
String className = MainActivity.class.getCanonicalName().replace(".MainActivity", ".Launcher");
|
||||||
|
ComponentName componentName = new ComponentName(getActivity().getPackageName(), className);
|
||||||
|
PackageManager packageManager = getActivity().getPackageManager();
|
||||||
|
if (packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
|
||||||
|
packageManager.setComponentEnabledSetting(
|
||||||
|
componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||||
|
builder.setMessage(getString(R.string.hidden_alert));
|
||||||
|
builder.setPositiveButton(android.R.string.ok, null);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(String permission) {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ContextCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPreferencesEnabled(boolean enabled) {
|
||||||
|
findPreference(KEY_DEVICE).setEnabled(enabled);
|
||||||
|
findPreference(KEY_URL).setEnabled(enabled);
|
||||||
|
findPreference(KEY_INTERVAL).setEnabled(enabled);
|
||||||
|
findPreference(KEY_DISTANCE).setEnabled(enabled);
|
||||||
|
findPreference(KEY_ANGLE).setEnabled(enabled);
|
||||||
|
findPreference(KEY_ACCURACY).setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
if (key.equals(KEY_STATUS)) {
|
||||||
|
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
|
||||||
|
startTrackingService(true, false);
|
||||||
|
} else {
|
||||||
|
stopTrackingService();
|
||||||
|
}
|
||||||
|
} else if (key.equals(KEY_DEVICE)) {
|
||||||
|
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.main, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.status) {
|
||||||
|
startActivity(new Intent(getActivity(), StatusActivity.class));
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.about) {
|
||||||
|
startActivity(new Intent(getActivity(), AboutActivity.class));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPreferences() {
|
||||||
|
PreferenceManager.setDefaultValues(getActivity(), R.xml.preferences, false);
|
||||||
|
|
||||||
|
if (!sharedPreferences.contains(KEY_DEVICE)) {
|
||||||
|
String id = String.valueOf(new Random().nextInt(900000) + 100000);
|
||||||
|
sharedPreferences.edit().putString(KEY_DEVICE, id).apply();
|
||||||
|
((EditTextPreference) findPreference(KEY_DEVICE)).setText(id);
|
||||||
|
}
|
||||||
|
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startTrackingService(boolean checkPermission, boolean permission) {
|
||||||
|
if (checkPermission) {
|
||||||
|
Set<String> missingPermissions = new HashSet<>();
|
||||||
|
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||||
|
missingPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
}
|
||||||
|
if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
|
||||||
|
missingPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
|
||||||
|
}
|
||||||
|
if (missingPermissions.isEmpty()) {
|
||||||
|
permission = true;
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
requestPermissions(missingPermissions.toArray(new String[missingPermissions.size()]),
|
||||||
|
PERMISSIONS_REQUEST_LOCATION);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission) {
|
||||||
|
setPreferencesEnabled(false);
|
||||||
|
ContextCompat.startForegroundService(getActivity(), new Intent(getActivity(), TrackingService.class));
|
||||||
|
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||||
|
ALARM_MANAGER_INTERVAL, ALARM_MANAGER_INTERVAL, alarmIntent);
|
||||||
|
} else {
|
||||||
|
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply();
|
||||||
|
TwoStatePreference preference = (TwoStatePreference) findPreference(KEY_STATUS);
|
||||||
|
preference.setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTrackingService() {
|
||||||
|
alarmManager.cancel(alarmIntent);
|
||||||
|
getActivity().stopService(new Intent(getActivity(), TrackingService.class));
|
||||||
|
setPreferencesEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
|
||||||
|
boolean granted = true;
|
||||||
|
for (int result : grantResults) {
|
||||||
|
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
granted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startTrackingService(false, granted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateServerURL(String userUrl) {
|
||||||
|
int port = Uri.parse(userUrl).getPort();
|
||||||
|
if (URLUtil.isValidUrl(userUrl) && (port == -1 || (port > 0 && port <= 65535))
|
||||||
|
&& (URLUtil.isHttpUrl(userUrl) || URLUtil.isHttpsUrl(userUrl))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Toast.makeText(getActivity(), R.string.error_msg_invalid_url, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
|
||||||
*
|
|
||||||
* 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.location.GpsStatus;
|
|
||||||
import android.location.Location;
|
|
||||||
import android.location.LocationListener;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class MixedPositionProvider extends PositionProvider implements LocationListener, GpsStatus.Listener {
|
|
||||||
|
|
||||||
private static int FIX_TIMEOUT = 30 * 1000;
|
|
||||||
|
|
||||||
private LocationListener backupListener;
|
|
||||||
private long lastFixTime;
|
|
||||||
|
|
||||||
public MixedPositionProvider(Context context, PositionListener listener) {
|
|
||||||
super(context, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startUpdates() {
|
|
||||||
lastFixTime = System.currentTimeMillis();
|
|
||||||
locationManager.addGpsStatusListener(this);
|
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, period, 0, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopUpdates() {
|
|
||||||
locationManager.removeUpdates(this);
|
|
||||||
locationManager.removeGpsStatusListener(this);
|
|
||||||
stopBackupProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startBackupProvider() {
|
|
||||||
Log.i(TAG, "backup provider start");
|
|
||||||
if (backupListener == null) {
|
|
||||||
|
|
||||||
backupListener = new LocationListener() {
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(Location location) {
|
|
||||||
Log.i(TAG, "backup provider location");
|
|
||||||
updateLocation(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStatusChanged(String s, int i, Bundle bundle) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderEnabled(String s) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderDisabled(String s) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
locationManager.requestLocationUpdates(
|
|
||||||
LocationManager.NETWORK_PROVIDER, period, 0, backupListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopBackupProvider() {
|
|
||||||
Log.i(TAG, "backup provider stop");
|
|
||||||
if (backupListener != null) {
|
|
||||||
locationManager.removeUpdates(backupListener);
|
|
||||||
backupListener = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(Location location) {
|
|
||||||
Log.i(TAG, "provider location");
|
|
||||||
stopBackupProvider();
|
|
||||||
lastFixTime = System.currentTimeMillis();
|
|
||||||
updateLocation(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderEnabled(String provider) {
|
|
||||||
Log.i(TAG, "provider enabled");
|
|
||||||
stopBackupProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderDisabled(String provider) {
|
|
||||||
Log.i(TAG, "provider disabled");
|
|
||||||
startBackupProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGpsStatusChanged(int event) {
|
|
||||||
if (backupListener == null && System.currentTimeMillis() - (lastFixTime + period) > FIX_TIMEOUT) {
|
|
||||||
startBackupProvider();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2015 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@@ -32,43 +33,110 @@ public class Position {
|
|||||||
altitude = location.getAltitude();
|
altitude = location.getAltitude();
|
||||||
speed = location.getSpeed() * 1.943844; // speed in knots
|
speed = location.getSpeed() * 1.943844; // speed in knots
|
||||||
course = location.getBearing();
|
course = location.getBearing();
|
||||||
|
if (location.getProvider() != null && !location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
|
||||||
|
accuracy = location.getAccuracy();
|
||||||
|
}
|
||||||
this.battery = battery;
|
this.battery = battery;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
public long getId() { return id; }
|
|
||||||
public void setId(long id) { this.id = id; }
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
private String deviceId;
|
private String deviceId;
|
||||||
public String getDeviceId() { return deviceId; }
|
|
||||||
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(String deviceId) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
private Date time;
|
private Date time;
|
||||||
public Date getTime() { return time; }
|
|
||||||
public void setTime(Date time) { this.time = time; }
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(Date time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
private double latitude;
|
private double latitude;
|
||||||
public double getLatitude() { return latitude; }
|
|
||||||
public void setLatitude(double latitude) { this.latitude = latitude; }
|
public double getLatitude() {
|
||||||
|
return latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLatitude(double latitude) {
|
||||||
|
this.latitude = latitude;
|
||||||
|
}
|
||||||
|
|
||||||
private double longitude;
|
private double longitude;
|
||||||
public double getLongitude() { return longitude; }
|
|
||||||
public void setLongitude(double longitude) { this.longitude = longitude; }
|
public double getLongitude() {
|
||||||
|
return longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLongitude(double longitude) {
|
||||||
|
this.longitude = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
private double altitude;
|
private double altitude;
|
||||||
public double getAltitude() { return altitude; }
|
|
||||||
public void setAltitude(double altitude) { this.altitude = altitude; }
|
public double getAltitude() {
|
||||||
|
return altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAltitude(double altitude) {
|
||||||
|
this.altitude = altitude;
|
||||||
|
}
|
||||||
|
|
||||||
private double speed;
|
private double speed;
|
||||||
public double getSpeed() { return speed; }
|
|
||||||
public void setSpeed(double speed) { this.speed = speed; }
|
public double getSpeed() {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(double speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
private double course;
|
private double course;
|
||||||
public double getCourse() { return course; }
|
|
||||||
public void setCourse(double course) { this.course = course; }
|
public double getCourse() {
|
||||||
|
return course;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCourse(double course) {
|
||||||
|
this.course = course;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double accuracy;
|
||||||
|
|
||||||
|
public double getAccuracy() {
|
||||||
|
return accuracy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccuracy(double accuracy) {
|
||||||
|
this.accuracy = accuracy;
|
||||||
|
}
|
||||||
|
|
||||||
private double battery;
|
private double battery;
|
||||||
public double getBattery() { return battery; }
|
|
||||||
public void setBattery(double battery) { this.battery = battery; }
|
public double getBattery() {
|
||||||
|
return battery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBattery(double battery) {
|
||||||
|
this.battery = battery;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2013 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,21 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public abstract class PositionProvider {
|
import com.mapzen.android.lost.api.LocationListener;
|
||||||
|
import com.mapzen.android.lost.api.LocationRequest;
|
||||||
|
import com.mapzen.android.lost.api.LocationServices;
|
||||||
|
import com.mapzen.android.lost.api.LostApiClient;
|
||||||
|
|
||||||
protected static final String TAG = PositionProvider.class.getSimpleName();
|
public class PositionProvider implements LostApiClient.ConnectionCallbacks, LocationListener {
|
||||||
|
|
||||||
|
private static final String TAG = PositionProvider.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int MINIMUM_INTERVAL = 1000;
|
||||||
|
|
||||||
public interface PositionListener {
|
public interface PositionListener {
|
||||||
void onPositionUpdate(Position position);
|
void onPositionUpdate(Position position);
|
||||||
@@ -38,52 +43,95 @@ public abstract class PositionProvider {
|
|||||||
private final PositionListener listener;
|
private final PositionListener listener;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
protected final LocationManager locationManager;
|
private LostApiClient apiClient;
|
||||||
|
|
||||||
private String deviceId;
|
private String deviceId;
|
||||||
protected String type;
|
private long interval;
|
||||||
protected final long period;
|
private double distance;
|
||||||
|
private double angle;
|
||||||
|
|
||||||
private long lastUpdateTime;
|
private Location lastLocation;
|
||||||
|
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
public PositionProvider(Context context, PositionListener listener) {
|
public PositionProvider(Context context, PositionListener listener) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
|
|
||||||
deviceId = preferences.getString(MainActivity.KEY_DEVICE, null);
|
deviceId = preferences.getString(MainFragment.KEY_DEVICE, "undefined");
|
||||||
period = Integer.parseInt(preferences.getString(MainActivity.KEY_INTERVAL, null)) * 1000;
|
interval = Long.parseLong(preferences.getString(MainFragment.KEY_INTERVAL, "600")) * 1000;
|
||||||
|
distance = Integer.parseInt(preferences.getString(MainFragment.KEY_DISTANCE, "0"));
|
||||||
type = preferences.getString(MainActivity.KEY_PROVIDER, null);
|
angle = Integer.parseInt(preferences.getString(MainFragment.KEY_ANGLE, "0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void startUpdates();
|
public void startUpdates() {
|
||||||
|
started = true;
|
||||||
|
apiClient = new LostApiClient.Builder(context).addConnectionCallbacks(this).build();
|
||||||
|
apiClient.connect();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void stopUpdates();
|
private int getPriority(String accuracy) {
|
||||||
|
switch (accuracy) {
|
||||||
protected void updateLocation(Location location) {
|
case "high":
|
||||||
if (location != null && location.getTime() != lastUpdateTime) {
|
return LocationRequest.PRIORITY_HIGH_ACCURACY;
|
||||||
Log.i(TAG, "location new");
|
case "low":
|
||||||
lastUpdateTime = location.getTime();
|
return LocationRequest.PRIORITY_LOW_POWER;
|
||||||
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel()));
|
default:
|
||||||
} else {
|
return LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
|
||||||
Log.i(TAG, location != null ? "location old" : "location nil");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
@SuppressLint("MissingPermission")
|
||||||
private double getBatteryLevel() {
|
@Override
|
||||||
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.ECLAIR) {
|
public void onConnected() {
|
||||||
Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
if (started) {
|
||||||
|
LocationRequest request = LocationRequest.create()
|
||||||
|
.setPriority(getPriority(preferences.getString(MainFragment.KEY_ACCURACY, "medium")))
|
||||||
|
.setInterval(distance > 0 || angle > 0 ? MINIMUM_INTERVAL : interval);
|
||||||
|
LocationServices.FusedLocationApi.requestLocationUpdates(apiClient, request, this);
|
||||||
|
} else {
|
||||||
|
apiClient.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location location) {
|
||||||
|
if (location != null && (lastLocation == null
|
||||||
|
|| location.getTime() - lastLocation.getTime() >= interval
|
||||||
|
|| distance > 0 && DistanceCalculator.distance(location.getLatitude(), location.getLongitude(), lastLocation.getLatitude(), lastLocation.getLongitude()) >= distance
|
||||||
|
|| angle > 0 && Math.abs(location.getBearing() - lastLocation.getBearing()) >= angle)) {
|
||||||
|
Log.i(TAG, "location new");
|
||||||
|
lastLocation = location;
|
||||||
|
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel(context)));
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, location != null ? "location ignored" : "location nil");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended() {
|
||||||
|
Log.i(TAG, "lost client suspended");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopUpdates() {
|
||||||
|
if (apiClient.isConnected()) {
|
||||||
|
LocationServices.FusedLocationApi.removeLocationUpdates(apiClient, this);
|
||||||
|
apiClient.disconnect();
|
||||||
|
}
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getBatteryLevel(Context context) {
|
||||||
|
Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
if (batteryIntent != null) {
|
||||||
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
|
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
|
||||||
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
|
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
|
||||||
return (level * 100.0) / scale;
|
return (level * 100.0) / scale;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,15 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.location.Location;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
public class ProtocolFormatter {
|
public class ProtocolFormatter {
|
||||||
|
|
||||||
public static String formatRequest(String address, int port, Position position) {
|
public static String formatRequest(String url, Position position) {
|
||||||
|
return formatRequest(url, position, null);
|
||||||
|
}
|
||||||
|
|
||||||
Uri.Builder builder = new Uri.Builder();
|
public static String formatRequest(String url, Position position, String alarm) {
|
||||||
builder.scheme("http").encodedAuthority(address + ':' + port)
|
Uri serverUrl = Uri.parse(url);
|
||||||
|
Uri.Builder builder = serverUrl.buildUpon()
|
||||||
.appendQueryParameter("id", position.getDeviceId())
|
.appendQueryParameter("id", position.getDeviceId())
|
||||||
.appendQueryParameter("timestamp", String.valueOf(position.getTime().getTime() / 1000))
|
.appendQueryParameter("timestamp", String.valueOf(position.getTime().getTime() / 1000))
|
||||||
.appendQueryParameter("lat", String.valueOf(position.getLatitude()))
|
.appendQueryParameter("lat", String.valueOf(position.getLatitude()))
|
||||||
@@ -31,9 +33,13 @@ public class ProtocolFormatter {
|
|||||||
.appendQueryParameter("speed", String.valueOf(position.getSpeed()))
|
.appendQueryParameter("speed", String.valueOf(position.getSpeed()))
|
||||||
.appendQueryParameter("bearing", String.valueOf(position.getCourse()))
|
.appendQueryParameter("bearing", String.valueOf(position.getCourse()))
|
||||||
.appendQueryParameter("altitude", String.valueOf(position.getAltitude()))
|
.appendQueryParameter("altitude", String.valueOf(position.getAltitude()))
|
||||||
|
.appendQueryParameter("accuracy", String.valueOf(position.getAccuracy()))
|
||||||
.appendQueryParameter("batt", String.valueOf(position.getBattery()));
|
.appendQueryParameter("batt", String.valueOf(position.getBattery()));
|
||||||
|
|
||||||
|
if (alarm != null) {
|
||||||
|
builder.appendQueryParameter("alarm", alarm);
|
||||||
|
}
|
||||||
|
|
||||||
return builder.build().toString();
|
return builder.build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package org.traccar.client;
|
package org.traccar.client;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -56,6 +57,7 @@ public class RequestManager {
|
|||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setReadTimeout(TIMEOUT);
|
connection.setReadTimeout(TIMEOUT);
|
||||||
connection.setConnectTimeout(TIMEOUT);
|
connection.setConnectTimeout(TIMEOUT);
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
connection.connect();
|
connection.connect();
|
||||||
inputStream = connection.getInputStream();
|
inputStream = connection.getInputStream();
|
||||||
while (inputStream.read() != -1);
|
while (inputStream.read() != -1);
|
||||||
@@ -68,7 +70,7 @@ public class RequestManager {
|
|||||||
inputStream.close();
|
inputStream.close();
|
||||||
}
|
}
|
||||||
} catch (IOException secondError) {
|
} catch (IOException secondError) {
|
||||||
return false;
|
Log.w(RequestManager.class.getSimpleName(), secondError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
|
*
|
||||||
|
* 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.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v4.content.pm.ShortcutInfoCompat;
|
||||||
|
import android.support.v4.content.pm.ShortcutManagerCompat;
|
||||||
|
import android.support.v4.graphics.drawable.IconCompat;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.mapzen.android.lost.api.LocationServices;
|
||||||
|
import com.mapzen.android.lost.api.LostApiClient;
|
||||||
|
|
||||||
|
public class ShortcutActivity extends AppCompatActivity implements LostApiClient.ConnectionCallbacks {
|
||||||
|
|
||||||
|
public static final String EXTRA_ACTION = "action";
|
||||||
|
public static final String ACTION_START = "start";
|
||||||
|
public static final String ACTION_STOP = "stop";
|
||||||
|
public static final String ACTION_SOS = "sos";
|
||||||
|
|
||||||
|
private static final String ALARM_SOS = "sos";
|
||||||
|
|
||||||
|
private LostApiClient apiClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (!executeAction(getIntent())) {
|
||||||
|
setContentView(R.layout.list);
|
||||||
|
|
||||||
|
final String[] items = new String[] {
|
||||||
|
getString(R.string.shortcut_start),
|
||||||
|
getString(R.string.shortcut_stop),
|
||||||
|
getString(R.string.shortcut_sos)
|
||||||
|
};
|
||||||
|
|
||||||
|
ListView listView = findViewById(android.R.id.list);
|
||||||
|
|
||||||
|
listView.setAdapter(new ArrayAdapter<>(
|
||||||
|
this, android.R.layout.simple_list_item_1, items));
|
||||||
|
|
||||||
|
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
setShortcutResult(items[position], R.mipmap.ic_start, ACTION_START);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
setShortcutResult(items[position], R.mipmap.ic_stop, ACTION_STOP);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
setShortcutResult(items[position], R.mipmap.ic_sos, ACTION_SOS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
executeAction(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShortcutResult(String label, @DrawableRes int iconResId, String action) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_DEFAULT, null, this, ShortcutActivity.class);
|
||||||
|
intent.putExtra(EXTRA_ACTION, action);
|
||||||
|
|
||||||
|
ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, action)
|
||||||
|
.setShortLabel(label)
|
||||||
|
.setIcon(IconCompat.createWithResource(this, iconResId))
|
||||||
|
.setIntent(intent)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAlarm() {
|
||||||
|
apiClient = new LostApiClient.Builder(this).addConnectionCallbacks(this).build();
|
||||||
|
apiClient.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("MissingPermission")
|
||||||
|
@Override
|
||||||
|
public void onConnected() {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
|
Location location = LocationServices.FusedLocationApi.getLastLocation(apiClient);
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
|
||||||
|
Position position = new Position(
|
||||||
|
preferences.getString(MainFragment.KEY_DEVICE, null),
|
||||||
|
location, PositionProvider.getBatteryLevel(this));
|
||||||
|
|
||||||
|
String request = ProtocolFormatter.formatRequest(
|
||||||
|
preferences.getString(MainFragment.KEY_URL, null), position, ALARM_SOS);
|
||||||
|
|
||||||
|
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(boolean success) {
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(ShortcutActivity.this, R.string.status_send_success, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(ShortcutActivity.this, R.string.status_send_fail, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.status_send_fail, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean executeAction(Intent intent) {
|
||||||
|
String action;
|
||||||
|
if (intent.hasExtra("shortcutAction")) {
|
||||||
|
action = intent.getBooleanExtra("shortcutAction", false)
|
||||||
|
? ACTION_START : ACTION_STOP;
|
||||||
|
} else {
|
||||||
|
action = intent.getStringExtra(EXTRA_ACTION);
|
||||||
|
}
|
||||||
|
if (action != null) {
|
||||||
|
switch (action) {
|
||||||
|
case ACTION_START:
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.edit().putBoolean(MainFragment.KEY_STATUS, true).apply();
|
||||||
|
ContextCompat.startForegroundService(this, new Intent(this, TrackingService.class));
|
||||||
|
Toast.makeText(this, R.string.status_service_create, Toast.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case ACTION_STOP:
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.edit().putBoolean(MainFragment.KEY_STATUS, false).apply();
|
||||||
|
stopService(new Intent(this, TrackingService.class));
|
||||||
|
Toast.makeText(this, R.string.status_service_destroy, Toast.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case ACTION_SOS:
|
||||||
|
sendAlarm();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
return action != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
|
||||||
*
|
|
||||||
* 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.location.Location;
|
|
||||||
import android.location.LocationListener;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
public class SimplePositionProvider extends PositionProvider implements LocationListener {
|
|
||||||
|
|
||||||
public SimplePositionProvider(Context context, PositionListener listener) {
|
|
||||||
super(context, listener);
|
|
||||||
if (!type.equals(LocationManager.NETWORK_PROVIDER)) {
|
|
||||||
type = LocationManager.GPS_PROVIDER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startUpdates() {
|
|
||||||
locationManager.requestLocationUpdates(type, period, 0, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopUpdates() {
|
|
||||||
locationManager.removeUpdates(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLocationChanged(Location location) {
|
|
||||||
updateLocation(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderEnabled(String provider) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderDisabled(String provider) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 - 2013 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,19 +21,20 @@ import java.util.HashSet;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import android.app.ListActivity;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
public class StatusActivity extends ListActivity {
|
public class StatusActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private static final int LIMIT = 20;
|
private static final int LIMIT = 20;
|
||||||
|
|
||||||
private static final LinkedList<String> messages = new LinkedList<String>();
|
private static final LinkedList<String> messages = new LinkedList<>();
|
||||||
private static final Set<ArrayAdapter<String>> adapters = new HashSet<ArrayAdapter<String>>();
|
private static final Set<ArrayAdapter<String>> adapters = new HashSet<>();
|
||||||
|
|
||||||
private static void notifyAdapters() {
|
private static void notifyAdapters() {
|
||||||
for (ArrayAdapter<String> adapter : adapters) {
|
for (ArrayAdapter<String> adapter : adapters) {
|
||||||
@@ -61,9 +62,10 @@ public class StatusActivity extends ListActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.status);
|
setContentView(R.layout.list);
|
||||||
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages);
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages);
|
||||||
setListAdapter(adapter);
|
ListView listView = findViewById(android.R.id.list);
|
||||||
|
listView.setAdapter(adapter);
|
||||||
adapters.add(adapter);
|
adapters.add(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package org.traccar.client;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
@@ -27,16 +26,16 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
|
|
||||||
private static final String TAG = TrackingController.class.getSimpleName();
|
private static final String TAG = TrackingController.class.getSimpleName();
|
||||||
private static final int RETRY_DELAY = 30 * 1000;
|
private static final int RETRY_DELAY = 30 * 1000;
|
||||||
private static final int WAKE_LOCK_TIMEOUT = 60 * 1000;
|
private static final int WAKE_LOCK_TIMEOUT = 120 * 1000;
|
||||||
|
|
||||||
private boolean isOnline;
|
private boolean isOnline;
|
||||||
private boolean isWaiting;
|
private boolean isWaiting;
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
private SharedPreferences preferences;
|
||||||
|
|
||||||
private String address;
|
private String url;
|
||||||
private int port;
|
|
||||||
|
|
||||||
private PositionProvider positionProvider;
|
private PositionProvider positionProvider;
|
||||||
private DatabaseHelper databaseHelper;
|
private DatabaseHelper databaseHelper;
|
||||||
@@ -45,11 +44,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
private PowerManager.WakeLock wakeLock;
|
private PowerManager.WakeLock wakeLock;
|
||||||
|
|
||||||
private void lock() {
|
private void lock() {
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
|
wakeLock.acquire(WAKE_LOCK_TIMEOUT);
|
||||||
wakeLock.acquire();
|
|
||||||
} else {
|
|
||||||
wakeLock.acquire(WAKE_LOCK_TIMEOUT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unlock() {
|
private void unlock() {
|
||||||
@@ -61,18 +56,13 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
public TrackingController(Context context) {
|
public TrackingController(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
handler = new Handler();
|
handler = new Handler();
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
if (preferences.getString(MainActivity.KEY_PROVIDER, null).equals("mixed")) {
|
positionProvider = new PositionProvider(context, this);
|
||||||
positionProvider = new MixedPositionProvider(context, this);
|
|
||||||
} else {
|
|
||||||
positionProvider = new SimplePositionProvider(context, this);
|
|
||||||
}
|
|
||||||
databaseHelper = new DatabaseHelper(context);
|
databaseHelper = new DatabaseHelper(context);
|
||||||
networkManager = new NetworkManager(context, this);
|
networkManager = new NetworkManager(context, this);
|
||||||
isOnline = networkManager.isOnline();
|
isOnline = networkManager.isOnline();
|
||||||
|
|
||||||
address = preferences.getString(MainActivity.KEY_ADDRESS, null);
|
url = preferences.getString(MainFragment.KEY_URL, context.getString(R.string.settings_url_default_value));
|
||||||
port = Integer.parseInt(preferences.getString(MainActivity.KEY_PORT, null));
|
|
||||||
|
|
||||||
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
|
||||||
@@ -82,13 +72,21 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
if (isOnline) {
|
if (isOnline) {
|
||||||
read();
|
read();
|
||||||
}
|
}
|
||||||
positionProvider.startUpdates();
|
try {
|
||||||
|
positionProvider.startUpdates();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
networkManager.start();
|
networkManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
networkManager.stop();
|
networkManager.stop();
|
||||||
positionProvider.stopUpdates();
|
try {
|
||||||
|
positionProvider.stopUpdates();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
handler.removeCallbacksAndMessages(null);
|
handler.removeCallbacksAndMessages(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +100,8 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNetworkUpdate(boolean isOnline) {
|
public void onNetworkUpdate(boolean isOnline) {
|
||||||
StatusActivity.addMessage(context.getString(R.string.status_connectivity_change));
|
int message = isOnline ? R.string.status_network_online : R.string.status_network_offline;
|
||||||
|
StatusActivity.addMessage(context.getString(message));
|
||||||
if (!this.isOnline && isOnline) {
|
if (!this.isOnline && isOnline) {
|
||||||
read();
|
read();
|
||||||
}
|
}
|
||||||
@@ -153,7 +152,11 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
public void onComplete(boolean success, Position result) {
|
public void onComplete(boolean success, Position result) {
|
||||||
if (success) {
|
if (success) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
send(result);
|
if (result.getDeviceId().equals(preferences.getString(MainFragment.KEY_DEVICE, null))) {
|
||||||
|
send(result);
|
||||||
|
} else {
|
||||||
|
delete(result);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
isWaiting = true;
|
isWaiting = true;
|
||||||
}
|
}
|
||||||
@@ -184,7 +187,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
|
|||||||
private void send(final Position position) {
|
private void send(final Position position) {
|
||||||
log("send", position);
|
log("send", position);
|
||||||
lock();
|
lock();
|
||||||
String request = ProtocolFormatter.formatRequest(address, port, position);
|
String request = ProtocolFormatter.formatRequest(url, position);
|
||||||
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
|
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(boolean success) {
|
public void onComplete(boolean success) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
|
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,12 +23,10 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class TrackingService extends Service {
|
public class TrackingService extends Service {
|
||||||
|
|
||||||
private static final String TAG = TrackingService.class.getSimpleName();
|
private static final String TAG = TrackingService.class.getSimpleName();
|
||||||
@@ -36,31 +34,25 @@ public class TrackingService extends Service {
|
|||||||
|
|
||||||
private TrackingController trackingController;
|
private TrackingController trackingController;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private static Notification createNotification(Context context) {
|
private static Notification createNotification(Context context) {
|
||||||
Intent notificationIntent = new Intent(context, MainActivity.class);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, MainApplication.PRIMARY_CHANNEL)
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
.setSmallIcon(R.drawable.ic_stat_notify)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
Notification notification = new Notification(android.R.drawable.stat_notify_sync_noanim, null, 0);
|
.setCategory(Notification.CATEGORY_SERVICE);
|
||||||
try {
|
Intent intent;
|
||||||
Method method = notification.getClass().getMethod("setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class);
|
if (!BuildConfig.HIDDEN_APP) {
|
||||||
try {
|
intent = new Intent(context, MainActivity.class);
|
||||||
method.invoke(notification, context, context.getString(R.string.app_name), context.getString(R.string.settings_status_on_summary), pendingIntent);
|
builder
|
||||||
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
|
.setContentTitle(context.getString(R.string.settings_status_on_summary))
|
||||||
Log.w(TAG, e);
|
.setTicker(context.getString(R.string.settings_status_on_summary))
|
||||||
}
|
.setColor(ContextCompat.getColor(context, R.color.primary_dark));
|
||||||
} catch (SecurityException | NoSuchMethodException e) {
|
} else {
|
||||||
Log.w(TAG, e);
|
intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
|
||||||
}
|
}
|
||||||
|
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
return builder.build();
|
||||||
notification.priority = Notification.PRIORITY_MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
return notification;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
|
||||||
public static class HideNotificationService extends Service {
|
public static class HideNotificationService extends Service {
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
@@ -88,10 +80,8 @@ public class TrackingService extends Service {
|
|||||||
trackingController = new TrackingController(this);
|
trackingController = new TrackingController(this);
|
||||||
trackingController.start();
|
trackingController.start();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
|
startForeground(NOTIFICATION_ID, createNotification(this));
|
||||||
startForeground(NOTIFICATION_ID, createNotification(this));
|
ContextCompat.startForegroundService(this, new Intent(this, HideNotificationService.class));
|
||||||
startService(new Intent(this, HideNotificationService.class));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,18 +89,12 @@ public class TrackingService extends Service {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
public void onStart(Intent intent, int startId) {
|
|
||||||
if (intent != null) {
|
|
||||||
AutostartReceiver.completeWakefulIntent(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
onStart(intent, startId);
|
if (intent != null) {
|
||||||
|
AutostartReceiver.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,9 +103,7 @@ public class TrackingService extends Service {
|
|||||||
Log.i(TAG, "service destroy");
|
Log.i(TAG, "service destroy");
|
||||||
StatusActivity.addMessage(getString(R.string.status_service_destroy));
|
StatusActivity.addMessage(getString(R.string.status_service_destroy));
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
|
stopForeground(true);
|
||||||
stopForeground(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackingController != null) {
|
if (trackingController != null) {
|
||||||
trackingController.stop();
|
trackingController.stop();
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* 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 android.os.PowerManager;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";
|
||||||
|
private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks = new SparseArray<>();
|
||||||
|
private static int mNextId = 1;
|
||||||
|
|
||||||
|
public static void startWakefulForegroundService(Context context, Intent intent) {
|
||||||
|
synchronized (mActiveWakeLocks) {
|
||||||
|
int id = mNextId;
|
||||||
|
mNextId++;
|
||||||
|
if (mNextId <= 0) {
|
||||||
|
mNextId = 1;
|
||||||
|
}
|
||||||
|
intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
|
||||||
|
ContextCompat.startForegroundService(context, intent);
|
||||||
|
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
|
WakefulBroadcastReceiver.class.getSimpleName());
|
||||||
|
wl.setReferenceCounted(false);
|
||||||
|
wl.acquire(60*1000);
|
||||||
|
mActiveWakeLocks.put(id, wl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean completeWakefulIntent(Intent intent) {
|
||||||
|
final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
|
||||||
|
if (id == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
synchronized (mActiveWakeLocks) {
|
||||||
|
PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
|
||||||
|
if (wl != null) {
|
||||||
|
wl.release();
|
||||||
|
mActiveWakeLocks.remove(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -1,56 +1,37 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent" >
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_toRightOf="@+id/logo"
|
||||||
|
android:layout_toEndOf="@+id/logo"
|
||||||
|
android:padding="@dimen/spacing_normal"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
<ImageView
|
<TextView
|
||||||
android:id="@+id/logo"
|
android:id="@+id/title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_marginBottom="@dimen/spacing_normal"
|
||||||
android:layout_alignParentStart="true"
|
android:text="@string/app_name"
|
||||||
android:layout_alignParentTop="true"
|
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||||
android:layout_marginLeft="@dimen/about_margin"
|
|
||||||
android:layout_marginStart="@dimen/about_margin"
|
|
||||||
android:layout_marginTop="@dimen/about_margin"
|
|
||||||
android:src="@mipmap/ic_launcher"
|
|
||||||
android:contentDescription="@string/app_logo" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="@dimen/about_margin"
|
android:layout_marginBottom="@dimen/spacing_normal"
|
||||||
android:layout_toRightOf="@+id/logo"
|
android:text="@string/about_description" />
|
||||||
android:layout_toEndOf="@+id/logo"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginBottom="@dimen/spacing_normal"
|
||||||
android:layout_marginBottom="@dimen/about_spacing"
|
android:text="@string/about_license" />
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="@dimen/about_spacing"
|
android:text="@string/about_web"
|
||||||
android:text="@string/about_description" />
|
android:autoLink="web" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="@dimen/about_spacing"
|
|
||||||
android:text="@string/about_license" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="@dimen/about_spacing"
|
|
||||||
android:text="@string/about_web"
|
|
||||||
android:autoLink="web" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|||||||
@@ -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" />
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?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="fill_parent"
|
|
||||||
android:layout_height="fill_parent" />
|
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
android:title="@string/menu_status"
|
android:title="@string/menu_status"
|
||||||
android:showAsAction="always" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/about"
|
android:id="@+id/about"
|
||||||
android:title="@string/menu_about"
|
android:title="@string/menu_about"
|
||||||
android:showAsAction="ifRoom" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/clear"
|
android:id="@+id/clear"
|
||||||
android:title="@string/menu_clear"
|
android:title="@string/menu_clear"
|
||||||
android:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/primary"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/primary"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_sos_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_sos_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_start_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_start_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_stop_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/white"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_stop_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
|
После Ширина: | Высота: | Размер: 4.2 KiB |
|
После Ширина: | Высота: | Размер: 3.0 KiB |
|
После Ширина: | Высота: | Размер: 3.1 KiB |
|
После Ширина: | Высота: | Размер: 1.5 KiB |
|
После Ширина: | Высота: | Размер: 3.2 KiB |
|
После Ширина: | Высота: | Размер: 1.7 KiB |
|
После Ширина: | Высота: | Размер: 3.1 KiB |
|
После Ширина: | Высота: | Размер: 1.6 KiB |
|
После Ширина: | Высота: | Размер: 2.5 KiB |
|
После Ширина: | Высота: | Размер: 1.7 KiB |
|
После Ширина: | Высота: | Размер: 1.9 KiB |
|
После Ширина: | Высота: | Размер: 930 B |
|
После Ширина: | Высота: | Размер: 2.0 KiB |
|
После Ширина: | Высота: | Размер: 1.0 KiB |
|
После Ширина: | Высота: | Размер: 2.0 KiB |
|
После Ширина: | Высота: | Размер: 943 B |
|
После Ширина: | Высота: | Размер: 5.9 KiB |
|
После Ширина: | Высота: | Размер: 3.8 KiB |
|
После Ширина: | Высота: | Размер: 4.5 KiB |
|
После Ширина: | Высота: | Размер: 2.1 KiB |
|
После Ширина: | Высота: | Размер: 4.6 KiB |
|
После Ширина: | Высота: | Размер: 2.4 KiB |
|
После Ширина: | Высота: | Размер: 4.5 KiB |
|
После Ширина: | Высота: | Размер: 2.1 KiB |
|
После Ширина: | Высота: | Размер: 9.4 KiB |
|
После Ширина: | Высота: | Размер: 7.4 KiB |
|
После Ширина: | Высота: | Размер: 7.3 KiB |
|
После Ширина: | Высота: | Размер: 4.0 KiB |
|
После Ширина: | Высота: | Размер: 7.6 KiB |
|
После Ширина: | Высота: | Размер: 4.6 KiB |
|
После Ширина: | Высота: | Размер: 7.3 KiB |
|
После Ширина: | Высота: | Размер: 4.0 KiB |
|
После Ширина: | Высота: | Размер: 13 KiB |
|
После Ширина: | Высота: | Размер: 8.8 KiB |
|
После Ширина: | Высота: | Размер: 11 KiB |
|
После Ширина: | Высота: | Размер: 5.4 KiB |
|
После Ширина: | Высота: | Размер: 11 KiB |
|
После Ширина: | Высота: | Размер: 6.2 KiB |
|
После Ширина: | Высота: | Размер: 11 KiB |
|
После Ширина: | Высота: | Размер: 5.4 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
<?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">رابط الخادم</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_provider_title">موفر الموقع</string>
|
||||||
|
<string name="settings_provider_summary">مصدر بيانات الموقع</string>
|
||||||
|
<string name="settings_provider_gps">موفر نظام تحديد الموقع</string>
|
||||||
|
<string name="settings_provider_network">موفر الشبكة</string>
|
||||||
|
<string name="settings_provider_mixed">موفر المختلط</string>
|
||||||
|
<string name="settings_foreground_title">الخدمة الأمامية</string>
|
||||||
|
<string name="settings_foreground_summary">زيادة أولوية الخدمة</string>
|
||||||
|
<string name="menu_status">الحالة</string>
|
||||||
|
<string name="menu_about">حول</string>
|
||||||
|
<string name="menu_shortcuts">إضافة اختصارات</string>
|
||||||
|
<string name="menu_clear">مسح</string>
|
||||||
|
<string name="about_description">محدد الموقع في الوقت الفعلي لأجهزة Android. متوافق مع خادم Traccar ونظم تعقب أخرى </string>
|
||||||
|
<string name="about_license">هذا التطبيق مجاني ومفتوح المصدر، الكود المصدري مرخص تحت رخصة Apache الإصدار 2.0 و متاح على GitHub.</string>
|
||||||
|
<string name="about_web">لمزيد من المعلومات زر \nwww.traccar.org/client</string>
|
||||||
|
<string name="status_send_success">تم الإرسال بنجاح</string>
|
||||||
|
<string name="status_send_fail">فشل الإرسال</string>
|
||||||
|
<string name="status_location_update">تحديث الموقع</string>
|
||||||
|
<string name="status_connectivity_change">تغير الارتباط</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,37 @@
|
|||||||
|
<?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_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_provider_title">Доставчик на местоположение</string>
|
||||||
|
<string name="settings_provider_summary">Източник на данни за местоположението</string>
|
||||||
|
<string name="settings_provider_gps">GPS</string>
|
||||||
|
<string name="settings_provider_network">Мрежи</string>
|
||||||
|
<string name="settings_provider_mixed">Смесен Доставчик</string>
|
||||||
|
<string name="settings_foreground_title">Foreground service</string>
|
||||||
|
<string name="settings_foreground_summary">Увеличаване на приоритета на услугата</string>
|
||||||
|
<string name="menu_status">Състояние</string>
|
||||||
|
<string name="menu_about">Относно</string>
|
||||||
|
<string name="menu_shortcuts">Добави пряк път</string>
|
||||||
|
<string name="menu_clear">Изтрий</string>
|
||||||
|
<string name="about_description">Real time GPS tracker for Android devices. Compatible with Traccar Server and other tracking systems.</string>
|
||||||
|
<string name="about_license">This application is free and open source, source code is licensed under Apache License Version 2.0 and available on GitHub.</string>
|
||||||
|
<string name="about_web">For more information visit\nwww.traccar.org/client</string>
|
||||||
|
<string name="status_send_success">Успешно изпращане</string>
|
||||||
|
<string name="status_send_fail">Грешка в изпращането</string>
|
||||||
|
<string name="status_location_update">Местоположението е обновено</string>
|
||||||
|
<string name="status_connectivity_change">Промяна в свързването</string>
|
||||||
|
<string name="hidden_app_name">Настройки</string>
|
||||||
|
<string name="hidden_alert">Приложението е скрито. За да го отворите отново, моля наберете 8722227 (TRACCAR).</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?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_provider_title">লোকেশন প্রদানকারী</string>
|
||||||
|
<string name="settings_provider_summary">লোকেশন ডেটা উৎস</string>
|
||||||
|
<string name="settings_provider_gps">জিপিএস প্রদানকারী</string>
|
||||||
|
<string name="settings_provider_network">নেটওয়ার্ক প্রদানকারী</string>
|
||||||
|
<string name="settings_provider_mixed">মিশ্র প্রদানকারী</string>
|
||||||
|
<string name="settings_foreground_title">ফোরগ্রাউন্ড সার্ভিস</string>
|
||||||
|
<string name="settings_foreground_summary">সার্ভিসের পূর্ববর্তিতা বৃদ্ধি</string>
|
||||||
|
<string name="menu_status">অবস্থা</string>
|
||||||
|
<string name="menu_about">সম্পর্কে / সম্বন্ধে</string>
|
||||||
|
<string name="menu_shortcuts">শর্টকাট যুক্ত করা</string>
|
||||||
|
<string name="menu_clear">পরিষ্কার</string>
|
||||||
|
<string name="about_description">অ্যান্ড্রয়েড ডিভাইসের জন্য রিয়েল টাইম GPS ট্র্যাকার। Traccar সার্ভার এবং অন্যান্য ট্র্যাকিং সিস্টেমগুলির সাথে সামঞ্জস্যপূর্ণ।</string>
|
||||||
|
<string name="about_license">এই অ্যাপ্লিকেশনটি বিনামূল্যে প্রদানকৃত এবং ওপেন সোর্স, সোর্স কোডটি Apache License Version 2.0 এর অধীনে লাইসেন্স করা এবং GitHub এ রক্ষিত।</string>
|
||||||
|
<string name="about_web">আরও তথ্যের জন্য www.traccar.org/client দেখুন</string>
|
||||||
|
<string name="status_send_success">সফলভাবে পাঠান হয়েছে</string>
|
||||||
|
<string name="status_send_fail">পাঠান ব্যর্থ হয়েছে</string>
|
||||||
|
<string name="status_location_update">অবস্থান আপডেট</string>
|
||||||
|
<string name="status_connectivity_change">সংযোগ পরিবর্তন হয়েছে</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>
|
||||||