Compare commits

...

57 Commits

Author SHA1 Message Date
dependabot[bot]
90e0d9f9be actions-update: Bump the actions-dependencies group across 1 directory with 3 updates (#763)
Bumps the actions-dependencies group with 3 updates in the / directory: [step-security/harden-runner](https://github.com/step-security/harden-runner), [actions/setup-java](https://github.com/actions/setup-java) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `step-security/harden-runner` from 2.13.2 to 2.13.3
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](95d9a5deda...df199fb7be)

Updates `actions/setup-java` from 5.0.0 to 5.1.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](dded088883...f2beeb24e1)

Updates `github/codeql-action` from 4.31.4 to 4.31.6
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](e12f017898...fe4161a26a)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: actions/setup-java
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
- dependency-name: github/codeql-action
  dependency-version: 4.31.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 17:22:09 -05:00
dependabot[bot]
80b6848a09 actions-update: Bump the actions-dependencies group across 1 directory with 4 updates (#760)
Bumps the actions-dependencies group with 4 updates in the / directory: [step-security/harden-runner](https://github.com/step-security/harden-runner), [actions/checkout](https://github.com/actions/checkout), [github/codeql-action](https://github.com/github/codeql-action) and [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner).


Updates `step-security/harden-runner` from 2.13.1 to 2.13.2
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](f4a75cfd61...95d9a5deda)

Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

Updates `github/codeql-action` from 4.31.0 to 4.31.4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4e94bd11f7...e12f017898)

Updates `reactivecircus/android-emulator-runner` from 2.34.0 to 2.35.0
- [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases)
- [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md)
- [Commits](1dcd009011...b530d96654)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
- dependency-name: github/codeql-action
  dependency-version: 4.31.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: reactivecircus/android-emulator-runner
  dependency-version: 2.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-22 21:20:45 -05:00
dependabot[bot]
c5a3d684e6 actions-update: Bump the actions-dependencies group across 1 directory with 3 updates (#755)
Bumps the actions-dependencies group with 3 updates in the / directory: [gradle/actions](https://github.com/gradle/actions), [github/codeql-action](https://github.com/github/codeql-action) and [actions/upload-artifact](https://github.com/actions/upload-artifact).


Updates `gradle/actions` from 4.4.3 to 5.0.0
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](ed408507ea...4d9f0ba002)

Updates `github/codeql-action` from 3.30.3 to 4.31.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](192325c861...4e94bd11f7)

Updates `actions/upload-artifact` from 4.6.2 to 5.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](ea165f8d65...330a01c490)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
- dependency-name: github/codeql-action
  dependency-version: 4.31.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
- dependency-name: actions/upload-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 07:44:21 -04:00
dependabot[bot]
f549508ad3 actions-update: Bump actions/cache in the actions-dependencies group (#751)
Bumps the actions-dependencies group with 1 update: [actions/cache](https://github.com/actions/cache).


Updates `actions/cache` from 4.2.4 to 4.3.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0400d5f644...0057852bfa)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-24 23:14:29 -04:00
dependabot[bot]
a3dfa50b99 actions-update: Bump github/codeql-action (#750)
Bumps the actions-dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.30.2 to 3.30.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](d3678e237b...192325c861)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-10 21:49:25 -04:00
dependabot[bot]
640bd4b9a7 actions-update: Bump the actions-dependencies group with 3 updates (#749)
Bumps the actions-dependencies group with 3 updates: [step-security/harden-runner](https://github.com/step-security/harden-runner), [gradle/actions](https://github.com/gradle/actions) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `step-security/harden-runner` from 2.13.0 to 2.13.1
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](ec9f2d5744...f4a75cfd61)

Updates `gradle/actions` from 4.4.2 to 4.4.3
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](017a9effdb...ed408507ea)

Updates `github/codeql-action` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f1f6e5f6af...d3678e237b)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: gradle/actions
  dependency-version: 4.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: github/codeql-action
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 22:32:28 -04:00
dependabot[bot]
2b1ed93629 actions-update: Bump the actions-dependencies group with 2 updates (#748)
Bumps the actions-dependencies group with 2 updates: [actions/setup-java](https://github.com/actions/setup-java) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `actions/setup-java` from 4.7.1 to 5.0.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](c5195efecf...dded088883)

Updates `github/codeql-action` from 3.29.10 to 3.29.11
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](96f518a34f...3c3833e0f8)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
- dependency-name: github/codeql-action
  dependency-version: 3.29.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-07 22:01:48 -04:00
Christian Rowlands
148b609e37 fix: Sets the correct color for the SouthPAN Legend diamond and dashes (#681)
Adds the diamond and dashes to the list of shapes and lines so that the colors get updated appropriately when dark mode is turned on
2025-09-06 12:36:43 -04:00
Izzy
acea0e0249 fastlane: slightly improve formatting for full description (#739) 2025-09-05 09:42:25 -04:00
dependabot[bot]
9fd28d5a61 actions-update: Bump github/codeql-action (#747)
Bumps the actions-dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.29.9 to 3.29.10
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](df559355d5...96f518a34f)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 16:56:08 -04:00
dependabot[bot]
4ac38c380f actions-update: Bump github/codeql-action (#745)
Bumps the actions-dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.29.8 to 3.29.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](76621b61de...df559355d5)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 11:14:15 -04:00
dependabot[bot]
3b23f9514b actions-update: Bump actions/checkout in the actions-dependencies group (#744)
Bumps the actions-dependencies group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 09:16:12 -04:00
dependabot[bot]
726481d50c actions-update: Bump github/codeql-action (#743)
Bumps the actions-dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.29.7 to 3.29.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](51f77329af...76621b61de)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-10 23:37:39 -04:00
dependabot[bot]
c304bc8fdb actions-update: Bump the actions-dependencies group across 1 directory with 2 updates (#742)
Bumps the actions-dependencies group with 2 updates in the / directory: [gradle/actions](https://github.com/gradle/actions) and [actions/cache](https://github.com/actions/cache).


Updates `gradle/actions` from 4.4.1 to 4.4.2
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](ac638b010c...017a9effdb)

Updates `actions/cache` from 4.2.3 to 4.2.4
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](5a3ec84eff...0400d5f644)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 4.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
- dependency-name: actions/cache
  dependency-version: 4.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:24:31 -04:00
dependabot[bot]
6eaabe2f8c actions-update: Bump github/codeql-action (#736)
Bumps the actions-dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.29.4 to 3.29.5
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4e828ff8d4...51f77329af)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 21:45:21 -04:00
Florian Stosse
6994b33a8c chore: Upgrade Gradle Wrapper to v8.14.3 (#734) 2025-07-27 16:23:59 -04:00
Florian Stosse
25902ed60f build: fix linting (#728)
* Update android.yml

* Update android.yml

* Update AndroidManifest.xml

* Update android.yml

* Update AndroidManifest.xml

* Separate steps

* Avoid linting

* List packages

* Build debug

* Add stacktrace

* Upload artifacts

* Checkout

* Wait for build

* Fix ADB path

* Fix paths

* Update targets

* Test and check

* Fix path

* Improve pipeline

* Fix CI

* Fix action

* Upload on failure

* Removel inting

* Fix lint report path

* Fix path

* Print paths content

* Add params

* Fix path

* Remove lint

* Check

* Test only

* Format

* Re add linting

* Remove format

* Fix paths

* Uneeded

* Disable telemetry

* Fix library use

* Revert lib change

* Use Gradle to deploy

* Run instrumentation tests after install

* Save artifacts directories

* Run instrumented tests on main app only

* Manual SDK install

* connectedCheck

* Use runner action

* No manual install

* Temporary comment

* No artifact download

* Add Checkstyle

* Pin actions and remove comments

* Needs build

* Add Dependabot

* Harden runners

* Add checkstyle configuration

* Consistency

* actions-update: Bump actions/checkout in the actions-dependencies group

Bumps the actions-dependencies group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 2 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* Hardening in block mode

* build(deps): Bump the gradle group across 1 directory with 3 updates

Bumps the gradle group with 3 updates in the / directory: commons-io:commons-io, [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson).


Updates `commons-io:commons-io` from 2.8.0 to 2.14.0

Updates `com.fasterxml.jackson.core:jackson-core` from 2.14.2 to 2.15.0
- [Changelog](https://github.com/FasterXML/jackson-core/blob/jackson-core-2.15.0/release.properties)
- [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.2...jackson-core-2.15.0)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.12.4 to 2.12.7.1
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-version: 2.14.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-version: 2.15.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.12.7.1
  dependency-type: direct:production
  dependency-group: gradle
...

Signed-off-by: dependabot[bot] <support@github.com>

* Gradle 8.14

* Fix egress

* Fix egress

* Audit

* Revert

* Run checkstyle

* Add task

* Remove checkstyle

* Remove checkstyle

* Fix name

* Add name

* Correct reports path

* Print paths

* Print

* Upload build logs

* Remove logs upload

* Misc

* Cache readonly (#4)

* Implement read only cache

* Build OsmDroid

* Parallelize jobs (#6)

* actions-update: Bump step-security/harden-runner (#5)

Bumps the actions-dependencies group with 1 update: [step-security/harden-runner](https://github.com/step-security/harden-runner).


Updates `step-security/harden-runner` from 2.12.2 to 2.13.0
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](6c439dc8bd...ec9f2d5744)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Upgrade to Gradle 8.13

* Upgrade Kotlin version

* Add buildConfig

* Optimize imports

* Dependabot: only upgrade at patch version level

* Revert Gradle update action

* Dependabot: add Gradle support for patch versions

* gradle-update: bump org.osmdroid:osmdroid-android (#7)

Bumps the actions-dependencies group with 1 update: [org.osmdroid:osmdroid-android](https://github.com/osmdroid/osmdroid).


Updates `org.osmdroid:osmdroid-android` from 6.1.11 to 6.1.20
- [Release notes](https://github.com/osmdroid/osmdroid/releases)
- [Changelog](https://github.com/osmdroid/osmdroid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/osmdroid/osmdroid/compare/osmdroid-parent-6.1.11...osmdroid-parent-6.1.20)

---
updated-dependencies:
- dependency-name: org.osmdroid:osmdroid-android
  dependency-version: 6.1.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* gradle-update: bump com.fasterxml.jackson.core:jackson-databind (#8)

Bumps [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) from 2.12.4 to 2.19.2.
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.19.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix typo in Dependabot conf

* Bump SDK version

* Upgrade SDK version

* Revert targetSdk

* Bump SDK

* Fix type

* Fix build

* Security updates for Gradle

* Ignore major and minor updates

* Lint

* Lint: ignore string format

* Lint: ignore string format for library

* Lint: disable translation checks

* Migrate activity!!

* Migrate context!!

* Add SARIF report generation and upload

* Lint: do not check dependencies

* Fix path again

* Lint: re-enable for all tasks

* Add gitignore

* Bump targetSdk

* Remove Dependabot Gradle config

* Remove crashpad handler fix

* Bump SARIF action to latest version

* Revert SDK upgrade

* Revert SDK upgrade related fixes

* Revert non-lint fixes

* Revert sensor fix

* Restore gitignore

* Delete build/reports/problems/problems-report.html

* Fix pipeline run

* Add required fixes from linting pass

* Fix conflict

* Revert targetSdk upgrade and ignore ExpiredTargetSdkVersion for linting

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-27 09:58:10 -04:00
Florian Stosse
1eb6ed9477 chore: Add Android focused gitignore file (#732) 2025-07-26 12:39:17 -04:00
Florian Stosse
acc19cff01 ci: Terminate crashpad_handler process on emulator exit (#733) 2025-07-26 11:05:28 -04:00
Florian Stosse
8a700e987e fix: Fix XML closing element (#731) 2025-07-25 14:03:45 -04:00
Florian Stosse
4e7c77fbb2 chore: Enable caching for Gradle (#730) 2025-07-25 13:53:58 -04:00
Florian Stosse
91378d5f79 chore: Upgrade Gradle, Kotlin and optimize imports (#726)
* Update android.yml

* Update android.yml

* Update AndroidManifest.xml

* Update android.yml

* Update AndroidManifest.xml

* Separate steps

* Avoid linting

* List packages

* Build debug

* Add stacktrace

* Upload artifacts

* Checkout

* Wait for build

* Fix ADB path

* Fix paths

* Update targets

* Test and check

* Fix path

* Improve pipeline

* Fix CI

* Fix action

* Upload on failure

* Removel inting

* Fix lint report path

* Fix path

* Print paths content

* Add params

* Fix path

* Remove lint

* Check

* Test only

* Format

* Re add linting

* Remove format

* Fix paths

* Uneeded

* Disable telemetry

* Fix library use

* Revert lib change

* Use Gradle to deploy

* Run instrumentation tests after install

* Save artifacts directories

* Run instrumented tests on main app only

* Manual SDK install

* connectedCheck

* Use runner action

* No manual install

* Temporary comment

* No artifact download

* Add Checkstyle

* Pin actions and remove comments

* Needs build

* Add Dependabot

* Harden runners

* Add checkstyle configuration

* Consistency

* actions-update: Bump actions/checkout in the actions-dependencies group

Bumps the actions-dependencies group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 2 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* Hardening in block mode

* build(deps): Bump the gradle group across 1 directory with 3 updates

Bumps the gradle group with 3 updates in the / directory: commons-io:commons-io, [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson).


Updates `commons-io:commons-io` from 2.8.0 to 2.14.0

Updates `com.fasterxml.jackson.core:jackson-core` from 2.14.2 to 2.15.0
- [Changelog](https://github.com/FasterXML/jackson-core/blob/jackson-core-2.15.0/release.properties)
- [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.2...jackson-core-2.15.0)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.12.4 to 2.12.7.1
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-version: 2.14.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-version: 2.15.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.12.7.1
  dependency-type: direct:production
  dependency-group: gradle
...

Signed-off-by: dependabot[bot] <support@github.com>

* Gradle 8.14

* Fix egress

* Fix egress

* Audit

* Revert

* Run checkstyle

* Add task

* Remove checkstyle

* Remove checkstyle

* Fix name

* Add name

* Correct reports path

* Print paths

* Print

* Upload build logs

* Remove logs upload

* Misc

* Cache readonly (#4)

* Implement read only cache

* Build OsmDroid

* Parallelize jobs (#6)

* actions-update: Bump step-security/harden-runner (#5)

Bumps the actions-dependencies group with 1 update: [step-security/harden-runner](https://github.com/step-security/harden-runner).


Updates `step-security/harden-runner` from 2.12.2 to 2.13.0
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](6c439dc8bd...ec9f2d5744)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Upgrade to Gradle 8.13

* Upgrade Kotlin version

* Add buildConfig

* Optimize imports

* Dependabot: only upgrade at patch version level

* Revert Gradle update action

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-20 12:56:11 -04:00
Florian Stosse
5d4a0f9a60 ci: Fix CI pipeline (#725)
* Update android.yml

* Update android.yml

* Update AndroidManifest.xml

* Update android.yml

* Update AndroidManifest.xml

* Separate steps

* Avoid linting

* List packages

* Build debug

* Add stacktrace

* Upload artifacts

* Checkout

* Wait for build

* Fix ADB path

* Fix paths

* Update targets

* Test and check

* Fix path

* Improve pipeline

* Fix CI

* Fix action

* Upload on failure

* Removel inting

* Fix lint report path

* Fix path

* Print paths content

* Add params

* Fix path

* Remove lint

* Check

* Test only

* Format

* Re add linting

* Remove format

* Fix paths

* Uneeded

* Disable telemetry

* Fix library use

* Revert lib change

* Use Gradle to deploy

* Run instrumentation tests after install

* Save artifacts directories

* Run instrumented tests on main app only

* Manual SDK install

* connectedCheck

* Use runner action

* No manual install

* Temporary comment

* No artifact download

* Add Checkstyle

* Pin actions and remove comments

* Needs build

* Add Dependabot

* Harden runners

* Add checkstyle configuration

* Consistency

* actions-update: Bump actions/checkout in the actions-dependencies group

Bumps the actions-dependencies group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 2 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* Hardening in block mode

* build(deps): Bump the gradle group across 1 directory with 3 updates

Bumps the gradle group with 3 updates in the / directory: commons-io:commons-io, [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson).


Updates `commons-io:commons-io` from 2.8.0 to 2.14.0

Updates `com.fasterxml.jackson.core:jackson-core` from 2.14.2 to 2.15.0
- [Changelog](https://github.com/FasterXML/jackson-core/blob/jackson-core-2.15.0/release.properties)
- [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.14.2...jackson-core-2.15.0)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.12.4 to 2.12.7.1
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-version: 2.14.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-version: 2.15.0
  dependency-type: direct:production
  dependency-group: gradle
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.12.7.1
  dependency-type: direct:production
  dependency-group: gradle
...

Signed-off-by: dependabot[bot] <support@github.com>

* Gradle 8.14

* Fix egress

* Fix egress

* Audit

* Revert

* Run checkstyle

* Add task

* Remove checkstyle

* Remove checkstyle

* Fix name

* Add name

* Correct reports path

* Print paths

* Print

* Upload build logs

* Remove logs upload

* Misc

* Cache readonly (#4)

* Implement read only cache

* Build OsmDroid

* Parallelize jobs (#6)

* actions-update: Bump step-security/harden-runner (#5)

Bumps the actions-dependencies group with 1 update: [step-security/harden-runner](https://github.com/step-security/harden-runner).


Updates `step-security/harden-runner` from 2.12.2 to 2.13.0
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](6c439dc8bd...ec9f2d5744)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 14:53:53 -04:00
Sean Barbeau
8f07afeb83 chore: Bump version for release 2024-12-28 17:58:52 -05:00
Sean Barbeau
2196e66428 fix: Update to targetSdkVersion 34
Fixes https://github.com/barbeau/gpstest/issues/694
2024-12-28 17:57:28 -05:00
Sean Barbeau
8c050491a3 chore: Add version file for fastlane (F-Droid) 2024-12-28 17:23:04 -05:00
Sean Barbeau
b54c203e1b fix: Don't minify the library so we can build releases
See https://stackoverflow.com/questions/79102013/r8-removes-entire-classes-it-shouldnt
2024-12-28 17:14:25 -05:00
Sean Barbeau
b7b3fbc66e chore: Bump version for release 2024-12-28 16:50:42 -05:00
Sean Barbeau
bf0bb3a54d chore: Update What's new 2024-12-28 16:49:31 -05:00
Sean Barbeau
4292ea40da feat: Implementation runtime permissions for notification (#705)
Fixes https://github.com/barbeau/gpstest/issues/694 and fixes https://github.com/barbeau/gpstest/issues/622
2024-12-28 16:33:42 -05:00
barbeau
9e8f9734be fix: Try to fix GitHub CI 2024-12-10 13:51:11 -05:00
barbeau
a057fe448f fix: Fix Status log signal count and signal index values
Fixes https://github.com/barbeau/gpstest/issues/696
2024-12-10 13:44:51 -05:00
barbeau
aa8a6ff4fe chore: Update Gradle and wrapper versions 2024-12-10 13:35:59 -05:00
Slanterns
b069b82802 docs: Replace dead link in README.md (#693) 2024-10-15 12:13:05 -04:00
Corbin Davenport
1dab959ba4 docs: Replace deprecated goo.gl links (#687) 2024-07-19 16:03:56 -04:00
Sean Barbeau
8c258b0cca chore: Update stale.yml 2024-06-23 10:47:28 -04:00
Narugakuruga
221e46cb30 Add support for BDS B2b and IRNSS L1. And update signal detection to match the actual BDS (#657)
* Update full_description.txt

* Update CarrierFreqUtilsTest.kt

* Update CarrierFreqUtils.java
2023-10-27 11:28:48 -04:00
Sean Barbeau
58a643e1f0 docs: Remove donation links 2023-08-15 13:10:21 -04:00
Sean Barbeau
680b5fde9d chore: Bump version for release 2023-08-15 11:02:08 -04:00
barbeau
fd26076608 fix: Only inject assist data once per logging session
Fixes #648
2023-08-11 10:45:17 -04:00
barbeau
9afca7a975 chore: Set sensor update rate explicitly to 100Hz
This matches GnssLogger.

Related to #648
2023-08-09 09:40:22 -04:00
barbeau
822888dcfe fix: Write sensor data using Dispatchers.IO
Related to #648
2023-08-09 09:19:39 -04:00
barbeau
4c3d3865fe chore: Bump version for release 2023-04-27 15:15:08 -04:00
barbeau
3cea066d9c fix: Use LocationListenerCompat to avoid crashes on API <=30
Fixes #627
2023-04-27 15:14:10 -04:00
barbeau
74645d72dd chore: Remove unneeded Application references 2023-04-24 09:51:52 -04:00
barbeau
f7567f1a1d fix: Don't cap precision of entered benchmark coordinates
Fixes #628
2023-04-24 09:43:48 -04:00
barbeau
3dd022876b chore: Bump version for release 2023-04-21 13:06:11 -04:00
barbeau
e2825cab31 chore: Update What's New 2023-04-21 13:04:52 -04:00
barbeau
23f0de3d7b feat: Copy the log file to "Downloads/GPSTest" after logging (Android 11 and up)
Closes https://github.com/barbeau/gpstest/issues/633
2023-04-21 13:03:28 -04:00
barbeau
dea3e77225 fix: Add compatibility for older Android versions / lower end phones
* For API Level 29 and below, we need to implement all the methods of an interface.
* Handle the case where the device doesn't support the ORIENTATION sensor

Fixes https://github.com/barbeau/gpstest/issues/627
2023-04-20 17:34:34 -04:00
barbeau
3b17dff8b8 fix: Make binding nullable and handle cases where it could be null (i.e., callbacks)
Fixes https://github.com/barbeau/gpstest/issues/634
2023-04-20 16:41:21 -04:00
barbeau
b0e718670d fix: Remove decimal places from Status elevation and azimuth
Fixes https://github.com/barbeau/gpstest/issues/626
2023-04-20 16:10:09 -04:00
barbeau
592c69d18d fix: Check for geo URIs with empty lat or lon
Fixes https://github.com/barbeau/gpstest/issues/635
2023-04-20 16:05:23 -04:00
barbeau
bd4fead10f fix: Handle case where onCreateView() is called early for ShareDialogFragment
Fixes #636
2023-04-20 15:52:02 -04:00
barbeau
63c9ffdd3a chore: Fix formatting 2023-04-20 15:11:53 -04:00
Dave Collett
1e71bb9896 feat: Add SouthPAN support (#601)
Closes https://github.com/barbeau/gpstest/issues/271.
2023-04-20 15:06:32 -04:00
barbeau
2a64a40fd9 fix: Limit Google Maps KTX library to only google flavor (not osmdroid)
See https://github.com/barbeau/gpstest/pull/631
2023-04-19 09:34:54 -04:00
79 changed files with 1467 additions and 462 deletions

14
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
commit-message:
prefix: "actions-update"
labels: [ 'enhancement' ]
schedule:
interval: "daily"
groups:
actions-dependencies:
applies-to: version-updates
patterns:
- "*"

4
.github/stale.yml vendored
View File

@@ -1,7 +1,7 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 200
daysUntilStale: 400
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 200
daysUntilClose: 400
# Issues with these labels will never be considered stale
exemptLabels:

View File

@@ -12,27 +12,146 @@ on:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
test:
runs-on: macos-latest
unit-test:
name: Perform checks
runs-on: ubuntu-latest
steps:
- name: Harden the runner (audit mode)
uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout the code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle and cache
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
gradle-version: wrapper
- name: Run checks
run: ./gradlew check
- name: Upload SARIF report
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
if: success() || failure()
with:
sarif_file: GPSTest/build/reports/lint-results-googleDebug.sarif
category: lint
build:
name: Build debug APK
runs-on: ubuntu-latest
steps:
- name: Harden the runner (audit mode)
uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout the code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
java-version: 17
distribution: 'temurin'
- name: Setup Android SDK
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2
with:
log-accepted-android-sdk-licenses: false
- name: Setup Gradle and cache
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
gradle-version: wrapper
cache-read-only: true
- name: Build app
run: ./gradlew assembleDebug --stacktrace
- name: Upload APKs
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: apks
path: |
GPSTest/build/outputs/apk/
wear/build/outputs/apk/
instrumentation-test:
name: Run instrumented tests
runs-on: ubuntu-latest
strategy:
matrix:
api-level: [24, 29]
api-level: [24, 29, 33]
target: [default, google_apis]
tasks: [connectedGoogleDebugAndroidTest, connectedOsmdroidDebugAndroidTest]
steps:
- name: checkout
uses: actions/checkout@v2
- name: Harden the runner (audit mode)
uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Setup Java
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'adopt'
- name: Checkout the code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: x86_64
profile: Nexus 6
script: ./gradlew test check connectedCheck -x lint --stacktrace
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
java-version: 17
distribution: 'temurin'
- name: Setup Gradle and cache
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
gradle-version: wrapper
cache-read-only: true
- name: Configure AVD cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.target }}-${{ matrix.api-level }}
- name: Create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
disable-animations: false
script: echo "Generated AVD snapshot for caching."
arch: x86_64
profile: Nexus 6
- name: Run instrumentation tests
uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
disable-animations: true
script: |
adb wait-for-device
./gradlew ${{ matrix.tasks }} -x lint --stacktrace && killall -INT crashpad_handler || true
arch: x86_64
profile: Nexus 6

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v6
- name: Run script to push English strings to Transifex
run: |
chmod +x ./scripts/push-to-transifex.sh

170
.gitignore vendored
View File

@@ -1,5 +1,167 @@
.gradle
/local.properties
/.idea/
# Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio
# Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio
### Android ###
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.DS_Store
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof
### Android Patch ###
gen-external-apklibs
# Replacement of .externalNativeBuild directories introduced
# with Android Studio 3.5.
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle
# Signing files
.signing/
# Local configuration file (sdk path, etc)
# Proguard folder generated by Eclipse
proguard/
# Log Files
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
.navigation/
*.ipr
*~
*.swp
# Keystore files
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Android Patch
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# NDK
obj/
# IntelliJ IDEA
*.iws
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/workspace.xml
.idea/tasks.xml
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
.idea/assetWizardSettings.xml
.idea/gradle.xml
.idea/jarRepositories.xml
.idea/navEditor.xml
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
hs_err_pid*
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio

2
FAQ.md
View File

@@ -77,7 +77,7 @@ Android 1.5 and up, in its simplest form. More advanced versions with an update
* **Start/Stop** - Start/stop the GPS hardware
* **Send Location** - After a latitude and longitude has been acquired, you can share this info
* **Inject Time** - Injects Time assistance data for GPS into the platform, using information from a [Network Time Protocol (NTP)](http://support.ntp.org/bin/view/Main/WebHome) server. Note that some devices don't use an NTP server for time data - if this is your device, you'll see a message saying "Platform does not support injecting time data".
* **Inject PSDS Data** - Injects Predicted Satellite Data Service (PSDS) assistance data for GNSS into the platform, using information from a PSDS server. Note that some devices don't use PSDS for assistance data - if this is your device, you'll see a message saying "Platform does not support injecting PSDS data". PSDS is the generic term for products like [XTRA assistance data](http://goo.gl/3RjWX).
* **Inject PSDS Data** - Injects Predicted Satellite Data Service (PSDS) assistance data for GNSS into the platform, using information from a PSDS server. Note that some devices don't use PSDS for assistance data - if this is your device, you'll see a message saying "Platform does not support injecting PSDS data". PSDS is the generic term for products like [XTRA assistance data](https://www.qualcomm.com/news/releases/2007/02/qualcomm-introduces-gpsonextra-assistance-expand-capabilities-standalone).
* **Clear Aiding Data** - Clears all assistance data used for GPS, including NTP and PSDS/XTRA data (Note: if you select this option to fix broken GPS on your device, for GPS to work again you may need to Inject Time and Inject PSDS data). Note that some devices don't support clearing assistance data - if this is your device, you'll see a message saying "Platform does not support deleting aiding data". You may also see a large delay until your device acquires a fix again, so please use this feature with caution.
* **Settings** - Set map tile type

View File

@@ -2,17 +2,18 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 24
targetSdkVersion 31
targetSdkVersion 34
multiDexEnabled true
// versionCode scheme - first two digits are minSdkVersion, last three digits are build number
versionCode 24094
versionName "3.10.0"
versionCode 24099
versionName "3.10.5"
vectorDrawables.useSupportLibrary = true
@@ -55,7 +56,8 @@ android {
}
lintOptions {
disable 'MissingTranslation', 'ExtraTranslation'
sarifReport true
disable 'MissingTranslation', 'ExtraTranslation', 'StringFormatInvalid'
}
if (project.hasProperty("secure.properties")
@@ -109,11 +111,11 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = JavaVersion.VERSION_17
}
testOptions {
@@ -130,11 +132,16 @@ android {
viewBinding true
}
buildFeatures {
buildConfig = true
}
// Gradle automatically adds 'android.test.runner' as a dependency.
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
namespace 'com.android.gpstest'
}
dependencies {
@@ -159,7 +166,7 @@ dependencies {
// Uploading device properties on user request
implementation 'androidx.core:core-ktx:1.10.0-rc01'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'commons-io:commons-io:2.8.0'
implementation 'commons-io:commons-io:2.14.0'
// Share dialog
implementation 'androidx.viewpager2:viewpager2:1.0.0'
@@ -189,7 +196,7 @@ dependencies {
// Calculating offset for camera target in map view (Google flavor only)
googleImplementation 'com.google.maps.android:android-maps-utils:2.2.6'
// Use suspend coroutines instead of callbacks (Google flavor only)
implementation 'com.google.maps.android:maps-ktx:3.1.0'
googleImplementation 'com.google.maps.android:maps-ktx:3.1.0'
// OSM Droid (fdroid flavor only)
osmdroidImplementation 'org.osmdroid:osmdroid-android:6.1.11'

View File

@@ -150,7 +150,7 @@ class CarrierFreqUtilsTest {
// Beidou
// Beidou B1
// Beidou B1I
val beidouB1 = SatelliteStatus(1,
GnssType.BEIDOU,
30f,
@@ -163,22 +163,7 @@ class CarrierFreqUtilsTest {
beidouB1.carrierFrequencyHz = 1561098000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1)
assertEquals("B1", label)
// Beidou B1-2
val beidouB1_2 = SatelliteStatus(1,
GnssType.BEIDOU,
30f,
true,
true,
true,
72f,
25f);
beidouB1_2.hasCarrierFrequency = true
beidouB1_2.carrierFrequencyHz = 1589742000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1_2)
assertEquals("B1-2", label)
assertEquals("B1I", label)
// Beidou B1C
val beidouB1c = SatelliteStatus(1,
@@ -210,21 +195,6 @@ class CarrierFreqUtilsTest {
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1c202)
assertEquals("B1C", label)
// Beidou B2
val beidouB2 = SatelliteStatus(1,
GnssType.BEIDOU,
30f,
true,
true,
true,
72f,
25f);
beidouB2.hasCarrierFrequency = true
beidouB2.carrierFrequencyHz = 1207140000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB2)
assertEquals("B2", label)
// Beidou B2a
val beidouB2a = SatelliteStatus(1,
GnssType.BEIDOU,
@@ -240,7 +210,23 @@ class CarrierFreqUtilsTest {
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB2a)
assertEquals("B2a", label)
// Beidou B3
// Beidou B2b
val beidouB2b = SatelliteStatus(1,
GnssType.BEIDOU,
30f,
true,
true,
true,
72f,
25f);
beidouB2b.hasCarrierFrequency = true
beidouB2b.carrierFrequencyHz = 1207140000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB2b)
assertEquals("B2b", label)
// Beidou B3I
val beidouB3 = SatelliteStatus(1,
GnssType.BEIDOU,
30f,
@@ -253,10 +239,25 @@ class CarrierFreqUtilsTest {
beidouB3.carrierFrequencyHz = 1268520000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB3)
assertEquals("B3", label)
assertEquals("B3I", label)
// IRNSS
// IRNSS L1
val irnssL1 = SatelliteStatus(1,
GnssType.IRNSS,
30f,
true,
true,
true,
72f,
25f);
irnssL1.hasCarrierFrequency = true
irnssL1.carrierFrequencyHz = 1575420000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(irnssL1)
assertEquals("L1", label)
// IRNSS L5
val irnssL5 = SatelliteStatus(1,
GnssType.IRNSS,
@@ -644,6 +645,41 @@ class CarrierFreqUtilsTest {
label = CarrierFreqUtils.getCarrierFrequencyLabel(sdcm125L5)
assertEquals("L5", label)
// INMARSAT_4F1 (SBAS)
// SouthPAN - ID 122 L1
val southpan122L1 = SatelliteStatus(122,
GnssType.SBAS,
30f,
true,
true,
true,
72f,
25f);
southpan122L1.hasCarrierFrequency = true
southpan122L1.carrierFrequencyHz = 1575420000.0
southpan122L1.sbasType = SbasType.SOUTHPAN
label = CarrierFreqUtils.getCarrierFrequencyLabel(southpan122L1)
assertEquals("L1", label)
// SouthPAN - ID 122 L5
val southpan122L5 = SatelliteStatus(122,
GnssType.SBAS,
30f,
true,
true,
true,
72f,
25f);
southpan122L5.hasCarrierFrequency = true
southpan122L5.carrierFrequencyHz = 1176450000.0
southpan122L5.sbasType = SbasType.SOUTHPAN
label = CarrierFreqUtils.getCarrierFrequencyLabel(southpan122L5)
assertEquals("L5", label)
// Test variations on the "same" numbers to make sure floating point equality works
val gpsL1variation = SatelliteStatus(1,
@@ -697,7 +733,7 @@ class CarrierFreqUtilsTest {
fun testIsPrimaryCarrier() {
assertTrue(CarrierFreqUtils.isPrimaryCarrier("L1"))
assertTrue(CarrierFreqUtils.isPrimaryCarrier("E1"))
assertTrue(CarrierFreqUtils.isPrimaryCarrier("B1"))
assertTrue(CarrierFreqUtils.isPrimaryCarrier("B1I"))
assertTrue(CarrierFreqUtils.isPrimaryCarrier("B1C"))
assertTrue(CarrierFreqUtils.isPrimaryCarrier("L1-C"))

View File

@@ -20,7 +20,10 @@ import android.location.Location
import androidx.test.InstrumentationRegistry.getTargetContext
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.android.gpstest.library.util.IOUtils
import junit.framework.Assert.*
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -216,6 +219,9 @@ class IOUtilsTest {
val invalidGeoUri2 = "http://not,a,geo,uri"
val result7 = IOUtils.getLocationFromGeoUri(getTargetContext(),invalidGeoUri2)
assertNull(result7)
assertNull(IOUtils.getLocationFromGeoUri(getTargetContext(), "geo:,43.06480"))
assertNull(IOUtils.getLocationFromGeoUri(getTargetContext(), "geo:37.786971,"))
}
/**

View File

@@ -15,6 +15,10 @@
*/
package com.android.gpstest;
import static androidx.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -27,10 +31,6 @@ import org.junit.runner.RunWith;
import java.util.Locale;
import static androidx.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4ClassRunner.class)
public class LocationUtilsTest {

View File

@@ -15,6 +15,8 @@
*/
package com.android.gpstest;
import static junit.framework.Assert.assertEquals;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.android.gpstest.library.util.MathUtils;
@@ -24,8 +26,6 @@ import org.junit.runner.RunWith;
import java.io.UnsupportedEncodingException;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4ClassRunner.class)
public class MathUtilsAndroidTest {

View File

@@ -15,14 +15,21 @@
*/
package com.android.gpstest
import android.location.GnssMeasurement.*
import android.location.GnssMeasurement.ADR_STATE_CYCLE_SLIP
import android.location.GnssMeasurement.ADR_STATE_HALF_CYCLE_REPORTED
import android.location.GnssMeasurement.ADR_STATE_HALF_CYCLE_RESOLVED
import android.location.GnssMeasurement.ADR_STATE_RESET
import android.location.GnssMeasurement.ADR_STATE_UNKNOWN
import android.location.GnssMeasurement.ADR_STATE_VALID
import android.os.Build
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.util.SatelliteUtils
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith

View File

@@ -17,19 +17,27 @@
package com.android.gpstest
import android.app.Application
import android.content.Context
import android.os.Build
import android.preference.PreferenceManager
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.InstrumentationRegistry
import androidx.test.InstrumentationRegistry.getTargetContext
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.android.gpstest.library.data.*
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.data.SharedAntennaManager
import com.android.gpstest.library.data.SharedGnssMeasurementManager
import com.android.gpstest.library.data.SharedGnssStatusManager
import com.android.gpstest.library.data.SharedLocationManager
import com.android.gpstest.library.data.SharedNavMessageManager
import com.android.gpstest.library.data.SharedNmeaManager
import com.android.gpstest.library.data.SharedSensorManager
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.ui.SignalInfoViewModel
import kotlinx.coroutines.GlobalScope
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

View File

@@ -35,22 +35,30 @@ import com.android.gpstest.Application
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.util.MathUtils
import com.android.gpstest.library.util.PreferenceUtil
import com.android.gpstest.map.MapConstants
import com.android.gpstest.map.MapViewModelController
import com.android.gpstest.map.MapViewModelController.MapInterface
import com.android.gpstest.map.OnMapClickListener
import com.android.gpstest.util.MapUtils
import com.android.gpstest.library.util.MathUtils
import com.android.gpstest.library.util.PreferenceUtil
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.*
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener
import com.google.android.gms.maps.LocationSource
import com.google.android.gms.maps.LocationSource.OnLocationChangedListener
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.*
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MapStyleOptions
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.Polyline
import com.google.android.gms.maps.model.PolylineOptions
import com.google.maps.android.SphericalUtil
import com.google.maps.android.ktx.awaitMap
import dagger.hilt.android.AndroidEntryPoint

View File

@@ -25,6 +25,12 @@
<!-- Required for foreground services on P+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Required for notifications on T+ -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Required for the location service when targeting API 34 and up -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<application
android:name=".Application"
android:label="@string/app_name"
@@ -52,7 +58,8 @@
<activity
android:name=".ui.Preferences"
android:label="@string/pref_title"
android:exported="true">
android:exported="true"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />

View File

@@ -15,13 +15,11 @@
*/
package com.android.gpstest
import android.Manifest
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
@@ -46,17 +44,20 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.io.CsvFileLogger
import com.android.gpstest.io.JsonFileLogger
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.model.SatelliteGroup
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.ui.MainActivity
import com.android.gpstest.library.util.FormatUtils.toNotificationTitle
import com.android.gpstest.library.util.IOUtils.*
import com.android.gpstest.library.util.IOUtils.deleteOldFiles
import com.android.gpstest.library.util.IOUtils.forcePsdsInjection
import com.android.gpstest.library.util.IOUtils.forceTimeInjection
import com.android.gpstest.library.util.IOUtils.writeMeasurementToLogcat
import com.android.gpstest.library.util.IOUtils.writeNavMessageToAndroidStudio
import com.android.gpstest.library.util.IOUtils.writeNmeaToAndroidStudio
import com.android.gpstest.library.util.LibUIUtils.toNotificationSummary
import com.android.gpstest.library.util.PreferenceUtil
import com.android.gpstest.library.util.PreferenceUtil.injectPsdsWhenLogging
import com.android.gpstest.library.util.PreferenceUtil.injectTimeWhenLogging
import com.android.gpstest.library.util.PreferenceUtil.isCsvLoggingEnabled
import com.android.gpstest.library.util.PreferenceUtil.isJsonLoggingEnabled
import com.android.gpstest.library.util.PreferenceUtil.writeAntennaInfoToFileCsv
@@ -75,15 +76,19 @@ import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.SatelliteUtil.toSatelliteGroup
import com.android.gpstest.library.util.SatelliteUtil.toSatelliteStatus
import com.android.gpstest.library.util.SatelliteUtils
import com.android.gpstest.library.util.LibUIUtils.toNotificationSummary
import com.android.gpstest.library.util.hasPermission
import com.android.gpstest.ui.MainActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.io.File
import java.util.*
import java.util.Date
import javax.inject.Inject
/**
@@ -131,6 +136,7 @@ class ForegroundOnlyLocationService : LifecycleService() {
private val loggingSettingListener: SharedPreferences.OnSharedPreferenceChangeListener =
PreferenceUtil.newFileLoggingListener(app, { initLogging() }, prefs)
private var deletedFiles = false
private var injectedAssistData = false
override fun onCreate() {
super.onCreate()
@@ -460,9 +466,15 @@ class ForegroundOnlyLocationService : LifecycleService() {
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
//Log.d(TAG, "Service sensor: orientation ${it.values[0]}, tilt ${it.values[1]}")
if (writeOrientationToFile(app, prefs)) {
initLogging()
csvFileLogger.onOrientationChanged(it, System.currentTimeMillis(), SystemClock.elapsedRealtime())
GlobalScope.launch(Dispatchers.IO) {
if (writeOrientationToFile(app, prefs)) {
initLogging()
csvFileLogger.onOrientationChanged(
it,
System.currentTimeMillis(),
SystemClock.elapsedRealtime()
)
}
}
}
.launchIn(lifecycleScope)
@@ -590,16 +602,10 @@ class ForegroundOnlyLocationService : LifecycleService() {
* file permissions. So we need to call this on each update in case the user just granted file
* permissions but logging hasn't been started yet.
*/
@Synchronized
private fun initLogging() {
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
// Inject time and/or PSDS to make sure timestamps and assistance are as updated as possible
if (injectTimeWhenLogging(app, prefs)) {
forceTimeInjection(app, locationManager)
}
if (injectPsdsWhenLogging(app, prefs)) {
forcePsdsInjection(app, locationManager)
}
maybeInjectAssistData()
val date = Date()
if (!csvFileLogger.isStarted && isCsvLoggingEnabled(app, prefs)) {
@@ -613,6 +619,21 @@ class ForegroundOnlyLocationService : LifecycleService() {
maybeDeleteFiles()
}
private fun maybeInjectAssistData() {
if (injectedAssistData) {
// Only inject once per logging session
return
}
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (PreferenceUtil.injectTimeWhenLogging(app, prefs)) {
forceTimeInjection(app, locationManager)
}
if (PreferenceUtil.injectPsdsWhenLogging(app, prefs)) {
forcePsdsInjection(app, locationManager)
}
injectedAssistData = true
}
private fun maybeDeleteFiles() {
if (deletedFiles) {
// If we've already deleted files on this application execution, don't do it again

View File

@@ -1,10 +1,18 @@
package com.android.gpstest.io;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Downloads;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import com.android.gpstest.Application;
import com.android.gpstest.R;
@@ -12,6 +20,8 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -24,6 +34,8 @@ public abstract class BaseFileLogger implements FileLogger {
protected final String TAG = this.getClass().getName();
protected static final String FILE_PREFIX = "gnss_log";
protected static final String DIRECTORY = "Download/GPSTest";
protected final Context context;
protected BufferedWriter fileWriter;
@@ -177,6 +189,10 @@ public abstract class BaseFileLogger implements FileLogger {
return;
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && file != null) {
copyFileToDownloads(file);
}
}
protected void logException(String errorMessage, Exception e) {
@@ -188,4 +204,21 @@ public abstract class BaseFileLogger implements FileLogger {
Log.e(TAG, errorMessage);
Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
}
@RequiresApi(Build.VERSION_CODES.R)
protected void copyFileToDownloads(File fileToCopy) {
ContentResolver contentResolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, fileToCopy.getName());
contentValues.put(Downloads.RELATIVE_PATH, DIRECTORY);
Uri fileUri =
contentResolver.insert(
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), contentValues);
try (OutputStream outputStream =
contentResolver.openOutputStream(fileUri)) {
Files.copy(fileToCopy.toPath(), outputStream);
} catch (IOException e) {
Log.e(TAG, "Error while writing to Downloads folder:", e);
}
}
}

View File

@@ -219,7 +219,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
int i = 0;
for (SatelliteStatus s : statuses) {
try {
writeStatusToFile(s, location != null ? location.getTime() : 0, i, statuses.size());
writeStatusToFile(s, location != null ? location.getTime() : 0, statuses.size(), i);
} catch (IOException e) {
logException(Application.Companion.getApp().getString(R.string.error_writing_file), e);
}

View File

@@ -49,9 +49,9 @@ import com.android.gpstest.chart.DistanceValueFormatter;
import com.android.gpstest.library.model.AvgError;
import com.android.gpstest.library.model.MeasuredError;
import com.android.gpstest.library.util.IOUtils;
import com.android.gpstest.library.util.LibUIUtils;
import com.android.gpstest.library.util.MathUtils;
import com.android.gpstest.library.util.PreferenceUtils;
import com.android.gpstest.library.util.LibUIUtils;
import com.android.gpstest.util.UIUtils;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;

View File

@@ -55,21 +55,21 @@ import com.android.gpstest.BuildConfig
import com.android.gpstest.ForegroundOnlyLocationService
import com.android.gpstest.ForegroundOnlyLocationService.LocalBinder
import com.android.gpstest.R
import com.android.gpstest.databinding.ActivityMainBinding
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.databinding.ActivityMainBinding
import com.android.gpstest.library.ui.SignalInfoViewModel
import com.android.gpstest.library.util.*
import com.android.gpstest.map.MapConstants
import com.android.gpstest.ui.NavigationDrawerFragment.NavigationDrawerCallbacks
import com.android.gpstest.ui.sky.SkyFragment
import com.android.gpstest.ui.status.StatusFragment
import com.android.gpstest.library.util.PreferenceUtil.darkTheme
import com.android.gpstest.library.util.PreferenceUtil.isFileLoggingEnabled
import com.android.gpstest.library.util.PreferenceUtil.minDistance
import com.android.gpstest.library.util.PreferenceUtil.minTimeMillis
import com.android.gpstest.library.util.PreferenceUtil.runInBackground
import com.android.gpstest.library.util.PreferenceUtils.isTrackingStarted
import com.android.gpstest.map.MapConstants
import com.android.gpstest.ui.NavigationDrawerFragment.NavigationDrawerCallbacks
import com.android.gpstest.ui.sky.SkyFragment
import com.android.gpstest.ui.status.StatusFragment
import com.android.gpstest.util.BuildUtils
import com.android.gpstest.util.UIUtils
import com.google.android.material.switchmaterial.SwitchMaterial
@@ -155,7 +155,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
/** Called when the activity is first created. */
public override fun onCreate(savedInstanceState: Bundle?) {
// Set theme
if (darkTheme(app,Application.prefs)) {
if (darkTheme(app, prefs)) {
setTheme(R.style.AppTheme_Dark_NoActionBar)
useDarkTheme = true
}
@@ -165,7 +165,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
saveInstanceState(savedInstanceState)
// Observe stopping location updates from the service
Application.prefs.registerOnSharedPreferenceChangeListener(stopTrackingListener)
prefs.registerOnSharedPreferenceChangeListener(stopTrackingListener)
// Set the default values from the XML file if this is the first execution of the app
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
@@ -277,7 +277,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
private fun maybeRecreateApp() {
// If the set language has changed since we created the Activity (e.g., returning from Settings), recreate App
if (Application.prefs.contains(getString(R.string.pref_key_language))) {
if (prefs.contains(getString(R.string.pref_key_language))) {
val currentLanguage = PreferenceUtils.getString(getString(R.string.pref_key_language), prefs)
if (currentLanguage != initialLanguage) {
initialLanguage = currentLanguage
@@ -337,7 +337,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
private fun requestPermissionAndInit(activity: Activity) {
if (PermissionUtils.hasGrantedPermissions(activity, PermissionUtils.REQUIRED_PERMISSIONS)) {
init()
initGnss()
} else {
// Request permissions from the user
ActivityCompat.requestPermissions(
@@ -349,20 +349,20 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
requestCode: Int, permissions: Array<String>, grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PermissionUtils.LOCATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
userDeniedPermission = false
init()
initGnss()
} else {
userDeniedPermission = true
}
}
}
private fun init() {
private fun initGnss() {
val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
val provider = locationManager.getProvider(LocationManager.GPS_PROVIDER)
if (provider == null) {
@@ -383,7 +383,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
this.useDarkTheme = useDarkTheme
recreate()
}
val settings = Application.prefs
val settings = prefs
checkKeepScreenOn(settings)
LibUIUtils.autoShowWhatsNew(prefs, app,this)
}
@@ -399,7 +399,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
@OptIn(ExperimentalCoroutinesApi::class)
private fun setupStartState(savedInstanceState: Bundle?) {
// Use "Auto-start GNSS" setting, or existing tracking state (e.g., if service is running)
if (Application.prefs.getBoolean(
if (prefs.getBoolean(
getString(R.string.pref_key_auto_start_gps),
true
) || isTrackingStarted(prefs)
@@ -435,7 +435,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
NavigationDrawerFragment.NAVDRAWER_ITEM_INJECT_PSDS_DATA -> forcePsdsInjection()
NavigationDrawerFragment.NAVDRAWER_ITEM_INJECT_TIME_DATA -> forceTimeInjection()
NavigationDrawerFragment.NAVDRAWER_ITEM_CLEAR_AIDING_DATA -> {
val prefs = Application.prefs
val prefs = prefs
if (!prefs.getBoolean(
getString(R.string.pref_key_never_show_clear_assist_warning),
false
@@ -624,7 +624,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_inject_psds),
app.getString(R.string.capability_key_inject_psds),
PreferenceUtils.CAPABILITY_SUPPORTED,
prefs
)
@@ -634,7 +634,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_inject_psds),
app.getString(R.string.capability_key_inject_psds),
PreferenceUtils.CAPABILITY_NOT_SUPPORTED,
prefs
)
@@ -650,7 +650,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_inject_time),
app.getString(R.string.capability_key_inject_time),
PreferenceUtils.CAPABILITY_SUPPORTED,
prefs
)
@@ -660,7 +660,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_inject_time),
app.getString(R.string.capability_key_inject_time),
PreferenceUtils.CAPABILITY_NOT_SUPPORTED,
prefs
)
@@ -682,7 +682,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_delete_assist),
app.getString(R.string.capability_key_delete_assist),
PreferenceUtils.CAPABILITY_SUPPORTED,
prefs
)
@@ -692,7 +692,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
Toast.LENGTH_SHORT
).show()
PreferenceUtils.saveInt(
Application.app.getString(R.string.capability_key_delete_assist),
app.getString(R.string.capability_key_delete_assist),
PreferenceUtils.CAPABILITY_NOT_SUPPORTED,
prefs
)
@@ -907,7 +907,7 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
prefs
)
}
val icon = ContextCompat.getDrawable(Application.app, R.drawable.ic_delete)
val icon = ContextCompat.getDrawable(app, R.drawable.ic_delete)
if (icon != null) {
DrawableCompat.setTint(icon, resources.getColor(R.color.colorPrimary))
}
@@ -928,6 +928,5 @@ class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks {
companion object {
private const val TAG = "GpsTestActivity"
private const val SECONDS_TO_MILLISECONDS = 1000
private const val GPS_RESUME = "gps_resume"
}
}

View File

@@ -15,29 +15,51 @@
*/
package com.android.gpstest.ui
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
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.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.preference.*
import android.preference.CheckBoxPreference
import android.preference.EditTextPreference
import android.preference.ListPreference
import android.preference.Preference
import android.preference.Preference.OnPreferenceChangeListener
import android.preference.PreferenceActivity
import android.preference.PreferenceCategory
import android.text.InputType
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.localeManager
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.BuildConfig
import com.android.gpstest.R
import com.android.gpstest.library.util.PermissionUtils
import com.android.gpstest.library.util.PreferenceUtil.enableMeasurementsPref
import com.android.gpstest.library.util.SatelliteUtils
import com.android.gpstest.library.util.LibUIUtils.resetActivityTitle
import com.android.gpstest.library.util.PermissionUtils
import com.android.gpstest.library.util.PreferenceUtil
import com.android.gpstest.library.util.PreferenceUtil.enableMeasurementsPref
import com.android.gpstest.library.util.PreferenceUtil.enableNavMessagesPref
import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.SatelliteUtils
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class Preferences : PreferenceActivity(), OnSharedPreferenceChangeListener {
var forceFullGnssMeasurements: CheckBoxPreference? = null
@@ -52,6 +74,8 @@ class Preferences : PreferenceActivity(), OnSharedPreferenceChangeListener {
var language: ListPreference? = null
var chkShowNotification: CheckBoxPreference? = null
var chkRunInBackground: CheckBoxPreference? = null
var chkLogFileNmea: CheckBoxPreference? = null
var chkLogFileNavMessages: CheckBoxPreference? = null
var chkLogFileMeasurements: CheckBoxPreference? = null
@@ -181,6 +205,8 @@ class Preferences : PreferenceActivity(), OnSharedPreferenceChangeListener {
chkAsNavMessages = findPreference(getString(R.string.pref_key_as_navigation_message_output)) as CheckBoxPreference
chkAsNavMessages?.isEnabled = enableNavMessagesPref(app, prefs)
initNotificationPermissionDialog()
prefs.registerOnSharedPreferenceChangeListener(this)
}
@@ -287,4 +313,186 @@ class Preferences : PreferenceActivity(), OnSharedPreferenceChangeListener {
}
}
}
/**
* Initializes the dialog for notification permissions, which is required for
* notifications, background execution and logging.
*/
@SuppressLint("NewApi")
private fun initNotificationPermissionDialog() {
chkShowNotification =
findPreference(getString(R.string.pref_key_show_notification)) as CheckBoxPreference
if (VERSION.SDK_INT < VERSION_CODES.TIRAMISU) {
// Notifications are always shown on Android 12 and lower
chkShowNotification?.isEnabled = false
PreferenceUtils.saveBoolean(
getString(R.string.pref_key_show_notification),
true,
prefs
)
return
}
// Permissions for notifications are used in place of a user-defined setting. This workflow
// supports users that have installed an update, who will already have permissions granted.
// Additionally, revoking notification permissions seems to be the only way to disable
// user-facing notifications for the foreground service, because they are required.
PreferenceUtils.saveBoolean(
getString(R.string.pref_key_show_notification),
PermissionUtils.hasGrantedNotificationPermissions(this),
prefs
)
chkRunInBackground =
findPreference(getString(R.string.pref_key_gnss_background)) as CheckBoxPreference
chkLogFileNmea =
findPreference(getString(R.string.pref_key_file_nmea_output)) as CheckBoxPreference
chkLogFileNavMessages =
findPreference(getString(R.string.pref_key_file_navigation_message_output)) as CheckBoxPreference
chkLogFileMeasurements =
findPreference(getString(R.string.pref_key_file_measurement_output)) as CheckBoxPreference
chkLogFileLocation =
findPreference(getString(R.string.pref_key_file_location_output)) as CheckBoxPreference
chkLogFileAntennaJson =
findPreference(getString(R.string.pref_key_file_antenna_output_json)) as CheckBoxPreference
chkLogFileAntennaCsv =
findPreference(getString(R.string.pref_key_file_antenna_output_csv)) as CheckBoxPreference
val prefsThatNeedNotificationPermissions = listOf(
chkShowNotification,
chkRunInBackground,
chkLogFileNmea,
chkLogFileNavMessages,
chkLogFileMeasurements,
chkLogFileLocation,
chkLogFileAntennaJson,
chkLogFileAntennaCsv
)
prefsThatNeedNotificationPermissions.forEach {
it?.onPreferenceChangeListener =
OnPreferenceChangeListener { preference, newValue ->
if (newValue as Boolean && !PermissionUtils.hasGrantedNotificationPermissions(
this
)
) {
// User must have granted notification permissions first
createNotificationPermissionDialog(this).show()
// Reject change to setting by returning false
return@OnPreferenceChangeListener false
} else {
if (preference == chkShowNotification && !newValue &&
(PreferenceUtil.runInBackground(
this,
prefs
) || PreferenceUtil.isFileLoggingEnabled(this, prefs))
) {
// Don't let the user disable notifications if background execution or logging is enabled
createCanNotDisableSettingDialog(this).show()
// Reject change to setting by returning false
return@OnPreferenceChangeListener false
}
if (preference == chkShowNotification && !newValue) {
// If the user disabled the notification setting prompt them to restart app
createRestartApplicationDialog(this).show()
return@OnPreferenceChangeListener false
}
// Accept change to setting by returning true
return@OnPreferenceChangeListener true
}
}
}
}
@RequiresApi(VERSION_CODES.TIRAMISU)
fun createNotificationPermissionDialog(activity: Activity): Dialog {
val view = activity.layoutInflater.inflate(R.layout.notification_permissions_dialog, null)
val textView = view.findViewById<TextView>(R.id.notification_permission_instructions)
textView.text = getString(R.string.notification_permission_required_dialog_text)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.notification_permission_required_dialog_title)
.setCancelable(false)
.setView(view)
.setPositiveButton(
R.string.ok
) { _: DialogInterface?, _: Int -> requestNotificationPermission() }
.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
return builder.create()
}
@RequiresApi(VERSION_CODES.TIRAMISU)
fun createCanNotDisableSettingDialog(activity: Activity): Dialog {
val view = activity.layoutInflater.inflate(R.layout.notification_permissions_dialog, null)
val textView = view.findViewById<TextView>(R.id.notification_permission_instructions)
textView.text = getString(R.string.can_not_disable_setting_dialog_text)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.notification_permission_required_dialog_title)
.setCancelable(false)
.setView(view)
.setPositiveButton(
R.string.ok
) { _: DialogInterface?, _: Int -> requestNotificationPermission() }
return builder.create()
}
@RequiresApi(VERSION_CODES.TIRAMISU)
fun createRestartApplicationDialog(activity: Activity): Dialog {
val view = activity.layoutInflater.inflate(R.layout.notification_permissions_dialog, null)
val textView = view.findViewById<TextView>(R.id.notification_permission_instructions)
textView.text = getString(R.string.need_to_restart_application_dialog_text)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.need_to_restart_application_dialog_title)
.setCancelable(false)
.setView(view)
.setPositiveButton(
R.string.ok
) { _: DialogInterface?, _: Int -> revokeNotificationPermissionAndRestartApplication() }
.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
return builder.create()
}
@RequiresApi(api = VERSION_CODES.TIRAMISU)
private fun requestNotificationPermission() {
ActivityCompat.requestPermissions(
this,
arrayOf(PermissionUtils.getNotificationPermission()),
PermissionUtils.NOTIFICATION_PERMISSION_REQUEST
)
}
@SuppressLint("NewApi")
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PermissionUtils.NOTIFICATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Notification permission granted - change the setting in the Preferences UI
// The notification will automatically be posted by the service
PreferenceUtils.saveBoolean(
getString(R.string.pref_key_show_notification),
true,
prefs
)
recreate()
} else {
// Prompt the user to grant permissions again
createNotificationPermissionDialog(this).show();
}
}
}
@RequiresApi(VERSION_CODES.TIRAMISU)
private fun Context.revokeNotificationPermissionAndRestartApplication() {
revokeSelfPermissionOnKill(Manifest.permission.POST_NOTIFICATIONS)
PreferenceUtils.saveBoolean(getString(R.string.pref_key_show_notification), false, prefs)
Executors.newSingleThreadScheduledExecutor().schedule({
val intent = packageManager.getLaunchIntentForPackage(packageName)
val componentName = intent?.component
val mainIntent = Intent.makeRestartActivityTask(componentName)
startActivity(mainIntent)
Runtime.getRuntime().exit(0)
}, 200, TimeUnit.MILLISECONDS)
}
}

View File

@@ -40,15 +40,17 @@ class ShareDialogFragment : DialogFragment() {
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view = activity!!.layoutInflater.inflate(R.layout.share, null)
val view = requireActivity().layoutInflater.inflate(R.layout.share, null)
setRetainInstance(true)
val builder = AlertDialog.Builder(activity!!)
val builder = AlertDialog.Builder(requireActivity())
.setTitle(R.string.share)
.setView(view)
.setNeutralButton(R.string.main_help_close) { dialog, _ -> }
shareCollectionAdapter = ShareCollectionAdapter(this)
shareCollectionAdapter.setArguments(arguments)
shareCollectionAdapter.setListener(listener)
if (this::listener.isInitialized) {
shareCollectionAdapter.setListener(listener)
}
viewPager = view.findViewById(R.id.pager)
viewPager.offscreenPageLimit = 2
viewPager.adapter = shareCollectionAdapter
@@ -80,6 +82,9 @@ class ShareDialogFragment : DialogFragment() {
fun setListener(listener: Listener) {
this.listener = listener
if (this::shareCollectionAdapter.isInitialized) {
shareCollectionAdapter.setListener(listener)
}
}
}

View File

@@ -18,8 +18,8 @@ import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.library.model.CoordinateType
import com.android.gpstest.library.util.IOUtils
import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.library.util.PreferenceUtils
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup

View File

@@ -2,7 +2,6 @@ package com.android.gpstest.ui.share
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -17,7 +16,6 @@ import com.android.gpstest.library.util.LibUIUtils
import com.google.android.material.button.MaterialButton
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
class ShareLogFragment : Fragment() {
@@ -91,7 +89,7 @@ class ShareLogFragment : Fragment() {
val uri = IOUtils.getUriFromFile(activity, BuildConfig.APPLICATION_ID, files?.get(0))
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.data = uri
activity!!.startActivityForResult(intent, LibUIUtils.PICKFILE_REQUEST_CODE)
requireActivity().startActivityForResult(intent, LibUIUtils.PICKFILE_REQUEST_CODE)
// Dismiss the dialog - it will be re-created in the callback to GpsTestActivity
listener.onFileBrowse()
}

View File

@@ -68,7 +68,7 @@ class UploadDeviceInfoFragment : Fragment() {
uploadNoLocationTextView.visibility = View.GONE
if (Geocoder.isPresent()) {
val geocoder = Geocoder(context!!)
val geocoder = Geocoder(requireContext())
var addresses: List<Address>? = emptyList()
try {
addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)

View File

@@ -44,23 +44,23 @@ import com.android.gpstest.Application
import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.databinding.GpsSkyBinding
import com.android.gpstest.databinding.GpsSkyLegendCardBinding
import com.android.gpstest.databinding.GpsSkySignalMeterBinding
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.ui.SignalInfoViewModel
import com.android.gpstest.ui.status.Filter
import com.android.gpstest.ui.theme.AppTheme
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.library.util.MathUtils
import com.android.gpstest.library.util.PreferenceUtil
import com.android.gpstest.library.util.PreferenceUtil.darkTheme
import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.PreferenceUtils.clearGnssFilter
import com.android.gpstest.library.util.PreferenceUtils.gnssFilter
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.ui.status.Filter
import com.android.gpstest.ui.theme.AppTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@@ -76,7 +76,7 @@ class SkyFragment : Fragment() {
// Binding variables
private var _binding: GpsSkyBinding? = null
private val binding get() = _binding!!
private val binding get() = _binding
private lateinit var legendLines: List<View>
private lateinit var legendShapes: List<ImageView>
private lateinit var meter: GpsSkySignalMeterBinding
@@ -110,9 +110,9 @@ class SkyFragment : Fragment() {
savedInstanceState: Bundle?
): View {
_binding = GpsSkyBinding.inflate(inflater, container, false)
val v = binding.root
meter = binding.skyCn0IndicatorCard.gpsSkySignalMeter
legend = binding.skyLegendCard
val v = binding!!.root
meter = binding!!.skyCn0IndicatorCard.gpsSkySignalMeter
legend = binding!!.skyLegendCard
initFilterView(viewModel)
@@ -215,14 +215,14 @@ class SkyFragment : Fragment() {
}
private fun updateGnssStatus(statuses: List<SatelliteStatus>) {
binding.skyView.setStatus(statuses)
binding?.skyView?.setStatus(statuses)
updateCn0AvgMeterText()
updateCn0Avgs()
}
@ExperimentalCoroutinesApi
private fun onGnssStarted() {
binding.skyView.setStarted()
binding?.skyView?.setStarted()
// Activity or service is observing updates, so observe here too
observeGnssStatus()
observeGnssStates()
@@ -235,8 +235,8 @@ class SkyFragment : Fragment() {
sensorFlow?.cancel()
gnssFlow?.cancel()
binding.skyView.setStopped()
binding.skyLock.visibility = View.GONE
binding?.skyView?.setStopped()
binding?.skyLock?.visibility = View.GONE
}
private fun onOrientationChanged(orientation: Double, tilt: Double) {
@@ -245,12 +245,12 @@ class SkyFragment : Fragment() {
if (!userVisibleHint) {
return
}
binding.skyView.onOrientationChanged(orientation, tilt)
binding?.skyView?.onOrientationChanged(orientation, tilt)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun initFilterView(viewModel: SignalInfoViewModel) {
binding.filterView.apply {
binding!!.filterView.apply {
// Dispose the Composition when the view's LifecycleOwner is destroyed
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
setContent {
@@ -314,7 +314,9 @@ class SkyFragment : Fragment() {
legend.skyLegendShapeLine15a,
legend.skyLegendShapeLine15b,
legend.skyLegendShapeLine16a,
legend.skyLegendShapeLine16b
legend.skyLegendShapeLine16b,
legend.skyLegendShapeLine17a,
legend.skyLegendShapeLine17b
)
// Shape Legend shapes
@@ -331,12 +333,13 @@ class SkyFragment : Fragment() {
legend.skyLegendDiamond4,
legend.skyLegendDiamond5,
legend.skyLegendDiamond6,
legend.skyLegendDiamond7
legend.skyLegendDiamond7,
legend.skyLegendDiamond8
)
}
private fun updateCn0AvgMeterText() {
binding.skyCn0IndicatorCard.gpsSkySignalTitle.apply {
binding?.skyCn0IndicatorCard?.gpsSkySignalTitle?.apply {
skyLegendCn0Title.setText(R.string.gps_cn0_column_label)
skyLegendCn0Units.setText(R.string.sky_legend_cn0_units)
}
@@ -350,6 +353,9 @@ class SkyFragment : Fragment() {
}
private fun updateCn0Avgs() {
if (binding == null) {
return
}
// Based on the avg C/N0 for "in view" and "used" satellites the left margins need to be adjusted accordingly
val meterWidthPx = (Application.app.resources.getDimension(R.dimen.cn0_meter_width)
.toInt()
@@ -366,18 +372,18 @@ class SkyFragment : Fragment() {
// Calculate normal offsets for avg in view satellite C/N0 value TextViews
var leftInViewTextViewMarginPx: Int? = null
if (MathUtils.isValidFloat(binding.skyView.cn0InViewAvg)) {
if (MathUtils.isValidFloat(binding!!.skyView.cn0InViewAvg)) {
leftInViewTextViewMarginPx = LibUIUtils.cn0ToTextViewLeftMarginPx(
binding.skyView.cn0InViewAvg,
binding!!.skyView.cn0InViewAvg,
minTextViewMarginPx, maxTextViewMarginPx
)
}
// Calculate normal offsets for avg used satellite C/N0 value TextViews
var leftUsedTextViewMarginPx: Int? = null
if (MathUtils.isValidFloat(binding.skyView.cn0UsedAvg)) {
if (MathUtils.isValidFloat(binding!!.skyView.cn0UsedAvg)) {
leftUsedTextViewMarginPx = LibUIUtils.cn0ToTextViewLeftMarginPx(
binding.skyView.cn0UsedAvg,
binding!!.skyView.cn0UsedAvg,
minTextViewMarginPx, maxTextViewMarginPx
)
}
@@ -397,12 +403,12 @@ class SkyFragment : Fragment() {
val pTopBottom = LibUIUtils.dpToPixels(Application.app, 4f)
// Set avg C/N0 of satellites in view of device
if (MathUtils.isValidFloat(binding.skyView.cn0InViewAvg)) {
if (MathUtils.isValidFloat(binding!!.skyView.cn0InViewAvg)) {
meter.cn0TextInView.cn0TextInView.text =
String.format("%.1f", binding.skyView.cn0InViewAvg)
String.format("%.1f", binding!!.skyView.cn0InViewAvg)
// Set color of TextView
val color = binding.skyView.getSatelliteColor(binding.skyView.cn0InViewAvg)
val color = binding!!.skyView.getSatelliteColor(binding!!.skyView.cn0InViewAvg)
val background = ContextCompat.getDrawable(
Application.app,
R.drawable.cn0_round_corner_background_in_view
@@ -447,7 +453,7 @@ class SkyFragment : Fragment() {
// Set position and visibility of indicator
val leftIndicatorMarginPx = LibUIUtils.cn0ToIndicatorLeftMarginPx(
binding.skyView.cn0InViewAvg,
binding!!.skyView.cn0InViewAvg,
minIndicatorMarginPx, maxIndicatorMarginPx
)
@@ -471,10 +477,10 @@ class SkyFragment : Fragment() {
}
// Set avg C/N0 of satellites used in fix
if (MathUtils.isValidFloat(binding.skyView.cn0UsedAvg)) {
meter.cn0TextUsed.cn0TextUsed.text = String.format("%.1f", binding.skyView.cn0UsedAvg)
if (MathUtils.isValidFloat(binding!!.skyView.cn0UsedAvg)) {
meter.cn0TextUsed.cn0TextUsed.text = String.format("%.1f", binding!!.skyView.cn0UsedAvg)
// Set color of TextView
val color = binding.skyView.getSatelliteColor(binding.skyView.cn0UsedAvg)
val color = binding!!.skyView.getSatelliteColor(binding!!.skyView.cn0UsedAvg)
val background =
ContextCompat.getDrawable(Application.app, usedCn0Background) as LayerDrawable?
@@ -511,7 +517,7 @@ class SkyFragment : Fragment() {
// Set position and visibility of indicator
val leftMarginPx = LibUIUtils.cn0ToIndicatorLeftMarginPx(
binding.skyView.cn0UsedAvg,
binding!!.skyView.cn0UsedAvg,
minIndicatorMarginPx, maxIndicatorMarginPx
)
@@ -571,14 +577,14 @@ class SkyFragment : Fragment() {
}
private fun showHaveFix() {
LibUIUtils.showViewWithAnimation(binding.skyLock, LibUIUtils.ANIMATION_DURATION_SHORT_MS)
binding?.let { LibUIUtils.showViewWithAnimation(it.skyLock, LibUIUtils.ANIMATION_DURATION_SHORT_MS) }
}
private fun showLostFix() {
LibUIUtils.hideViewWithAnimation(binding.skyLock, LibUIUtils.ANIMATION_DURATION_SHORT_MS)
binding?.let { LibUIUtils.hideViewWithAnimation(it.skyLock, LibUIUtils.ANIMATION_DURATION_SHORT_MS) }
}
companion object {
const val TAG = "GpsSkyFragment"
}
}
}

View File

@@ -23,15 +23,36 @@ import android.text.TextUtils
import android.text.format.DateFormat
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.animation.*
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.expandVertically
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.AlertDialog
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -56,11 +77,10 @@ import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.model.CoordinateType
import com.android.gpstest.library.model.DilutionOfPrecision
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.util.*
import com.android.gpstest.library.util.DateTimeUtils
import com.android.gpstest.library.util.FormatUtils.formatAccuracy
import com.android.gpstest.library.util.FormatUtils.formatAltitude
import com.android.gpstest.library.util.FormatUtils.formatAltitudeMsl
import com.android.gpstest.ui.components.LinkifyText
import com.android.gpstest.library.util.FormatUtils.formatBearing
import com.android.gpstest.library.util.FormatUtils.formatBearingAccuracy
import com.android.gpstest.library.util.FormatUtils.formatDoP
@@ -69,10 +89,14 @@ import com.android.gpstest.library.util.FormatUtils.formatLatOrLon
import com.android.gpstest.library.util.FormatUtils.formatNumSats
import com.android.gpstest.library.util.FormatUtils.formatSpeed
import com.android.gpstest.library.util.FormatUtils.formatSpeedAccuracy
import com.android.gpstest.library.util.IOUtils
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.library.util.PreferenceUtil.coordinateFormat
import com.android.gpstest.library.util.PreferenceUtil.shareIncludeAltitude
import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.PreferenceUtils.gnssFilter
import com.android.gpstest.library.util.SatelliteUtil.isVerticalAccuracySupported
import com.android.gpstest.ui.components.LinkifyText
import java.text.SimpleDateFormat
@Preview

View File

@@ -17,7 +17,12 @@
package com.android.gpstest.ui.status
import android.os.Bundle
import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.Fragment
@@ -26,8 +31,8 @@ import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.library.ui.SignalInfoViewModel
import com.android.gpstest.ui.theme.AppTheme
import com.android.gpstest.library.util.PreferenceUtil.darkTheme
import com.android.gpstest.ui.theme.AppTheme
import com.android.gpstest.util.UIUtils.showSortByDialog
import dagger.hilt.android.AndroidEntryPoint

View File

@@ -18,8 +18,23 @@ package com.android.gpstest.ui.status
import android.location.Location
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -45,7 +60,11 @@ import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.model.*
import com.android.gpstest.library.model.DilutionOfPrecision
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.ui.SignalInfoViewModel
import com.android.gpstest.library.util.CarrierFreqUtils
import com.android.gpstest.library.util.MathUtils
@@ -263,6 +282,9 @@ fun SbasFlag(status: SatelliteStatus, modifier: Modifier = Modifier) {
SbasType.SACCSA -> {
FlagImage(R.drawable.ic_flag_icao, R.string.saccsa_content_description, modifier)
}
SbasType.SOUTHPAN -> {
FlagImage(R.drawable.ic_flag_southpan, R.string.southpan_content_description, modifier)
}
SbasType.UNKNOWN -> {
Box(
modifier = modifier
@@ -436,4 +458,4 @@ fun NotAvailableText(text: String) {
@Composable
fun StatusRowFooter() {
Spacer(modifier = Modifier.padding(bottom = 5.dp))
}
}

View File

@@ -17,10 +17,8 @@ package com.android.gpstest.util
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import com.android.gpstest.R
import android.location.Location
import android.net.Uri
import android.os.Bundle
@@ -34,13 +32,14 @@ import androidx.fragment.app.FragmentActivity
import com.android.gpstest.Application
import com.android.gpstest.Application.Companion.app
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.R
import com.android.gpstest.io.CsvFileLogger
import com.android.gpstest.io.JsonFileLogger
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.util.IOUtils
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.library.util.LocationUtils
import com.android.gpstest.library.util.PreferenceUtils
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.ui.GnssFilterDialog
import com.android.gpstest.ui.HelpActivity
import com.android.gpstest.ui.share.ShareDialogFragment
@@ -49,7 +48,6 @@ import com.android.gpstest.ui.share.ShareDialogFragment.Companion.KEY_LOCATION
import com.android.gpstest.ui.share.ShareDialogFragment.Companion.KEY_LOGGING_ENABLED
import com.android.gpstest.ui.share.ShareDialogFragment.Companion.KEY_LOG_FILES
import java.io.File
import java.util.*
/**
* Utilities for processing user inteface elements
@@ -258,7 +256,7 @@ internal object UIUtils {
activity
)
builder.setTitle(R.string.menu_option_sort_by)
val currentSatOrder = PreferenceUtils.getSatSortOrderFromPreferences(app, Application.prefs)
val currentSatOrder = PreferenceUtils.getSatSortOrderFromPreferences(app, prefs)
builder.setSingleChoiceItems(
R.array.sort_sats, currentSatOrder
) { dialog: DialogInterface, index: Int ->

View File

@@ -26,10 +26,10 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ScrollView;
import com.android.gpstest.R;
import androidx.core.view.ViewCompat;
import com.android.gpstest.R;
/**
* A layout that draws something in the insets passed to {@link #fitSystemWindows(android.graphics.Rect)},
* i.e. the area above UI chrome

View File

@@ -100,7 +100,7 @@
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
app:srcCompat="@drawable/square"
android:contentDescription="@string/square"/>/>
android:contentDescription="@string/square"/>
<View
android:id="@+id/sky_legend_shape_line2a"
@@ -684,6 +684,55 @@
android:layout_margin="5dp"
android:text="@string/sky_legend_shape_saccsa" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/sky_legend_diamond8"
android:layout_width="@dimen/sky_legend_shape_size"
android:layout_height="@dimen/sky_legend_shape_size"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
app:srcCompat="@drawable/diamond"
android:contentDescription="@string/diamond"/>
<View
android:id="@+id/sky_legend_shape_line17a"
android:layout_width="@dimen/sky_legend_shape_small_line"
android:layout_height="1dp"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:background="@color/body_text_2_light" />
<ImageView
android:id="@+id/legend_flag_southpan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:background="@color/body_text_2_light"
android:padding="1dp"
app:srcCompat="@drawable/ic_flag_southpan"
android:contentDescription="@string/southpan_flag"/>
<View
android:id="@+id/sky_legend_shape_line17b"
android:layout_width="@dimen/sky_legend_shape_small_line"
android:layout_height="1dp"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:background="@color/body_text_2_light" />
<TextView
android:id="@+id/sky_southpan_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="@string/sky_legend_shape_southpan" />
</TableRow>
</TableLayout>
<TextView
@@ -793,4 +842,4 @@
</TableRow>
</TableLayout>
</RelativeLayout>
</androidx.cardview.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="24dp"
android:paddingStart="24dp"
android:paddingRight="12dp"
android:paddingEnd="12dp">
<TextView
android:id="@+id/notification_permission_instructions"
style="?android:attr/textAppearance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="14dp"
android:paddingBottom="6dp"
android:text="@string/notification_permission_required_dialog_text"
android:textSize="16sp"
android:autoLink="web"
android:linksClickable="true" />
</LinearLayout>

View File

@@ -51,6 +51,11 @@
<PreferenceCategory
android:title="@string/pref_gps_category_title"
android:key="@string/pref_key_gps_category">
<CheckBoxPreference
android:key="@string/pref_key_show_notification"
android:title="@string/pref_gnss_show_notification_title"
android:summary="@string/pref_gnss_show_notification_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_gnss_background"
android:title="@string/pref_gnss_background_title"

View File

@@ -15,14 +15,14 @@
*/
package com.android.gpstest.util;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import com.android.gpstest.library.model.DilutionOfPrecision;
import com.android.gpstest.library.util.NmeaUtils;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
public class NmeaUtilsTest {
/**
* Test getting altitude above mean sea level (geoid) from NMEA sentences

View File

@@ -12,14 +12,14 @@ Steps to log and view data:
1. In the GPSTest app, go to "Settings", scroll down, tap on "Logging and Output" and under "File Output" make sure the box is checked for each data output type you'd like to see (see next sections).
1. You can share CSV and JSON log files via the "Share" button in the top action bar of the app, or by copying them off internal memory or SD card over USB.
*Note: The file output from GPSTest is buffered, so if you manually copy the file off the device while the app is running you can end up with a partial file if the buffer hasn't been fully flushed. You can force the flush of the buffer by killing the app (e.g., via the Back button), and then if you refresh your file view you should get the complete file with all your data.*
*Note: The file output from GPSTest is buffered, so if you manually copy the file off the device while the app is running you can end up with a partial file if the buffer hasn't been fully flushed. You can force the flush of the buffer by tapping the "Share" button in the app and sharing the file or the "Stop" button in the notification, and then if you refresh your file view you should get the complete file with all your data.*
#### Data output - CSV
Here are the details of file logging:
* CSV format - Same file format as the Google [GPS Measurement Tools (GNSS Logger) project](https://github.com/google/gps-measurement-tools) so you can use MATLAB tools under that project to [analyze the data](https://github.com/google/gps-measurement-tools#to-process-a-log-file-you-collected-from-gnsslogger).
* Files are saved to your Android device storage under the `gnss_log` directory.
* After you've enabled logging via Settings, each time you start the app a new file will be created named with the date and time (e.g., `gnss_log_2019_09_11_13_09_50.txt`). If you end the app (e.g., hit back button) and restart it, another file gets created.
* Files are saved to your Android device storage under the `Downloads/GPSTest` directory for Android 11 and up, and in the `gnss_log` directory for Android 10 and under.
* After you've enabled logging via Settings, each time you start the app a new file will be created named with the date and time (e.g., `gnss_log_2019_09_11_13_09_50.txt`). If you tap on the notification "Stop" button or share the file, another file gets created.
* Each row of the file is prefixed with a string designating the data type:
* `Raw` - Raw GNSS measurements
* `Fix` - Location fix information
@@ -211,7 +211,7 @@ Here's a screenshot from the GPS Measurement Tools desktop software:
![image](https://user-images.githubusercontent.com/928045/64804800-844fae00-d55d-11e9-8212-b0ef65885dc7.png)
## Accessing the system log via Android Studio
## Accessing the real-time output via Android Studio
You can view the data output from GPSTest by using Android Monitor, which is included with Android Studio.

View File

@@ -1,7 +1,6 @@
# <img src="/icons/ic_launcher-playstore.png" width="50"/> GPSTest
[![Build Status](https://github.com/barbeau/gpstest/actions/workflows/android.yml/badge.svg)](https://github.com/barbeau/gpstest/actions/workflows/android.yml)
[![GitHub issues](https://img.shields.io/github/issues/barbeau/gpstest?color=red)](https://github.com/barbeau/gpstest/issues)
[![Donate](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow)](https://www.buymeacoffee.com/barbeau)
[![Twitter Follow](https://img.shields.io/twitter/follow/sjbarbeau.svg?style=social&label=Follow)](https://twitter.com/sjbarbeau)
[![GitHub](https://img.shields.io/github/license/barbeau/gpstest)](/LICENSE)
@@ -30,6 +29,7 @@ It supports dual-frequency¹ GNSS for:
* System for Differential Corrections and Monitoring (SDCM) (Russia)
* Satellite Navigation Augmentation System (SNAS) (China)
* Soluciόn de Aumentaciόn para Caribe, Centro y Sudamérica (SACCSA) (ICAO)
* Southern Positioning Augmentation Network (SouthPAN) (Australia / New Zealand)
¹*Dual-frequency GNSS requires device hardware support and Android 8.0 Oreo or higher. See [Dual-frequency GNSS on Android](https://medium.com/@sjbarbeau/dual-frequency-gnss-on-android-devices-152b8826e1c) for more details.*
@@ -67,15 +67,9 @@ Questions? Check out the [FAQ](FAQ.md), the [Slack group](https://gpstest-andro
- Building the project: You can open and build this project using [Android Studio](https://developer.android.com/studio). For more details, see the [Build documentation](BUILD.MD).
## Donate
GPSTest is free and open-source, and I work on it on my lunch breaks as a side project! If you'd like to support GPSTest, please consider [buying me a coffee](https://www.buymeacoffee.com/barbeau).
[![Donate by buying me a coffee](https://user-images.githubusercontent.com/928045/113343981-82476e80-92fe-11eb-9bc6-c7a281761bad.png)](https://www.buymeacoffee.com/barbeau)
## Trusted by industry experts
Notable appearances of GPSTest:
* Xiaomi - [*Xiaomi Redmi Note 9 Pro Max launch*](https://youtu.be/Y_5cfCZBOV4?t=3035), March 12, 2020.
* European Union Global Navigation Satellite Systems Agency (GSA) - [*Test your Android devices satellite navigation performance*](https://www.gsa.europa.eu/newsroom/news/test-your-android-device-s-satellite-navigation-performance), August 21, 2018.
* European Union Global Navigation Satellite Systems Agency (GSA) - [*Test your Android devices satellite navigation performance*](https://www.euspa.europa.eu/newsroom-events/news-archive/test-your-android-devices-satellite-navigation-performance), August 21, 2018.

View File

@@ -4,17 +4,18 @@ buildscript {
compose_version = '1.2.0-alpha05'
wear_compose_version = '1.0.2'
}
ext.kotlin_version = '1.8.0'
ext.hilt_version = '2.45'
ext.kotlin_version = '2.1.0'
ext.hilt_version = '2.56.2'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:8.11.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// Hilt for dependency injection
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.1.0'
}
}

View File

@@ -0,0 +1,3 @@
• Notification permissions - On fresh installs the GNSS status notification now needs to be enabled in Settings due to changes in how Android handles permissions.
• Add support for BDS B2b and IRNSS L1 - Thanks Narugakuruga!
• Bug fixes - see http://bit.ly/gpstest-releases

View File

@@ -1,23 +1,25 @@
No ads, and no trackers - GPSTest displays real-time information for GNSS and SBAS satellites in view of your device. A vital open-source testing tool for platform engineers, developers, and power users, GPSTest can also assist in understanding why your GPS/GNSS isn't working.
Supports dual-frequency* GNSS for:
• GPS (USA Navstar) (L1, L2, L3, L4, L5)
Galileo (European Union) (E1, E5, E5a, E5b, E6)
GLONASS (Russia) (L1, L2, L3, L5)
• QZSS (Japan) (L1, L2, L5, L6)
• BeiDou/COMPASS (China) (B1, B1-2, B2, B2a, B3)
• IRNSS/NavIC (India) (L5, S)
• Various satellite-based augmentation systems SBAS (e.g., GAGAN, Anik F1, Galaxy 15, Inmarsat 3-F2, Inmarsat 4-F3, SES-5) (L1, L5)
* GPS (USA Navstar) (L1, L2, L3, L4, L5)
* Galileo (European Union) (E1, E5, E5a, E5b, E6)
* GLONASS (Russia) (L1, L2, L3, L5)
* QZSS (Japan) (L1, L2, L5, L6)
* BeiDou/COMPASS (China) (B1I, B2a, B2b, B3I)
* IRNSS/NavIC (India) (L1, L5, S)
* Various satellite-based augmentation systems SBAS (e.g., GAGAN, Anik F1, Galaxy 15, Inmarsat 3-F2, Inmarsat 4-F3, SES-5) (L1, L5)
*Dual-frequency GNSS requires device hardware support and Android 8.0 Oreo or higher. More at https://medium.com/@sjbarbeau/dual-frequency-gnss-on-android-devices-152b8826e1c.
The "Accuracy" feature lets you measure the error in your device's position against your *actual* location (entered by you). Other apps show you the *estimated* accuracy, which is generated by your device. GPSTest lets you to compare this estimated accuracy to *actual* accuracy!
Menu options:
• Inject Time Data - Injects Time assistance data for GPS into the platform, using information from a Network Time Protocol (NTP) server
Inject PSDS Data - Injects Predicted Satellite Data Service (PSDS) assistance data for GNSS into the platform, using information from a PSDS server. Note that some devices don't use PSDS for assistance data - if this is your device, you'll see a message saying "Platform does not support injecting PSDS data". PSDS is the generic term for products like [XTRA assistance data](http://goo.gl/3RjWX).
• Clear Assist Data - Clears all assistance data used for GNSS, including NTP and XTRA data (Note: if you select this option to fix broken GNSS on your device, for GPS to work again you may need to Inject Time and Inject PSDS data. You may also see a large delay until your device acquires a fix again, so please use this feature with caution.)
• Settings - Switch between light and dark themes, change map tile type, auto-start GPS on startup, minimum time and distance between GPS updates, keep screen on.
* Inject Time Data - Injects Time assistance data for GPS into the platform, using information from a Network Time Protocol (NTP) server
* Inject PSDS Data - Injects Predicted Satellite Data Service (PSDS) assistance data for GNSS into the platform, using information from a PSDS server. Note that some devices don't use PSDS for assistance data - if this is your device, you'll see a message saying "Platform does not support injecting PSDS data". PSDS is the generic term for products like [XTRA assistance data](https://www.qualcomm.com/news/releases/2007/02/qualcomm-introduces-gpsonextra-assistance-expand-capabilities-standalone).
* Clear Assist Data - Clears all assistance data used for GNSS, including NTP and XTRA data (Note: if you select this option to fix broken GNSS on your device, for GPS to work again you may need to Inject Time and Inject PSDS data. You may also see a large delay until your device acquires a fix again, so please use this feature with caution.)
* Settings - Switch between light and dark themes, change map tile type, auto-start GPS on startup, minimum time and distance between GPS updates, keep screen on.
Beta versions:
https://play.google.com/apps/testing/com.android.gpstest
@@ -35,4 +37,4 @@ Nostalgic for old releases? Download old versions here:
https://github.com/barbeau/gpstest/releases
Also available on Google Play:
https://play.google.com/store/apps/details?id=com.android.gpstest
https://play.google.com/store/apps/details?id=com.android.gpstest

View File

@@ -1,7 +1,9 @@
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=false
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.caching=true
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
android.enableJetifier=true
android.useAndroidX=true
android.useAndroidX=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

Binary file not shown.

View File

@@ -1,6 +1,7 @@
#Tue Dec 01 16:08:35 EST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

312
gradlew vendored
View File

@@ -1,78 +1,130 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,92 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

78
gradlew.bat vendored
View File

@@ -1,4 +1,22 @@
@if "%DEBUG%" == "" @echo off
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -9,25 +27,29 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -35,48 +57,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -18,12 +18,13 @@ android {
buildTypes {
release {
minifyEnabled true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
}
lint {
disable "JvmStaticProvidesInObjectDetector", "FieldSiteTargetOnQualifierAnnotation", "ModuleCompanionObjects", "ModuleCompanionObjectsNotInModuleParent"
lintOptions {
disable "JvmStaticProvidesInObjectDetector", "FieldSiteTargetOnQualifierAnnotation", "ModuleCompanionObjects", "ModuleCompanionObjectsNotInModuleParent", 'StringFormatInvalid', 'MissingTranslation', 'ExtraTranslation', 'ExpiredTargetSdkVersion'
}
buildFeatures {
@@ -36,11 +37,11 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = JavaVersion.VERSION_17
}
}

View File

@@ -1,13 +1,11 @@
package com.android.library
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>

View File

@@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.shareIn
import java.util.*
private const val TAG = "SharedAntennaManager"

View File

@@ -36,7 +36,12 @@ import com.android.gpstest.library.util.hasPermission
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.shareIn
private const val TAG = "SharedGnssMeasurementManager"
@@ -74,6 +79,7 @@ class SharedGnssMeasurementManager constructor(
trySend(event)
}
@Deprecated("Deprecated in Java")
override fun onStatusChanged(status: Int) {
// These status messages are deprecated on Android S and higher and should not be used
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {

View File

@@ -33,7 +33,12 @@ import com.android.gpstest.library.util.hasPermission
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.shareIn
import java.util.concurrent.TimeUnit
private const val TAG = "SharedGnssStatusManager"

View File

@@ -20,16 +20,21 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.util.Log
import androidx.core.location.LocationListenerCompat
import com.android.gpstest.library.util.PreferenceUtil.minDistance
import com.android.gpstest.library.util.PreferenceUtil.minTimeMillis
import com.android.gpstest.library.util.hasPermission
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.shareIn
private const val TAG = "SharedLocationManager"
@@ -53,7 +58,8 @@ class SharedLocationManager constructor(
@SuppressLint("MissingPermission")
private val _locationUpdates = callbackFlow {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val callback = LocationListener { location ->
// Use LocationListenerCompat to avoid crashes on API Level 30 and lower (#627)
val callback = LocationListenerCompat { location ->
//Log.d(TAG, "New location: ${location.toNotificationTitle()}")
// Send the new location to the Flow observers
trySend(location)

View File

@@ -68,6 +68,7 @@ class SharedNavMessageManager constructor(
trySend(event)
}
@Deprecated("Deprecated in Java")
override fun onStatusChanged(status: Int) {
// These status messages are deprecated on Android S and higher and should not be used
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {

View File

@@ -19,7 +19,11 @@ import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.hardware.*
import android.hardware.GeomagneticField
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.hardware.display.DisplayManager
import android.location.LocationManager
import android.util.Log
@@ -52,8 +56,10 @@ private const val TAG = "SharedSensorManager"
class SharedSensorManager constructor(
private val prefs: SharedPreferences,
private val context: Context,
externalScope: CoroutineScope
externalScope: CoroutineScope,
) {
private val ROT_VECTOR_SENSOR_DELAY_MICROS = 10 * 1000 // 100Hz updates
// Holds sensor data
private val rotationMatrix = FloatArray(16)
private val remappedMatrix = FloatArray(16)
@@ -132,19 +138,25 @@ class SharedSensorManager constructor(
sensorManager.registerListener(
callback,
vectorSensor,
SensorManager.SENSOR_DELAY_FASTEST
ROT_VECTOR_SENSOR_DELAY_MICROS
)
} else if (SatelliteUtils.isOrientationSensorSupported(context)) {
// Use the legacy orientation sensors
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION)
if (sensor != null) {
sensorManager.registerListener(
callback,
sensor,
ROT_VECTOR_SENSOR_DELAY_MICROS
)
} else {
// Use the legacy orientation sensors
val sensor: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION)
sensorManager.registerListener(
callback,
sensor,
SensorManager.SENSOR_DELAY_GAME
)
// No sensors to observe
Log.e(TAG, "Device doesn't support sensor TYPE_ROTATION_VECTOR or TYPE_ORIENTATION")
close()
}
}
} catch (e: Exception) {
Log.e(TAG, "Exception in location flow: $e")
Log.e(TAG, "Exception in sensor flow: $e")
close(e) // in case of exception, close the Flow
}

View File

@@ -3,7 +3,13 @@ package com.android.gpstest.library.di
import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager
import com.android.gpstest.library.data.*
import com.android.gpstest.library.data.SharedAntennaManager
import com.android.gpstest.library.data.SharedGnssMeasurementManager
import com.android.gpstest.library.data.SharedGnssStatusManager
import com.android.gpstest.library.data.SharedLocationManager
import com.android.gpstest.library.data.SharedNavMessageManager
import com.android.gpstest.library.data.SharedNmeaManager
import com.android.gpstest.library.data.SharedSensorManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn

View File

@@ -19,5 +19,5 @@ package com.android.gpstest.library.model;
* Types of Global Navigation Satellite Systems
*/
public enum SatelliteName {
ANIK, GALAXY_15, INMARSAT_3F2, INMARSAT_3F5, INMARSAT_4F3, SES_5, ASTRA_5B, GEO5, UNKNOWN
ANIK, GALAXY_15, INMARSAT_3F2, INMARSAT_3F5, INMARSAT_4F1, INMARSAT_4F3, SES_5, ASTRA_5B, GEO5, UNKNOWN
}

View File

@@ -21,5 +21,5 @@ package com.android.gpstest.library.model;
* http://mgex.igs.org/IGS_MGEX_Status_SBAS.php.
*/
public enum SbasType {
WAAS, EGNOS, GAGAN, MSAS, SDCM, SNAS, SACCSA, UNKNOWN
WAAS, EGNOS, GAGAN, MSAS, SDCM, SNAS, SACCSA, SOUTHPAN, UNKNOWN
}

View File

@@ -28,7 +28,13 @@ import androidx.lifecycle.viewModelScope
import com.android.gpstest.library.data.FirstFixState
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.model.*
import com.android.gpstest.library.model.DilutionOfPrecision
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.Satellite
import com.android.gpstest.library.model.SatelliteGroup
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.util.CarrierFreqUtils.getCarrierFrequencyLabel
import com.android.gpstest.library.util.FormatUtils.formatTtff
import com.android.gpstest.library.util.NmeaUtils

View File

@@ -126,17 +126,15 @@ public class CarrierFreqUtils {
*/
public static String getBeidoucCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1561.098, CF_TOLERANCE_MHZ)) {
return "B1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1589.742, CF_TOLERANCE_MHZ)) {
return "B1-2";
return "B1I";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "B1C";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14, CF_TOLERANCE_MHZ)) {
return "B2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "B2a";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14, CF_TOLERANCE_MHZ)) {
return "B2b";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1268.52, CF_TOLERANCE_MHZ)) {
return "B3";
return "B3I";
} else {
return CF_UNKNOWN;
}
@@ -188,7 +186,9 @@ public class CarrierFreqUtils {
* @return carrier frequency label
*/
public static String getIrnssCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 2492.028, CF_TOLERANCE_MHZ)) {
return "S";
@@ -211,6 +211,13 @@ public class CarrierFreqUtils {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
}
} else if (svid == 122) {
// SouthPAN (Australia/New Zealand)
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
}
} else if (svid == 129 || svid == 137 || svid == 139) {
// MSAS (Japan) - https://gssc.esa.int/navipedia/index.php/MSAS_Space_Segment
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
@@ -282,6 +289,6 @@ public class CarrierFreqUtils {
* * frequency
*/
public static boolean isPrimaryCarrier(String label) {
return label.equals("L1") || label.equals("E1") || label.equals("L1-C") || label.equals("B1") || label.equals("B1C");
return label.equals("L1") || label.equals("E1") || label.equals("L1-C") || label.equals("B1I") || label.equals("B1C");
}
}

View File

@@ -9,7 +9,12 @@ import android.location.Location
import android.os.Build
import androidx.annotation.RequiresApi
import com.android.gpstest.library.R
import com.android.gpstest.library.model.*
import com.android.gpstest.library.model.CoordinateType
import com.android.gpstest.library.model.DilutionOfPrecision
import com.android.gpstest.library.model.Orientation
import com.android.gpstest.library.model.SatelliteGroup
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.util.SatelliteUtil.isBearingAccuracySupported
import com.android.gpstest.library.util.SatelliteUtil.isSpeedAccuracySupported
import com.android.gpstest.library.util.SatelliteUtil.isVerticalAccuracySupported

View File

@@ -189,6 +189,9 @@ public class IOUtils {
String removedQuery = removedPrefix.split("\\?")[0];
String removedMetadata = removedQuery.split(";")[0];
String[] coords = removedMetadata.split(",");
if (coords.length < 2 || TextUtils.isEmpty(coords[0]) || TextUtils.isEmpty(coords[1])) {
return null;
}
if (isValidLatitude(Double.parseDouble(coords[0])) && isValidLongitude(Double.parseDouble(coords[1]))) {
l = new Location("Geo URI");
l.setLatitude(Double.parseDouble(coords[0]));

View File

@@ -52,7 +52,7 @@ import com.android.gpstest.library.ui.SignalInfoViewModel
import com.google.android.material.chip.Chip
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.*
import java.util.Collections
/**
* Utilities for processing user interface elements

View File

@@ -16,19 +16,22 @@
package com.android.gpstest.library.util;
import android.Manifest;
import android.Manifest.permission;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.core.app.ActivityCompat;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
public class PermissionUtils {
public static final int LOCATION_PERMISSION_REQUEST = 1;
public static final int NOTIFICATION_PERMISSION_REQUEST = 1;
public static final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
/**
@@ -45,4 +48,19 @@ public class PermissionUtils {
}
return true;
}
/** Returns the permission string for notification permission */
@RequiresApi(api = VERSION_CODES.TIRAMISU)
public static String getNotificationPermission() {
return Manifest.permission.POST_NOTIFICATIONS;
}
/** Returns true if the context has been granted notification permissions, false if it has not. */
public static boolean hasGrantedNotificationPermissions(Context context) {
if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
return ContextCompat.checkSelfPermission(context, getNotificationPermission())
== PackageManager.PERMISSION_GRANTED;
}
return true;
}
}

View File

@@ -21,6 +21,7 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import com.android.gpstest.library.R;
import com.android.gpstest.library.model.GnssType;

View File

@@ -19,8 +19,16 @@ import android.annotation.SuppressLint
import android.location.GnssStatus
import android.location.Location
import android.os.Build
import com.android.gpstest.library.model.*
import com.android.gpstest.library.util.CarrierFreqUtils.*
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.Satellite
import com.android.gpstest.library.model.SatelliteGroup
import com.android.gpstest.library.model.SatelliteMetadata
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.util.CarrierFreqUtils.CF_UNKNOWN
import com.android.gpstest.library.util.CarrierFreqUtils.CF_UNSUPPORTED
import com.android.gpstest.library.util.CarrierFreqUtils.getCarrierFrequencyLabel
import com.android.gpstest.library.util.CarrierFreqUtils.isPrimaryCarrier
import com.android.gpstest.library.util.SatelliteUtils.createGnssSatelliteKey
object SatelliteUtil {
@@ -302,7 +310,9 @@ object SatelliteUtil {
return SbasType.GAGAN
} else if (this == 129 || this == 137) {
return SbasType.MSAS
} else if (this == 122) {
return SbasType.SOUTHPAN
}
return SbasType.UNKNOWN
}
}
}

View File

@@ -64,6 +64,8 @@ public class SatelliteUtils {
case SBAS:
if (svid == 120) {
return SatelliteName.INMARSAT_3F2;
} else if (svid == 122) {
return SatelliteName.INMARSAT_4F1;
} else if (svid == 123) {
return SatelliteName.ASTRA_5B;
} else if (svid == 126) {
@@ -100,6 +102,19 @@ public class SatelliteUtils {
return sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) != null;
}
/**
* Returns true if this device supports the Sensor.TYPE_ORIENTATION sensor, false if it
* doesn't
*
* @return true if this device supports the Sensor.TYPE_ORIENTATION sensor, false if it
* doesn't
*/
public static boolean isOrientationSensorSupported(Context context) {
SensorManager sensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
return sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION) != null;
}
/**
* Returns true if the platform supports providing carrier frequencies for each satellite, false if it does not
*

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -47,8 +47,8 @@
<string name="gps_num_sats_value">%1$d/%2$d/%3$d</string>
<string name="pdop_value">%1$.1f</string>
<string name="hvdop_value">%1$.1f/%2$.1f</string>
<string name="gps_elevation_column_value">%1$.1f\u00B0</string>
<string name="gps_azimuth_column_value">%1$.1f\u00B0</string>
<string name="gps_elevation_column_value">%1$.0f\u00B0</string>
<string name="gps_azimuth_column_value">%1$.0f\u00B0</string>
<string name="feet_abbreviation">ft</string>
<string name="meters_abbreviation">m</string>
<string name="sbas">SBAS</string>
@@ -62,8 +62,8 @@
<string name="sky_legend_cn0_units">(dB-Hz)</string>
<!-- Values and units display - Accuracy View -->
<string name="benchmark_lat_long">%1$.7f</string>
<string name="benchmark_alt">%1$.1f</string>
<string name="benchmark_lat_long">%1$.10f</string>
<string name="benchmark_alt">%1$.4f</string>
<string name="benchmark_error">%1$.2f</string>
<string name="benchmark_header_error_divider">/</string>
@@ -90,6 +90,7 @@
<string name="pref_key_gps_min_time">gps_min_time</string>
<string name="pref_key_gps_min_distance">gps_min_distance</string>
<string name="pref_key_gnss_background">gnss_background</string>
<string name="pref_key_show_notification">show_notification</string>
<string name="pref_key_display_category">display_category</string>
<string name="pref_key_keep_screen_on">keep_screen_on</string>

View File

@@ -17,7 +17,7 @@
** limitations under the License.
*/
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Fragments -->
<string name="gps_status_title">Status</string>
<string name="gps_map_title">Map</string>
@@ -149,6 +149,7 @@
<string name="sdcm_content_description">SDCM</string>
<string name="snas_content_description">SNAS</string>
<string name="saccsa_content_description">SACCSA</string>
<string name="southpan_content_description">SouthPAN</string>
<!-- Map view -->
<string name="ground_truth_latitude">Your latitude</string>
@@ -193,6 +194,7 @@
<string name="sky_legend_shape_sdcm">SDCM (Russia)</string>
<string name="sky_legend_shape_snas">BDSBAS/SNAS (China)</string>
<string name="sky_legend_shape_saccsa">SACCSA (ICAO)</string>
<string name="sky_legend_shape_southpan">SouthPAN (Australia / New Zealand)</string>
<string name="sky_legend_shape_availability_title">Signal Availability</string>
<string name="sky_legend_shape_not_in_view">Not in view of device</string>
@@ -208,6 +210,7 @@
<string name="japan_flag">Japan flag</string>
<string name="india_flag">India flag</string>
<string name="icao_flag">ICAO flag</string>
<string name="southpan_flag">SouthPAN flag</string>
<string name="circle">circle</string>
<string name="square">square</string>
@@ -243,6 +246,26 @@
<string name="ok">OK</string>
<string name="exit">EXIT</string>
<string name="notification_permission_required_dialog_title" tools:ignore="MissingTranslation">
Notification permission required
</string>
<string name="notification_permission_required_dialog_text" tools:ignore="MissingTranslation">
You must grant notification permissions before enabling this setting. Tap \"Ok\" to grant permissions.
</string>
<string name="can_not_disable_setting_dialog_text" tools:ignore="MissingTranslation">
You can\'t disable notifications while background execution or file logging is enabled. Please disable these settings first.
</string>
<string name="need_to_restart_application_dialog_title" tools:ignore="MissingTranslation">
App restart required
</string>
<string name="need_to_restart_application_dialog_text" tools:ignore="MissingTranslation">
To revoke notification permissions the application needs to be restarted. Tap \"Ok\" to restart.
</string>
<!-- Send feedback -->
<string name="feedback_subject">GPSTest: Feedback</string>
<string name="feedback_body">Complaints, kudos, or bug reports are welcome!\n\n\n</string>
@@ -252,14 +275,10 @@
<!-- What's New -->
<string name="main_help_whatsnew_title">What\'s New?</string>
<string name="main_help_whatsnew">&#8226;\u0020
<b>Background execution</b>
- You can now log on your device while you do something else!\n&#8226;\u0020
<b>GNSS status notification</b>
- Shows the number of signals &amp; satellites in view and in use in a convenient notification.\n&#8226;\u0020
<b>Filter the Sky</b>
- The filter feature now works on the Status AND Sky screens!\n&#8226;\u0020
<b>Themed icon support (Android 13 and up)</b>
- No more disruptions to your otherwise color-perfect home screen :).\n&#8226;\u0020
<b>Notification permissions</b>
- On fresh installs the GNSS status notification now needs to be enabled in Settings due to changes in how Android handles permissions.\n&#8226;\u0020
<b>Add support for BDS B2b and IRNSS L1</b>
- Thanks Narugakuruga!\n&#8226;\u0020
<b>Bug fixes</b>
- See https://bit.ly/gpstest-releases
</string>
@@ -290,6 +309,9 @@
<string name="pref_auto_start_gps_title">Auto-Start GNSS</string>
<string name="pref_auto_start_gps_summary">Start GNSS when the app starts</string>
<string name="pref_gnss_show_notification_title">Show a notification</string>
<string name="pref_gnss_show_notification_summary">If checked, the app will show a notification with real-time GNSS information. This setting must be enabled to run in the background or log data. When disabling this setting you must kill and restart the app for it to take effect. This setting can only be changed on Android 13 and up.</string>
<string name="pref_gnss_background_title">Run GNSS in background</string>
<string name="pref_gnss_background_summary">If checked, when you leave the app a notification will keep GNSS running in the background until you stop it from that notification. This setting is automatically enabled when you enable logging data to files to allow logging in the background.</string>
@@ -457,7 +479,7 @@
- Pentagons\n&#8226;\u0020
<b>IRNSS/NavIC (India)</b>
- Ovals\n&#8226;\u0020
<b>SBAS (WAAS, EGNOS, GAGAN, MSAS, SDCM, BDSBAS/SNAS, SACCSA)</b>
<b>SBAS (WAAS, EGNOS, GAGAN, MSAS, SDCM, BDSBAS/SNAS, SACCSA, SouthPAN)</b>
- Diamonds\n\n
&#8226;\u0020 Transparent satellites - Not in view\n
@@ -485,6 +507,7 @@
&#8226;\u0020 <b>Mike Lockwood</b> - original developer for v1.x\n
&#8226;\u0020 <b>Sean Barbeau</b> - developer for v2.x and higher\n
&#8226;\u0020 GPSTest launcher icon is derived from the Android Material Design "satellite" icon, licensed by Google under Apache v2.0. Other Google material design icons are also used under Apache v2.0. QR code icon by Austin Andrews from https://materialdesignicons.com/icon/qrcode.\n
&#8226;\u0020 Land Information New Zealand has approved use of the SouthPAN logo for the GPSTest open-source application. For further information, please contact southpan@linz.govt.nz \n
&#8226;\u0020 Chinese translation provided by wangkf, liuyunli, SakuraSa233 &amp; Skimige\n
&#8226;\u0020 German translation provided by SIRSteiner\n
&#8226;\u0020 Dutch translation provided by Dennis Dolman, Meteor0id\n

View File

@@ -1,9 +1,8 @@
package com.android.library
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*

View File

@@ -3,6 +3,7 @@ plugins {
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'org.jetbrains.kotlin.plugin.compose'
}
android {
@@ -33,11 +34,11 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = JavaVersion.VERSION_17
freeCompilerArgs += [
"-opt-in=kotlin.RequiresOptIn",
"-Xjvm-default=all"]

View File

@@ -37,4 +37,4 @@
</activity>
</application>
</manifest>
</manifest>

View File

@@ -13,12 +13,15 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -34,13 +37,19 @@ import androidx.core.app.ActivityCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.wear.compose.material.*
import androidx.wear.compose.material.Text
import com.android.gpstest.Application.Companion.prefs
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.data.LocationRepository
import com.android.gpstest.library.model.*
import com.android.gpstest.library.model.GnssType
import com.android.gpstest.library.model.SatelliteStatus
import com.android.gpstest.library.model.SbasType
import com.android.gpstest.library.ui.SignalInfoViewModel
import com.android.gpstest.library.util.*
import com.android.gpstest.library.util.CarrierFreqUtils
import com.android.gpstest.library.util.LibUIUtils
import com.android.gpstest.library.util.MathUtils
import com.android.gpstest.library.util.PermissionUtils
import com.android.gpstest.library.util.PreferenceUtil
import com.android.gpstest.library.util.PreferenceUtils
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@@ -323,6 +332,9 @@ fun SbasFlag(status: SatelliteStatus, modifier: Modifier = Modifier) {
SbasType.SACCSA -> {
FlagImage(R.drawable.ic_flag_icao, R.string.saccsa_content_description, modifier)
}
SbasType.SOUTHPAN -> {
FlagImage(R.drawable.ic_flag_southpan, R.string.southpan_content_description, modifier)
}
SbasType.UNKNOWN -> {
Box(
modifier = modifier

View File

@@ -23,7 +23,14 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.rotary.onRotaryScrollEvent
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.*
import androidx.wear.compose.material.AutoCenteringParams
import androidx.wear.compose.material.PositionIndicator
import androidx.wear.compose.material.Scaffold
import androidx.wear.compose.material.ScalingLazyColumn
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.TimeSource
import androidx.wear.compose.material.TimeText
import androidx.wear.compose.material.rememberScalingLazyListState
import com.android.gpstest.Application
import com.android.gpstest.library.data.FixState
import com.android.gpstest.library.model.CoordinateType