Compare commits

...

117 Commits

Author SHA1 Message Date
Román 16c20287ca Release v3.9.3 2020-08-31 12:45:20 +02:00
Román a733d87c37 Bugfix: export all failed trying to convert to string the Track.Id. 2020-08-31 12:25:58 +02:00
Dennis Guse 79d8ba9ef8 Configure Dependabot for Gradle and Github actions 2020-08-30 23:28:50 +02:00
Dennis Guse 3ee7d4504c Release v3.9.2 2020-08-30 23:05:51 +02:00
Dennis Guse 579509aad8 Upgrade dependencies. 2020-08-30 19:01:27 +02:00
Peter Storch 24ff73ab06 Fix force close on stop button for older devices (#393)
* correct thread handling in ButtonDelay

Discovered in #390 for devices with API Level 25. Not fixing the whole issue, but a first step.

* fix #390 crash when stopping for older devices

The VectorDrawable ic_button_stop.xml referenced from withing the AnimatedVectorDrawable ic_button_stop_anim.xml was a problem for older devices. Now the VectorDrawable is expanded (duplicated) inside the AnimatedVectorDrawable. Now it is working. Even with the animation.
2020-08-27 08:11:10 +02:00
Dennis Guse 60d5e7e515 Reset recordingTrackId on application Startup.
This is necessary if the TrackRecordingService crashed.
2020-08-23 17:36:30 +02:00
Dennis Guse c610758ae1 Move CursorLoader to ContentProviderUtils. 2020-08-23 00:09:02 +02:00
Dennis Guse 17d2755844 Bugfix: MarkerDetailActivity: compare waypointId properly using equals.
Introduced in bcbaf4985a
2020-08-23 00:08:58 +02:00
Dennis Guse 0ab14e0f3d Implement Track.Id and Waypoint.Id (with tests).
Fixes #276.
2020-08-22 23:18:48 +02:00
Dennis Guse bcbaf4985a Implement Track.Id and Waypoint.Id (without tests).
Fixes #276.
2020-08-22 23:18:48 +02:00
Dennis Guse b2e8713db0 Desugar: removed manual date parsing on API26. 2020-08-21 07:01:16 +02:00
Dennis Guse af60e1fdfb Enable Google's desugar plugin (enables stream/map etc.).
Fixes  #347.
2020-08-20 18:31:47 +02:00
Román 75043325ff Fix comment. 2020-08-19 22:04:40 +02:00
Dennis Guse 337e5b9e4d Release v3.9.1 2020-08-19 18:13:10 +02:00
Román 1ff986f1e6 Gps Status: bugfixes.
Bugfix 1: race condition issue fixed with a flag.
Bugfix 2: location time issue fixed setting the time every time a location is received.
2020-08-19 14:02:47 +02:00
thrillfall 6412bb9fcd remove useless parameter 2020-08-18 16:37:44 +02:00
thrillfall 8425997c29 add overload for exporter that takes single track 2020-08-18 16:37:44 +02:00
thrillfall f4b585b830 do not pass complete track data to exporter factory method just to knwo if there is more then one track to export 2020-08-18 16:37:44 +02:00
thrillfall 47c259fa9a move tracks array from class state to method argument 2020-08-18 16:37:44 +02:00
thrillfall 80208e3188 move condition for writing multitrack begin and end outside of methods 2020-08-18 16:37:44 +02:00
thrillfall 2655d3e773 fix decorator TrackExporter interface 2020-08-18 16:37:44 +02:00
Dennis Guse ca8814c478 Release v3.9.0 2020-08-16 20:37:38 +02:00
Dennis Guse 6a397922d2 Cleanup using IntelliJ's analyze code. 2020-08-16 20:13:09 +02:00
Dennis Guse ee062f8a38 Cleanup. 2020-08-16 20:13:09 +02:00
Dennis Guse c8bdf8e641 Wheel circumference is now a textfield accepting input as mm.
Fixes #278.
2020-08-16 20:12:20 +02:00
Dennis Guse 0b2bd6899f Pull request template: add note about adding new libraries. 2020-08-16 12:49:50 +02:00
Román 32d811b46e Bugfix: NPE fixed because listeners/client were null in some cases.
TrackRecordingService: before destroying it sends GPS Status NONE to all listeners.
Cleanup Logs used for debugging.
2020-08-16 07:27:44 +02:00
Román 80a8017a18 GPS Status: animation icon for ready but not fixed status (GPS bad signal). 2020-08-16 07:27:44 +02:00
Román 2b90bfd054 ServiceUtils deleted because it doesn't need anymore.
It uses gps status received from service.
2020-08-16 07:27:44 +02:00
Román 8cb6d63abe Cleanup and rename. 2020-08-16 07:27:44 +02:00
Román 9837680540 GPS Status: TrackListActivity uses the value of gps status from the service to select appropiate icon. 2020-08-16 07:27:44 +02:00
Román e2b741dd08 Added GPS Status to the service so activities can access to the GPS Status. 2020-08-16 07:27:44 +02:00
Peter Storch 18546fd3cc fix #241 stop button lock (#362)
* fix #241 stop button lock

Adds a delay and animation to the stop and pause buttons

- short vibration when starting to push the buttons
- long vibration when action was executed
- Toast to show "Hold to stop/pause recording"
2020-08-15 21:50:06 +02:00
Dennis Guse a87d2e5b09 Bugfix: prevent NPE.
Fixes #374.
2020-08-15 21:23:14 +02:00
Dennis Guse 3ae30e2468 Settings: do prevent import of already existing tracks by default. 2020-08-15 21:12:10 +02:00
Dennis Guse f821c8ba86 Fixed tests. 2020-08-15 21:12:10 +02:00
Dennis Guse 5c96774e79 ContentProvider can now lookup tracks via UUID. 2020-08-15 21:12:10 +02:00
Dennis Guse c26a5edc03 Make track import behavior on duplicate UUID configurable (default: import with a new UUID). 2020-08-15 21:12:10 +02:00
Dennis Guse f8afa9dbad Import/export track UUID (necessary for synchronization).
If a track with a UUID is presented in OpenTracks, it cannot be imported again (enforced by database constraint).
Fixes #307.
2020-08-15 21:12:10 +02:00
Dennis Guse db1167ead5 Find track by UUID. 2020-08-15 21:12:10 +02:00
Dennis Guse 0008673ab0 Add class diagram for export/import code. 2020-08-15 21:12:10 +02:00
Dennis Guse 59d4bfa4ac Export KML: report OpenTracks as generator. 2020-08-15 21:12:10 +02:00
Dennis Guse 04f146c641 Bugfix: formatting of pace was not formatted correctly (e.g., 4:60).
Fixes #370.
2020-08-15 20:19:46 +02:00
Dennis Guse ea97ff65df MarkerEditActivity: fixed NPE on orientation change. 2020-08-15 09:12:19 +02:00
Peter Storch 9827b3fc92 fix #371 mountain bike icon not shown 2020-08-12 20:53:58 +02:00
Peter Storch a35ed2e9b1 move debug resources into debug and cleanup build.gradle 2020-08-11 21:42:38 +02:00
Dennis Guse 26d2a861d7 Add missing bluetooth icon (SVG). 2020-08-10 16:31:38 +02:00
Dennis Guse 389ed9b362 Update dependencies. 2020-08-10 15:52:52 +02:00
Dennis Guse c5e5e862ad App launcher icon: moved arrow slightly too the left. 2020-08-10 15:52:11 +02:00
Dennis Guse b676fd6614 SearchEngineTest: do not use production database.
Partly fixes #143.
2020-08-10 15:39:53 +02:00
Peter Storch 0194efafbc added more .idea files to gitignore 2020-08-08 12:41:50 +02:00
Peter Storch 9ba1357dbb added new activity types (#360)
* added new activity types

- escooter
- inline_skating
- motorbike
- skate_boarding
- skiing
- snowboarding
- climbing
- sailing
- mtb
2020-08-08 12:38:51 +02:00
Dennis Guse 64b66a0c57 Github sponsor button points to OpenTracks-team (liberapay). 2020-08-07 16:15:36 +02:00
Dennis Guse 7787dae9f6 Bluetooth preference use vector icon. 2020-08-06 12:34:01 +02:00
Dennis Guse d0ca06f964 Switch Liberapay to separate OpenTracks-team. 2020-08-05 17:19:01 +02:00
Dennis Guse f7d1a53622 File open intent-filters now expose host (required by lint). 2020-08-05 12:49:30 +02:00
Dennis Guse 5f13d027af CI: CodeQL use custom build command. 2020-08-04 22:58:57 +02:00
Dennis Guse cf45a8079c Simplified getting the BluetoothAdapter. 2020-08-04 22:56:35 +02:00
Dennis Guse 9e553cc900 CI: enable CodeQL 2020-08-04 22:49:14 +02:00
transifex-integration[bot] 1f3466e4b2 Translate /src/main/res/values/strings.xml in eu
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'eu' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:49:12 +02:00
transifex-integration[bot] c813934bcb Translate /src/main/res/values/strings.xml in cs
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'cs' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:48:49 +02:00
transifex-integration[bot] 480202853d Translate /src/main/res/values/strings.xml in da
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'da' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:48:29 +02:00
transifex-integration[bot] 4c58bd81bb Translate /src/main/res/values/strings.xml in nl
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'nl' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:48:12 +02:00
transifex-integration[bot] 9f9b6966a8 Translate /src/main/res/values/strings.xml in et
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'et' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:47:41 +02:00
transifex-integration[bot] 52c97b63d5 Translate /src/main/res/values/strings.xml in fi
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'fi' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:47:24 +02:00
transifex-integration[bot] eee18ec7a4 Translate /src/main/res/values/strings.xml in fr
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'fr' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:46:53 +02:00
transifex-integration[bot] 4d444046de Translate /src/main/res/values/strings.xml in gl
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'gl' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:46:34 +02:00
transifex-integration[bot] 4d5e31b231 Translate /src/main/res/values/strings.xml in de
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'de' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:46:10 +02:00
transifex-integration[bot] 8544f7d20a Translate /src/main/res/values/strings.xml in es
at least 95% translated updated for the source file '/src/main/res/values/strings.xml'
on the 'es' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-08-03 21:45:16 +02:00
Dennis Guse 1feb5cbb65 Release v3.8.1 2020-08-03 08:03:13 +02:00
Dennis Guse 1d2fde6e69 DashboardAPI: report if track is recording and start Dashboard on same task.
Necessary for Picture-in-Picture mode.
2020-08-03 07:59:17 +02:00
Dennis Guse 20423121ee Dashboard API: only forward fullscreen setting, when recording. 2020-08-03 07:59:17 +02:00
Dennis Guse ebf7b0a0c3 TrackRecordingActivity: added setting for fullscreen.
Fixes #342.
2020-08-03 07:59:17 +02:00
Dennis Guse 1189a07794 TrackController: center time. 2020-08-02 16:36:03 +02:00
Román 63e9041922 Orientation change when edit marker has to update photo uri in waypoint for delete button update (hide/show). 2020-07-31 20:29:56 +02:00
Dennis Guse 699be450b0 Fixed too optimistic intent-filter for imports.
Fixes #340.
2020-07-29 23:19:32 +02:00
Dennis Guse 66b175c56c Updated dependencies. 2020-07-26 17:39:30 +02:00
Dennis Guse 81a7aca4a0 Release v3.8.0 2020-07-24 22:22:48 +02:00
Dennis Guse 065f42fbef GPX import/export: speed via TrackPointExtension/v2.
Fixed data type for heartrate/speed (no decimals) allowed.
Fixes #335.
Fixes #181.

GPXTrackWriter: set Trackpointv2 header correctly and export time in correct sequence.
2020-07-24 22:06:58 +02:00
ronajon c92371b36a GPX import/export: add heartrate and cadence via TrackPointExtension/v1. 2020-07-24 22:06:58 +02:00
Dennis Guse 97c46183c3 TrackRecordingServiceConnection: always bind (and start) the TrackRecordingService.
Seemed to be a memory optimization.
2020-07-21 19:44:39 +02:00
Dennis Guse c02b8fab53 HandlerServer: use executor service to send data to TrackRecordingService.
Fixes #329.
2020-07-21 19:43:51 +02:00
Dennis Guse 6540913d59 Moved R.string.stats_unit_key initialization into Startup. 2020-07-20 20:46:41 +02:00
Dennis Guse b70c3be5af Cleanup: static code analysis using AndroidStudio. 2020-07-20 20:05:45 +02:00
Dennis Guse 461f1c246b MarkerEditActivity: do not overwrite new photoUri with previous one. (#332)
Fixes #330.

Tested and it works 👍
2020-07-20 19:30:23 +02:00
Dennis Guse 6d091dfa81 Fixes NPE on MarkerEditActivity when taking a photo and trigger an orientation change. (#327)
Fixes #325.
2020-07-18 22:52:19 +02:00
Peter Storch 97ad6bc2f8 expose waypoints to dashboard
Necessary for https://github.com/OpenTracksApp/OSMDashboard/issues/48
2020-07-18 18:56:53 +02:00
Dennis Guse 93ee8a53ed Import from intent: also match file suffix as fallback. 2020-07-18 18:55:30 +02:00
Dennis Guse bf9b6d6627 Import: individual files chosen by an external app. 2020-07-18 18:55:30 +02:00
Dennis Guse 5f637952aa Replaced blue pushpin icon (png) with with orange icon (svg).
Fixes #115.
2020-07-18 18:51:34 +02:00
Román Martínez 17de8cea6f Service refactoring: move location management to the LocationHandle.
Tests refactoring: location and service tests re-worked.
2020-07-17 12:29:30 +02:00
Deus Figendi afea79476f united the gradiants in ic_marker_orange_pushpin 2020-07-16 22:57:44 +02:00
Deus Figendi 7215a2def0 Added an orange version of the ic_marker pushpin 2020-07-16 22:57:44 +02:00
Román b8d2d14d68 Preferred rate is used for aggregated stats. Fixes #321.
Lambda expression for Observer.
2020-07-15 21:43:55 +02:00
Dennis Guse a2b53c75f4 Release v3.7.6 2020-07-13 18:47:50 +02:00
Dennis Guse 7a5f69d3a5 Settings: default export format is now KMZ incl. data and pictures. 2020-07-13 18:19:34 +02:00
Dennis Guse 7fd9893560 TrackDeleteActivity: keep a wakelock while deletion is ongoing. 2020-07-13 18:19:34 +02:00
Román f209031df0 Pass extra TrackFileFormat enum like a Serializable instead of String (with String resulted in Null after get extra). 2020-07-13 18:19:34 +02:00
Dennis Guse 2dbab929ac Export: replaced async task with thread.
Almost identical to the import.
2020-07-13 18:19:34 +02:00
Dennis Guse e9eb764d12 Import: split ImportActivity into fragments.
The fragements keep the state while the activity is re-created (e.g., orientation change).
2020-07-13 18:19:34 +02:00
Dennis Guse 41a4a5527a Import: replaced async task with thread.
Extracted directory chooser into a separate activity.
2020-07-13 18:19:34 +02:00
Dennis Guse 4ded3c6bc8 TrackDelete: use a Thread and always require TrackIds. 2020-07-08 08:25:36 +02:00
Dennis Guse 51e573e475 TrackDelete: hide progress bar before finishing and add custom animations. 2020-07-08 08:25:36 +02:00
Dennis Guse 98c3169929 Track deletion UI: code cleanup. 2020-07-08 08:25:36 +02:00
Dennis Guse ff65a4d04f Track deletion shows an infinite progressbar.
Fixes #242.
2020-07-08 08:25:36 +02:00
Dennis Guse 278f49dec8 TrackRecordingActivity: removed start/resume toast message. 2020-07-07 18:13:37 +02:00
Dennis Guse d444e1ca3c TrackRecordingActivity: recreate activity properly. 2020-07-07 18:13:37 +02:00
Dennis Guse 13e07696b9 Re-implemented landscape mode.
Fixes #32.
2020-07-07 18:13:37 +02:00
Román Martínez 23c3a4d651 Import/Export speed in GPX and KML files. (#306)
Import/Export speed in GPX and KML files. Fixes #300.
2020-06-30 14:39:44 +02:00
Dennis Guse c9869bab0e Generate UUID for each track (necessary for synchronization).
Fixes #294.
2020-06-30 12:39:30 +02:00
Dennis Guse ecdb509371 Export/import test now verifies the TrackPoints. 2020-06-22 21:56:22 +02:00
transifex-integration[bot] de376a84f8 Apply translations in fr
translation completed for the source file '/fastlane/metadata/android/en-US/short_description.txt'
on the 'fr' language.
2020-06-21 18:19:11 +02:00
transifex-integration[bot] 9abd8bcaa6 Apply translations in de
translation completed for the source file '/fastlane/metadata/android/en-US/short_description.txt'
on the 'de' language.
2020-06-21 18:18:58 +02:00
Román Martínez 8ddadb8e6d Aggregated statistics: updated PlantUML and handle empty list. (#296) 2020-06-20 19:02:02 +02:00
transifex-integration[bot] 924ef3313e Translate /src/main/res/values/strings.xml in nl
translation completed for the source file '/src/main/res/values/strings.xml'
on the 'nl' language.
2020-06-16 19:33:52 +02:00
Dennis Guse 8cb68a6e64 Migrate to default branch main. 2020-06-15 12:10:26 +02:00
204 changed files with 10618 additions and 3477 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# These are supported funding model platforms
liberapay: dennis.guse
liberapay: OpenTracks
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
+4 -1
View File
@@ -7,4 +7,7 @@ A clear and concise description of what the pull request changes/adds.
(If available): The link to the issue that this pull request solves.
**License agreement**
By opening this pull request, you are providing your contribution under the _Apache License 2.0_ (see [LICENSE.md](LICENSE.md)).
By opening this pull request, you are providing your contribution under the _Apache License 2.0_ (see [LICENSE.md](LICENSE.md)).
**Note: new dependencies/libraries**
Please refrain from introducing new libraries without consulting the team.
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
+34
View File
@@ -0,0 +1,34 @@
name: "CodeQL"
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
jobs:
analyse:
name: Analyse
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 2
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: java
- run: |
./gradlew build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+4 -1
View File
@@ -66,6 +66,9 @@ captures/
.idea/sqlDataSources.xml
.idea/vcs.xml
.idea/uiDesigner.xml
.idea/jarRepositories.xml
.idea/markdown-navigator-enh.xml
.idea/markdown-navigator.xml
# Keystore files
*.jks
@@ -76,4 +79,4 @@ captures/
# Fastlane
Appfile
releasePlayStore/*
Gemfile.lock
Gemfile.lock
+1 -1
View File
@@ -21,7 +21,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
</a>
</td>
<td>
<a href="https://liberapay.com/dennis.guse/donate">
<a href="https://liberapay.com/OpenTracks/donate">
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
</a>
</td>
+1 -1
View File
@@ -11,7 +11,7 @@ For the time being, the OpenTracks Data API can be used to implement mainly cust
## Implementation
OpenTracks by default does not expose any data to 3rd-party applications.
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s of one or more selected tracks.
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary), the `TrackPoint`s, and the `Waypoint`s of one or more selected tracks.
For each, one URI is provided.
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
+1 -1
View File
@@ -2,7 +2,7 @@
## Craft new releases
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
1. Get next _version code_ (main): `git rev-list HEAD --count main`
2. Decide on _version name_ (semantic versioning)
3. Manually update _version code_ and _version name_ in `build.gradle`
4. Create changelog (`_version code_.txt`)
+15 -40
View File
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.android.tools.build:gradle:4.0.1'
}
}
@@ -17,20 +17,6 @@ allprojects {
}
}
def getVersionCode = { ->
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-list', 'HEAD', '--count'
standardOutput = stdout
}
return Integer.parseInt(stdout.toString().trim())
}
catch (ignored) {
return -1
}
}
def getVersionName = { ->
try {
def stdout = new ByteArrayOutputStream()
@@ -51,6 +37,8 @@ def getVersionName = { ->
android {
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -59,14 +47,8 @@ android {
defaultConfig {
applicationId "de.dennisguse.opentracks"
manifestPlaceholders = [
applicationLogo: "@mipmap/ic_launcher",
applicationName: "@string/app_name"
]
versionCode 3427
//versionCode getVersionCode() //Useful for debugging.
versionName "v3.7.5"
versionCode 3544
versionName "v3.9.3"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -82,10 +64,6 @@ android {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
manifestPlaceholders = [
applicationLogo: "@mipmap/ic_launcher_debug",
applicationName: "OpenTracks (Debug)"
]
}
release {
@@ -104,20 +82,17 @@ android {
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.google.android.material:material:1.2.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'org.mockito:mockito-android:3.0.0'
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:3.0.0'
androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'org.mockito:mockito-android:3.4.6'
}
+5 -1
View File
@@ -12,6 +12,7 @@ endlegend
TrackListActivity --> TrackRecordingActivity : Start recording
TrackListActivity --> SearchListActivity
TrackListActivity --> MarkerListActivity : Show all markers
TrackListActivity --> AggregatedStatisticsActivity : Aggregated stats
TrackListActivity --> TrackRecordedActivity : Show track
TrackListActivity --> TrackDeleteActivity
@@ -46,6 +47,9 @@ MarkerDetailActivity --> CoordinateOnMapIntent : URI
SettingsActivity --> AboutActivity
SettingsActivity --> HelpActivity
SettingsActivity --> ExportActivity
SettingsActivity --> ImportActivity
SettingsActivity --> DirectoryChooserActivity
SettingsActivity --> DirectoryChooserActivity
DirectoryChooserActivity$ImportDirectoryChooserActivity --> ImportActivity
DirectoryChooserActivity$ExportDirectoryChooserActivity --> ExportActivity
@enduml
+29
View File
@@ -0,0 +1,29 @@
@startuml
interface TrackExporter {
}
class KmzTrackExporter implements TrackExporter
KmzTrackExporter o-- FileTrackExporter
class FileTrackExporter implements TrackExporter {
/' One track only '/
}
FileTrackExporter o-- TrackWriter
interface TrackWriter {
}
class GpxTrackWriter implements TrackWriter
class KmlTrackWriter implements TrackWriter
interface TrackImporter {
}
class KmzTrackImporter implements TrackImporter {
}
KmzTrackImporter o-- KmlFileTrackImporter
class AbstractFileTrackImporter implements TrackImporter {
}
class GpxFileTrackImporter extends AbstractFileTrackImporter {
}
class KmlFileTrackImporter extends AbstractFileTrackImporter {
/' Supports multiple tracks via folders '/
}
@enduml
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
<xs:element name="trackuuid"
type="xs:string" />
</xs:schema>
+474
View File
@@ -0,0 +1,474 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="ic_activity_climbing.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 5.8208333 5.8208333"
height="22"
width="22">
<defs
id="defs2">
<inkscape:path-effect
lpeversion="1"
is_visible="true"
id="path-effect3162"
effect="spiro" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
lpeversion="1"
is_visible="true"
id="path-effect3149"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2514"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect950"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect946"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
lpeversion="1"
is_visible="true"
id="path-effect918"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect912"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
lpeversion="1"
is_visible="true"
id="path-effect894"
effect="fillet_chamfer" />
</defs>
<sodipodi:namedview
inkscape:window-maximized="0"
inkscape:window-y="64"
inkscape:window-x="219"
inkscape:window-height="880"
inkscape:window-width="1500"
inkscape:guide-bbox="true"
showguides="false"
inkscape:snap-text-baseline="true"
inkscape:snap-center="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-midpoints="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="12.271037"
inkscape:cx="7.0404004"
inkscape:zoom="22.390336"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#232629"
id="base">
<inkscape:grid
dotted="true"
empspacing="22"
id="grid833"
type="xygrid" />
<inkscape:grid
originy="0.26458333"
originx="0.26458333"
spacingx="1.0583333"
spacingy="1.0583333"
empspacing="1"
id="grid835"
type="xygrid" />
<sodipodi:guide
inkscape:color="rgb(0,0,255)"
inkscape:locked="false"
inkscape:label=""
id="guide887"
orientation="0.96592583,0.25881905"
position="4.195,4.2333333" />
<sodipodi:guide
id="guide900"
orientation="0,-1"
position="0.26458333,1.9843751" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)"
id="g954">
<rect
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect919"
width="68.791664"
height="79.375"
x="-357.1875"
y="-7.9375" />
<rect
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect851"
width="10.583333"
height="10.583333"
x="-354.54169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect853"
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect855"
width="10.583333"
height="10.583333"
x="-333.375"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect857"
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect859"
width="10.583333"
height="10.583333"
x="-312.20834"
y="-5.2916665" />
<rect
y="5.291667"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect861"
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect863"
width="10.583333"
height="10.583333"
x="-343.95834"
y="5.291667" />
<rect
y="5.291667"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect865"
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect867"
width="10.583333"
height="10.583333"
x="-322.79169"
y="5.291667" />
<rect
y="5.291667"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect869"
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect871"
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect873"
width="10.583333"
height="10.583333"
x="-343.95834"
y="21.166666" />
<rect
y="21.166666"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect875"
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect877"
width="10.583333"
height="10.583333"
x="-322.79169"
y="21.166666" />
<rect
y="21.166666"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect879"
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect881"
width="10.583333"
height="10.583333"
x="-354.54169"
y="31.75" />
<rect
y="31.75"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect883"
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect885"
width="10.583333"
height="10.583333"
x="-333.375"
y="31.75" />
<rect
y="31.75"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect887"
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect889"
width="10.583333"
height="10.583333"
x="-312.20834"
y="31.75" />
<rect
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect891"
width="10.583333"
height="10.583333"
x="-301.625"
y="21.166666" />
<rect
y="31.75"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect893"
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect895"
width="10.583333"
height="10.583333"
x="-354.54166"
y="47.625" />
<rect
y="47.625"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect897"
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect899"
width="10.583333"
height="10.583333"
x="-333.37497"
y="47.625" />
<rect
y="47.625"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect901"
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect903"
width="10.583333"
height="10.583333"
x="-312.20831"
y="47.625" />
<rect
y="58.208336"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect905"
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect907"
width="10.583333"
height="10.583333"
x="-343.95831"
y="58.208336" />
<rect
y="58.208336"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect909"
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect911"
width="10.583333"
height="10.583333"
x="-322.79166"
y="58.208336" />
<rect
y="58.208336"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect913"
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
</g>
<path
id="path920"
d="M 6.3144531 -0.15234375 L 5.6464844 0.34179688 L 4.8945312 2.8847656 L 3.7050781 3.2324219 L 3.0292969 4.9121094 L 1.6992188 5.1113281 L 1.5371094 5.4570312 L -0.2265625 5.8183594 L -0.17382812 6.0761719 L 1.7207031 5.6914062 L 1.8789062 5.3515625 L 3.21875 5.1503906 L 3.9023438 3.4492188 L 5.1074219 3.0976562 L 5.875 0.50195312 L 6.4726562 0.060546875 L 6.3144531 -0.15234375 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<g
transform="matrix(1.226984,0,0,1.226984,-0.81596813,-1.2462535)"
id="g924">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.168524;stroke-opacity:1"
id="path17"
d="M 2.4564013,2.0252139 C 2.6387748,1.9919855 2.7608027,1.8155843 2.7275743,1.6332107 2.6943459,1.4508371 2.5179443,1.3288092 2.3355708,1.3620376 2.1531971,1.395266 2.0311693,1.5716675 2.0643978,1.7540412 2.0976262,1.9364148 2.2740276,2.0584424 2.4564013,2.0252139 Z M 2.0390821,4.4361706 2.2561417,3.8057362 2.6726641,3.9140028 3.0571092,4.4259571 3.3404272,4.1900271 2.8859586,3.5506757 2.4962919,3.3764255 2.6074394,2.8541847 C 2.8682831,3.0636061 3.230079,3.168985 3.5948261,3.1025284 L 3.5344109,2.7709399 C 3.219402,2.8283344 2.9239238,2.7108724 2.7489979,2.5029267 L 2.5348714,2.2678637 C 2.4504294,2.1804703 2.3388697,2.1322773 2.2228138,2.1534226 c -0.049738,0.00906 -0.079877,0.031683 -0.1296145,0.040745 L 1.9734996,2.8024787 1.8537998,3.4107898 1.689464,4.211545 1.0707466,5.2436753 1.3712806,5.4904898 Z"
sodipodi:nodetypes="sssssccccccccccccsccccccc" />
<path
id="rect915"
style="opacity:0.997;fill:#ffffff;stroke:#ffffff;stroke-width:0.248933;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
transform="matrix(0.99300752,0.11805114,-0.2203786,0.97541441,0,0)"
d="m 2.276527,2.111288 v 0 0.8237278 -0.08059 z"
sodipodi:nodetypes="ccccc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

+496
View File
@@ -0,0 +1,496 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
viewBox="0 0 5.8208333 5.8208333"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_activity_escooter.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect3162"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3149"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect2514"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect950"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect946"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect918"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect912"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect894"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#232629"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="22.390336"
inkscape:cx="13.383174"
inkscape:cy="12.569634"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1444"
inkscape:window-height="780"
inkscape:window-x="245"
inkscape:window-y="142"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid833"
empspacing="22"
dotted="true" />
<inkscape:grid
type="xygrid"
id="grid835"
empspacing="1"
spacingy="1.0583333"
spacingx="1.0583333"
originx="0.26458333"
originy="0.26458333" />
<sodipodi:guide
position="4.195,4.2333333"
orientation="0.96592583,0.25881905"
id="guide887"
inkscape:label=""
inkscape:locked="false"
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="0.26458333,1.9843751"
orientation="0,-1"
id="guide900" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g3168"
style="stroke:#909090;stroke-opacity:1">
<path
id="path901"
d="M 2.3632812 1.984375 A 0.1324995 0.1324995 0 0 0 2.25 2.1347656 A 0.1324995 0.1324995 0 0 0 2.3984375 2.2480469 C 2.6424435 2.2158044 2.9061719 2.3043649 3.0820312 2.4765625 C 3.2048822 2.5968554 3.2894144 2.7590671 3.3085938 2.9257812 C 3.3213476 3.0366427 3.3054638 3.1596364 3.2929688 3.2949219 C 3.2804368 3.4306071 3.2701901 3.583197 3.3105469 3.7382812 C 3.3438455 3.8662421 3.4102315 3.9828889 3.5019531 4.078125 C 3.5936748 4.1733611 3.7093185 4.2451189 3.8359375 4.2832031 A 0.1324995 0.1324995 0 0 0 4.0019531 4.1933594 A 0.1324995 0.1324995 0 0 0 3.9121094 4.0292969 C 3.8297238 4.0045171 3.7530387 3.9564973 3.6933594 3.8945312 C 3.63368 3.8325652 3.5900254 3.7531805 3.5683594 3.6699219 C 3.5418024 3.5678678 3.5466893 3.4492033 3.5585938 3.3203125 C 3.5705351 3.191022 3.5879256 3.0476308 3.5703125 2.8945312 C 3.5435298 2.6617257 3.4320694 2.4481756 3.2675781 2.2871094 C 3.0309201 2.0553789 2.691646 1.9409856 2.3632812 1.984375 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#909090;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
transform="scale(0.26458333)"
id="path905"
d="M 6 5.5 A 0.50083507 0.50083507 0 0 0 5.5 6 L 5.5 10 A 0.50083507 0.50083507 0 0 0 6 10.5 L 7 10.5 C 8.3752137 10.5 9.5000001 9.3752139 9.5 8 C 9.4999999 6.6247863 8.3752137 5.5000001 7 5.5 L 6 5.5 z M 6.5 6.5 L 7 6.5 C 7.8339253 6.5000001 8.4999999 7.1660747 8.5 8 C 8.5000001 8.8339251 7.8339253 9.4999999 7 9.5 L 6.5 9.5 L 6.5 6.5 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#909090;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
id="path909"
d="M 1.0585938 1.71875 A 0.1324995 0.1324995 0 0 0 0.92578125 1.8515625 A 0.1324995 0.1324995 0 0 0 1.0585938 1.984375 L 1.5878906 1.984375 A 0.1324995 0.1324995 0 0 0 1.7207031 1.8515625 A 0.1324995 0.1324995 0 0 0 1.5878906 1.71875 L 1.0585938 1.71875 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#909090;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
id="path913"
d="M 1.0585938 2.2480469 A 0.1324995 0.1324995 0 0 0 0.92578125 2.3808594 A 0.1324995 0.1324995 0 0 0 1.0585938 2.5136719 L 1.5878906 2.5136719 A 0.1324995 0.1324995 0 0 0 1.7207031 2.3808594 A 0.1324995 0.1324995 0 0 0 1.5878906 2.2480469 L 1.0585938 2.2480469 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#909090;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
</g>
<path
id="path924"
style="opacity:1;fill:#fdbc4b;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke"
d="m -1.3252903,1.5032615 -0.4100651,0.6834418 a 0.12845631,0.12845631 60.481872 0 0 0.1101504,0.1945466 l 0.2495783,-10e-8 -0.1100188,0.5495576 a 0.08982544,0.08982544 21.142221 0 0 0.1651025,0.063848 L -0.91047797,2.3112131 A 0.12845634,0.12845634 60.481877 0 0 -1.0206284,2.1166665 H -1.2702067 L -1.1601879,1.567109 a 0.08982541,0.08982541 21.142225 0 0 -0.1651024,-0.063848 z"
sodipodi:nodetypes="ccccccc"
inkscape:original-d="m -1.0583333,1.0583332 -0.79375,1.3229167 0.4764566,-10e-8 L -1.5875,3.4395832 -0.79375003,2.1166665 H -1.2702067 Z"
inkscape:path-effect="#path-effect950" />
<g
id="g954"
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
<rect
y="-7.9375"
x="-357.1875"
height="79.375"
width="68.791664"
id="rect919"
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="-5.2916665"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect851"
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect853"
width="10.583333"
height="10.583333"
x="-343.95834"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect855"
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect857"
width="10.583333"
height="10.583333"
x="-322.79169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect859"
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect861"
width="10.583333"
height="10.583333"
x="-354.54169"
y="5.291667" />
<rect
y="5.291667"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect863"
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect865"
width="10.583333"
height="10.583333"
x="-333.375"
y="5.291667" />
<rect
y="5.291667"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect867"
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect869"
width="10.583333"
height="10.583333"
x="-312.20834"
y="5.291667" />
<rect
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect871"
width="10.583333"
height="10.583333"
x="-354.54169"
y="21.166666" />
<rect
y="21.166666"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect873"
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect875"
width="10.583333"
height="10.583333"
x="-333.375"
y="21.166666" />
<rect
y="21.166666"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect877"
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect879"
width="10.583333"
height="10.583333"
x="-312.20834"
y="21.166666" />
<rect
y="31.75"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect881"
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect883"
width="10.583333"
height="10.583333"
x="-343.95834"
y="31.75" />
<rect
y="31.75"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect885"
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect887"
width="10.583333"
height="10.583333"
x="-322.79169"
y="31.75" />
<rect
y="31.75"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect889"
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect891"
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect893"
width="10.583333"
height="10.583333"
x="-301.625"
y="31.75" />
<rect
y="47.625"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect895"
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect897"
width="10.583333"
height="10.583333"
x="-343.95831"
y="47.625" />
<rect
y="47.625"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect899"
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect901"
width="10.583333"
height="10.583333"
x="-322.79166"
y="47.625" />
<rect
y="47.625"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect903"
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect905"
width="10.583333"
height="10.583333"
x="-354.54166"
y="58.208336" />
<rect
y="58.208336"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect907"
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect909"
width="10.583333"
height="10.583333"
x="-333.37497"
y="58.208336" />
<rect
y="58.208336"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect911"
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect913"
width="10.583333"
height="10.583333"
x="-312.20831"
y="58.208336" />
</g>
<path
transform="scale(0.26458333)"
id="path887"
d="M 13.994141 4.5 A 0.49999949 0.49999949 0 0 0 13.552734 4.7773438 A 0.49999949 0.49999949 0 0 0 13.777344 5.4472656 L 14.908203 6.0136719 C 15.328243 6.2241517 15.636226 6.6066586 15.75 7.0625 L 17.390625 13.623047 C 15.381402 13.909129 13.837817 15.475476 13.601562 17.5 L 8.3984375 17.5 C 8.1378934 15.267332 6.3043435 13.50201 4.0039062 13.5 C 3.982495 13.499838 3.9608645 13.499856 3.9394531 13.5 L 2 13.5 A 0.49999949 0.49999949 0 0 0 1.5 14 A 0.49999949 0.49999949 0 0 0 2 14.5 L 3.9394531 14.5 A 0.50004949 0.50004949 0 0 0 3.9433594 14.5 C 3.9610145 14.499866 3.9784387 14.499866 3.9960938 14.5 A 0.50004949 0.50004949 0 0 0 4 14.5 C 5.9389193 14.5 7.5000003 16.061081 7.5 18 A 0.50004949 0.50004949 0 0 0 8 18.5 L 14 18.5 A 0.50004949 0.50004949 0 0 0 14.5 18 C 14.5 16.061081 16.061081 14.5 18 14.5 A 0.50004949 0.50004949 0 0 0 18.484375 13.878906 L 16.71875 6.8203125 C 16.534472 6.0819921 16.037756 5.460052 15.357422 5.1191406 A 0.50004949 0.50004949 0 0 0 15.355469 5.1191406 L 14.222656 4.5527344 A 0.49999949 0.49999949 0 0 0 13.994141 4.5 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157481;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
id="path970"
d="M 4.7617188 4.1367188 C 4.4265639 4.1367188 4.1523438 4.4109389 4.1523438 4.7460938 C 4.1523438 5.0812486 4.4265639 5.3554687 4.7617188 5.3554688 C 5.0968736 5.3554687 5.3710937 5.0812486 5.3710938 4.7460938 C 5.3710937 4.4109389 5.0968736 4.1367188 4.7617188 4.1367188 z M 4.7617188 4.3378906 C 4.9879388 4.3378906 5.1679687 4.5198737 5.1679688 4.7460938 C 5.1679687 4.9723138 4.9879388 5.1542969 4.7617188 5.1542969 C 4.5354987 5.1542969 4.3535156 4.9723138 4.3535156 4.7460938 C 4.3535156 4.5198737 4.5354987 4.3378906 4.7617188 4.3378906 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.201567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:0.997" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.997;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.201567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 1.0221536,4.1367188 c -0.33515491,0 -0.60937501,0.2742201 -0.60937501,0.609375 0,0.3351548 0.2742201,0.6093749 0.60937501,0.609375 0.3351548,-10e-8 0.6093749,-0.2742202 0.609375,-0.609375 -1e-7,-0.3351549 -0.2742202,-0.609375 -0.609375,-0.609375 z m 0,0.2011718 c 0.22622,0 0.4062499,0.1819831 0.40625,0.4082032 -1e-7,0.2262201 -0.18003,0.4082031 -0.40625,0.4082031 -0.22622011,0 -0.40820321,-0.181983 -0.40820321,-0.4082031 0,-0.2262201 0.1819831,-0.4082032 0.40820321,-0.4082032 z"
id="path970-7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

+515
View File
@@ -0,0 +1,515 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="ic_activity_inlineskater.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 5.8208333 5.8208333"
height="22"
width="22">
<defs
id="defs2">
<inkscape:path-effect
lpeversion="1"
is_visible="true"
id="path-effect3162"
effect="spiro" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
lpeversion="1"
is_visible="true"
id="path-effect3149"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2514"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect950"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect946"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
lpeversion="1"
is_visible="true"
id="path-effect918"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect912"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
lpeversion="1"
is_visible="true"
id="path-effect894"
effect="fillet_chamfer" />
</defs>
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="27"
inkscape:window-x="67"
inkscape:window-height="1025"
inkscape:window-width="1853"
inkscape:guide-bbox="true"
showguides="false"
inkscape:snap-text-baseline="true"
inkscape:snap-center="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-midpoints="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="10.868962"
inkscape:cx="8.471442"
inkscape:zoom="22.390336"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#232629"
id="base">
<inkscape:grid
dotted="true"
empspacing="22"
id="grid833"
type="xygrid" />
<inkscape:grid
originy="0.26458333"
originx="0.26458333"
spacingx="1.0583333"
spacingy="1.0583333"
empspacing="1"
id="grid835"
type="xygrid" />
<sodipodi:guide
inkscape:color="rgb(0,0,255)"
inkscape:locked="false"
inkscape:label=""
id="guide887"
orientation="0.96592583,0.25881905"
position="4.195,4.2333333" />
<sodipodi:guide
id="guide900"
orientation="0,-1"
position="0.26458333,1.9843751" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
d="m 3.3722093,1.2527829 c 0.2415068,0 0.4391032,-0.1975962 0.4391032,-0.43910305 0,-0.24150691 -0.1975964,-0.43910339 -0.4391032,-0.43910339 -0.241507,0 -0.4391033,0.19759648 -0.4391033,0.43910339 0,0.24150685 0.1975963,0.43910305 0.4391033,0.43910305 z M 2.5818232,4.3045509 2.8013749,3.3385235 3.2624334,3.7776271 V 5.0949369 H 3.7015366 V 3.4482998 L 3.2404782,3.0091963 3.3722093,2.3505413 c 0.285417,0.3293275 0.7245203,0.5488792 1.207534,0.5488792 V 2.4603172 c -0.4171482,0 -0.7684308,-0.2195517 -0.944072,-0.5269241 L 3.4161195,1.5821105 C 3.3282991,1.4503796 3.1965678,1.3625589 3.0428817,1.3625589 c -0.065865,0 -0.1097755,0.021955 -0.1756413,0.021955 L 1.7255717,1.867528 V 2.8994208 H 2.1646753 V 2.1529448 L 2.5598682,1.9992587 2.2085854,3.7776271 1.1327827,3.5580754 1.0449623,3.9971789 Z"
id="path17"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.219552;stroke-opacity:1" />
<g
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)"
id="g954">
<rect
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect919"
width="68.791664"
height="79.375"
x="-357.1875"
y="-7.9375" />
<rect
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect851"
width="10.583333"
height="10.583333"
x="-354.54169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect853"
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect855"
width="10.583333"
height="10.583333"
x="-333.375"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect857"
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect859"
width="10.583333"
height="10.583333"
x="-312.20834"
y="-5.2916665" />
<rect
y="5.291667"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect861"
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect863"
width="10.583333"
height="10.583333"
x="-343.95834"
y="5.291667" />
<rect
y="5.291667"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect865"
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect867"
width="10.583333"
height="10.583333"
x="-322.79169"
y="5.291667" />
<rect
y="5.291667"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect869"
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect871"
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect873"
width="10.583333"
height="10.583333"
x="-343.95834"
y="21.166666" />
<rect
y="21.166666"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect875"
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect877"
width="10.583333"
height="10.583333"
x="-322.79169"
y="21.166666" />
<rect
y="21.166666"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect879"
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect881"
width="10.583333"
height="10.583333"
x="-354.54169"
y="31.75" />
<rect
y="31.75"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect883"
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect885"
width="10.583333"
height="10.583333"
x="-333.375"
y="31.75" />
<rect
y="31.75"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect887"
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect889"
width="10.583333"
height="10.583333"
x="-312.20834"
y="31.75" />
<rect
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect891"
width="10.583333"
height="10.583333"
x="-301.625"
y="21.166666" />
<rect
y="31.75"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect893"
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect895"
width="10.583333"
height="10.583333"
x="-354.54166"
y="47.625" />
<rect
y="47.625"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect897"
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect899"
width="10.583333"
height="10.583333"
x="-333.37497"
y="47.625" />
<rect
y="47.625"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect901"
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect903"
width="10.583333"
height="10.583333"
x="-312.20831"
y="47.625" />
<rect
y="58.208336"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect905"
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect907"
width="10.583333"
height="10.583333"
x="-343.95831"
y="58.208336" />
<rect
y="58.208336"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect909"
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect911"
width="10.583333"
height="10.583333"
x="-322.79166"
y="58.208336" />
<rect
y="58.208336"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect913"
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
</g>
<g
transform="matrix(-0.30905888,1.6151387,-1.6151387,-0.30905888,9.3627856,1.8488419)"
id="g943">
<circle
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers"
id="path892"
cx="1.6512442"
cy="4.9452085"
r="0.12575205" />
<circle
r="0.12575205"
cy="4.9452085"
cx="1.9359355"
id="path892-3"
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers" />
<circle
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers"
id="path892-3-6"
cx="2.2206268"
cy="4.9452085"
r="0.12575205" />
<circle
r="0.12575205"
cy="4.9452085"
cx="2.5053182"
id="path892-3-6-7"
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers" />
</g>
<g
id="g943-2"
transform="matrix(1.6443153,-0.0204346,0.0204346,1.6443153,-0.01361945,-2.6499566)">
<circle
r="0.12575205"
cy="4.9452085"
cx="1.6512442"
id="path892-7"
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers" />
<circle
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers"
id="path892-3-0"
cx="1.9359355"
cy="4.9452085"
r="0.12575205" />
<circle
r="0.12575205"
cy="4.9452085"
cx="2.2206268"
id="path892-3-6-9"
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers" />
<circle
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-linejoin:round;paint-order:stroke fill markers"
id="path892-3-6-7-3"
cx="2.5053182"
cy="4.9452085"
r="0.12575205" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

+546
View File
@@ -0,0 +1,546 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="ic_activity_motorbike.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 5.8208333 5.8208333"
height="22"
width="22">
<defs
id="defs2">
<inkscape:path-effect
lpeversion="1"
is_visible="true"
id="path-effect1386"
effect="spiro" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1382"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.99999977,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
lpeversion="1"
is_visible="true"
id="path-effect1373"
effect="spiro" />
<inkscape:path-effect
effect="spiro"
id="path-effect1369"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1357"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,2,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.093080149,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3277"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,2,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0,0,1 @ F,0,1,1,0,0,0,1 @ F,0,0,1,0,3.210527,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2514"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect950"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect946"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
lpeversion="1"
is_visible="true"
id="path-effect918"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect912"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
lpeversion="1"
is_visible="true"
id="path-effect894"
effect="fillet_chamfer" />
</defs>
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="27"
inkscape:window-x="67"
inkscape:window-height="1025"
inkscape:window-width="1853"
inkscape:guide-bbox="true"
showguides="false"
inkscape:snap-text-baseline="true"
inkscape:snap-center="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-midpoints="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="12.219337"
inkscape:cx="11.776234"
inkscape:zoom="27.983802"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#232629"
id="base">
<inkscape:grid
dotted="true"
empspacing="22"
id="grid833"
type="xygrid" />
<inkscape:grid
originy="0.26458333"
originx="0.26458333"
spacingx="1.0583333"
spacingy="1.0583333"
empspacing="1"
id="grid835"
type="xygrid" />
<sodipodi:guide
inkscape:color="rgb(0,0,255)"
inkscape:locked="false"
inkscape:label=""
id="guide887"
orientation="0.96592583,0.25881905"
position="4.195,4.2333333" />
<sodipodi:guide
id="guide900"
orientation="0,-1"
position="0.26458333,1.9843751" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)"
id="g954">
<rect
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect919"
width="68.791664"
height="79.375"
x="-357.1875"
y="-7.9375" />
<rect
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect851"
width="10.583333"
height="10.583333"
x="-354.54169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect853"
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect855"
width="10.583333"
height="10.583333"
x="-333.375"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect857"
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect859"
width="10.583333"
height="10.583333"
x="-312.20834"
y="-5.2916665" />
<rect
y="5.291667"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect861"
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect863"
width="10.583333"
height="10.583333"
x="-343.95834"
y="5.291667" />
<rect
y="5.291667"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect865"
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect867"
width="10.583333"
height="10.583333"
x="-322.79169"
y="5.291667" />
<rect
y="5.291667"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect869"
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect871"
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect873"
width="10.583333"
height="10.583333"
x="-343.95834"
y="21.166666" />
<rect
y="21.166666"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect875"
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect877"
width="10.583333"
height="10.583333"
x="-322.79169"
y="21.166666" />
<rect
y="21.166666"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect879"
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect881"
width="10.583333"
height="10.583333"
x="-354.54169"
y="31.75" />
<rect
y="31.75"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect883"
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect885"
width="10.583333"
height="10.583333"
x="-333.375"
y="31.75" />
<rect
y="31.75"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect887"
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect889"
width="10.583333"
height="10.583333"
x="-312.20834"
y="31.75" />
<rect
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect891"
width="10.583333"
height="10.583333"
x="-301.625"
y="21.166666" />
<rect
y="31.75"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect893"
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect895"
width="10.583333"
height="10.583333"
x="-354.54166"
y="47.625" />
<rect
y="47.625"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect897"
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect899"
width="10.583333"
height="10.583333"
x="-333.37497"
y="47.625" />
<rect
y="47.625"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect901"
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect903"
width="10.583333"
height="10.583333"
x="-312.20831"
y="47.625" />
<rect
y="58.208336"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect905"
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect907"
width="10.583333"
height="10.583333"
x="-343.95831"
y="58.208336" />
<rect
y="58.208336"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect909"
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect911"
width="10.583333"
height="10.583333"
x="-322.79166"
y="58.208336" />
<rect
y="58.208336"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect913"
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
</g>
<path
id="path901"
d="M 1.8574219 3.3066406 A 0.1322915 0.1322915 0 0 0 1.7363281 3.375 L 1.0742188 4.5664062 A 0.13230472 0.13230472 0 0 0 1.2050781 4.7617188 L 2.3964844 4.6289062 A 0.1322915 0.1322915 0 0 0 2.5136719 4.4824219 A 0.1322915 0.1322915 0 0 0 2.3671875 4.3671875 L 1.4316406 4.4707031 L 1.9667969 3.5039062 A 0.1322915 0.1322915 0 0 0 1.9160156 3.3242188 A 0.1322915 0.1322915 0 0 0 1.8574219 3.3066406 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
id="path905"
d="M 3.1757812 1.984375 A 0.1322915 0.1322915 0 0 0 3.0429688 2.1171875 A 0.1322915 0.1322915 0 0 0 3.1757812 2.2480469 L 3.6113281 2.2480469 L 4.5058594 4.6757812 A 0.1322915 0.1322915 0 0 0 4.6757812 4.7539062 A 0.1322915 0.1322915 0 0 0 4.7539062 4.5839844 L 3.828125 2.0703125 A 0.13230472 0.13230472 0 0 0 3.7050781 1.984375 L 3.1757812 1.984375 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
transform="scale(0.26458333)"
id="path909"
d="M 4.9765625 11.5 A 0.50004949 0.50004949 0 0 0 4.5 12 L 4.5 13 A 0.50004949 0.50004949 0 0 0 5 13.5 L 8 13.5 A 0.50004949 0.50004949 0 0 0 8.4882812 13.064453 A 0.50004949 0.50004949 0 0 0 8.4921875 13.005859 A 0.50004949 0.50004949 0 0 0 8.2871094 12.595703 A 0.50004949 0.50004949 0 0 0 8.2382812 12.564453 A 0.50004949 0.50004949 0 0 0 8.0703125 12.505859 A 0.50004949 0.50004949 0 0 0 8.0117188 12.5 A 0.50004949 0.50004949 0 0 0 8 12.5 C 7.0833333 12.5 6.4130278 12.27292 5.9726562 12.052734 C 5.5322847 11.832549 5.3535156 11.646484 5.3535156 11.646484 A 0.50004949 0.50004949 0 0 0 4.9765625 11.5 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
id="path913"
d="M 2.7773438 4.1015625 A 0.13249999 0.13249999 0 0 0 2.6464844 4.2324219 A 0.13249999 0.13249999 0 0 0 2.7773438 4.3652344 L 3.0429688 4.3652344 A 0.13249999 0.13249999 0 0 0 3.1757812 4.2324219 A 0.13249999 0.13249999 0 0 0 3.0429688 4.1015625 L 2.7773438 4.1015625 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<g
style="opacity:1"
transform="scale(0.26458333)"
id="g915">
<path
id="path917"
d="m 16.5,10.5 c -0.271556,0.0063 -0.500074,0.228371 -0.5,0.5 0,0.276144 0.223858,0.5 0.5,0.5 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1.0;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;" />
<path
id="path919"
d="M 16.488281 10 C 15.950439 10.012478 15.499849 10.447427 15.5 11 C 15.5 11.546787 15.953215 12 16.5 12 A 0.50083507 0.50083507 0 0 0 17 11.5 L 17 10.5 A 0.50083507 0.50083507 0 0 0 16.488281 10 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1.0;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;" />
</g>
<path
transform="scale(0.26458333)"
id="path923"
d="M 11 11.5 C 10.371809 11.5 9.9307721 11.946836 9.7089844 12.5 L 8 12.5 A 0.50004949 0.50004949 0 0 0 7.5234375 13.152344 L 9.1230469 18.152344 A 0.50004949 0.50004949 0 0 0 9.5996094 18.5 L 13 18.5 A 0.50004949 0.50004949 0 0 0 13.5 18 C 13.5 17.777537 13.499839 17.669786 13.5 17.5 C 13.500093 15.822204 14.542706 14.325749 16.117188 13.746094 A 0.50004949 0.50004949 0 0 0 16.414062 13.103516 L 15.943359 11.826172 A 0.50004949 0.50004949 0 0 0 15.474609 11.5 L 11 11.5 z M 11 12.5 L 15.126953 12.5 L 15.357422 13.128906 C 13.674401 13.966365 12.500106 15.589879 12.5 17.5 L 9.9648438 17.5 L 8.6855469 13.5 L 10 13.5 A 0.50004949 0.50004949 0 0 0 10.5 13 C 10.5 12.717935 10.717935 12.5 11 12.5 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:0.994135" />
<path
id="path927"
d="M 2.7773438 3.703125 A 0.13249999 0.13249999 0 0 0 2.6464844 3.8359375 A 0.13249999 0.13249999 0 0 0 2.7773438 3.96875 L 3.0429688 3.96875 A 0.13249999 0.13249999 0 0 0 3.1757812 3.8359375 A 0.13249999 0.13249999 0 0 0 3.0429688 3.703125 L 2.7773438 3.703125 z "
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.997;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.261354;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 4.6258209,3.8250746 c -0.4345653,0 -0.7901218,0.3555565 -0.7901218,0.7901218 0,0.4345652 0.3555565,0.7901217 0.7901218,0.7901218 0.4345652,-1e-7 0.7901217,-0.3555566 0.7901217,-0.7901218 0,-0.4345653 -0.3555565,-0.7901218 -0.7901217,-0.7901218 z m 0,0.2608413 c 0.2933192,0 0.5267476,0.2359612 0.5267479,0.5292805 0,0.2933193 -0.2334287,0.5292803 -0.5267479,0.5292803 -0.2933193,0 -0.5292805,-0.235961 -0.5292805,-0.5292803 0,-0.2933193 0.2359612,-0.5292805 0.5292805,-0.5292805 z"
id="path970-7-3" />
<path
id="path970-7-3-6"
d="m 1.2138007,3.8250746 c -0.43456528,0 -0.79012178,0.3555565 -0.79012178,0.7901218 0,0.4345652 0.3555565,0.7901217 0.79012178,0.7901218 0.4345652,-1e-7 0.7901217,-0.3555566 0.7901217,-0.7901218 0,-0.4345653 -0.3555565,-0.7901218 -0.7901217,-0.7901218 z m 0,0.2608413 c 0.2933192,0 0.5267476,0.2359612 0.5267479,0.5292805 0,0.2933193 -0.2334287,0.5292803 -0.5267479,0.5292803 -0.29331928,0 -0.52928048,-0.235961 -0.52928048,-0.5292803 0,-0.2933193 0.2359612,-0.5292805 0.52928048,-0.5292805 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.997;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.261354;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

+464
View File
@@ -0,0 +1,464 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
viewBox="0 0 5.8208333 5.8208333"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_activity_mtb.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect3162"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3149"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect2514"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect950"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect946"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect918"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect912"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect894"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#232629"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="15.832358"
inkscape:cx="2.2323103"
inkscape:cy="8.8776466"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1596"
inkscape:window-height="917"
inkscape:window-x="123"
inkscape:window-y="27"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid833"
empspacing="22"
dotted="true" />
<inkscape:grid
type="xygrid"
id="grid835"
empspacing="1"
spacingy="1.0583333"
spacingx="1.0583333"
originx="0.26458333"
originy="0.26458333" />
<sodipodi:guide
position="4.195,4.2333333"
orientation="0.96592583,0.25881905"
id="guide887"
inkscape:label=""
inkscape:locked="false"
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="0.26458333,1.9843751"
orientation="0,-1"
id="guide900" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g954"
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
<rect
y="-7.9375"
x="-357.1875"
height="79.375"
width="68.791664"
id="rect919"
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="-5.2916665"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect851"
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect853"
width="10.583333"
height="10.583333"
x="-343.95834"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect855"
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect857"
width="10.583333"
height="10.583333"
x="-322.79169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect859"
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect861"
width="10.583333"
height="10.583333"
x="-354.54169"
y="5.291667" />
<rect
y="5.291667"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect863"
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect865"
width="10.583333"
height="10.583333"
x="-333.375"
y="5.291667" />
<rect
y="5.291667"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect867"
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect869"
width="10.583333"
height="10.583333"
x="-312.20834"
y="5.291667" />
<rect
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect871"
width="10.583333"
height="10.583333"
x="-354.54169"
y="21.166666" />
<rect
y="21.166666"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect873"
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect875"
width="10.583333"
height="10.583333"
x="-333.375"
y="21.166666" />
<rect
y="21.166666"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect877"
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect879"
width="10.583333"
height="10.583333"
x="-312.20834"
y="21.166666" />
<rect
y="31.75"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect881"
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect883"
width="10.583333"
height="10.583333"
x="-343.95834"
y="31.75" />
<rect
y="31.75"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect885"
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect887"
width="10.583333"
height="10.583333"
x="-322.79169"
y="31.75" />
<rect
y="31.75"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect889"
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect891"
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect893"
width="10.583333"
height="10.583333"
x="-301.625"
y="31.75" />
<rect
y="47.625"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect895"
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect897"
width="10.583333"
height="10.583333"
x="-343.95831"
y="47.625" />
<rect
y="47.625"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect899"
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect901"
width="10.583333"
height="10.583333"
x="-322.79166"
y="47.625" />
<rect
y="47.625"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect903"
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect905"
width="10.583333"
height="10.583333"
x="-354.54166"
y="58.208336" />
<rect
y="58.208336"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect907"
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect909"
width="10.583333"
height="10.583333"
x="-333.37497"
y="58.208336" />
<rect
y="58.208336"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect911"
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect913"
width="10.583333"
height="10.583333"
x="-312.20831"
y="58.208336" />
</g>
<path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.196349"
id="path51"
d="M 1.8042087,1.0709424 C 1.9783861,0.94322723 2.0164009,0.69622363 1.8886857,0.52204593 1.7609704,0.34786823 1.5139667,0.30985363 1.3397894,0.43756873 1.1656114,0.56528423 1.1275967,0.81228773 1.2553124,0.98646553 1.3830273,1.1606434 1.6300308,1.198658 1.8042087,1.0709424 Z M 0.89628461,3.3192757 C 0.4529229,3.6443694 0.35999832,4.2481554 0.68509192,4.6915169 1.0101856,5.1348783 1.6139715,5.2278033 2.0573332,4.9027093 2.5006949,4.5776158 2.5936197,3.9738298 2.2685259,3.5304682 1.9434323,3.0871067 1.3396463,2.9941819 0.89628461,3.3192757 Z M 1.883176,4.6651945 C 1.5823234,4.8857933 1.143206,4.8182123 0.92260684,4.5173598 0.70200762,4.2165074 0.76958946,3.77739 1.0704419,3.5567908 1.3712945,3.3361914 1.8104113,3.4037735 2.0310104,3.7046258 2.2516097,4.0054783 2.1840285,4.4445951 1.883176,4.6651945 Z M 1.6405189,2.4083524 1.7418913,1.7496767 1.9614496,1.7834677 c 0.3567831,0.05491 0.7188509,-0.015794 1.051372,-0.2596139 L 2.7806119,1.2071669 C 2.5430963,1.3813245 2.2834214,1.4256443 2.0364186,1.3876293 L 1.5149667,1.3073761 C 1.3893532,1.3020906 1.2869605,1.3284748 1.1919542,1.3981379 1.0969485,1.4678006 1.0409979,1.5575218 1.0399363,1.6556909 L 0.92166874,2.4241458 c -0.0168959,0.1097797 0.009488,0.2121721 0.0675406,0.2913438 0.0696627,0.095006 0.15938086,0.150959 0.25755306,0.1520177 L 2.0785546,2.9393333 2.6590789,3.7310504 2.9757655,3.4988407 2.2559153,2.5171116 Z M 3.1130918,1.6938078 C 2.6697307,2.0189011 2.5768054,2.6226876 2.9018991,3.0660491 3.2269927,3.5094107 3.8307793,3.602335 4.2741404,3.2772417 4.7175021,2.952148 4.8104268,2.348362 4.4853331,1.9050004 4.1602394,1.4616389 3.5564535,1.368714 3.1130918,1.6938078 Z M 4.0999832,3.0397266 C 3.7991307,3.2603259 3.3600139,3.1927439 3.1394147,2.8918914 2.9188155,2.5910391 2.9863967,2.1519221 3.2872491,1.9313229 3.5881015,1.7107237 4.0272189,1.7783052 4.2478182,2.0791576 4.4684174,2.38001 4.4008356,2.8191274 4.0999832,3.0397266 Z" />
<path
sodipodi:nodetypes="ccccccccccccc"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 6.2355927,2.3458251 4.366485,3.4225311 3.0292969,4.9121094 2.0999371,5.0657395 1.5371094,5.4570312 -0.2265625,5.8183594 -0.17382812,6.0761719 1.7207031,5.6914062 2.2796245,5.3059739 3.21875,5.1503906 4.5637507,3.639328 6.4484834,2.5587157 Z"
id="path920" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

+477
View File
@@ -0,0 +1,477 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
viewBox="0 0 5.8208333 5.8208333"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_activity_sailing.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect3162"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3149"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect2514"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect950"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect946"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect918"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect912"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect894"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#232629"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="22.390336"
inkscape:cx="13.656644"
inkscape:cy="9.1983306"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1596"
inkscape:window-height="917"
inkscape:window-x="123"
inkscape:window-y="27"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid833"
empspacing="22"
dotted="true" />
<inkscape:grid
type="xygrid"
id="grid835"
empspacing="1"
spacingy="1.0583333"
spacingx="1.0583333"
originx="0.26458333"
originy="0.26458333" />
<sodipodi:guide
position="4.195,4.2333333"
orientation="0.96592583,0.25881905"
id="guide887"
inkscape:label=""
inkscape:locked="false"
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="0.26458333,1.9843751"
orientation="0,-1"
id="guide900" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g954"
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
<rect
y="-7.9375"
x="-357.1875"
height="79.375"
width="68.791664"
id="rect919"
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="-5.2916665"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect851"
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect853"
width="10.583333"
height="10.583333"
x="-343.95834"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect855"
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect857"
width="10.583333"
height="10.583333"
x="-322.79169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect859"
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect861"
width="10.583333"
height="10.583333"
x="-354.54169"
y="5.291667" />
<rect
y="5.291667"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect863"
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect865"
width="10.583333"
height="10.583333"
x="-333.375"
y="5.291667" />
<rect
y="5.291667"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect867"
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect869"
width="10.583333"
height="10.583333"
x="-312.20834"
y="5.291667" />
<rect
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect871"
width="10.583333"
height="10.583333"
x="-354.54169"
y="21.166666" />
<rect
y="21.166666"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect873"
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect875"
width="10.583333"
height="10.583333"
x="-333.375"
y="21.166666" />
<rect
y="21.166666"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect877"
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect879"
width="10.583333"
height="10.583333"
x="-312.20834"
y="21.166666" />
<rect
y="31.75"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect881"
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect883"
width="10.583333"
height="10.583333"
x="-343.95834"
y="31.75" />
<rect
y="31.75"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect885"
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect887"
width="10.583333"
height="10.583333"
x="-322.79169"
y="31.75" />
<rect
y="31.75"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect889"
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect891"
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect893"
width="10.583333"
height="10.583333"
x="-301.625"
y="31.75" />
<rect
y="47.625"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect895"
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect897"
width="10.583333"
height="10.583333"
x="-343.95831"
y="47.625" />
<rect
y="47.625"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect899"
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect901"
width="10.583333"
height="10.583333"
x="-322.79166"
y="47.625" />
<rect
y="47.625"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect903"
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect905"
width="10.583333"
height="10.583333"
x="-354.54166"
y="58.208336" />
<rect
y="58.208336"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect907"
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect909"
width="10.583333"
height="10.583333"
x="-333.37497"
y="58.208336" />
<rect
y="58.208336"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect911"
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect913"
width="10.583333"
height="10.583333"
x="-312.20831"
y="58.208336" />
</g>
<path
sodipodi:nodetypes="ccccc"
id="path900"
d="M 0.56041665,4.4708333 1.1604167,5.4 h 3.5 l 0.6,-0.9291667 z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="path902"
d="M 2.8024332,0.32721116 V 4.5 H 3.0184001 V 0.32721116 Z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.244823px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path904"
d="M 2.6527855,1.0066933 2.6459669,4.192806 0.73848581,4.2035269 Z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.257218px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.281927px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.1616713,0.35463877 0.00682,3.82765233 1.9074808,0.01288 z"
id="path904-3" />
<path
id="path921"
d="M 2.8105515,0.32721116 V 0.75450025 L 2.0324942,0.57664397 Z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.286457px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

+482
View File
@@ -0,0 +1,482 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
viewBox="0 0 5.8208333 5.8208333"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_activity_skateboarding.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect3162"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3149"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect2514"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect950"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect946"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect918"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect912"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect894"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
guidetolerance="7.7"
objecttolerance="7.7"
inkscape:lockguides="true"
id="base"
pagecolor="#232629"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="11.195168"
inkscape:cx="12.030411"
inkscape:cy="9.6162221"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1853"
inkscape:window-height="1025"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
snapvisiblegridlinesonly="false"
type="xygrid"
id="grid833"
empspacing="22"
dotted="true" />
<inkscape:grid
type="xygrid"
id="grid835"
empspacing="1"
spacingy="1.0583333"
spacingx="1.0583333"
originx="0.26458333"
originy="0.26458333" />
<sodipodi:guide
position="4.195,4.2333333"
orientation="0.96592583,0.25881905"
id="guide887"
inkscape:label=""
inkscape:locked="true"
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
inkscape:locked="true"
position="0.26458333,1.9843751"
orientation="0,-1"
id="guide900" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:nodetypes="sssssccccccccccccsccccccccccc"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.244651;stroke-opacity:1"
id="path17"
d="M 3.4497594,1.2723995 C 3.7188321,1.2675879 3.9350456,1.043501 3.9302338,0.7744294 3.9254222,0.50535723 3.7013352,0.28914379 3.4322626,0.29395542 3.1631898,0.29876705 2.9469764,0.5228543 2.9517881,0.7919262 2.9565981,1.0609979 3.1806867,1.2772111 3.4497594,1.2723995 Z M 3.0637352,4.3310097 2.8499094,3.4654121 3.5050565,3.7377246 4.0318851,5.1078311 4.4885872,4.9322216 3.8300515,3.2195885 3.1749046,2.9472761 3.3770914,2.4212785 C 3.7016475,2.7825092 4.1834322,3.0452834 4.7215778,3.0356596 L 4.7128291,2.5464372 C 4.2480673,2.5547482 3.9586663,2.3644029 3.7568534,2.0254461 L 3.5052429,1.6384419 C 3.4047741,1.4934249 3.2562572,1.3982048 3.0850292,1.4012668 c -0.073384,0.00131 -0.1218679,0.026649 -0.1952518,0.02796 L 1.627421,1.9901187 1.2471269,3.0265669 1.6538376,3.2423177 2.0691965,2.3071052 2.5595693,2.120264 2.1081061,3.2524111 2.4089713,4.1591882 1.3164273,4.8578585 1.9480117,4.971638 Z" />
<g
id="g954"
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
<rect
y="-7.9375"
x="-357.1875"
height="79.375"
width="68.791664"
id="rect919"
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="-5.2916665"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect851"
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect853"
width="10.583333"
height="10.583333"
x="-343.95834"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect855"
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect857"
width="10.583333"
height="10.583333"
x="-322.79169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect859"
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect861"
width="10.583333"
height="10.583333"
x="-354.54169"
y="5.291667" />
<rect
y="5.291667"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect863"
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect865"
width="10.583333"
height="10.583333"
x="-333.375"
y="5.291667" />
<rect
y="5.291667"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect867"
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect869"
width="10.583333"
height="10.583333"
x="-312.20834"
y="5.291667" />
<rect
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect871"
width="10.583333"
height="10.583333"
x="-354.54169"
y="21.166666" />
<rect
y="21.166666"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect873"
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect875"
width="10.583333"
height="10.583333"
x="-333.375"
y="21.166666" />
<rect
y="21.166666"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect877"
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect879"
width="10.583333"
height="10.583333"
x="-312.20834"
y="21.166666" />
<rect
y="31.75"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect881"
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect883"
width="10.583333"
height="10.583333"
x="-343.95834"
y="31.75" />
<rect
y="31.75"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect885"
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect887"
width="10.583333"
height="10.583333"
x="-322.79169"
y="31.75" />
<rect
y="31.75"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect889"
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect891"
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect893"
width="10.583333"
height="10.583333"
x="-301.625"
y="31.75" />
<rect
y="47.625"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect895"
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect897"
width="10.583333"
height="10.583333"
x="-343.95831"
y="47.625" />
<rect
y="47.625"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect899"
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect901"
width="10.583333"
height="10.583333"
x="-322.79166"
y="47.625" />
<rect
y="47.625"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect903"
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect905"
width="10.583333"
height="10.583333"
x="-354.54166"
y="58.208336" />
<rect
y="58.208336"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect907"
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect909"
width="10.583333"
height="10.583333"
x="-333.37497"
y="58.208336" />
<rect
y="58.208336"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect911"
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect913"
width="10.583333"
height="10.583333"
x="-312.20831"
y="58.208336" />
</g>
<path
sodipodi:nodetypes="cccccccccccccc"
d="M 0.70720657,4.618942 0.50519813,4.8871827 c -3e-8,0 0.1915452,0.1543669 0.29349555,0.2268814 0.0408697,0.022353 0.0297057,0.028139 0.10131117,0.035126 l 2.08762525,-0.0023 2.0876253,0.0023 c 0.071605,-0.00699 0.061055,-0.012773 0.1019258,-0.035126 C 5.2791315,5.04155 5.4700628,4.8871827 5.4700628,4.8871827 L 5.2680537,4.618942 5.0181529,4.8124246 3.1356059,4.811273 H 2.8396543 l -1.88193269,0.00115 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.304444px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
id="path894-3" />
<circle
r="0.1149179"
cy="5.4143333"
cx="1.4282604"
id="path999"
style="opacity:0.997;fill:#ffffff;stroke:#ffffff;stroke-width:0.264999;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers" />
<circle
style="opacity:0.997;fill:#ffffff;stroke:#ffffff;stroke-width:0.264999;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
id="path999-5"
cx="4.4387093"
cy="5.4143333"
r="0.1149179" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

+469
View File
@@ -0,0 +1,469 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="ic_activity_skiing.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 5.8208333 5.8208333"
height="22"
width="22">
<defs
id="defs2">
<inkscape:path-effect
lpeversion="1"
is_visible="true"
id="path-effect3162"
effect="spiro" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
lpeversion="1"
is_visible="true"
id="path-effect3149"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2514"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect950"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect946"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
lpeversion="1"
is_visible="true"
id="path-effect918"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect912"
effect="fillet_chamfer" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
lpeversion="1"
is_visible="true"
id="path-effect894"
effect="fillet_chamfer" />
</defs>
<sodipodi:namedview
inkscape:window-maximized="0"
inkscape:window-y="175"
inkscape:window-x="67"
inkscape:window-height="780"
inkscape:window-width="1444"
inkscape:guide-bbox="true"
showguides="false"
inkscape:snap-text-baseline="true"
inkscape:snap-center="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-midpoints="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="11.008507"
inkscape:cx="12.634965"
inkscape:zoom="15.832358"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#232629"
id="base">
<inkscape:grid
dotted="true"
empspacing="22"
id="grid833"
type="xygrid" />
<inkscape:grid
originy="0.26458333"
originx="0.26458333"
spacingx="1.0583333"
spacingy="1.0583333"
empspacing="1"
id="grid835"
type="xygrid" />
<sodipodi:guide
inkscape:color="rgb(0,0,255)"
inkscape:locked="false"
inkscape:label=""
id="guide887"
orientation="0.96592583,0.25881905"
position="4.195,4.2333333" />
<sodipodi:guide
id="guide900"
orientation="0,-1"
position="0.26458333,1.9843751" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
sodipodi:nodetypes="ssssscccccccccccsacccccc"
d="M 4.5585535,1.8682464 C 4.730575,2.037758 5.0100109,2.0357042 5.1795224,1.8636824 5.3490338,1.6916608 5.3469796,1.4122249 5.1749586,1.2427137 5.0029369,1.0732022 4.7235009,1.075256 4.5539893,1.2472777 4.3844781,1.419299 4.3865322,1.6987351 4.5585535,1.8682464 Z M 2.5616473,2.8895379 2.9818066,3.7412695 2.1627798,4.156586 2.4890714,4.5159652 3.6644142,3.9783412 3.3324214,3.2071044 3.7880462,2.6501643 C 3.760192,3.0850707 3.9188577,3.5496566 4.2629009,3.8886795 L 4.5711034,3.575913 C 4.2739752,3.2831205 4.1778634,2.8801752 4.2684987,2.5379577 L 4.3586776,2.1336426 C 4.3885856,1.9781722 4.356396,1.8231572 4.2469278,1.7152862 4.2000132,1.6690561 4.0431309,1.5310148 3.9171527,1.4852939 3.6109432,1.3741621 2.9436171,1.4000918 2.9436171,1.4000918 L 1.9817647,1.9917143 2.2945313,2.2999171 3.0666299,1.8154812 3.4559921,1.8297725 Z"
id="path17"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.219552;stroke-opacity:1" />
<g
transform="matrix(0.38576851,0,0,0.38576851,152.97482,-12.494408)"
id="g954">
<rect
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect919"
width="68.791664"
height="79.375"
x="-357.1875"
y="-7.9375" />
<rect
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect851"
width="10.583333"
height="10.583333"
x="-354.54169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect853"
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect855"
width="10.583333"
height="10.583333"
x="-333.375"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect857"
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect859"
width="10.583333"
height="10.583333"
x="-312.20834"
y="-5.2916665" />
<rect
y="5.291667"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect861"
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect863"
width="10.583333"
height="10.583333"
x="-343.95834"
y="5.291667" />
<rect
y="5.291667"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect865"
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect867"
width="10.583333"
height="10.583333"
x="-322.79169"
y="5.291667" />
<rect
y="5.291667"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect869"
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect871"
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect873"
width="10.583333"
height="10.583333"
x="-343.95834"
y="21.166666" />
<rect
y="21.166666"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect875"
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect877"
width="10.583333"
height="10.583333"
x="-322.79169"
y="21.166666" />
<rect
y="21.166666"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect879"
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect881"
width="10.583333"
height="10.583333"
x="-354.54169"
y="31.75" />
<rect
y="31.75"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect883"
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect885"
width="10.583333"
height="10.583333"
x="-333.375"
y="31.75" />
<rect
y="31.75"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect887"
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect889"
width="10.583333"
height="10.583333"
x="-312.20834"
y="31.75" />
<rect
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect891"
width="10.583333"
height="10.583333"
x="-301.625"
y="21.166666" />
<rect
y="31.75"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect893"
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect895"
width="10.583333"
height="10.583333"
x="-354.54166"
y="47.625" />
<rect
y="47.625"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect897"
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect899"
width="10.583333"
height="10.583333"
x="-333.37497"
y="47.625" />
<rect
y="47.625"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect901"
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect903"
width="10.583333"
height="10.583333"
x="-312.20831"
y="47.625" />
<rect
y="58.208336"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect905"
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect907"
width="10.583333"
height="10.583333"
x="-343.95831"
y="58.208336" />
<rect
y="58.208336"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect909"
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect911"
width="10.583333"
height="10.583333"
x="-322.79166"
y="58.208336" />
<rect
y="58.208336"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect913"
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
</g>
<path
sodipodi:nodetypes="cccccccc"
id="path894-3"
d="m 0.42966147,3.4124371 3.27271563,1.98984 c 0.1037203,0.05566 0.091268,0.04209 0.159023,0.05977 0.1768227,0.031256 0.4686557,-0.030884 0.4686557,-0.030884 L 4.2534675,5.1832708 3.8763995,5.1892147 0.56600626,3.186415 Z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.369514px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
<path
d="M 1.3650688,1.2665262 1.2347897,1.4356529 1.0541865,1.3107744 0.939761,1.4515535 1.1246616,1.5780728 0.99349664,1.748178 1.1114823,1.8390193 1.247937,1.6625738 4.3621692,3.7943987 4.471835,3.6701937 1.357598,1.5201785 1.4830545,1.3573675 Z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.238916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
id="path898" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

+470
View File
@@ -0,0 +1,470 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
viewBox="0 0 5.8208333 5.8208333"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_activity_snowboarding.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect3162"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3149"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
hide_knots="false"
only_selected="false"
apply_with_radius="true"
apply_no_radius="true"
use_knot_distance="true"
flexible="false"
chamfer_steps="1"
radius="0"
mode="F"
method="auto"
unit="px"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
lpeversion="1"
is_visible="true"
id="path-effect2514"
effect="fillet_chamfer" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect950"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect946"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect918"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect912"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect894"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
unit="px"
method="auto"
mode="F"
radius="0"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
guidetolerance="7.7"
objecttolerance="7.7"
inkscape:lockguides="true"
id="base"
pagecolor="#232629"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="22.390336"
inkscape:cx="3.9483707"
inkscape:cy="10.41996"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1853"
inkscape:window-height="1025"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
snapvisiblegridlinesonly="false"
type="xygrid"
id="grid833"
empspacing="22"
dotted="true" />
<inkscape:grid
type="xygrid"
id="grid835"
empspacing="1"
spacingy="1.0583333"
spacingx="1.0583333"
originx="0.26458333"
originy="0.26458333" />
<sodipodi:guide
position="4.195,4.2333333"
orientation="0.96592583,0.25881905"
id="guide887"
inkscape:label=""
inkscape:locked="true"
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
inkscape:locked="true"
position="0.26458333,1.9843751"
orientation="0,-1"
id="guide900" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:nodetypes="sssssccccccccccccsccccccccccc"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.244651;stroke-opacity:1"
id="path17"
d="M 3.4497594,1.6623572 C 3.7188321,1.6575456 3.9350456,1.4334587 3.9302338,1.1643861 3.9254222,0.89531328 3.7013352,0.67909984 3.4322626,0.68391147 3.1631898,0.6887231 2.9469764,0.91281035 2.9517881,1.181883 2.9566019,1.4509556 3.1806867,1.6671688 3.4497594,1.6623572 Z M 3.0637352,4.7209674 2.8499094,3.8553698 3.5050565,4.1276823 4.0318851,5.4977888 4.4885872,5.3221793 3.8300515,3.6095462 3.1749046,3.3372338 3.3770914,2.8112362 C 3.7016475,3.1724669 4.1834322,3.4352411 4.7215778,3.4256173 L 4.7128291,2.9363949 C 4.2480673,2.9447059 3.9586663,2.7543606 3.7568534,2.4154038 L 3.5052429,2.0283996 C 3.4047741,1.8833826 3.2562572,1.7881625 3.0850292,1.7912245 3.0116457,1.7925394 2.9631613,1.8178734 2.8897774,1.819185 L 1.627421,2.3800764 1.2471269,3.4165246 1.6538376,3.6322754 2.0691965,2.6970629 2.5595693,2.5102217 2.1081061,3.6423688 2.4089713,4.5491459 1.3164273,5.2478162 1.9480117,5.3615957 Z" />
<g
id="g954"
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
<rect
y="-7.9375"
x="-357.1875"
height="79.375"
width="68.791664"
id="rect919"
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="-5.2916665"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect851"
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect853"
width="10.583333"
height="10.583333"
x="-343.95834"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect855"
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect857"
width="10.583333"
height="10.583333"
x="-322.79169"
y="-5.2916665" />
<rect
y="-5.2916665"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect859"
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect861"
width="10.583333"
height="10.583333"
x="-354.54169"
y="5.291667" />
<rect
y="5.291667"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect863"
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect865"
width="10.583333"
height="10.583333"
x="-333.375"
y="5.291667" />
<rect
y="5.291667"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect867"
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect869"
width="10.583333"
height="10.583333"
x="-312.20834"
y="5.291667" />
<rect
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect871"
width="10.583333"
height="10.583333"
x="-354.54169"
y="21.166666" />
<rect
y="21.166666"
x="-343.95834"
height="10.583333"
width="10.583333"
id="rect873"
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect875"
width="10.583333"
height="10.583333"
x="-333.375"
y="21.166666" />
<rect
y="21.166666"
x="-322.79169"
height="10.583333"
width="10.583333"
id="rect877"
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect879"
width="10.583333"
height="10.583333"
x="-312.20834"
y="21.166666" />
<rect
y="31.75"
x="-354.54169"
height="10.583333"
width="10.583333"
id="rect881"
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect883"
width="10.583333"
height="10.583333"
x="-343.95834"
y="31.75" />
<rect
y="31.75"
x="-333.375"
height="10.583333"
width="10.583333"
id="rect885"
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect887"
width="10.583333"
height="10.583333"
x="-322.79169"
y="31.75" />
<rect
y="31.75"
x="-312.20834"
height="10.583333"
width="10.583333"
id="rect889"
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
y="21.166666"
x="-301.625"
height="10.583333"
width="10.583333"
id="rect891"
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect893"
width="10.583333"
height="10.583333"
x="-301.625"
y="31.75" />
<rect
y="47.625"
x="-354.54166"
height="10.583333"
width="10.583333"
id="rect895"
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect897"
width="10.583333"
height="10.583333"
x="-343.95831"
y="47.625" />
<rect
y="47.625"
x="-333.37497"
height="10.583333"
width="10.583333"
id="rect899"
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect901"
width="10.583333"
height="10.583333"
x="-322.79166"
y="47.625" />
<rect
y="47.625"
x="-312.20831"
height="10.583333"
width="10.583333"
id="rect903"
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect905"
width="10.583333"
height="10.583333"
x="-354.54166"
y="58.208336" />
<rect
y="58.208336"
x="-343.95831"
height="10.583333"
width="10.583333"
id="rect907"
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect909"
width="10.583333"
height="10.583333"
x="-333.37497"
y="58.208336" />
<rect
y="58.208336"
x="-322.79166"
height="10.583333"
width="10.583333"
id="rect911"
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
<rect
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
id="rect913"
width="10.583333"
height="10.583333"
x="-312.20831"
y="58.208336" />
</g>
<path
sodipodi:nodetypes="cccccccccccccc"
d="M 0.70720657,5.0088997 0.50519813,5.2771404 c -3e-8,0 0.1915452,0.1543669 0.29349555,0.2268814 0.0408697,0.022353 0.0297057,0.028139 0.10131117,0.035126 l 2.08762525,-0.0023 2.0876253,0.0023 c 0.071605,-0.00699 0.061055,-0.012773 0.1019258,-0.035126 C 5.2791315,5.4315077 5.4700628,5.2771404 5.4700628,5.2771404 L 5.2680537,5.0088997 5.0181529,5.2023823 3.1356059,5.2012307 H 2.8396543 l -1.88193269,0.00115 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.304444px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
id="path894-3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
id="svg6"
version="1.1"
height="24px"
width="24px"
fill="black"
viewBox="0 0 24 24">
<metadata id="metadata12">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
id="dotSmall"
d="m 14.24,12.01 2.32,2.32 C 16.84,13.61 17,12.82 17,12 17,11.18 16.84,10.41 16.57,9.69 Z" />
<path
id="dotBig"
d="m 19.53,6.71 -1.26,1.26 c 0.63,1.21 0.98,2.57 0.98,4.02 0,1.45 -0.36,2.82 -0.98,4.02 l 1.2,1.2 C 20.44,15.67 21.01,13.85 21.01,11.9 21,10.01 20.46,8.23 19.53,6.71 Z" />
<path
d="M 12.88,16.29 11,18.17 V 14.41 Z M 11,5.83 12.88,7.71 11,9.59 Z M 15.71,7.71 10,2 H 9 V 9.59 L 4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41 V 22 h 1 L 15.71,16.29 11.41,12 Z"
id="path4" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="ic_marker_orange_pushpin_with_shadow.svg">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient871"
x1="91.712517"
y1="266.48581"
x2="95.902588"
y2="266.72339"
gradientUnits="userSpaceOnUse"
gradientTransform="rotate(22.027833,208.46275,168.26892)"
>
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop867" />
<stop
style="stop-color:#898d8d;stop-opacity:1"
offset="1"
id="stop869" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="298.98541"
inkscape:cy="561.0427"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="0"
inkscape:window-y="1080"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<path
d="m 108.52826,213.29755 a 56.035402,7.0061195 5.281088 0 0 -22.425508,2.5321 56.035402,7.0061195 5.281088 0 0 -0.233576,0.17697 l -0.206706,-0.0237 c -0.04471,0.11554 -0.07454,0.2314 -0.09612,0.34752 a 56.035402,7.0061195 5.281088 0 0 0.337963,1.58159 c 0.980726,1.70884 3.760629,3.46326 6.583579,5.2188 l -22.744347,15.50009 a 67.064653,11.556784 4.2275244 0 0 -39.967588,5.26867 67.064653,11.556784 4.2275244 0 0 -0.729154,0.57564 67.064653,11.556784 4.2275244 0 0 -2.754353,1.36358 67.064653,11.556784 4.2275244 0 0 38.908736,16.04324 l -32.176847,27.0646 52.228334,-24.74551 a 67.064653,11.556784 4.2275244 0 0 71.411147,-3.8186 67.064653,11.556784 4.2275244 0 0 0.72812,-0.57507 67.064653,11.556784 4.2275244 0 0 2.75539,-1.36415 67.064653,11.556784 4.2275244 0 0 -12.38374,-10.59818 40.033569,5.0733922 5.2502179 0 0 0.0563,-0.0369 40.033569,5.0733922 5.2502179 0 0 0.37672,-0.34325 l 22.45186,-15.30075 c 8.23597,-0.65161 17.65581,-1.47811 22.94433,-3.00957 a 56.035402,7.0061195 5.281088 0 0 2.65514,-1.03772 56.035402,7.0061195 5.281088 0 0 -46.08401,-12.26953 56.035402,7.0061195 5.281088 0 0 -41.6357,-2.54991 z"
style="fill:#333333;fill-opacity:0.49421525;stroke:#000000;stroke-width:0.138931px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.265501"
id="path839" />
<path
style="fill:url(#linearGradient871);fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.265501"
d="M 70.603317,174.30832 33.024148,288.94657 94.566672,183.19473 Z"
id="path865"
sodipodi:nodetypes="cccc" />
<ellipse
cx="144.42586"
cy="113.06232"
rx="70.318817"
ry="39.974442"
id="ellipse26"
style="fill:#d45500;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
transform="rotate(22.027833)" />
<ellipse
style="fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
id="ellipse30"
ry="39.974442"
rx="70.318817"
cy="105.23625"
cx="145.017"
transform="rotate(22.027833)" />
<path
id="rect28"
style="fill:#d45500;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
d="m 85.662408,24.322061 c -0.890996,8.350369 2.928035,17.133472 6.825491,25.923906 l -23.333902,57.672763 -0.730686,1.80598 0.248621,0.10059 a 42.43035,17.360788 22.027833 0 0 33.303948,30.10067 42.43035,17.360788 22.027833 0 0 44.86507,1.52585 l 0.13892,0.0562 0.24519,-0.60599 a 42.43035,17.360788 22.027833 0 0 0.59472,-1.15581 42.43035,17.360788 22.027833 0 0 0.37668,-1.24514 l 22.45185,-55.492655 c 9.28986,-2.66566 20.09069,-6.13552 24.76397,-13.17485 z" />
<ellipse
style="fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
id="path24"
ry="23.966745"
rx="59.409279"
cy="-10.262156"
cx="148.14029"
transform="rotate(22.027833)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

@@ -1 +1 @@
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
@@ -0,0 +1,11 @@
v3.7.6: OpenTracks
Changes:
- Landscape mode
- Track deletion UI is now only a spinner
- Speed is exported and imported (GPX/KML/KMZ)
- Default export format is KMZ incl. all data and pictures
Developer:
- Replaced AsyncTasks with Threads
- Synchronization: UUID is generated per track (not yet exported)
@@ -0,0 +1,16 @@
v3.8.0: OpenTracks
Breaking change:
- GPX import/export speed, cadence, and heartrate via TrackPointExtension/v2
Changes:
- Dashboard API: provide access to markers (incl. callback to show in OpenTracks)
- New marker icon (orange push pin, SVG)
- Files to import can be selected via file manager
Bugfixes:
- Aggregated stats UI respects preferred rate
- Editing a marker failed on orientation change
Developer:
- Refactored TrackRecordingService
@@ -0,0 +1,8 @@
v3.8.1: OpenTracks
Changes:
- While recording: add option for fullscreen
- Dashboard is started on the same task
Bugfixes:
- Intent filters for importing files were far too optimistic
@@ -0,0 +1,14 @@
v3.9.0: OpenTracks
Changes:
- Export tracks with their UUID
- Prevent import of already existing tracks by UUID (configurable; default yes)
- Pause/stop button now require long press
- GPS pre-start shows status of fix
- Added new activity icons
- Speed sensor: wheelsize now configurable in millimeter
Bugfixes:
- Fixed some rare crashes (NPE)
- Pace formatting corrected
- App icon in launcher repositioned
@@ -0,0 +1,4 @@
v3.9.1: OpenTracks
Bugfixes:
- GPS pre-start shows status of fix
@@ -0,0 +1,5 @@
v3.9.2: OpenTracks
Bugfixes:
- Fixed force close on API<25 (due to an animation)
- Reset recording state (on app startup) if crashed while recording
@@ -0,0 +1,4 @@
v3.9.3: OpenTracks
Bugfixes:
- Fixed export all tracks bug.
@@ -1 +1 @@
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
@@ -67,7 +67,7 @@ public class DescriptionGeneratorTest {
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks</a><p>"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
+ "Name: -<br>"
+ "Activity type: hiking<br>"
+ "Description: -<br>"
@@ -103,7 +103,7 @@ public class DescriptionGeneratorTest {
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks</a><p>"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
+ "Name: -<br>"
+ "Activity type: hiking<br>"
+ "Description: -<br>"
@@ -17,18 +17,17 @@
package de.dennisguse.opentracks.content;
import android.content.ContentUris;
import android.content.Context;
import android.location.Location;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.provider.ProviderTestRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
@@ -39,6 +38,8 @@ import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.content.provider.CustomSQLiteOpenHelper;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
@@ -48,29 +49,32 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
*
* @author Rodrigo Damazio
*/
@RunWith(AndroidJUnit4.class)
public class SearchEngineTest {
private static final String TAG = SearchEngineTest.class.getSimpleName();
private static final Location HERE = new Location("gps");
private static final long NOW = SearchEngine.OLDEST_ALLOWED_TIMESTAMP + 1000; // After OLDEST_ALLOWED_TIMESTAMP
private static final long NOW = SearchEngine.OLDEST_ALLOWED_TIMESTAMP + 1000;
@Rule
public ProviderTestRule sqliteContentProviderRule = new ProviderTestRule.Builder(CustomContentProvider.class, ContentProviderUtils.AUTHORITY_PACKAGE).setPrefix(TAG).build();
private ContentProviderUtils providerUtils;
private SearchEngine engine;
private final Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
providerUtils = new ContentProviderUtils(context);
providerUtils = new ContentProviderUtils(sqliteContentProviderRule.getResolver());
engine = new SearchEngine(providerUtils);
}
@After
public void tearDown() {
providerUtils.deleteAllTracks(context);
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase(TAG + CustomSQLiteOpenHelper.DATABASE_NAME);
}
private long insertTrack(String title, String description, String category, long hoursAgo) {
private Track.Id insertTrack(String title, String description, String category, long hoursAgo) {
Track track = new Track();
track.setName(title);
track.setDescription(description);
@@ -84,18 +88,18 @@ public class SearchEngineTest {
}
Uri uri = providerUtils.insertTrack(track);
return ContentUris.parseId(uri);
return new Track.Id(ContentUris.parseId(uri));
}
private long insertTrack(String title, String description, String category) {
private Track.Id insertTrack(String title, String description, String category) {
return insertTrack(title, description, category, -1);
}
private long insertTrack(String title, long hoursAgo) {
private Track.Id insertTrack(String title, long hoursAgo) {
return insertTrack(title, "", "", hoursAgo);
}
private long insertWaypoint(String title, String description, String category, double distance, long hoursAgo, long trackId) {
private Waypoint.Id insertWaypoint(String title, String description, String category, double distance, long hoursAgo, Track.Id trackId) {
Location location = new Location(HERE);
location.setLatitude(location.getLatitude() + distance);
location.setLongitude(location.getLongitude() + distance);
@@ -109,22 +113,22 @@ public class SearchEngineTest {
waypoint.setTrackId(trackId);
Uri uri = providerUtils.insertWaypoint(waypoint);
return ContentUris.parseId(uri);
return new Waypoint.Id(ContentUris.parseId(uri));
}
private long insertWaypoint(String title, String description, String category) {
return insertWaypoint(title, description, category, 0.0, -1, -1);
private Waypoint.Id insertWaypoint(String title, String description, String category) {
return insertWaypoint(title, description, category, 0.0, -1, null);
}
private long insertWaypoint(String title, double distance) {
return insertWaypoint(title, "", "", distance, -1, -1);
private Waypoint.Id insertWaypoint(String title, double distance) {
return insertWaypoint(title, "", "", distance, -1, null);
}
private long insertWaypoint(String title, long hoursAgo) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, -1);
private Waypoint.Id insertWaypoint(String title, long hoursAgo) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, null);
}
private long insertWaypoint(String title, long hoursAgo, long trackId) {
private Waypoint.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
}
@@ -139,14 +143,14 @@ public class SearchEngineTest {
// - one which will match in title and description
// - one which will match in all fields
insertTrack("bb", "cc", "dd");
long descriptionMatchId = insertTrack("bb", "aa", "cc");
long categoryMatchId = insertTrack("bb", "cc", "aa");
long titleMatchId = insertTrack("aa", "bb", "cc");
long titleCategoryMatchId = insertTrack("aa", "bb", "ca");
long titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
long allMatchId = insertTrack("aa", "ba", "ca");
Track.Id descriptionMatchId = insertTrack("bb", "aa", "cc");
Track.Id categoryMatchId = insertTrack("bb", "cc", "aa");
Track.Id titleMatchId = insertTrack("aa", "bb", "cc");
Track.Id titleCategoryMatchId = insertTrack("aa", "bb", "ca");
Track.Id titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
Track.Id allMatchId = insertTrack("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
@@ -164,14 +168,14 @@ public class SearchEngineTest {
// - one which will match in title and description
// - one which will match in all fields
insertWaypoint("bb", "cc", "dd");
long descriptionMatchId = insertWaypoint("bb", "aa", "cc");
long categoryMatchId = insertWaypoint("bb", "cc", "aa");
long titleMatchId = insertWaypoint("aa", "bb", "cc");
long titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
long titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
long allMatchId = insertWaypoint("aa", "ba", "ca");
Waypoint.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc");
Waypoint.Id categoryMatchId = insertWaypoint("bb", "cc", "aa");
Waypoint.Id titleMatchId = insertWaypoint("aa", "bb", "cc");
Waypoint.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
Waypoint.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
Waypoint.Id allMatchId = insertWaypoint("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
@@ -186,13 +190,13 @@ public class SearchEngineTest {
// - one waypoint which will match by title
// - one track which won't match
// - one track which will match by title
long descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
Waypoint.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
insertWaypoint("bb", "cc", "dd");
long titleWaypointId = insertWaypoint("aa", "bb", "cc");
Waypoint.Id titleWaypointId = insertWaypoint("aa", "bb", "cc");
insertTrack("bb", "cc", "dd");
long trackId = insertTrack("aa", "bb", "cc");
Track.Id trackId = insertTrack("aa", "bb", "cc");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
@@ -205,11 +209,11 @@ public class SearchEngineTest {
@Test
public void testSearchWaypointDistance() {
// All results match text, but they're at difference distances from the user.
long farFarAwayId = insertWaypoint("aa", 0.3);
long nearId = insertWaypoint("ab", 0.1);
long farId = insertWaypoint("ac", 0.2);
Waypoint.Id farFarAwayId = insertWaypoint("aa", 0.3);
Waypoint.Id nearId = insertWaypoint("ab", 0.1);
Waypoint.Id farId = insertWaypoint("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
SearchQuery query = new SearchQuery("a", HERE, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Distance order.
@@ -218,11 +222,11 @@ public class SearchEngineTest {
public void testSearchTrackRecent() {
// All results match text, but they're were recorded at different times.
long oldestId = insertTrack("aa", 3);
long recentId = insertTrack("ab", 1);
long oldId = insertTrack("ac", 2);
Track.Id oldestId = insertTrack("aa", 3);
Track.Id recentId = insertTrack("ab", 1);
Track.Id oldId = insertTrack("ac", 2);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Reverse time order.
@@ -232,11 +236,11 @@ public class SearchEngineTest {
@Test
public void testSearchWaypointRecent() {
// All results match text, but they're were recorded at different times.
long oldestId = insertWaypoint("aa", 2);
long recentId = insertWaypoint("ab", 0);
long oldId = insertWaypoint("ac", 1);
Waypoint.Id oldestId = insertWaypoint("aa", 2);
Waypoint.Id recentId = insertWaypoint("ab", 0);
Waypoint.Id oldId = insertWaypoint("ac", 1);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Reverse time order.
@@ -246,8 +250,8 @@ public class SearchEngineTest {
@Test
public void testSearchCurrentTrack() {
// All results match text, but one of them is the current track.
long currentId = insertTrack("ab", 1);
long otherId = insertTrack("aa", 1);
Track.Id currentId = insertTrack("ab", 1);
Track.Id otherId = insertTrack("aa", 1);
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
@@ -259,24 +263,29 @@ public class SearchEngineTest {
@Test
public void testSearchCurrentTrackWaypoint() {
// All results match text, but one of them is in the current track.
long otherId = insertWaypoint("aa", 1, 456);
long currentId = insertWaypoint("ab", 1, 123);
Waypoint.Id otherId = insertWaypoint("aa", 1, new Track.Id(456));
Waypoint.Id currentId = insertWaypoint("ab", 1, new Track.Id(123));
SearchQuery query = new SearchQuery("a", null, 123, NOW);
SearchQuery query = new SearchQuery("a", null, new Track.Id(123), NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Waypoint in current track should be promoted.
assertWaypointResults(results, currentId, otherId);
}
private void assertTrackResult(long trackId, ScoredResult result) {
private void assertTrackResult(Track.Id trackId, ScoredResult result) {
Assert.assertNotNull("Not a track", result.track);
Assert.assertNull("Ambiguous result", result.waypoint);
Assert.assertEquals(trackId, result.track.getId());
}
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) {
long[] longTrackIds = new long[trackIds.length];
for (int i = 0; i < trackIds.length; i++) {
longTrackIds[i] = trackIds[i].getId();
}
String errMsg = "Expected IDs=" + Arrays.toString(longTrackIds) + "; results=" + results;
Assert.assertEquals(results.size(), trackIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
@@ -286,14 +295,19 @@ public class SearchEngineTest {
}
}
private void assertWaypointResult(long waypointId, ScoredResult result) {
private void assertWaypointResult(Waypoint.Id waypointId, ScoredResult result) {
Assert.assertNotNull("Not a waypoint", result.waypoint);
Assert.assertNull("Ambiguous result", result.track);
Assert.assertEquals(waypointId, result.waypoint.getId());
}
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
private void assertWaypointResults(List<ScoredResult> results, Waypoint.Id... waypointIds) {
long[] longWaypointIds = new long[waypointIds.length];
for (int i = 0; i < waypointIds.length; i++) {
longWaypointIds[i] = waypointIds[i].getId();
}
String errMsg = "Expected IDs=" + Arrays.toString(longWaypointIds) + "; results=" + results;
Assert.assertEquals(results.size(), waypointIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
@@ -20,10 +20,10 @@ public class TestDataUtil {
/**
* Create a track without any trackPoints.
*/
public static Track createTrack(long trackId) {
public static Track createTrack(Track.Id trackId) {
Track track = new Track();
track.setId(trackId);
track.setName("Test: " + trackId);
track.setName("Test: " + trackId.getId());
return track;
}
@@ -34,18 +34,18 @@ public class TestDataUtil {
* @param trackId the trackId of the track
* @param numPoints the trackPoints number in the track
*/
public static Pair<Track, TrackPoint[]> createTrack(long trackId, int numPoints) {
public static Pair<Track, TrackPoint[]> createTrack(Track.Id trackId, int numPoints) {
Track track = createTrack(trackId);
TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; i++) {
trackPoints[i] = (createTrackPoint(i));
trackPoints[i] = createTrackPoint(i);
}
return new Pair<>(track, trackPoints);
}
public static Track createTrackAndInsert(ContentProviderUtils contentProviderUtils, long trackId, int numPoints) {
public static Track createTrackAndInsert(ContentProviderUtils contentProviderUtils, Track.Id trackId, int numPoints) {
Pair<Track, TrackPoint[]> pair = createTrack(trackId, numPoints);
insertTrackWithLocations(contentProviderUtils, pair.first, pair.second);
@@ -56,17 +56,22 @@ public class TestDataUtil {
/**
* Creates a location.
*
* @param i the index to set the value of location.
* @return created location
* @param i the index for the TrackPoint.
*/
public static TrackPoint createTrackPoint(int i) {
Location location = new Location("test");
location.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
location.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
location.setAccuracy((float) i / 100.0f);
location.setAltitude(i * ALTITUDE_INTERVAL);
location.setTime(i + 1);
return new TrackPoint(location);
TrackPoint trackPoint = new TrackPoint();
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
trackPoint.setAccuracy((float) i / 100.0f);
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
trackPoint.setTime(i + 1);
trackPoint.setSpeed(5f + (i / 10));
trackPoint.setHeartRate_bpm(100f + i);
trackPoint.setCyclingCadence_rpm(200f + i);
trackPoint.setCyclingCadence_rpm(300f + i);
trackPoint.setPower(400f + i);
return trackPoint;
}
/**
@@ -88,12 +93,11 @@ public class TestDataUtil {
* @param location The location.
* @return the Waypoint created.
*/
public static Waypoint createWaypointWithPhoto(Context context, long trackId, Location location) throws IOException {
String photoUrl = "";
public static Waypoint createWaypointWithPhoto(Context context, Track.Id trackId, Location location) throws IOException {
File dstFile = new File(FileUtils.getImageUrl(context, trackId));
dstFile.createNewFile();
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
photoUrl = photoUri.toString();
String photoUrl = photoUri.toString();
return new Waypoint("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl);
}
@@ -22,7 +22,6 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -30,6 +29,9 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Tests {@link CustomContentProvider}.
*
@@ -51,7 +53,7 @@ public class CustomContentProviderTest {
*/
@Test
public void testOnCreate() {
Assert.assertTrue(customContentProvider.onCreate(context));
assertTrue(customContentProvider.onCreate(context));
}
/**
@@ -59,13 +61,13 @@ public class CustomContentProviderTest {
*/
@Test
public void testGetType() {
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
Assert.assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
Assert.assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
Assert.assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
}
}
@@ -37,6 +37,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
@@ -47,6 +48,7 @@ import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.UUIDUtils;
import static org.mockito.Mockito.when;
@@ -81,39 +83,39 @@ public class CustomContentProviderUtilsTest {
@Test
public void testLocationIterator_noPoints() {
testIterator(1, 0, 1, false);
testIterator(new Track.Id(1), 0, 1, false);
}
@Test
public void testLocationIterator_noBatchAscending() {
testIterator(1, 50, 100, false);
testIterator(2, 50, 50, false);
testIterator(new Track.Id(1), 50, 100, false);
testIterator(new Track.Id(2), 50, 50, false);
}
@Test
public void testLocationIterator_noBatchDescending() {
testIterator(1, 50, 100, true);
testIterator(2, 50, 50, true);
testIterator(new Track.Id(1), 50, 100, true);
testIterator(new Track.Id(2), 50, 50, true);
}
@Test
public void testLocationIterator_batchAscending() {
testIterator(1, 50, 11, false);
testIterator(2, 50, 25, false);
testIterator(new Track.Id(1), 50, 11, false);
testIterator(new Track.Id(2), 50, 25, false);
}
@Test
public void testLocationIterator_batchDescending() {
testIterator(1, 50, 11, true);
testIterator(2, 50, 25, true);
testIterator(new Track.Id(1), 50, 11, true);
testIterator(new Track.Id(2), 50, 25, true);
}
@Test
public void testLocationIterator_largeTrack() {
testIterator(1, 20000, 2000, false);
testIterator(new Track.Id(1), 20000, 2000, false);
}
private void testIterator(long trackId, int numPoints, int batchSize, boolean descending) {
private void testIterator(Track.Id trackId, int numPoints, int batchSize, boolean descending) {
long lastPointId = initializeTrack(trackId, numPoints);
contentProviderUtils.setDefaultCursorBatchSize(batchSize);
List<TrackPoint> locations = new ArrayList<>(numPoints);
@@ -130,10 +132,10 @@ public class CustomContentProviderUtilsTest {
}
}
private long initializeTrack(long id, int numPoints) {
private long initializeTrack(Track.Id id, int numPoints) {
Track track = new Track();
track.setId(id);
track.setName("Test: " + id);
track.setName("Test: " + id.getId());
contentProviderUtils.insertTrack(track);
track = contentProviderUtils.getTrack(id);
Assert.assertNotNull(track);
@@ -171,22 +173,26 @@ public class CustomContentProviderUtilsTest {
*/
@Test
public void testCreateTrack() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
Track.Id trackId = new Track.Id(System.currentTimeMillis());
int columnIndex = 1;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getLong(columnIndex)).thenReturn(trackId.getId());
//Uuid
columnIndex++;
when(cursorMock.getColumnIndexOrThrow(TracksColumns.UUID)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getBlob(columnIndex)).thenReturn(UUIDUtils.toBytes(UUID.randomUUID()));
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long trackId = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
// Name
String name = NAME_PREFIX + trackId;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
columnIndex++;
String name = NAME_PREFIX + trackId.getId();
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getString(columnIndex)).thenReturn(name);
Track track = contentProviderUtils.createTrack(cursorMock);
Assert.assertEquals(trackId, track.getId());
@@ -199,8 +205,8 @@ public class CustomContentProviderUtilsTest {
@Test
public void testDeleteAllTracks() {
// Insert track, points and waypoint at first.
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
contentProviderUtils.insertWaypoint(waypoint);
@@ -229,7 +235,7 @@ public class CustomContentProviderUtilsTest {
@Test
public void testDeleteAllTracks_withWaypointAndPhoto() throws IOException {
// Insert track, points and waypoint with photo at first.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
@@ -262,20 +268,23 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, long)}.
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, Track.Id)}.
*/
@Test
public void testDeleteTrack() {
// Insert three tracks, points of two tracks and way point of one track.
long random = System.currentTimeMillis();
Track.Id trackId1 = new Track.Id(random);
Track.Id trackId2 = new Track.Id(random + 1);
Track.Id trackId3 = new Track.Id(random + 2);
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 0);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId1, 0);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 1, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId + 1));
waypoint.setTrackId(trackId);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId2));
waypoint.setTrackId(trackId1);
contentProviderUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
@@ -286,7 +295,7 @@ public class CustomContentProviderUtilsTest {
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Delete one track.
contentProviderUtils.deleteTrack(context, trackId);
contentProviderUtils.deleteTrack(context, trackId1);
// Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount());
@@ -297,26 +306,30 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, long)}.
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, Track.Id)}.
*/
@Test
public void testDeleteTrack_withWaypointPhoto() throws IOException {
// Insert three tracks.
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 1, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 2, 10);
long random = System.currentTimeMillis();
Track.Id trackId1 = new Track.Id(random);
Track.Id trackId2 = new Track.Id(random + 1);
Track.Id trackId3 = new Track.Id(random + 2);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId1, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
// Insert a waypoint in tracks trackId and trackId + 1.
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint1.getLocation());
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId1, trackPoint1.getLocation());
contentProviderUtils.insertWaypoint(waypoint1);
File dir1 = FileUtils.getPhotoDir(context, trackId);
File dir1 = FileUtils.getPhotoDir(context, trackId1);
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId + 1);
Waypoint waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId + 1, trackPoint2.getLocation());
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
Waypoint waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId2, trackPoint2.getLocation());
contentProviderUtils.insertWaypoint(waypoint2);
File dir2 = FileUtils.getPhotoDir(context, trackId + 1);
File dir2 = FileUtils.getPhotoDir(context, trackId2);
// Check.
ContentResolver contentResolver = context.getContentResolver();
@@ -334,7 +347,7 @@ public class CustomContentProviderUtilsTest {
Assert.assertEquals(1, dir2.list().length);
Assert.assertTrue(dir2.exists());
// Delete one track.
contentProviderUtils.deleteTrack(context, trackId);
contentProviderUtils.deleteTrack(context, trackId1);
// Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount());
@@ -347,17 +360,17 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getAllTracks()}
* Tests the method {@link ContentProviderUtils#getTracks()}
*/
@Test
public void testGetAllTracks() {
// given
int initialTrackNumber = contentProviderUtils.getAllTracks().size();
long trackId = System.currentTimeMillis();
int initialTrackNumber = contentProviderUtils.getTracks().size();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
// when
List<Track> allTracks = contentProviderUtils.getAllTracks();
List<Track> allTracks = contentProviderUtils.getTracks();
// then
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
@@ -369,28 +382,45 @@ public class CustomContentProviderUtilsTest {
*/
@Test
public void testGetLastTrack() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
}
/**
* Tests the method {@link ContentProviderUtils#getTrack(long)}
* Tests the method {@link ContentProviderUtils#getTrack(Track.Id)}
*/
@Test
public void testGetTrack() {
long trackId = System.currentTimeMillis();
public void testGetTrack_by_id() {
// given
Track.Id trackId = new Track.Id(System.currentTimeMillis());
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
// when / then
Assert.assertNotNull(contentProviderUtils.getTrack(trackId));
}
/**
* Tests the method {@link ContentProviderUtils#getTrack(Track.Id)}
*/
@Test
public void testGetTrack_by_uuid() {
// given
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrack(trackId);
contentProviderUtils.insertTrack(track);
// when / then
Assert.assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
}
/**
* Tests the method {@link ContentProviderUtils#updateTrack(Track)}
*/
@Test
public void testUpdateTrack() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrack(trackId);
String nameOld = "name1";
String nameNew = "name2";
@@ -409,7 +439,7 @@ public class CustomContentProviderUtilsTest {
*/
@Test
public void testCreateContentValues_waypoint() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
// Bottom
@@ -435,10 +465,10 @@ public class CustomContentProviderUtilsTest {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
long waypointId = System.currentTimeMillis();
Waypoint.Id waypointId = new Waypoint.Id(System.currentTimeMillis());
waypoint.setId(waypointId);
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
Assert.assertEquals(waypointId.getId(), contentValues.get(WaypointsColumns._ID));
Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE));
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
}
@@ -472,19 +502,19 @@ public class CustomContentProviderUtilsTest {
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock);
Assert.assertEquals(id, waypoint.getId());
Assert.assertEquals(id, waypoint.getId().getId());
Assert.assertEquals(name, waypoint.getName());
Assert.assertEquals(trackId, waypoint.getTrackId());
Assert.assertEquals(trackId, waypoint.getTrackId().getId());
}
/**
* Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, long)}
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)}
* when there is only one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_onlyOneWayPoint() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
@@ -497,7 +527,7 @@ public class CustomContentProviderUtilsTest {
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
// Get waypoint id that needs to delete.
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
// Delete
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
@@ -507,12 +537,12 @@ public class CustomContentProviderUtilsTest {
/**
* Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, long)}
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)}
* when there is only one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_onlyOneWayPointWithPhotoUrl() throws IOException {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
@@ -524,7 +554,7 @@ public class CustomContentProviderUtilsTest {
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
// Get waypoint id that needs to delete.
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
// Check waypoint has photo and it's in the external storage.
Assert.assertTrue(waypoint1.hasPhoto());
@@ -542,11 +572,11 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#deleteWaypoint(Context, long)} when there is more than one waypoint in the track.
* Tests the method {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)} when there is more than one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_hasNextWayPoint() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Track track = TestDataUtil.createTrackAndInsert(trackId, 10);
@@ -569,12 +599,12 @@ public class CustomContentProviderUtilsTest {
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setDescription(MOCK_DESC);
waypoint1.setTrackId(trackId);
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint2.setDescription(MOCK_DESC);
waypoint2.setTrackId(trackId);
long waypoint2Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2));
Waypoint.Id waypoint2Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2)));
// Delete
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id));
@@ -585,11 +615,11 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(long)}.
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(Track.Id)}.
*/
@Test
public void testGetNextWaypointNumber() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
@@ -610,17 +640,17 @@ public class CustomContentProviderUtilsTest {
/**
* Tests the method {@link ContentProviderUtils#insertWaypoint(Waypoint)} and
* {@link ContentProviderUtils#getWaypoint(long)}.
* {@link ContentProviderUtils#getWaypoint(Waypoint.Id)}.
*/
@Test
public void testInsertAndGetWaypoint() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription());
}
@@ -630,14 +660,14 @@ public class CustomContentProviderUtilsTest {
*/
@Test
public void testUpdateWaypoint() {
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
// Update
waypoint = contentProviderUtils.getWaypoint(waypointId);
@@ -654,7 +684,7 @@ public class CustomContentProviderUtilsTest {
public void testUpdateWaypoint_withPhoto() throws IOException {
// tests after update waypoint with photo the photo remains in the storage.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
@@ -662,9 +692,9 @@ public class CustomContentProviderUtilsTest {
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length);
@@ -690,7 +720,7 @@ public class CustomContentProviderUtilsTest {
public void testUpdateWaypoint_delPhotoAndDir() throws IOException {
// tests after update waypoint if user deletes the photo then file photo is deleted from the storage. Also empty directory is deleted.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
@@ -698,9 +728,9 @@ public class CustomContentProviderUtilsTest {
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length);
@@ -725,7 +755,7 @@ public class CustomContentProviderUtilsTest {
public void testUpdateWaypoint_delPhotoNotDir() throws IOException {
// tests after update waypoint if user deletes the photo then file photo is deleted from the storage. Directory remains if there are more photos from other waypoints.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert two waypoints with photos.
@@ -736,10 +766,10 @@ public class CustomContentProviderUtilsTest {
Waypoint otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
otherWaypoint.setDescription(TEST_DESC);
otherWaypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
long otherWaypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(otherWaypoint));
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
contentProviderUtils.insertWaypoint(otherWaypoint);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(2, dir.list().length);
@@ -757,12 +787,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#bulkInsertTrackPoint(TrackPoint[], long)}.
* Tests the method {@link ContentProviderUtils#bulkInsertTrackPoint(TrackPoint[], Track.Id)}.
*/
@Test
public void testBulkInsertTrackPoint() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track.first, track.second);
@@ -818,12 +848,12 @@ public class CustomContentProviderUtilsTest {
/**
* Tests the method
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, long)}.
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, Track.Id)}.
*/
@Test
public void testInsertTrackPoint() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
@@ -833,7 +863,7 @@ public class CustomContentProviderUtilsTest {
@Test
public void testInsertAndLoadTrackPoint() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
@@ -853,12 +883,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(long)}.
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(Track.Id)}.
*/
@Test
public void testGetLastValidTrackPoint() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint lastTrackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
@@ -866,12 +896,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in descending.
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(Track.Id, long, int, boolean)} in descending.
*/
@Test
public void testGetTrackPointCursor_desc() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
@@ -888,12 +918,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in ascending.
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(Track.Id, long, int, boolean)} in ascending.
*/
@Test
public void testGetTrackPointCursor_asc() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
@@ -910,12 +940,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean)} in descending.
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(Track.Id, long, boolean)} in descending.
*/
@Test
public void testGetTrackPointLocationIterator_desc() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
@@ -939,12 +969,12 @@ public class CustomContentProviderUtilsTest {
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean)} in ascending.
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(Track.Id, long, boolean)} in ascending.
*/
@Test
public void testGetTrackPointLocationIterator_asc() {
// given
long trackId = System.currentTimeMillis();
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
@@ -972,7 +1002,7 @@ public class CustomContentProviderUtilsTest {
/**
* Checks the value of a location.
*
* @param i the index of this location which created in the method {@link TestDataUtil#createTrack(long, int)}
* @param i the index of this location which created in the method {@link TestDataUtil#createTrack(Track.Id, int)}
* @param location the location to be checked
*/
private void checkLocation(int i, Location location) {
@@ -982,12 +1012,10 @@ public class CustomContentProviderUtilsTest {
Assert.assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
}
@Test
public void testFormatIdListForUri() {
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri(new long[]{}));
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new long[]{12}));
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new long[]{42, 43, 44}));
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri());
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
}
}
@@ -2,13 +2,15 @@ package de.dennisguse.opentracks.content.provider;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.VisibleForTesting;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -20,6 +22,10 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class CustomSQLiteOpenHelperTest {
@@ -36,7 +42,8 @@ public class CustomSQLiteOpenHelperTest {
*
* @return Map(TableName, SQL)
*/
private static Map<String, String> getTableSQL(SQLiteDatabase db) {
@VisibleForTesting
public static Map<String, String> getTableSQL(SQLiteDatabase db) {
HashMap<String, String> tableSQL = new HashMap<>();
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + WaypointsColumns.TABLE_NAME + "')", null, null, null, "name")) {
if (cursor != null) {
@@ -53,6 +60,7 @@ public class CustomSQLiteOpenHelperTest {
*
* @return Map(tableName, SQL)
*/
@VisibleForTesting
public static Map<String, String> getIndexSQL(SQLiteDatabase db) {
HashMap<String, String> indexSQL = new HashMap<>();
try (Cursor cursor = db.rawQuery("SELECT tbl_name, SQL FROM sqlite_master WHERE type = 'index'", null)) {
@@ -85,6 +93,7 @@ public class CustomSQLiteOpenHelperTest {
}
@Before
@After
public void setUp() {
context.deleteDatabase(DATABASE_NAME);
}
@@ -92,36 +101,21 @@ public class CustomSQLiteOpenHelperTest {
@Test
public void onCreate() {
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) {
Assert.assertTrue(hasSqlCreate(db, TracksColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, TracksColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX));
assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX));
Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX));
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX));
} catch (Exception e) {
fail();
}
}
@Test
public void onUpgrade_FromVersion23() {
// Manually create database schema with version 23 (base version)
{
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}
createVersion23();
// Open database with SQL upgrade
Map<String, String> tableByUpgrade;
@@ -130,10 +124,9 @@ public class CustomSQLiteOpenHelperTest {
tableByUpgrade = getTableSQL(dbUpgraded);
indicesByUpgrade = getIndexSQL(dbUpgraded);
}
// Open database via
context.deleteDatabase(DATABASE_NAME);
// Open database via creation script
Map<String, String> tablesByCreate;
Map<String, String> indicesByCreate;
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
@@ -142,17 +135,18 @@ public class CustomSQLiteOpenHelperTest {
}
// then - verify table structure
Assert.assertEquals(3, tableByUpgrade.size());
Assert.assertEquals(tableByUpgrade.size(), tablesByCreate.size());
assertEquals(3, tableByUpgrade.size());
assertEquals(tableByUpgrade.size(), tablesByCreate.size());
Assert.assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME));
Assert.assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME));
Assert.assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME));
assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME));
assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME));
assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME));
// then - verify custom indices
Assert.assertEquals(2, indicesByCreate.size());
Assert.assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME));
Assert.assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME));
assertEquals(3, indicesByCreate.size());
assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME));
assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME));
assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME));
}
@Test
@@ -169,11 +163,41 @@ public class CustomSQLiteOpenHelperTest {
}
// then - verify table structure
Assert.assertEquals(TRACKS_CREATE_TABLE_V23, tablesByDowngrade.get("tracks"));
Assert.assertEquals(TRACKPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("trackpoints"));
Assert.assertEquals(WAYPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("waypoints"));
assertEquals(TRACKS_CREATE_TABLE_V23, tablesByDowngrade.get("tracks"));
assertEquals(TRACKPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("trackpoints"));
assertEquals(WAYPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("waypoints"));
// then - verify custom indices
Assert.assertEquals(0, indicesByDowngrade.size());
assertEquals(0, indicesByDowngrade.size());
}
@Test
public void track_uuid_unique() {
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) {
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
fail("unique constraint not enforced");
} catch (SQLiteConstraintException e) {
assertTrue(e.getMessage().contains("UNIQUE constraint failed: tracks.uuid"));
}
}
private void createVersion23() {
// Manually create database schema with version 23 (base version)
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}
}
@@ -6,6 +6,10 @@ import android.util.Pair;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
public class ShareContentProviderTest {
@@ -14,20 +18,25 @@ public class ShareContentProviderTest {
public void testCreateandParseURI_invalid() {
Uri uri = Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/1");
Assert.assertArrayEquals(new long[]{}, ShareContentProvider.parseURI(uri));
Assert.assertEquals(0, ShareContentProvider.parseURI(uri).size());
}
@Test
public void testCreateandParseURI_valid() {
long[] trackIds = {1, 3, 5};
Set<Track.Id> trackIds = new HashSet<>();
trackIds.add(new Track.Id(1));
trackIds.add(new Track.Id(3));
trackIds.add(new Track.Id(5));
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_ONLY_TRACK);
Assert.assertArrayEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
Assert.assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
}
@Test
public void testCreateURIescapeFilename() {
long[] trackIds = {1};
Set<Track.Id> trackIds = new HashSet<>();
trackIds.add(new Track.Id(1));
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_ONLY_TRACK);
Assert.assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
@@ -253,7 +253,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint1.getTime() + 222);
trackPoint2.setSpeed(130);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point.getSpeed(), 0.01);
Assert.assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
}
/**
@@ -278,7 +278,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
Assert.assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
}
/**
@@ -303,7 +303,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point.getPace(), 0.01);
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
}
/**
@@ -29,13 +29,13 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
@@ -64,11 +64,11 @@ public abstract class AbstractTestFileTrackImporter {
static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
static final long TRACK_ID_0 = 1;
static final long TRACK_POINT_ID_0 = 1;
static final long TRACK_POINT_ID_1 = 2;
static final long TRACK_POINT_ID_3 = 4;
static final Uri TRACK_ID_0_URI = ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build();
private static final long WAYPOINT_ID_0 = 1;
private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
@@ -76,7 +76,7 @@ public abstract class AbstractTestFileTrackImporter {
static {
// We can't omit the timezones in the test, otherwise it'll use the local timezone and fail depending on where the test runner is.
SimpleTimeZone utc = new SimpleTimeZone(0, "UTC");
TimeZone utc = SimpleTimeZone.getTimeZone(ZoneOffset.UTC);
DATE_FORMAT_0.setTimeZone(utc);
DATE_FORMAT_1.setTimeZone(utc);
}
@@ -96,11 +96,11 @@ public abstract class AbstractTestFileTrackImporter {
/**
* Expects the first track point to be added.
*
* @param trackPoint the trackPoint
* @param trackPoint the trackPoint
* @param trackId the track id
* @param trackPointId the track point id
*/
protected void expectFirstTrackPoint(TrackPoint trackPoint, long trackId, long trackPointId) {
protected void expectFirstTrackPoint(TrackPoint trackPoint, Track.Id trackId, long trackPointId) {
when(contentProviderUtils.bulkInsertTrackPoint(trackPoint != null ? (TrackPoint[]) any() : (TrackPoint[]) any(), eq(trackId))).thenReturn(1);
}
@@ -111,10 +111,10 @@ public abstract class AbstractTestFileTrackImporter {
* @param lastTrack true if it is the last track in the gpx
* @param trackId the track id
*/
protected void expectTrackUpdate(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, long trackId) {
protected void expectTrackUpdate(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, Track.Id trackId) {
contentProviderUtils.updateTrack(trackCaptor.capture());
when(contentProviderUtils.insertWaypoint((Waypoint) any())).thenReturn(WAYPOINT_ID_O_URI);
when(contentProviderUtils.insertWaypoint(any())).thenReturn(WAYPOINT_ID_O_URI);
if (lastTrack) {
// Return null to not add waypoints
when(contentProviderUtils.getTrack(trackId)).thenReturn(null);
@@ -17,8 +17,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -27,9 +29,11 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
@@ -48,9 +52,12 @@ public class ExportImportTest {
private static final String TRACK_ICON = "the track icon";
private static final String TRACK_CATEGORY = "the category";
private static final String TRACK_DESCRIPTION = "the description";
private final List<Waypoint> waypoints = new ArrayList<>();
private long importTrackId;
private final long trackId = System.currentTimeMillis();
private final List<TrackPoint> trackPoints = new ArrayList<>();
private Track.Id importTrackId;
private final Track.Id trackId = new Track.Id(System.currentTimeMillis());
@Before
public void setUp() {
@@ -61,6 +68,9 @@ public class ExportImportTest {
contentProviderUtils.insertTrack(track.first);
contentProviderUtils.bulkInsertTrackPoint(track.second, track.first.getId());
trackPoints.clear();
trackPoints.addAll(Arrays.asList(track.second));
for (int i = 0; i < 3; i++) {
Waypoint waypoint = new Waypoint(track.second[i].getLocation());
waypoint.setName("the waypoint " + i);
@@ -96,26 +106,59 @@ public class ExportImportTest {
// given
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.newTrackExporter(context);
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(context, outputStream);
System.out.println(outputStream.toString());
trackExporter.writeTrack(track, outputStream);
contentProviderUtils.deleteTrack(context, trackId);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context, -1L);
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
importTrackId = trackImporter.importFile(inputStream);
// then
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals(track.getCategory(), importedTrack.getCategory());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.getIcon(), importedTrack.getIcon());
assertEquals(track.getUuid(), importedTrack.getUuid());
// 2. waypoints
assertWaypoints();
// 3. trackpoints
assertTrackpoints(false, false, false);
}
@LargeTest
@Test
public void kml_with_trackdetail_and_sensordata() {
// given
Track track = contentProviderUtils.getTrack(trackId);
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context);
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(track, outputStream);
contentProviderUtils.deleteTrack(context, trackId);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
importTrackId = trackImporter.importFile(inputStream);
// then
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
//TODO assertEquals(track.getTrackPoints(), importedTrack.getTrackPoints());
assertEquals(track.getCategory(), importedTrack.getCategory());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
@@ -124,14 +167,32 @@ public class ExportImportTest {
// 2. waypoints
assertWaypoints();
//TODO Check absolute time of trackpoints
// 3. trackpoints
assertTrackpoints(true, true, true);
}
@LargeTest
@Test
public void kml_with_trackdetail_and_sensordata() {
// TODO
Log.e(TAG, "Test not implemented.");
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
// given
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
Track track = contentProviderUtils.getTrack(trackId);
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context);
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(track, outputStream);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
importTrackId = trackImporter.importFile(inputStream);
// then
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNull(importedTrack);
}
@LargeTest
@@ -168,13 +229,13 @@ public class ExportImportTest {
// given
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.GPX;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(context, outputStream);
trackExporter.writeTrack(track, outputStream);
contentProviderUtils.deleteTrack(context, trackId);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
@@ -185,7 +246,6 @@ public class ExportImportTest {
// 1. track
Track trackImported = contentProviderUtils.getTrack(importTrackId);
assertNotNull(trackImported);
//TODO assertEquals(track.getTrackPoints(), trackImported.getTrackPoints());
assertEquals(track.getCategory(), trackImported.getCategory());
assertEquals(track.getDescription(), trackImported.getDescription());
assertEquals(track.getName(), trackImported.getName());
@@ -196,7 +256,33 @@ public class ExportImportTest {
// 2. waypoints
assertWaypoints();
//TODO Check absolute time of trackpoints
// 3. trackpoints
assertTrackpoints(false, true, true);
}
@LargeTest
@Test
public void gpx_duplicate_trackUUID() {
// given
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
Track track = contentProviderUtils.getTrack(trackId);
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(track, outputStream);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
importTrackId = trackImporter.importFile(inputStream);
// then
// 1. track
Track trackImported = contentProviderUtils.getTrack(importTrackId);
assertNull(trackImported);
}
private void assertWaypoints() {
@@ -217,4 +303,33 @@ public class ExportImportTest {
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001);
}
}
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) {
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
assertEquals(trackPoints.size(), importedTrackPoints.size());
for (int i = 0; i < trackPoints.size(); i++) {
TrackPoint trackPoint = trackPoints.get(i);
TrackPoint importedTrackPoint = importedTrackPoints.get(i);
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime(), 0.01);
// TODO Not exported for GPX/KML
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
assertEquals(trackPoint.getAltitude(), importedTrackPoint.getAltitude(), 0.001);
assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.001);
if (verifyHeartrate) {
assertEquals(trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
}
if (verifyCadence) {
assertEquals(trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
}
if (verifyPower) {
assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
}
}
}
}
@@ -1,217 +0,0 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.io.file.importer;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link GpxFileTrackImporter}.
*
* @author Steffen Horlacher
*/
@RunWith(MockitoJUnitRunner.class)
public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg><trkseg>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "1000.0");
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(TRACK_TIME_0, "invalid");
private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_ELEVATION), "invalid");
private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "invalid");
private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LONGITUDE), "invalid");
private static String getNameAndDescription(String name, String description) {
return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
}
private static String getTrackPoint(int index, String time) {
String latitude = Double.toString(TRACK_LATITUDE + index);
String longitude = Double.toString(TRACK_LONGITUDE + index);
String elevation = Double.toString(TRACK_ELEVATION + index);
StringBuilder buffer = new StringBuilder();
buffer.append("<trkpt lat=\"").append(latitude).append("\" lon=\"").append(longitude).append("\"><ele>").append(elevation).append("</ele>");
if (time != null) {
buffer.append("<time>").append(time).append("</time>");
}
buffer.append("</trkpt>");
return buffer.toString();
}
@Test
public void testOneTrackOneSegment() throws Exception {
// given
TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
TrackPoint trackPoint1 = createTrackPoint(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0);
// A flush happens at the end
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(1);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
// when
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
long trackId = gpxFileTrackImporter.importFile(inputStream);
// then
Assert.assertEquals(TRACK_ID_0, trackId);
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTrackStatistics().getTotalTime());
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
}
@Test
public void testOneTrackTwoSegments() throws Exception {
// given
TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0);
// A flush happens at the end
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
// when
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
long trackId = gpxFileTrackImporter.importFile(inputStream);
// then
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
Assert.assertEquals(TRACK_ID_0, trackId);
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTrackStatistics().getTotalTime());
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
}
@Test
public void testOneTrackTwoSegmentsNoTime() {
// given
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0);
// A flush happens at the end
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
// when
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
long trackId = gpxFileTrackImporter.importFile(inputStream);
// then
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
Assert.assertEquals(TRACK_ID_0, trackId);
Assert.assertEquals(0, trackCaptor.getValue().getTrackStatistics().getTotalTime());
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
}
@Test
public void testInvalidXml() {
testInvalidGpx(INVALID_XML_GPX);
}
@Test
public void testInvalidLocation() {
testInvalidGpx(INVALID_LOCATION_GPX);
}
@Test
public void testInvalidTime() {
testInvalidGpx(INVALID_TIME_GPX);
}
@Test
public void testInvalidAltitude() {
testInvalidGpx(INVALID_ALTITUDE_GPX);
}
@Test
public void testInvalidLatitude() {
testInvalidGpx(INVALID_LATITUDE_GPX);
}
@Test
public void testInvalidLongitude() {
testInvalidGpx(INVALID_LONGITUDE_GPX);
}
private void testInvalidGpx(String xml) {
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
// For the following, use StubReturn since we don't care whether they are invoked or not.
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), anyLong())).thenReturn(1);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
contentProviderUtils.deleteTrack(context, TRACK_ID_0);
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
long trackId = gpxFileTrackImporter.importFile(inputStream);
Assert.assertEquals(-1L, trackId);
}
}
@@ -1,133 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.io.file.importer;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link KmlFileTrackImporter}.
*
* @author Jimmy Shih
*/
@RunWith(MockitoJUnitRunner.class)
public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</gx:Track><gx:Track>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static String getNameAndDescription(String name, String description) {
return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description
+ "]]></description>";
}
private static String getTrackPoint(int index, String time) {
String latitude = Double.toString(TRACK_LATITUDE + index);
String longitude = Double.toString(TRACK_LONGITUDE + index);
String altitude = Double.toString(TRACK_ELEVATION + index);
return "<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " " + altitude + "</gx:coord>";
}
@Test
public void testOneTrackOneSegment() throws Exception {
// given
TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
TrackPoint trackPointl1 = createTrackPoint(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
contentProviderUtils.clearTrack(context, TRACK_ID_0);
expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0);
// A flush happens at the end
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(1);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
// when
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
long trackId = kmlFileTrackImporter.importFile(inputStream);
// then
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
Assert.assertEquals(TRACK_ID_0, trackId);
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTrackStatistics().getTotalTime());
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
}
@Test
public void testOneTrackTwoSegments() throws Exception {
// given
TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
contentProviderUtils.clearTrack(context, TRACK_ID_0);
expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0);
// A flush happens at the end
when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
// when
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
long trackId = kmlFileTrackImporter.importFile(inputStream);
// then
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
Assert.assertEquals(TRACK_ID_0, trackId);
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTrackStatistics().getTotalTime());
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
}
}
@@ -21,6 +21,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
/**
* Tests the {@link AdaptiveLocationListenerPolicy}.
*
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.IBinder;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,6 +34,7 @@ import androidx.test.rule.ServiceTestRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -69,10 +71,17 @@ public class TrackRecordingServiceTest {
@Rule
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
if (Looper.myLooper() == null) Looper.prepare();
}
private final Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils contentProviderUtils;
private final long trackId = Math.abs(new Random().nextLong());
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
static Intent createStartIntent(Context context) {
return new Intent(context, TrackRecordingService.class);
@@ -125,7 +134,7 @@ public class TrackRecordingServiceTest {
@Test
public void testRecording_noTracks() throws Exception {
// given
List<Track> tracks = contentProviderUtils.getAllTracks();
List<Track> tracks = contentProviderUtils.getTracks();
Assert.assertTrue(tracks.isEmpty());
// when
@@ -136,7 +145,7 @@ public class TrackRecordingServiceTest {
// then
// Test if we start in no-recording mode by default.
Assert.assertFalse(service.isRecording());
Assert.assertEquals(-1L, service.getRecordingTrackId());
Assert.assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -150,7 +159,7 @@ public class TrackRecordingServiceTest {
// then
Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
Assert.assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -172,7 +181,7 @@ public class TrackRecordingServiceTest {
// given
createDummyTrack(trackId, -1L, true);
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
insertLocation(service);
newTrackPoint(service);
// when
service.pauseCurrentTrack();
@@ -182,7 +191,7 @@ public class TrackRecordingServiceTest {
//when
service.resumeTrack(trackId);
insertLocation(service);
newTrackPoint(service);
// then
Assert.assertTrue(service.isRecording());
@@ -201,14 +210,14 @@ public class TrackRecordingServiceTest {
// given
createDummyTrack(trackId, -1L, true);
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
insertLocation(service);
newTrackPoint(service);
service.endCurrentTrack();
Assert.assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
// when
service.resumeTrack(trackId);
insertLocation(service);
newTrackPoint(service);
// then
Assert.assertTrue(service.isRecording());
@@ -234,7 +243,7 @@ public class TrackRecordingServiceTest {
// then
Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
Assert.assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -245,13 +254,13 @@ public class TrackRecordingServiceTest {
service.startNewTrack();
Assert.assertTrue(service.isRecording());
long trackId = service.getRecordingTrackId();
Track.Id trackId = service.getRecordingTrackId();
// when
long newTrackId = service.startNewTrack();
Track.Id newTrackId = service.startNewTrack();
// then
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, newTrackId);
Assert.assertNull(newTrackId);
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId());
@@ -270,7 +279,7 @@ public class TrackRecordingServiceTest {
// then
Assert.assertFalse(PreferencesUtils.isRecording(context));
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
Assert.assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -281,10 +290,10 @@ public class TrackRecordingServiceTest {
Assert.assertFalse(service.isRecording());
// when
long waypointId = service.insertWaypoint(null, null, null, null);
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null);
// then
Assert.assertEquals(-1L, waypointId);
Assert.assertNull(waypointId);
}
@MediumTest
@@ -294,11 +303,11 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Assert.assertTrue(service.isRecording());
insertLocation(service);
long trackId = service.getRecordingTrackId();
newTrackPoint(service);
Track.Id trackId = service.getRecordingTrackId();
// when
long waypointId = service.insertWaypoint(null, null, null, null);
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null);
// then
Assert.assertNotEquals(-1L, waypointId);
@@ -313,15 +322,15 @@ public class TrackRecordingServiceTest {
}
private void addTrack(Track track, boolean isRecording) {
Assert.assertTrue(track.getId() >= 0);
Assert.assertTrue(track.getId().isValid());
contentProviderUtils.insertTrack(track);
Assert.assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
}
// NOTE: Do not use to create a track that is currently recording.
private void createDummyTrack(long id, long stopTime, boolean isRecording) {
private void createDummyTrack(Track.Id id, long stopTime, boolean isRecording) {
Track dummyTrack = new Track();
dummyTrack.setId(id);
dummyTrack.setName("Dummy Track");
@@ -331,18 +340,18 @@ public class TrackRecordingServiceTest {
addTrack(dummyTrack, isRecording);
}
private static void insertLocation(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
insertLocation(trackRecordingService, 45.0f, 35f, 5, 10, System.currentTimeMillis());
private static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
newTrackPoint(trackRecordingService, 45.0f, 35f, 5, 10, System.currentTimeMillis());
}
static void insertLocation(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed) throws InterruptedException {
insertLocation(trackRecordingService, latitude, longitude, accuracy, speed, System.currentTimeMillis());
static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed) throws InterruptedException {
newTrackPoint(trackRecordingService, latitude, longitude, accuracy, speed, System.currentTimeMillis());
}
/**
* Inserts a location and waits for 200ms.
*/
private static void insertLocation(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed, long time) throws InterruptedException {
private static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed, long time) throws InterruptedException {
Location location = new Location("gps");
location.setLongitude(longitude);
location.setLatitude(latitude);
@@ -350,7 +359,9 @@ public class TrackRecordingServiceTest {
location.setSpeed(speed);
location.setTime(time);
location.setBearing(3.0f);
trackRecordingService.insertLocation(location);
TrackPoint trackPoint = new TrackPoint(location);
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(ApplicationProvider.getApplicationContext());
trackRecordingService.newTrackPoint(trackPoint, prefAccuracy);
//TODO Needed?
Thread.sleep(200);
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
@@ -72,14 +73,11 @@ public class TrackRecordingServiceTestLocation {
sharedPreferences.edit().clear().commit();
service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
//Disable executorService to not insert locations from GPS via LocationManager
service.enableLocationExecutor(false);
}
@After
public void tearDown() {
// Reset service (if some previous test failed)
service.enableLocationExecutor(true);
if (service.isRecording() || service.isPaused()) {
service.endCurrentTrack();
}
@@ -92,15 +90,15 @@ public class TrackRecordingServiceTestLocation {
@Test
public void testOnLocationChangedAsync_movingAccurate() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0001, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0002, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0003, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0004, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0005, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0001, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0002, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0003, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0004, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0005, 35.0, 5, 15);
service.endCurrentTrack();
@@ -112,42 +110,19 @@ public class TrackRecordingServiceTestLocation {
Assert.assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01);
}
@MediumTest
@Test
public void testOnLocationChangedAsync_movingInaccurate() throws Exception {
// given
long trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.1, 35.0, Long.MAX_VALUE, 15);
TrackRecordingServiceTest.insertLocation(service, 45.2, 35.0, Long.MAX_VALUE, 15);
TrackRecordingServiceTest.insertLocation(service, 45.3, 35.0, Long.MAX_VALUE, 15);
TrackRecordingServiceTest.insertLocation(service, 99.0, 35.0, Long.MAX_VALUE, 15);
service.endCurrentTrack();
// then
Assert.assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(1, trackPoints.size());
Assert.assertEquals(45.0, trackPoints.get(0).getLatitude(), 0.01);
}
@MediumTest
@Test
public void testOnLocationChangedAsync_slowMovingAccurate() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.000001, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.000002, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.000003, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.000004, 35.0, 5, 15);
TrackRecordingServiceTest.insertLocation(service, 45.000005, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.000001, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.000002, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.000003, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.000004, 35.0, 5, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.000005, 35.0, 5, 15);
service.endCurrentTrack();
@@ -181,15 +156,15 @@ public class TrackRecordingServiceTestLocation {
@Test
public void testOnLocationChangedAsync_idle() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 6, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 0);
service.endCurrentTrack();
@@ -207,15 +182,15 @@ public class TrackRecordingServiceTestLocation {
@Test
public void testOnLocationChangedAsync_idle_withMovement() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 1, 15);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 6, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 15);
service.endCurrentTrack();
@@ -235,7 +210,7 @@ public class TrackRecordingServiceTestLocation {
@Test
public void testOnLocationChangedAsync_idle_withSensorData() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
service.setRemoteSensorManager(new BluetoothRemoteSensorManager(context) {
@@ -253,12 +228,12 @@ public class TrackRecordingServiceTestLocation {
});
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 0, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 5, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 0, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
service.endCurrentTrack();
@@ -279,14 +254,14 @@ public class TrackRecordingServiceTestLocation {
@Test
public void testOnLocationChangedAsync_segment() throws Exception {
// given
long trackId = service.startNewTrack();
Track.Id trackId = service.startNewTrack();
// when
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.insertLocation(service, 45.1, 35.0, 2, 0);
TrackRecordingServiceTest.insertLocation(service, 45.1, 35.0, 3, 0);
TrackRecordingServiceTest.insertLocation(service, 45.2, 35.0, 4, 0);
TrackRecordingServiceTest.insertLocation(service, 45.2, 35.0, 5, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.1, 35.0, 2, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.1, 35.0, 3, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.2, 35.0, 4, 0);
TrackRecordingServiceTest.newTrackPoint(service, 45.2, 35.0, 5, 0);
service.endCurrentTrack();
@@ -303,25 +278,4 @@ public class TrackRecordingServiceTestLocation {
Assert.assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
}
@MediumTest
@Test
public void testOnLocationChangedAsync_firstTrackPointInvalid() throws Exception {
// given
long trackId = service.startNewTrack();
// when
service.insertLocation(TrackPoint.createPause().getLocation());
TrackRecordingServiceTest.insertLocation(service, 45.0, 35.0, 0, 0);
service.insertLocation(TrackPoint.createPause().getLocation());
service.endCurrentTrack();
// then
Assert.assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(1, trackPoints.size());
Assert.assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
}
}
@@ -26,6 +26,7 @@ import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.stats.TrackStatistics;
@@ -186,7 +187,7 @@ public class TrackRecordingServiceTestLooper {
@MediumTest
@Test
public void testIntegration_completeRecordingSession() throws TimeoutException {
List<Track> tracks = contentProviderUtils.getAllTracks();
List<Track> tracks = contentProviderUtils.getTracks();
Assert.assertTrue(tracks.isEmpty());
fullRecordingSession();
}
@@ -196,14 +197,14 @@ public class TrackRecordingServiceTestLooper {
Assert.assertFalse(service.isRecording());
// Start a track.
long id = service.startNewTrack();
Assert.assertTrue(id >= 0);
Track.Id trackId = service.startNewTrack();
Assert.assertTrue(trackId.isValid());
Assert.assertTrue(service.isRecording());
Track track = contentProviderUtils.getTrack(id);
Track track = contentProviderUtils.getTrack(trackId);
Assert.assertNotNull(track);
Assert.assertEquals(id, track.getId());
Assert.assertEquals(id, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(id, service.getRecordingTrackId());
Assert.assertEquals(trackId, track.getId());
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId());
// Insert a few points, markers and statistics.
long startTime = System.currentTimeMillis();
@@ -215,7 +216,9 @@ public class TrackRecordingServiceTestLooper {
location.setSpeed(10);
location.setTime(startTime + i * 10000);
location.setBearing(3.0f);
service.insertLocation(location);
TrackPoint trackPoint = new TrackPoint(location);
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
service.newTrackPoint(trackPoint, prefAccuracy);
if (i % 7 == 0) {
service.insertWaypoint(null, null, null, null);
@@ -225,10 +228,10 @@ public class TrackRecordingServiceTestLooper {
// Stop the track. Validate if it has correct data.
service.endCurrentTrack();
Assert.assertFalse(service.isRecording());
Assert.assertEquals(-1L, service.getRecordingTrackId());
track = contentProviderUtils.getTrack(id);
Assert.assertNull(service.getRecordingTrackId());
track = contentProviderUtils.getTrack(trackId);
Assert.assertNotNull(track);
Assert.assertEquals(id, track.getId());
Assert.assertEquals(trackId, track.getId());
TrackStatistics trackStatistics = track.getTrackStatistics();
Assert.assertNotNull(trackStatistics);
Assert.assertTrue(trackStatistics.getStartTime_ms() > 0);
@@ -0,0 +1,63 @@
package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import de.dennisguse.opentracks.content.data.TrackPoint;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class HandlerServerTest {
@Mock
private Context context;
@Mock
private HandlerServer.HandlerServerInterface server;
@Mock
private LocationHandler locationHandler;
private HandlerServer subject;
@Before
public void setUp() {
subject = new HandlerServer(locationHandler, server);
subject.start(context);
}
@After
public void tearDown() {
subject.stop(context);
}
@Test
public void onSharedPreferenceChanged() {
// when
subject.onSharedPreferenceChanged(null, null, null);
// then
verify(locationHandler).onSharedPreferenceChanged(null, null, null);
}
@Test
public void sendTrackPoint() throws InterruptedException {
// given
TrackPoint trackPoint = new TrackPoint();
int accuracy = 50;
// when
subject.sendTrackPoint(trackPoint, accuracy);
// then
Thread.sleep(10); // Wait for executor service
verify(server).newTrackPoint(trackPoint, accuracy);
}
}
@@ -0,0 +1,117 @@
package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.content.SharedPreferences;
import android.location.Location;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class LocationHandlerTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Mock
private HandlerServer handlerServer;
@InjectMocks
private LocationHandler locationHandler;
@Before
public void setUp() {
// Let's use default values.
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
sharedPreferences.edit().clear().commit();
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.recording_gps_accuracy_key));
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.min_recording_interval_key));
locationHandler.onStart(context);
}
/**
* When a valid location changed in LocationHandler -> newTrackPoint service method is called.
*/
@Test
public void testOnLocationChanged_okay() {
// when
locationHandler.onLocationChanged(createLocation(45f, 35f, 3, 5, System.currentTimeMillis()));
// then
verify(handlerServer, times(1)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
}
/**
* When location changed in LocationHandler with bad location -> newTrackPoint service method is not called.
*/
@Test
public void testOnLocationChanged_badLocation() {
// given
float latitude = 91f;
// when
// bad latitude
locationHandler.onLocationChanged(createLocation(latitude, 35f, 3, 5, System.currentTimeMillis()));
// then
verify(handlerServer, times(0)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
}
/**
* When location changed in LocationHandler with poor accuracy -> newTrackPoint service method is not called.
*/
@Test
public void testOnLocationChanged_poorAccuracy() {
// given
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
// when
// poor latitude
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
// then
// no newTrackPoint called
verify(handlerServer, times(0)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
}
@Test
public void testOnLocationChanged_movingInaccurate() {
// when
locationHandler.onLocationChanged(createLocation(45.0, 35.0, 5, 15, System.currentTimeMillis()));
locationHandler.onLocationChanged(createLocation(45.1, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
locationHandler.onLocationChanged(createLocation(45.2, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
locationHandler.onLocationChanged(createLocation(45.3, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
locationHandler.onLocationChanged(createLocation(99.0, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
// then
verify(handlerServer, times(1)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
}
/**
* Creates a location with parameters and returns the Location object.
*/
private static Location createLocation(double latitude, double longitude, float accuracy, long speed, long time) {
Location location = new Location("gps");
location.setLongitude(longitude);
location.setLatitude(latitude);
location.setAccuracy(accuracy);
location.setSpeed(speed);
location.setTime(time);
location.setBearing(3.0f);
return location;
}
}
@@ -212,4 +212,16 @@ public class StringUtilsTest {
Assert.assertEquals("1", StringUtils.formatDecimal(0.99, 1));
}
@Test
public void testGetSpeedParts() {
Assert.assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
Assert.assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
Assert.assertEquals("11.99", StringUtils.getSpeedParts(context, 3.33, true, true).first);
Assert.assertEquals("7.45", StringUtils.getSpeedParts(context, 3.33, false, true).first);
Assert.assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
Assert.assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
}
}
@@ -30,6 +30,7 @@ import java.util.Date;
import java.util.Locale;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
/**
* Tests {@link TrackNameUtils}.
@@ -39,7 +40,7 @@ import de.dennisguse.opentracks.R;
@RunWith(AndroidJUnit4.class)
public class TrackNameUtilsTest {
private static final long TRACK_ID = 1L;
private static final Track.Id TRACK_ID = new Track.Id(1L);
private static final long START_TIME = 1288213406000L;
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
@@ -69,6 +70,6 @@ public class TrackNameUtilsTest {
@Test
public void testTrackName_number() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
Assert.assertEquals("Track " + TRACK_ID, TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
Assert.assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
}
}
@@ -0,0 +1,23 @@
package de.dennisguse.opentracks.util;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class UUIDUtilsTest {
@Test
public void test() {
UUID uuid = UUID.randomUUID();
byte[] bytes = UUIDUtils.toBytes(uuid);
UUID output = UUIDUtils.fromBytes(bytes);
Assert.assertEquals(uuid, output);
}
}
@@ -60,7 +60,7 @@ public class AggregatedStatisticsTest {
// then
Assert.assertEquals(1, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertNotNull(aggregatedStatistics.get(biking));
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
@@ -82,7 +82,7 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(track);
// then
Assert.assertTrue(aggregatedStatistics.get(mountainBiking) != null);
Assert.assertNotNull(aggregatedStatistics.get(mountainBiking));
}
@Test
@@ -99,7 +99,7 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(track);
// then
Assert.assertTrue(aggregatedStatistics.get(trailRunning) != null);
Assert.assertNotNull(aggregatedStatistics.get(trailRunning));
}
@Test
@@ -121,7 +121,7 @@ public class AggregatedStatisticsTest {
// then
Assert.assertEquals(1, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertNotNull(aggregatedStatistics.get(biking));
Assert.assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
@@ -153,9 +153,9 @@ public class AggregatedStatisticsTest {
// then
Assert.assertEquals(3, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertTrue(aggregatedStatistics.get(running) != null);
Assert.assertTrue(aggregatedStatistics.get(walking) != null);
Assert.assertNotNull(aggregatedStatistics.get(biking));
Assert.assertNotNull(aggregatedStatistics.get(running));
Assert.assertNotNull(aggregatedStatistics.get(walking));
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
@@ -215,10 +215,10 @@ public class AggregatedStatisticsTest {
Assert.assertEquals(4, aggregatedStatistics.getCount());
// There is a map for every sport.
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertTrue(aggregatedStatistics.get(running) != null);
Assert.assertTrue(aggregatedStatistics.get(walking) != null);
Assert.assertTrue(aggregatedStatistics.get(driving) != null);
Assert.assertNotNull(aggregatedStatistics.get(biking));
Assert.assertNotNull(aggregatedStatistics.get(running));
Assert.assertNotNull(aggregatedStatistics.get(walking));
Assert.assertNotNull(aggregatedStatistics.get(driving));
// Number of tracks by sport.
Assert.assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_debug_background" />
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_logo_color_108dp" />
</adaptive-icon>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_debug_background">#8F0000</color>
<color name="ic_launcher_background">#8F0000</color>
</resources>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2013 Google Inc.
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@ 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_button_stop_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_button_stop" />
</selector>
<resources>
<string name="app_name">OpenTracks (Debug)</string>
</resources>
+66 -47
View File
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.dennisguse.opentracks"
android:installLocation="auto">
@@ -24,45 +25,91 @@ limitations under the License.
android:smallScreens="true"
android:xlargeScreens="true" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="true" />
<uses-feature
android:name="android.hardware.android.hardware.bluetooth_le"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.sensor.compass"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application
android:name=".Startup"
android:allowBackup="false"
android:hasFragileUserData="true"
android:hardwareAccelerated="true"
android:icon="${applicationLogo}"
android:label="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/ThemeCustom">
<!-- Activities -->
<activity android:name=".AboutActivity" />
<activity
android:name=".TrackDeleteActivity"
android:theme="@style/ThemeCustomTransparent" />
android:theme="@style/TrackDeleteActivity" />
<activity
android:name=".HelpActivity"
android:label="@string/menu_help" />
<activity android:name=".io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity" />
<activity
android:name=".io.file.importer.ImportActivity"
android:theme="@style/ThemeCustomTransparent">
android:theme="@style/ThemeCustomTransparentActivity">
<intent-filter>
<action android:name="android.intent.action.ATTACH_DATA" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.google-earth.kmz" />
<data
android:scheme="content"
android:host="*"
android:pathPattern="*kmz" />
</intent-filter>
<data android:mimeType="application/gpx" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.google-earth.kml+xml" />
<data
android:scheme="content"
android:host="*"
android:pathPattern="*kml" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/gpx+xml" />
<data android:mimeType="application/vnd.google-earth.gpx" />
<data android:mimeType="application/vnd.google-earth.gpx+xml" />
<data android:scheme="file" />
<data
android:scheme="content"
android:host="*"
android:pathPattern="*gpx" />
</intent-filter>
</activity>
<activity android:name=".MarkerDetailActivity" />
<activity android:name=".MarkerDetailActivity">
<intent-filter>
<action android:name="de.dennisguse.opentracks.MarkerDetails"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".MarkerEditActivity" />
@@ -85,13 +132,9 @@ limitations under the License.
android:resource="@xml/searchable" />
</activity>
<activity
android:name=".TrackRecordedActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackRecordedActivity" />
<activity
android:name=".TrackRecordingActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackRecordingActivity" />
<activity android:name=".TrackEditActivity" />
@@ -105,14 +148,13 @@ limitations under the License.
android:value=".SearchListActivity" />
</activity>
<activity
android:name=".AggregatedStatisticsActivity"
android:screenOrientation="userPortrait"
android:label="@string/menu_aggregated_statistics"/>
<activity android:name=".AggregatedStatisticsActivity" />
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
<activity
android:name=".io.file.exporter.ExportActivity"
android:theme="@style/ThemeCustomTransparent" />
android:theme="@style/ThemeCustomTransparentActivity" />
<activity android:name=".settings.SettingsActivity" />
@@ -153,27 +195,4 @@ limitations under the License.
android:icon="@drawable/ic_logo_color_24dp"
android:label="@string/recording_service" />
</application>
<!-- Permissions to record locations -->
<uses-feature
android:name="android.hardware.location.gps"
android:required="true" />
<uses-feature
android:name="android.hardware.android.hardware.bluetooth_le"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.sensor.compass"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
</manifest>
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks;
import android.os.Bundle;
import android.view.Menu;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
@@ -30,7 +29,7 @@ public class AboutActivity extends AbstractActivity {
TextView textURL = findViewById(R.id.about_app_url);
textURL.setText(getString(R.string.about_url, getString(R.string.app_web_url)));
ViewUtils.makeClickableLinks((ViewGroup) findViewById(android.R.id.content));
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
}
protected int getLayoutResId() {
@@ -18,6 +18,10 @@ package de.dennisguse.opentracks;
import android.content.Intent;
import java.util.ArrayList;
import java.util.Arrays;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
@@ -55,30 +59,29 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
*
* @param trackIds the track ids
*/
protected void deleteTracks(long[] trackIds) {
protected void deleteTracks(Track.Id... trackIds) {
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
}
@Override
public void onConfirmDeleteDone(long[] trackIds) {
public void onConfirmDeleteDone(Track.Id... trackIds) {
boolean stopRecording = false;
if (trackIds.length == 1 && trackIds[0] == -1L) {
stopRecording = true;
} else {
long recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
for (long trackId : trackIds) {
if (trackId == recordingTrackId) {
stopRecording = true;
break;
}
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
for (Track.Id trackId : trackIds) {
if (trackId.equals(recordingTrackId)) {
stopRecording = true;
break;
}
}
if (stopRecording) {
getTrackRecordingServiceConnection().stopRecording(this, false);
}
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds);
intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
startActivityForResult(intent, DELETE_REQUEST_CODE);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
/**
@@ -3,11 +3,9 @@ package de.dennisguse.opentracks;
import android.os.Bundle;
import android.widget.ListView;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import de.dennisguse.opentracks.adapters.AggregatedStatisticsAdapter;
import de.dennisguse.opentracks.viewmodels.AggregatedStatistics;
import de.dennisguse.opentracks.viewmodels.AggregatedStatisticsModel;
public class AggregatedStatisticsActivity extends AbstractActivity {
@@ -20,17 +18,15 @@ public class AggregatedStatisticsActivity extends AbstractActivity {
super.onCreate(savedInstanceState);
listView = findViewById(R.id.aggregated_stats_list);
listView.setEmptyView(findViewById(R.id.aggregated_stats_empty_view));
final AggregatedStatisticsModel viewModel = new ViewModelProvider(this).get(AggregatedStatisticsModel.class);
viewModel.getAggregatedStats().observe(this, new Observer<AggregatedStatistics>() {
@Override
public void onChanged(AggregatedStatistics aggregatedStatistics) {
if (aggregatedStatistics != null) {
adapter = new AggregatedStatisticsAdapter(getApplicationContext(), aggregatedStatistics);
listView.setAdapter(adapter);
}
adapter.notifyDataSetChanged();
viewModel.getAggregatedStats().observe(this, aggregatedStatistics -> {
if (aggregatedStatistics != null) {
adapter = new AggregatedStatisticsAdapter(getApplicationContext(), aggregatedStatistics);
listView.setAdapter(adapter);
}
adapter.notifyDataSetChanged();
});
}
@@ -1,94 +0,0 @@
package de.dennisguse.opentracks;
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import android.content.Context;
import android.os.AsyncTask;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
/**
* Async Task to delete tracks.
*
* @author Jimmy Shih
*/
class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private final long[] trackIds;
private final Context context;
private TrackDeleteActivity deleteActivity;
// true if the AsyncTask has completed
private boolean completed;
/**
* Creates an AsyncTask.
*
* @param deleteActivity the activity currently associated with this task
* @param trackIds the track ids to delete. To delete all, set to size 1 with
* trackIds[0] == -1L
*/
public DeleteAsyncTask(TrackDeleteActivity deleteActivity, long[] trackIds) {
this.deleteActivity = deleteActivity;
this.trackIds = trackIds;
context = deleteActivity.getApplicationContext();
completed = false;
}
/**
* Sets the current activity associated with this AyncTask.
*
* @param deleteActivity the current activity, can be null
*/
public void setActivity(TrackDeleteActivity deleteActivity) {
this.deleteActivity = deleteActivity;
if (completed && deleteActivity != null) {
deleteActivity.onAsyncTaskCompleted();
}
}
@Override
protected void onPreExecute() {
if (deleteActivity != null) {
deleteActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
if (trackIds.length == 1 && trackIds[0] == -1L) {
contentProviderUtils.deleteAllTracks(context);
} else {
for (long id : trackIds) {
if (isCancelled()) {
return false;
}
contentProviderUtils.deleteTrack(context, id);
}
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
completed = true;
if (deleteActivity != null) {
deleteActivity.onAsyncTaskCompleted();
}
}
}
@@ -1,7 +1,6 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
@@ -12,7 +11,7 @@ public class HelpActivity extends AbstractActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.makeClickableLinks((ViewGroup) findViewById(android.R.id.content));
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
}
@Override
@@ -48,32 +48,33 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
private List<Long> markerIds;
private List<Waypoint.Id> waypointIds;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId == -1L) {
Waypoint.Id waypointId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
if (waypointId == null) {
Log.d(TAG, "invalid marker id");
finish();
return;
}
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId);
markerIds = new ArrayList<>();
waypointIds = new ArrayList<>();
int markerIndex = -1;
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), -1L, -1)) {
//TODO Load only waypointIds, not the whole waypoint
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), null, -1)) {
if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) {
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
markerIds.add(currentMarker.getId());
if (currentMarker.getId() == markerId) {
markerIndex = markerIds.size() - 1;
waypointIds.add(currentMarker.getId());
if (waypointId.equals(currentMarker.getId())) {
markerIndex = waypointIds.size() - 1;
}
cursor.moveToNext();
@@ -120,13 +121,13 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter {
MarkerDetailPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@Override
@NonNull
public Fragment getItem(int position) {
return MarkerDetailFragment.newInstance(markerIds.get(position));
return MarkerDetailFragment.newInstance(waypointIds.get(position));
}
@Nullable
@@ -137,7 +138,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override
public int getCount() {
return markerIds.size();
return waypointIds.size();
}
}
}
@@ -29,7 +29,6 @@ import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
@@ -43,6 +42,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
@@ -59,13 +59,14 @@ public class MarkerEditActivity extends AbstractActivity {
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id";
private static final String BUNDLE_PHOTO_URI = "photo_uri";
private static final int CAMERA_REQUEST_CODE = 5;
private static final int GALLERY_IMG_REQUEST_CODE = 7;
private static final String TAG = MarkerEditActivity.class.getSimpleName();
private long trackId;
private long markerId;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private Track.Id trackId;
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
private Waypoint waypoint;
private MenuItem insertPhotoMenuItem;
@@ -80,15 +81,13 @@ public class MarkerEditActivity extends AbstractActivity {
private EditText waypointDescription;
private ImageView waypointPhoto;
private ImageView waypointDeletePhotoBtn;
private Button done;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(null);
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
Waypoint.Id waypointId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
@@ -101,26 +100,67 @@ public class MarkerEditActivity extends AbstractActivity {
waypointPhoto = findViewById(R.id.marker_edit_waypoint_photo);
waypointDeletePhotoBtn = findViewById(R.id.marker_edit_waypoint_photo_delete);
waypointDeletePhotoBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (waypoint != null && waypoint.hasPhoto())
waypoint.setPhotoUrl(null);
waypointPhoto.setImageBitmap(null);
photoUri = null;
hideAndShowOptions();
waypointDeletePhotoBtn.setOnClickListener(v -> {
if (waypoint != null && waypoint.hasPhoto()) {
waypoint.setPhotoUrl(null);
}
waypointPhoto.setImageBitmap(null);
photoUri = null;
hideAndShowOptions();
});
Button cancel = findViewById(R.id.marker_edit_cancel);
cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
cancel.setOnClickListener(v -> finish());
final boolean isNewMarker = waypointId == null;
setTitle(isNewMarker ? R.string.menu_insert_marker : R.string.menu_edit);
Button done = findViewById(R.id.marker_edit_done);
done.setText(isNewMarker ? R.string.generic_add : R.string.generic_save);
done.setOnClickListener(v -> {
if (isNewMarker) {
addMarker();
} else {
saveMarker();
}
finish();
});
done = findViewById(R.id.marker_edit_done);
updateUiByMarkerId();
if (isNewMarker) {
int nextWaypointNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId);
if (nextWaypointNumber == -1) {
nextWaypointNumber = 0;
}
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber));
waypointName.selectAll();
waypointMarkerType.setText("");
waypointDescription.setText("");
} else {
waypoint = new ContentProviderUtils(this).getWaypoint(waypointId);
if (waypoint == null) {
Log.d(TAG, "waypoint is null");
finish();
return;
}
waypointName.setText(waypoint.getName());
waypointMarkerType.setText(waypoint.getCategory());
waypointDescription.setText(waypoint.getDescription());
if (waypoint.hasPhoto()) {
photoUri = waypoint.getPhotoURI();
}
}
if (savedInstanceState != null) {
photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI);
if (waypoint != null) {
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
}
}
if (photoUri != null) {
setWaypointImageView(photoUri);
}
hideAndShowOptions();
}
@Override
@@ -129,6 +169,12 @@ public class MarkerEditActivity extends AbstractActivity {
trackRecordingServiceConnection.startConnection(this);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BUNDLE_PHOTO_URI, photoUri);
}
@Override
protected void onStop() {
super.onStop();
@@ -198,54 +244,6 @@ public class MarkerEditActivity extends AbstractActivity {
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Updates the UI based on the marker id.
*/
private void updateUiByMarkerId() {
final boolean newMarker = markerId == -1L;
setTitle(newMarker ? R.string.menu_insert_marker : R.string.menu_edit);
done.setText(newMarker ? R.string.generic_add : R.string.generic_save);
done.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (newMarker) {
addMarker();
} else {
saveMarker();
}
finish();
}
});
if (newMarker) {
int nextWaypointNumber = trackId == -1L ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId);
if (nextWaypointNumber == -1) {
nextWaypointNumber = 0;
}
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber));
waypointName.selectAll();
waypointMarkerType.setText("");
waypointDescription.setText("");
} else {
waypoint = new ContentProviderUtils(this).getWaypoint(markerId);
if (waypoint == null) {
Log.d(TAG, "waypoint is null");
finish();
return;
}
waypointName.setText(waypoint.getName());
waypointMarkerType.setText(waypoint.getCategory());
waypointDescription.setText(waypoint.getDescription());
if (waypoint.hasPhoto()) {
photoUri = waypoint.getPhotoURI();
setWaypointImageView(photoUri);
}
}
hideAndShowOptions();
}
/**
* Checks and hide/shows all buttons/options about marker photo options.
*
@@ -263,8 +261,8 @@ public class MarkerEditActivity extends AbstractActivity {
/**
* Returns the trackId. If trackId == -1 then get it from waypoint object.
*/
private long getTrackId() {
return trackId == -1 ? waypoint.getTrackId() : trackId;
private Track.Id getTrackId() {
return trackId == null ? waypoint.getTrackId() : trackId;
}
/**
@@ -272,13 +270,13 @@ public class MarkerEditActivity extends AbstractActivity {
*
* @param uri the uri photo.
*/
private void setWaypointImageView(Uri uri) {
private void setWaypointImageView(@NonNull Uri uri) {
try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r")) {
FileDescriptor fd = pfd.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd);
waypointPhoto.setImageBitmap(bitmap);
hideAndShowOptions();
} catch(IOException e) {
} catch (IOException e) {
Log.e(TAG, e.getMessage());
Toast.makeText(this, R.string.marker_add_photo_canceled, Toast.LENGTH_LONG).show();
}
@@ -295,9 +293,6 @@ public class MarkerEditActivity extends AbstractActivity {
startActivityForResult(intent, GALLERY_IMG_REQUEST_CODE);
}
/**
* Adds a marker.
*/
private void addMarker() {
trackRecordingServiceConnection.addMarker(this,
waypointName.getText().toString(),
@@ -306,9 +301,6 @@ public class MarkerEditActivity extends AbstractActivity {
photoUri != null ? photoUri.toString() : null);
}
/**
* Saves a marker.
*/
private void saveMarker() {
waypoint.setName(waypointName.getText().toString());
waypoint.setCategory(waypointMarkerType.getText().toString());
@@ -26,18 +26,16 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.cursoradapter.widget.ResourceCursorAdapter;
import androidx.loader.app.LoaderManager;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
@@ -59,16 +57,11 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
private static final String TAG = MarkerListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[]{WaypointsColumns._ID,
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY,
WaypointsColumns.TIME, WaypointsColumns.PHOTOURL,
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE};
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@@ -82,7 +75,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(MarkerListActivity.this);
}
if (key != null) {
runOnUiThread(() -> MarkerListActivity.this.invalidateOptionsMenu());
runOnUiThread(MarkerListActivity.this::invalidateOptionsMenu);
}
}
};
@@ -119,23 +112,20 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
Track.Id trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
listView = findViewById(R.id.marker_list);
listView.setEmptyView(findViewById(R.id.marker_list_empty));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id);
startActivity(intent);
}
listView.setOnItemClickListener((parent, view, position, id) -> {
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Waypoint.Id(id));
startActivity(intent);
});
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@Override
@@ -168,14 +158,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
@NonNull
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
if (track != null) {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.TRACKID + "=?",
new String[]{String.valueOf(track.getId())}, null);
} else {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
null, null, null);
}
return ContentProviderUtils.getWaypointsLoader(MarkerListActivity.this, track != null ? track.getId() : null);
}
@Override
@@ -228,7 +211,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
insertMarkerMenuItem.setVisible(track != null && track.getId() == recordingTrackId && !recordingTrackPaused);
insertMarkerMenuItem.setVisible(track != null && track.getId().equals(recordingTrackId) && !recordingTrackPaused);
return super.onPrepareOptionsMenu(menu);
}
@@ -246,34 +229,38 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
/**
* Handles a context item selection.
*
* @param itemId the menu item id
* @param markerIds the marker ids
* @param itemId the menu item id
* @param longWaypointIds the marker ids
* @return true if handled.
*/
private boolean handleContextItem(int itemId, long[] markerIds) {
private boolean handleContextItem(int itemId, long... longWaypointIds) {
Waypoint.Id[] waypointIds = new Waypoint.Id[longWaypointIds.length];
for (int i = 0; i < longWaypointIds.length; i++) {
waypointIds[i] = new Waypoint.Id(longWaypointIds[i]);
}
Intent intent;
switch (itemId) {
case R.id.list_context_menu_show_on_map:
if (markerIds.length == 1) {
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getWaypoint(markerIds[0]));
if (waypointIds.length == 1) {
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getWaypoint(waypointIds[0]));
}
return true;
case R.id.list_context_menu_edit:
if (markerIds.length == 1) {
if (waypointIds.length == 1) {
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]);
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointIds[0]);
startActivity(intent);
}
return true;
case R.id.list_context_menu_delete:
if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
markerIds = new long[]{-1L};
if (waypointIds.length > 1 && waypointIds.length == listView.getCount()) {
waypointIds = null;
}
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerIds);
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointIds);
return true;
case R.id.list_context_menu_select_all:
int size = listView.getCount();
for (int i = 0; i < size; i++) {
for (int i = 0; i < listView.getCount(); i++) {
listView.setItemChecked(i, true);
}
return false;
@@ -26,8 +26,6 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
@@ -99,7 +97,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
@@ -167,7 +165,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
contentProviderUtils = new ContentProviderUtils(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(null);
trackRecordingServiceConnection = new TrackRecordingServiceConnection();
searchEngine = new SearchEngine(contentProviderUtils);
arrayAdapter = new ArrayAdapter<Map<String, Object>>(this, R.layout.list_item, R.id.list_item_name) {
@@ -203,20 +201,17 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
ListView listView = findViewById(R.id.search_list);
listView.setAdapter(arrayAdapter);
listView.setEmptyView(findViewById(R.id.search_list_empty));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String, Object> item = arrayAdapter.getItem(position);
Long trackId = (Long) item.get(TRACK_ID_FIELD);
Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class);
if (markerId != null) {
intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId);
} else {
intent = intent.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
}
startActivity(intent);
listView.setOnItemClickListener((parent, view, position, id) -> {
Map<String, Object> item = arrayAdapter.getItem(position);
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class);
if (markerId != null) {
intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId);
} else {
intent = intent.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
}
startActivity(intent);
});
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
handleIntent(getIntent());
@@ -275,22 +270,22 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
return false;
}
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
Long trackId = (Long) item.get(TRACK_ID_FIELD);
Long markerId = (Long) item.get(MARKER_ID_FIELD);
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
Waypoint.Id waypointId = (Waypoint.Id) item.get(MARKER_ID_FIELD);
Intent intent;
switch (itemId) {
case R.id.list_context_menu_show_on_map:
IntentUtils.showCoordinateOnMap(this, (double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
return true;
case R.id.list_context_menu_share:
intent = IntentUtils.newShareFileIntent(this, new long[]{trackId});
intent = IntentUtils.newShareFileIntent(this, trackId);
intent = Intent.createChooser(intent, null);
startActivity(intent);
return true;
case R.id.list_context_menu_edit:
if (markerId != null) {
if (waypointId != null) {
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointId);
} else {
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
@@ -301,10 +296,10 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
finish();
return true;
case R.id.list_context_menu_delete:
if (markerId != null) {
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), new long[]{markerId});
if (waypointId != null) {
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointId);
} else {
deleteTracks(new long[]{trackId});
deleteTracks(trackId);
}
return true;
default:
@@ -336,7 +331,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
* @param textQuery the query
*/
private void doSearch(String textQuery) {
SearchQuery query = new SearchQuery(textQuery, null, -1L, System.currentTimeMillis());
SearchQuery query = new SearchQuery(textQuery, null, null, System.currentTimeMillis());
SortedSet<ScoredResult> scoredResults = searchEngine.search(query);
final List<Map<String, Object>> displayResults = prepareResultsforDisplay(scoredResults);
@@ -377,8 +372,8 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
//TODO: It may be more appropriate to obtain the track name as a join in the retrieval phase of the searchable.
String trackName = null;
long trackId = waypoint.getTrackId();
if (trackId != -1L) {
Track.Id trackId = waypoint.getTrackId();
if (!trackId.isValid()) {
Track track = contentProviderUtils.getTrack(trackId);
if (track != null) {
trackName = track.getName();
@@ -8,6 +8,9 @@ import android.util.Pair;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Set;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.provider.ShareContentProvider;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
@@ -18,7 +21,7 @@ import de.dennisguse.opentracks.util.IntentDashboardUtils;
*/
public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
private TrackFileFormat trackFileFormat;
private final TrackFileFormat trackFileFormat;
protected ShowOnMapProxyActivity(TrackFileFormat trackFileFormat) {
this.trackFileFormat = trackFileFormat;
@@ -26,7 +29,7 @@ public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
long[] trackIds = IntentDashboardUtils.extractTrackIdsFromIntent(getIntent());
Set<Track.Id> trackIds = IntentDashboardUtils.extractTrackIdsFromIntent(getIntent());
showTrackTrackfileFormat(this, trackFileFormat, trackIds);
@@ -39,14 +42,14 @@ public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
* @param context the context
* @param trackIds the track ids
*/
private static void showTrackTrackfileFormat(Context context, TrackFileFormat trackFileFormat, long[] trackIds) {
if (trackIds.length == 0) {
private static void showTrackTrackfileFormat(Context context, TrackFileFormat trackFileFormat, Set<Track.Id> trackIds) {
if (trackIds.size() == 0) {
return;
}
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(TrackPointsColumns.TRACKID, trackIds[0]);
intent.putExtra(TrackPointsColumns.TRACKID, trackIds.toArray(new Track.Id[0])[0]);
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", trackFileFormat);
intent.setDataAndType(uriAndMime.first, uriAndMime.second);
@@ -4,6 +4,8 @@ import android.app.Application;
import android.os.StrictMode;
import android.util.Log;
import java.util.Locale;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
@@ -25,7 +27,16 @@ public class Startup extends Application {
//Set default values of preferences on first start.
PreferencesUtils.resetPreferences(this, false);
if (PreferencesUtils.getString(this, R.string.stats_units_key, "").equals("")) {
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits);
}
//TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed.
if (PreferencesUtils.isRecording(this)) {
Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed.");
PreferencesUtils.setLong(this, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
}
//In debug builds: show thread and VM warnings.
if (BuildConfig.DEBUG) {
@@ -16,15 +16,24 @@
package de.dennisguse.opentracks;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.UnitConversions;
@@ -46,12 +55,12 @@ public class TrackController {
private final ImageButton recordImageButton;
private final ImageButton stopImageButton;
private final boolean alwaysShow;
private ButtonDelay buttonDelay;
private boolean isRecording;
private boolean isPaused;
private long totalTime = 0;
// the timestamp for the toal time
private long totalTimeTimestamp = 0;
private boolean isResumed = false;
@@ -66,6 +75,7 @@ public class TrackController {
}
};
@SuppressLint("ClickableViewAccessibility")
TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
this.activity = activity;
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
@@ -76,10 +86,91 @@ public class TrackController {
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
recordImageButton = activity.findViewById(R.id.track_controller_record);
recordImageButton.setOnClickListener(recordListener);
recordImageButton.setOnTouchListener((view, motionEvent) -> onRecordTouch(activity, recordListener, motionEvent));
stopImageButton = activity.findViewById(R.id.track_controller_stop);
stopImageButton.setOnClickListener(stopListener);
stopImageButton.setOnTouchListener((view, motionEvent) -> onStopTouch(activity, stopListener, motionEvent));
}
private boolean onRecordTouch(final Activity activity, final OnClickListener recordListener, final MotionEvent motionEvent) {
if (isRecording && !isPaused) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
buttonDelay = new ButtonDelay(activity, recordImageButton, R.drawable.ic_button_pause_anim, R.string.hold_to_pause, recordListener);
new Thread(buttonDelay).start();
return true;
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
recordImageButton.setImageResource(R.drawable.ic_button_pause);
if (buttonDelay != null) {
buttonDelay.canceled = true;
}
return true;
}
} else if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
recordListener.onClick(null);
return true;
}
return false;
}
private boolean onStopTouch(final Activity activity, final OnClickListener stopListener, final MotionEvent motionEvent) {
if (isRecording) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
buttonDelay = new ButtonDelay(activity, stopImageButton, R.drawable.ic_button_stop_anim, R.string.hold_to_stop, stopListener);
new Thread(buttonDelay).start();
return true;
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
stopImageButton.setImageResource(R.drawable.ic_button_stop);
if (buttonDelay != null) {
buttonDelay.canceled = true;
}
return true;
}
}
return false;
}
private static class ButtonDelay implements Runnable {
private boolean canceled = false;
private final ImageButton imageButton;
private final Activity activity;
private final OnClickListener clickListener;
private final int delayMillis;
private final Drawable drawable;
private final int delayMessageId;
private ButtonDelay(final Activity activity, final ImageButton imageButton, final int animDrawableId, final int delayMessageId, final OnClickListener clickListener) {
this.activity = activity;
this.clickListener = clickListener;
this.delayMillis = activity.getResources().getInteger(R.integer.buttonDelayMillis);
this.imageButton = imageButton;
this.drawable = ContextCompat.getDrawable(activity, animDrawableId);
this.delayMessageId = delayMessageId;
}
@Override
public void run() {
activity.runOnUiThread(()->{
imageButton.setImageDrawable(drawable);
if (drawable instanceof AnimatedVectorDrawable) {
((AnimatedVectorDrawable) drawable).start();
}
ActivityUtils.vibrate(activity, 150);
ActivityUtils.toast(activity, delayMessageId, Toast.LENGTH_SHORT, Gravity.TOP);
});
try {
Thread.sleep(delayMillis);
} catch (InterruptedException ignored) {
}
if (!canceled) {
activity.runOnUiThread(()-> {
clickListener.onClick(null);
ActivityUtils.vibrate(activity, 1000);
});
}
}
}
public void update(boolean recording, boolean paused) {
@@ -96,10 +187,10 @@ public class TrackController {
return;
}
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
stopImageButton.setImageResource(isRecording ? R.drawable.button_stop : R.drawable.ic_button_stop_disabled);
stopImageButton.setImageResource(isRecording ? R.drawable.ic_button_stop : R.drawable.ic_button_stop_disabled);
stopImageButton.setEnabled(isRecording);
statusTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
@@ -16,26 +16,29 @@
package de.dennisguse.opentracks;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.View;
import de.dennisguse.opentracks.util.DialogUtils;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.SystemUtils;
/**
* An activity for delete tracks.
* An activity for deleting tracks.
*
* @author Jimmy Shih
*/
public class TrackDeleteActivity extends Activity {
public class TrackDeleteActivity extends AbstractActivity {
public static final String EXTRA_TRACK_IDS = "track_ids";
private static final int DIALOG_PROGRESS_ID = 0;
private List<Track.Id> trackIds;
private DeleteAsyncTask deleteAsyncTask;
private Thread deleteThread;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -44,53 +47,48 @@ public class TrackDeleteActivity extends Activity {
setResult(RESULT_CANCELED);
Intent intent = getIntent();
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
trackIds = intent.getParcelableArrayListExtra(EXTRA_TRACK_IDS);
deleteThread = new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof DeleteAsyncTask) {
deleteAsyncTask = (DeleteAsyncTask) retained;
deleteAsyncTask.setActivity(this);
} else {
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
deleteAsyncTask.execute();
}
PowerManager.WakeLock wakeLock = SystemUtils.acquireWakeLock(TrackDeleteActivity.this, null);
for (Track.Id id : trackIds) {
if (Thread.interrupted()) {
break;
}
contentProviderUtils.deleteTrack(TrackDeleteActivity.this, id);
}
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
if (Thread.interrupted()) {
return;
}
runOnUiThread(TrackDeleteActivity.this::onAsyncTaskCompleted);
});
}
@Override
public Object onRetainNonConfigurationInstance() {
deleteAsyncTask.setActivity(null);
return deleteAsyncTask;
protected void onStart() {
super.onStart();
deleteThread.start();
}
@Override
protected Dialog onCreateDialog(int id) {
if (id != DIALOG_PROGRESS_ID) {
return null;
}
return DialogUtils.createSpinnerProgressDialog(
this, R.string.track_delete_progress_message, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
deleteAsyncTask.cancel(true);
dialog.dismiss();
finish();
}
});
protected void onStop() {
super.onStop();
deleteThread.interrupt();
}
@Override
protected int getLayoutResId() {
return R.layout.track_delete;
}
/**
* Invokes when the associated AsyncTask completes.
*/
public void onAsyncTaskCompleted() {
removeDialog(DIALOG_PROGRESS_ID);
findViewById(R.id.progressbar).setVisibility(View.INVISIBLE);
setResult(RESULT_OK);
finish();
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
}
}
@@ -21,7 +21,6 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
@@ -65,9 +64,9 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(null);
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
if (trackId == -1L) {
trackRecordingServiceConnection = new TrackRecordingServiceConnection();
Track.Id trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
if (!trackId.isValid()) {
Log.e(TAG, "invalid trackId");
finish();
return;
@@ -89,20 +88,12 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
activityType.setAdapter(adapter);
activityType.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
activityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position))));
activityType.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
setActivityTypeIcon(TrackIconUtils.getIconValue(
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position)));
}
});
activityType.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
setActivityTypeIcon(TrackIconUtils.getIconValue(
TrackEditActivity.this, activityType.getText().toString()));
}
TrackEditActivity.this, activityType.getText().toString()));
}
});
@@ -116,37 +107,28 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
activityTypeIcon = findViewById(R.id.track_edit_activity_type_icon);
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(this, iconValue));
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
}
return true;
activityTypeIcon.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP) {
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
}
return true;
});
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
}
return true;
activityTypeIcon.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
}
return true;
});
description = findViewById(R.id.track_edit_description);
description.setText(track.getDescription());
Button saveButton = findViewById(R.id.track_edit_save);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TrackUtils.updateTrack(TrackEditActivity.this, track, nameEditText.getText().toString(),
activityType.getText().toString(), description.getText().toString(),
contentProviderUtils);
finish();
}
saveButton.setOnClickListener(v -> {
TrackUtils.updateTrack(TrackEditActivity.this, track, nameEditText.getText().toString(),
activityType.getText().toString(), description.getText().toString(),
contentProviderUtils);
finish();
});
Button cancel = findViewById(R.id.track_edit_cancel);
@@ -155,12 +137,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
cancel.setVisibility(View.GONE);
} else {
setTitle(R.string.menu_edit);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
cancel.setOnClickListener(v -> finish());
cancel.setVisibility(View.VISIBLE);
}
}
@@ -24,6 +24,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
@@ -33,8 +34,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
@@ -44,26 +43,23 @@ import androidx.core.app.ActivityCompat;
import androidx.cursoradapter.widget.ResourceCursorAdapter;
import androidx.loader.app.LoaderManager;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import java.util.Locale;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.ServiceUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils;
/**
* An activity displaying a list of tracks.
@@ -74,10 +70,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private static final String TAG = TrackListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON};
// The following are set in onCreate
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
@@ -85,12 +77,12 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private TrackController trackController;
private ListView listView;
private ResourceCursorAdapter resourceCursorAdapter;
private GpsStatusValue gpsStatusValue;
private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null,
null, TrackUtils.TRACK_SORT_ORDER);
return ContentProviderUtils.getTracksCursorLoader(TrackListActivity.this);
}
@Override
@@ -106,7 +98,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Preferences
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private Track.Id recordingTrackId = null;
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@@ -127,8 +119,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private boolean recordingTrackPaused;
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
@@ -166,28 +157,37 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
}
};
private boolean startGps = false; // true to start gps
// Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() {
@Override
public void run() {
// After binding changes (e.g., becomes available), update the total time in trackController.
runOnUiThread(() -> trackController.update(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused));
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
if (!startGps) {
return;
}
// After binding changes (e.g., becomes available), update the total time in trackController.
runOnUiThread(() -> trackController.update(isRecording, recordingTrackPaused));
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
Log.d(TAG, "service not available to start gps or a new recording");
gpsStatusValue = GpsStatusValue.GPS_NONE;
return;
}
if (startGps) {
service.startGps();
startGps = false;
// Get GPS status and listen GPS status changes.
gpsStatusValue = service.getGpsStatus();
updateGpsMenuItem(true, isRecording);
service.addListener(newStatus -> {
gpsStatusValue = newStatus;
updateGpsMenuItem(true, isRecording);
});
if (isGpsStarted()) {
return;
}
service.startGps();
gpsStatusValue = GpsStatusValue.GPS_ENABLED;
updateGpsMenuItem(true, isRecording);
}
};
@@ -216,6 +216,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gpsStatusValue = GpsStatusValue.GPS_NONE;
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
@@ -229,31 +231,23 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Show trackController when search dialog is dismissed
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
if (searchManager != null) {
searchManager.setOnDismissListener(new SearchManager.OnDismissListener() {
@Override
public void onDismiss() {
trackController.show();
}
});
searchManager.setOnDismissListener(() -> trackController.show());
}
listView = findViewById(R.id.track_list);
listView.setEmptyView(findViewById(R.id.track_list_empty_view));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long trackId) {
Intent newIntent;
if (trackId == recordingTrackId) {
// Is recording -> open record activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
} else {
// Not recording -> open detail activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
}
startActivity(newIntent);
listView.setOnItemClickListener((parent, view, position, trackId) -> {
Intent newIntent;
if (trackId == recordingTrackId.getId()) {
// Is recording -> open record activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
} else {
// Not recording -> open detail activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
}
startActivity(newIntent);
});
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@@ -268,7 +262,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY);
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
long trackId = cursor.getLong(idIndex);
Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
boolean isRecording = trackId == recordingTrackId;
String icon = cursor.getString(iconIndex);
int iconId = TrackIconUtils.getIconDrawable(icon);
@@ -290,7 +284,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
LoaderManager.getInstance(this).initLoader(0, null, loaderCallbacks);
showStartupDialogs();
requestGPSPermissions();
}
@Override
@@ -357,7 +352,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isGpsStarted = ServiceUtils.isTrackRecordingServiceRunning(this);
boolean isGpsStarted = isGpsStarted();
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
updateMenuItems(isGpsStarted, isRecording);
@@ -377,10 +372,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
} else {
startGps = !ServiceUtils.isTrackRecordingServiceRunning(this);
// Invoke trackRecordingService
if (startGps) {
if (!isGpsStarted()) {
trackRecordingServiceConnection.startAndBind(this);
bindChangedCallback.run();
} else {
@@ -436,17 +429,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Do nothing
}
/**
* Shows start up dialogs.
*/
private void showStartupDialogs() {
// If stats_units_key is undefined, set it
if (PreferencesUtils.getString(this, R.string.stats_units_key, "").equals("")) {
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits);
}
requestGPSPermissions();
private boolean isGpsStarted() {
return gpsStatusValue != GpsStatusValue.GPS_NONE && gpsStatusValue != GpsStatusValue.GPS_DISABLED;
}
private void requestGPSPermissions() {
@@ -456,17 +440,30 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
}
/**
* Updates the menu items.
* Updates the menu items with not fixed icon for gps option.
*
* @param isGpsStarted true if gps is started
* @param isRecording true if recording
*/
private void updateMenuItems(boolean isGpsStarted, boolean isRecording) {
updateGpsMenuItem(isGpsStarted, isRecording);
}
/**
* Updates the menu items with the icon specified.
*
* @param isGpsStarted true if gps is started
* @param isRecording true if recording
*/
private void updateGpsMenuItem(boolean isGpsStarted, boolean isRecording) {
if (startGpsMenuItem != null) {
startGpsMenuItem.setVisible(!isRecording);
if (!isRecording) {
startGpsMenuItem.setTitle(isGpsStarted ? R.string.menu_stop_gps : R.string.menu_start_gps);
startGpsMenuItem.setIcon(isGpsStarted ? R.drawable.ic_gps_fixed_24dp : R.drawable.ic_gps_off_24dp);
startGpsMenuItem.setIcon(isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp);
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
}
}
}
}
@@ -474,15 +471,20 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
/**
* Handles a context item selection.
*
* @param itemId the menu item id
* @param trackIds the track ids
* @param itemId the menu item id
* @param longTrackIds the track ids
* @return true if handled.
*/
private boolean handleContextItem(int itemId, long[] trackIds) {
private boolean handleContextItem(int itemId, long... longTrackIds) {
Track.Id[] trackIds = new Track.Id[longTrackIds.length];
for (int i = 0; i < longTrackIds.length; i++) {
trackIds[i] = new Track.Id(longTrackIds[i]);
}
Intent intent;
switch (itemId) {
case R.id.list_context_menu_show_on_map:
IntentDashboardUtils.startDashboard(this, trackIds);
IntentDashboardUtils.startDashboard(this, false, trackIds);
return true;
case R.id.list_context_menu_share:
intent = IntentUtils.newShareFileIntent(this, trackIds);
@@ -495,9 +497,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
startActivity(intent);
return true;
case R.id.list_context_menu_delete:
if (trackIds.length > 1 && trackIds.length == listView.getCount()) {
trackIds = new long[]{-1L};
}
deleteTracks(trackIds);
return true;
case R.id.list_context_menu_select_all:
@@ -66,7 +66,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
private ViewPager pager;
// From intent.
private long trackId;
private Track.Id trackId;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -133,7 +133,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
// Update UI
this.invalidateOptionsMenu();
if (trackId != -1L) {
if (trackId.isValid()) {
trackDataHub.loadTrack(trackId);
}
}
@@ -182,12 +182,12 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
Intent intent;
switch (item.getItemId()) {
case R.id.track_detail_share:
intent = IntentUtils.newShareFileIntent(this, new long[]{trackId});
intent = IntentUtils.newShareFileIntent(this, trackId);
intent = Intent.createChooser(intent, null);
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentDashboardUtils.startDashboard(this, trackId);
IntentDashboardUtils.startDashboard(this, false, trackId);
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
@@ -200,7 +200,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
startActivity(intent);
return true;
case R.id.track_detail_delete:
deleteTracks(new long[]{trackId});
deleteTracks(trackId);
return true;
case R.id.track_detail_resume_track:
Intent newIntent = IntentUtils.newIntent(TrackRecordedActivity.this, TrackRecordingActivity.class)
@@ -238,12 +238,12 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
}
private void handleIntent(Intent intent) {
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId != -1L) {
Waypoint.Id waypointId = intent.getParcelableExtra(EXTRA_MARKER_ID);
if (waypointId != null) {
// Use the trackId from the marker
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId);
if (waypoint == null) {
finish();
return;
@@ -251,7 +251,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
trackId = waypoint.getTrackId();
}
if (trackId == -1L) {
if (trackId == null) {
Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID.");
finish();
}
@@ -11,10 +11,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -41,6 +41,8 @@ import de.dennisguse.opentracks.util.TrackUtils;
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
//This mode of communication should be removed anyhow.
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -57,11 +59,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private ViewPager pager;
private TrackController trackController;
// From intent
private long trackId;
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
private Track.Id trackId;
// Preferences
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused;
private final Runnable bindChangedCallback = new Runnable() {
@@ -70,29 +71,22 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// After binding changes (is available), update the total time in trackController.
runOnUiThread(() -> trackController.update(true, recordingTrackPaused));
if (recordingTrackId == -1L) {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
Log.d(TAG, "could not get TrackRecordingService");
return;
}
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
Log.d(TAG, "could not get TrackRecordingService");
return;
}
if (!service.isRecording()) {
// Starts or resumes a track.
int msg;
if (trackId == -1L) {
if (trackId == null) {
// trackId isn't initialized -> leads a new recording.
trackId = service.startNewTrack();
recordingTrackId = trackId;
msg = R.string.track_detail_record_success;
} else {
// trackId is initialized -> resumes the track.
recordingTrackId = trackId;
service.resumeTrack(trackId);
msg = R.string.track_detail_resume_success;
}
// A recording track is on.
Toast.makeText(TrackRecordingActivity.this, msg, Toast.LENGTH_SHORT).show();
trackDataHub.loadTrack(trackId);
trackController.update(true, false);
trackController.onResume(true, recordingTrackPaused);
@@ -103,12 +97,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackRecordingActivity.this);
setLockscreenPolicy();
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackRecordingActivity.this);
setLockscreenPolicy();
@@ -122,6 +110,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_fullscreen_while_recording_key, key)) {
setFullscreenPolicy();
}
if (key == null) return;
@@ -168,52 +159,28 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
handleIntent(getIntent());
trackId = null;
if (savedInstanceState != null) {
//Activity was recreated.
trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID);
} else {
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
if (trackId != null && contentProviderUtils.getTrack(trackId) == null) {
Log.w(TAG, "TrackId does not exists; cannot continue the recording.");
finish();
}
}
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
trackDataHub = new TrackDataHub(this);
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordingFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
}
return "Unknown Tab";
}
};
pager = findViewById(R.id.track_detail_activity_view_pager);
pager.setAdapter(adapter);
pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), 1));
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
tabs.setupWithViewPager(pager);
if (savedInstanceState != null) {
@@ -227,12 +194,12 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
public void onAttachedToWindow() {
setLockscreenPolicy();
setScreenOnPolicy();
setFullscreenPolicy();
super.onAttachedToWindow();
}
private void setLockscreenPolicy() {
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackRecordingActivity.this)
&& PreferencesUtils.isRecording(this);
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackRecordingActivity.this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(showOnLockScreen);
@@ -244,8 +211,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
}
private void setScreenOnPolicy() {
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackRecordingActivity.this)
&& PreferencesUtils.isRecording(this);
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackRecordingActivity.this);
if (keepScreenOn) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -254,6 +220,16 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
}
}
private void setFullscreenPolicy() {
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(TrackRecordingActivity.this);
if (keepScreenOn) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
@Override
protected void onStart() {
super.onStart();
@@ -272,12 +248,25 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// Update UI
this.invalidateOptionsMenu();
if (trackId != -1L) {
if (trackId != null) {
trackDataHub.loadTrack(trackId);
trackController.onResume(true, recordingTrackPaused);
}
startRecording();
/*
* If the binding has happened, then invoke the callback to start a new recording.
* If the binding hasn't happened, then invoking the callback will have no effect.
* But when the binding occurs, the callback will get invoked.
*/
trackRecordingServiceConnection.startAndBind(this);
bindChangedCallback.run();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
outState.putParcelable(EXTRA_TRACK_ID, trackId);
}
@Override
@@ -294,24 +283,11 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackDataHub.stop();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
}
@Override
protected int getLayoutResId() {
return R.layout.track_record;
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_record, menu);
@@ -339,7 +315,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentDashboardUtils.startDashboard(this, trackId);
IntentDashboardUtils.startDashboard(this, true, trackId);
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
@@ -368,22 +344,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
return trackDataHub;
}
private void handleIntent(Intent intent) {
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
if (trackId == -1L) {
return;
}
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
track = contentProviderUtils.getLastTrack();
if (track != null) {
trackId = track.getId();
return;
}
finish();
}
}
/**
* Updates the menu items.
*/
@@ -404,14 +364,44 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
}
private void startRecording() {
trackRecordingServiceConnection.startAndBind(this);
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
/*
* If the binding has happened, then invoke the callback to start a new recording.
* If the binding hasn't happened, then invoking the callback will have no effect.
* But when the binding occurs, the callback will get invoked.
*/
bindChangedCallback.run();
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordingFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
}
}
@@ -17,8 +17,8 @@ import de.dennisguse.opentracks.viewmodels.AggregatedStatistics;
public class AggregatedStatisticsAdapter extends BaseAdapter {
private AggregatedStatistics aggregatedStatistics;
private Context context;
private final AggregatedStatistics aggregatedStatistics;
private final Context context;
public AggregatedStatisticsAdapter(Context context, AggregatedStatistics aggregatedStatistics) {
this.context = context;
@@ -83,12 +83,12 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
protected TextView distanceUnit;
protected TextView time;
protected boolean metricsUnits;
public ViewHolder() {
metricsUnits = PreferencesUtils.isMetricUnits(context);
}
protected boolean reportSpeed;
public void setValues(View view, int iconDrawable, String name, AggregatedStatistics.AggregatedStatistic aggregatedStats) {
reportSpeed = PreferencesUtils.isReportSpeed(context, name);
metricsUnits = PreferencesUtils.isMetricUnits(context);
sportIcon = view.findViewById(R.id.aggregated_stats_sport_icon);
typeLabel = view.findViewById(R.id.aggregated_stats_type_label);
numTracks = view.findViewById(R.id.aggregated_stats_num_tracks);
@@ -128,14 +128,14 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
maxSpeedLabel = view.findViewById(R.id.aggregated_stats_max_rate_label);
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, true);
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, reportSpeed);
avgSpeed.setText(parts.first);
avgSpeedUnit.setText(parts.second);
avgSpeedLabel.setText(context.getString(R.string.stats_average_moving_speed));
}
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, true);
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, reportSpeed);
maxSpeed.setText(parts.first);
maxSpeedUnit.setText(parts.second);
maxSpeedLabel.setText(context.getString(R.string.stats_max_speed));
@@ -164,14 +164,14 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
maxPaceLabel = view.findViewById(R.id.aggregated_stats_max_rate_label);
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, false);
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, reportSpeed);
avgPace.setText(parts.first);
avgPaceUnit.setText(parts.second);
avgPaceLabel.setText(R.string.stats_average_moving_pace);
}
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, false);
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, reportSpeed);
maxPace.setText(parts.first);
maxPaceUnit.setText(parts.second);
maxPaceLabel.setText(R.string.stats_fastest_pace);
@@ -43,7 +43,7 @@ public class ChartPoint {
elevation *= UnitConversions.M_TO_FT;
}
speed = trackStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
speed = trackStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MPS_TO_KMH;
if (!metricUnits) {
speed *= UnitConversions.KM_TO_MI;
}
@@ -185,7 +185,7 @@ public class DescriptionGenerator {
*/
@VisibleForTesting
void writeSpeed(double speed, StringBuilder builder, int resId, String lineBreak) {
double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
double speedInKmHr = speed * UnitConversions.MPS_TO_KMH;
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
builder.append(lineBreak);
@@ -108,28 +108,25 @@ public class SearchEngine {
/**
* Comparator for scored results.
*/
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = new Comparator<ScoredResult>() {
@Override
public int compare(ScoredResult r1, ScoredResult r2) {
// Score ordering.
int scoreDiff = Double.compare(r2.score, r1.score);
if (scoreDiff != 0) {
return scoreDiff;
}
// Make tracks come before waypoints.
if (r1.waypoint != null && r2.track != null) {
return 1;
} else if (r1.track != null && r2.waypoint != null) {
return -1;
}
// Finally, use arbitrary ordering, by ID.
long id1 = r1.track != null ? r1.track.getId() : r1.waypoint.getId();
long id2 = r2.track != null ? r2.track.getId() : r2.waypoint.getId();
long idDiff = id2 - id1;
return Long.signum(idDiff);
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = (r1, r2) -> {
// Score ordering.
int scoreDiff = Double.compare(r2.score, r1.score);
if (scoreDiff != 0) {
return scoreDiff;
}
// Make tracks come before waypoints.
if (r1.waypoint != null && r2.track != null) {
return 1;
} else if (r1.track != null && r2.waypoint != null) {
return -1;
}
// Finally, use arbitrary ordering, by ID.
long id1 = r1.track != null ? r1.track.getId().getId() : r1.waypoint.getId().getId();
long id2 = r2.track != null ? r2.track.getId().getId() : r2.waypoint.getId().getId();
long idDiff = id2 - id1;
return Long.signum(idDiff);
};
private final ContentProviderUtils providerUtils;
@@ -293,7 +290,7 @@ public class SearchEngine {
score *= getTimeBoost(query, location.getTime());
// Score waypoints in the currently-selected track higher (searching inside the current track).
if (query.currentTrackId != -1 && waypoint.getTrackId() == query.currentTrackId) {
if (waypoint.getTrackId() != null && waypoint.getTrackId().equals(query.currentTrackId)) {
score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
}
@@ -385,10 +382,10 @@ public class SearchEngine {
public static class SearchQuery {
final String textQuery;
final Location currentLocation;
final long currentTrackId;
final Track.Id currentTrackId;
final long currentTimestamp;
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId, long currentTimestamp) {
public SearchQuery(String textQuery, Location currentLocation, Track.Id currentTrackId, long currentTimestamp) {
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
this.currentLocation = currentLocation;
this.currentTrackId = currentTrackId;
@@ -420,8 +417,8 @@ public class SearchEngine {
@Override
public String toString() {
return "ScoredResult ["
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
+ (track != null ? ("trackId=" + track.getId().getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId().getId() + ", ") : "")
+ "score=" + score + "]";
}
}
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.Collections;
@@ -78,8 +79,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
private Handler handler;
// Preference values
private long selectedTrackId;
private long recordingTrackId;
private Track.Id selectedTrackId;
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
// Track points sampling state
@@ -174,10 +175,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
handler = null;
}
public void loadTrack(final long trackId) {
public void loadTrack(final @NonNull Track.Id trackId) {
runInHandlerThread(() -> {
if (trackId == selectedTrackId) {
Log.i(TAG, "Not reloading track " + trackId);
if (trackId.equals(selectedTrackId)) {
Log.i(TAG, "Not reloading track " + trackId.getId());
return;
}
selectedTrackId = trackId;
@@ -212,14 +213,14 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* Returns true if the selected track is recording.
*/
public boolean isSelectedTrackRecording() {
return selectedTrackId == recordingTrackId && PreferencesUtils.isRecording(recordingTrackId);
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && PreferencesUtils.isRecording(recordingTrackId);
}
/**
* Returns true if the selected track is paused.
*/
public boolean isSelectedTrackPaused() {
return selectedTrackId == recordingTrackId && recordingTrackPaused;
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
}
@Override
@@ -311,7 +312,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
trackDataListener.clearWaypoints();
}
try (Cursor cursor = contentProviderUtils.getWaypointCursor(selectedTrackId, -1L, MAX_DISPLAYED_WAYPOINTS)) {
try (Cursor cursor = contentProviderUtils.getWaypointCursor(selectedTrackId, null, MAX_DISPLAYED_WAYPOINTS)) {
if (cursor != null && cursor.moveToFirst()) {
do {
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
@@ -356,6 +357,11 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
long localLastSeenTrackPointIdId = updateSamplingState ? lastSeenTrackPointId : -1L;
long maxPointId = updateSamplingState ? -1L : lastSeenTrackPointId;
if (selectedTrackId == null) {
Log.w(TAG, "This should not happen, but it does"); //TODO
return;
}
long lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId);
int samplingFrequency = -1;
boolean includeNextPoint = false;
@@ -16,7 +16,17 @@
package de.dennisguse.opentracks.content.data;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
import java.util.UUID;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* A track.
@@ -26,7 +36,9 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
*/
public class Track {
private long id = -1L;
private Track.Id id;
private UUID uuid = UUID.randomUUID();
private String name = "";
private String description = "";
private String category = "";
@@ -38,14 +50,26 @@ public class Track {
public Track() {
}
public long getId() {
/**
* May be null if the track was not loaded from the database.
*/
public @Nullable
Id getId() {
return id;
}
public void setId(long id) {
public void setId(Id id) {
this.id = id;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
@@ -85,4 +109,66 @@ public class Track {
public void setTrackStatistics(TrackStatistics trackStatistics) {
this.trackStatistics = trackStatistics;
}
public static class Id implements Parcelable {
private final long id;
public Id(long id) {
this.id = id;
}
public Id(@NonNull String id) {
this(Long.parseLong(id));
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() {
return id;
}
@Deprecated //TODO Use a Track.Id of null instead
public boolean isValid() {
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id1 = (Id) o;
return id == id1.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public int describeContents() {
return 0;
}
@NonNull
@Override
public String toString() {
throw new RuntimeException("Not supported");
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(id);
}
public static final Parcelable.Creator<Track.Id> CREATOR = new Parcelable.Creator<Track.Id>() {
public Track.Id createFromParcel(Parcel in) {
return new Track.Id(in.readLong());
}
public Track.Id[] newArray(int size) {
return new Track.Id[size];
}
};
}
}
@@ -33,7 +33,7 @@ public interface TrackPointsColumns extends BaseColumns {
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
String DEFAULT_SORT_ORDER = "_id";
String DEFAULT_SORT_ORDER = _ID;
// Columns
String TRACKID = "trackid";
@@ -32,9 +32,10 @@ public interface TracksColumns extends BaseColumns {
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.track";
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.track";
String DEFAULT_SORT_ORDER = "_id";
String DEFAULT_SORT_ORDER = _ID;
// Columns
String UUID = "uuid";
String NAME = "name"; // track name
String DESCRIPTION = "description"; // track description
String CATEGORY = "category"; // track activity type
@@ -71,5 +72,9 @@ public interface TracksColumns extends BaseColumns {
+ MINELEVATION + " FLOAT, " // min elevation
+ MAXELEVATION + " FLOAT, " // max elevation
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
+ ICON + " TEXT)"; // icon
+ ICON + " TEXT, " // icon
+ UUID + " BLOB)"; // UUID
String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")";
}
@@ -18,10 +18,17 @@ package de.dennisguse.opentracks.content.data;
import android.location.Location;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.util.Objects;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* A waypoint.
*
@@ -30,15 +37,16 @@ import androidx.annotation.VisibleForTesting;
*/
public final class Waypoint {
private long id = -1L;
private Id id;
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private long trackId = -1L;
private Track.Id trackId;
private double length = 0.0;
private long duration = 0;
private Location location;
@Deprecated //TODO Make an URI instead of String
private String photoUrl = "";
@VisibleForTesting
@@ -50,7 +58,7 @@ public final class Waypoint {
this.location = location;
}
public Waypoint(String name, String description, String category, String icon, long trackId, double length, long duration, @NonNull Location location, String photoUrl) {
public Waypoint(String name, String description, String category, String icon, @NonNull Track.Id trackId, double length, long duration, @NonNull Location location, String photoUrl) {
this.name = name;
this.description = description;
this.category = category;
@@ -62,11 +70,15 @@ public final class Waypoint {
this.photoUrl = photoUrl;
}
public long getId() {
/**
* May be null if the waypoint was not loaded from the database.
*/
public @Nullable
Id getId() {
return id;
}
public void setId(long id) {
public void setId(Id id) {
this.id = id;
}
@@ -102,11 +114,11 @@ public final class Waypoint {
this.icon = icon;
}
public long getTrackId() {
public Track.Id getTrackId() {
return trackId;
}
public void setTrackId(long trackId) {
public void setTrackId(Track.Id trackId) {
this.trackId = trackId;
}
@@ -146,4 +158,66 @@ public final class Waypoint {
public boolean hasPhoto() {
return photoUrl != null && !"".equals(photoUrl);
}
public static class Id implements Parcelable {
private final long id;
public Id(long id) {
this.id = id;
}
public Id(@NonNull String id) {
this(Long.parseLong(id));
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() {
return id;
}
@Deprecated //TODO Use a Waypoint.Id of null instead
public boolean isValid() {
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id1 = (Id) o;
return id == id1.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@NonNull
@Override
public String toString() {
throw new RuntimeException("Not supported");
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(id);
}
public static final Parcelable.Creator<Id> CREATOR = new Parcelable.Creator<Id>() {
public Id createFromParcel(Parcel in) {
return new Id(in.readLong());
}
public Id[] newArray(int size) {
return new Id[size];
}
};
}
}
@@ -30,9 +30,10 @@ public interface WaypointsColumns extends BaseColumns {
String TABLE_NAME = "waypoints";
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
String DEFAULT_SORT_ORDER = "_id";
String DEFAULT_SORT_ORDER = _ID;
// Columns
String NAME = "name"; // waypoint name
@@ -23,13 +23,19 @@ import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.android.ContentResolverWrapper;
@@ -42,6 +48,8 @@ import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.TrackUtils;
import de.dennisguse.opentracks.util.UUIDUtils;
/**
* {@link ContentProviderUtils} implementation.
@@ -55,7 +63,8 @@ public class ContentProviderUtils {
private static final int MAX_LATITUDE = 90000000;
// The authority (the first part of the URI) for the app's content provider.
static final String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
@VisibleForTesting
public static final String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
// The base URI for the app's content provider.
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
@@ -85,8 +94,8 @@ public class ContentProviderUtils {
*
* @param trackId the track id
*/
public void clearTrack(Context context, long trackId) {
deleteTrackPointsAndWaypoints(context, trackId);
public void clearTrack(Track.Id trackId) {
deleteTrackPointsAndWaypoints(trackId);
Track track = new Track();
track.setId(trackId);
updateTrack(track);
@@ -99,6 +108,7 @@ public class ContentProviderUtils {
*/
public Track createTrack(Cursor cursor) {
int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
@@ -116,7 +126,10 @@ public class ContentProviderUtils {
Track track = new Track();
TrackStatistics trackStatistics = track.getTrackStatistics();
if (!cursor.isNull(idIndex)) {
track.setId(cursor.getLong(idIndex));
track.setId(new Track.Id(cursor.getLong(idIndex)));
}
if (!cursor.isNull(uuidIndex)) {
track.setUuid(UUIDUtils.fromBytes(cursor.getBlob(uuidIndex)));
}
if (!cursor.isNull(nameIndex)) {
track.setName(cursor.getString(nameIndex));
@@ -160,9 +173,7 @@ public class ContentProviderUtils {
return track;
}
/**
* Deletes all tracks (including waypoints and trackPoints).
*/
@VisibleForTesting
public void deleteAllTracks(Context context) {
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null);
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
@@ -178,14 +189,17 @@ public class ContentProviderUtils {
*
* @param trackId the track id
*/
public void deleteTrack(Context context, long trackId) {
deleteTrackPointsAndWaypoints(context, trackId);
public void deleteTrack(Context context, Track.Id trackId) {
if (trackId == null) {
return;
}
deleteTrackPointsAndWaypoints(trackId);
// Delete track folder resources.
FileUtils.deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId));
// Delete track last since it triggers a database vacuum call
contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", new String[]{Long.toString(trackId)});
contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", new String[]{Long.toString(trackId.getId())});
}
/**
@@ -193,21 +207,15 @@ public class ContentProviderUtils {
*
* @param trackId the track id
*/
private void deleteTrackPointsAndWaypoints(Context context, long trackId) {
String where = TrackPointsColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId)};
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, where, selectionArgs);
private void deleteTrackPointsAndWaypoints(@NonNull Track.Id trackId) {
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", new String[]{Long.toString(trackId)});
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs);
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", selectionArgs);
}
/**
* Gets all the tracks.
*
* @return the tracks do not have any trackPoints attached.
*/
@VisibleForTesting
public List<Track> getAllTracks() {
public List<Track> getTracks() {
ArrayList<Track> tracks = new ArrayList<>();
try (Cursor cursor = getTrackCursor(null, null, TracksColumns._ID)) {
if (cursor != null && cursor.moveToFirst()) {
@@ -220,6 +228,17 @@ public class ContentProviderUtils {
return tracks;
}
public static Loader<Cursor> getTracksCursorLoader(Context context) {
String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON};
return new CursorLoader(context, TracksColumns.CONTENT_URI, PROJECTION, null, null, TrackUtils.TRACK_SORT_ORDER);
}
@Deprecated
//TODO Do not use; rather get the proper TrackId (e.g., resume a track might give you some weird effects).
@VisibleForTesting
public Track getLastTrack() {
try (Cursor cursor = getTrackCursor(null, null, TracksColumns.STARTTIME + " DESC")) {
// Using the same order as shown in the track list
@@ -232,13 +251,25 @@ public class ContentProviderUtils {
/**
* @param trackId the track id.
* @return the track doesn't have any trackPoints attached
*/
public Track getTrack(long trackId) {
if (trackId < 0) {
public Track getTrack(Track.Id trackId) {
if (trackId == null || !trackId.isValid()) {
return null;
}
try (Cursor cursor = getTrackCursor(TracksColumns._ID + "=?", new String[]{Long.toString(trackId)}, TracksColumns._ID)) {
try (Cursor cursor = getTrackCursor(TracksColumns._ID + "=?", new String[]{Long.toString(trackId.getId())}, null)) {
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
}
return null;
}
/**
* @param trackUUID the track uuid.
*/
public Track getTrack(@NonNull UUID trackUUID) {
String trackUUIDsearch = UUIDUtils.toHex(trackUUID);
try (Cursor cursor = getTrackCursor("hex(" + TracksColumns.UUID + ")=?", new String[]{trackUUIDsearch}, null)) {
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
@@ -276,7 +307,7 @@ public class ContentProviderUtils {
* @param track the track
*/
public void updateTrack(Track track) {
contentResolver.update(TracksColumns.CONTENT_URI, createContentValues(track), TracksColumns._ID + "=?", new String[]{Long.toString(track.getId())});
contentResolver.update(TracksColumns.CONTENT_URI, createContentValues(track), TracksColumns._ID + "=?", new String[]{Long.toString(track.getId().getId())});
}
private ContentValues createContentValues(Track track) {
@@ -284,9 +315,10 @@ public class ContentProviderUtils {
TrackStatistics trackStatistics = track.getTrackStatistics();
// Value < 0 indicates no id is available
if (track.getId() >= 0) {
values.put(TracksColumns._ID, track.getId());
if (track.getId() != null && track.getId().isValid()) {
values.put(TracksColumns._ID, track.getId().getId());
}
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
values.put(TracksColumns.NAME, track.getName());
values.put(TracksColumns.DESCRIPTION, track.getDescription());
values.put(TracksColumns.CATEGORY, track.getCategory());
@@ -350,7 +382,7 @@ public class ContentProviderUtils {
Waypoint waypoint = new Waypoint(location);
if (!cursor.isNull(idIndex)) {
waypoint.setId(cursor.getLong(idIndex));
waypoint.setId(new Waypoint.Id(cursor.getLong(idIndex)));
}
if (!cursor.isNull(nameIndex)) {
waypoint.setName(cursor.getString(nameIndex));
@@ -365,7 +397,7 @@ public class ContentProviderUtils {
waypoint.setIcon(cursor.getString(iconIndex));
}
if (!cursor.isNull(trackIdIndex)) {
waypoint.setTrackId(cursor.getLong(trackIdIndex));
waypoint.setTrackId(new Track.Id(cursor.getLong(trackIdIndex)));
}
if (!cursor.isNull(lengthIndex)) {
waypoint.setLength(cursor.getFloat(lengthIndex));
@@ -380,10 +412,10 @@ public class ContentProviderUtils {
return waypoint;
}
public void deleteWaypoint(Context context, long waypointId) {
public void deleteWaypoint(Context context, Waypoint.Id waypointId) {
final Waypoint waypoint = getWaypoint(waypointId);
deleteWaypointPhoto(context, waypoint);
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId)});
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId.getId())});
}
/**
@@ -392,13 +424,13 @@ public class ContentProviderUtils {
* @param trackId the track id
* @return -1 if not able to get the next waypoint number.
*/
public int getNextWaypointNumber(long trackId) {
if (trackId < 0) {
public int getNextWaypointNumber(Track.Id trackId) {
if (!trackId.isValid()) {
return -1;
}
String[] projection = {WaypointsColumns._ID};
String selection = WaypointsColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId)};
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1)) {
if (cursor != null) {
return cursor.getCount();
@@ -407,12 +439,12 @@ public class ContentProviderUtils {
return -1;
}
public Waypoint getWaypoint(long waypointId) {
if (waypointId < 0) {
public Waypoint getWaypoint(Waypoint.Id waypointId) {
if (!waypointId.isValid()) {
return null;
}
try (Cursor cursor = getWaypointCursor(null, WaypointsColumns._ID + "=?",
new String[]{Long.toString(waypointId)}, WaypointsColumns._ID, 1)) {
new String[]{Long.toString(waypointId.getId())}, WaypointsColumns._ID, 1)) {
if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor);
}
@@ -438,30 +470,30 @@ public class ContentProviderUtils {
* The caller owns the returned cursor and is responsible for closing it.
*
* @param trackId the track id
* @param minWaypointId the minimum waypoint id. -1L to ignore
* @param minWaypointId the minimum waypoint id. null to ignore
* @param maxWaypoints the maximum number of waypoints to return. -1 for no limit
*/
public Cursor getWaypointCursor(long trackId, long minWaypointId, int maxWaypoints) {
if (trackId < 0) {
public Cursor getWaypointCursor(Track.Id trackId, @Nullable Waypoint.Id minWaypointId, int maxWaypoints) {
if (!trackId.isValid()) {
return null;
}
String selection;
String[] selectionArgs;
if (minWaypointId >= 0) {
if (minWaypointId != null && minWaypointId.isValid()) {
selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + ">=?";
selectionArgs = new String[]{Long.toString(trackId), Long.toString(minWaypointId)};
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minWaypointId.getId())};
} else {
selection = WaypointsColumns.TRACKID + "=?";
selectionArgs = new String[]{Long.toString(trackId)};
selectionArgs = new String[]{Long.toString(trackId.getId())};
}
return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints);
}
@VisibleForTesting
public List<Waypoint> getWaypoints(long trackId) {
public List<Waypoint> getWaypoints(Track.Id trackId) {
ArrayList<Waypoint> waypoints = new ArrayList<>();
try (Cursor cursor = getWaypointCursor(trackId, -1L, -1)) {
try (Cursor cursor = getWaypointCursor(trackId, null, -1)) {
if (cursor.moveToFirst()) {
do {
waypoints.add(createWaypoint(cursor));
@@ -471,19 +503,34 @@ public class ContentProviderUtils {
return waypoints;
}
public static Loader<Cursor> getWaypointsLoader(Context context, @Nullable Track.Id trackId) {
final String[] PROJECTION = new String[]{WaypointsColumns._ID,
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY,
WaypointsColumns.TIME, WaypointsColumns.PHOTOURL,
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE};
if (trackId != null) {
return new CursorLoader(context, WaypointsColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.TRACKID + "=?",
new String[]{String.valueOf(trackId.getId())}, null);
} else {
return new CursorLoader(context, WaypointsColumns.CONTENT_URI, PROJECTION, null, null, null);
}
}
/**
* Gets the number of waypoints for a track.
*
* @param trackId the track id
*/
public int getWaypointCount(long trackId) {
if (trackId < 0) {
public int getWaypointCount(Track.Id trackId) {
if (!trackId.isValid()) {
return 0;
}
String[] projection = new String[]{"count(*) AS count"};
String selection = WaypointsColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId)};
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, WaypointsColumns._ID)) {
if (cursor == null) {
return 0;
@@ -499,8 +546,8 @@ public class ContentProviderUtils {
* @param waypoint the waypoint
* @return the content provider URI of the inserted waypoint.
*/
public Uri insertWaypoint(Waypoint waypoint) {
waypoint.setId(-1L);
public Uri insertWaypoint(@NonNull Waypoint waypoint) {
waypoint.setId(null);
return contentResolver.insert(WaypointsColumns.CONTENT_URI, createContentValues(waypoint));
}
@@ -535,22 +582,23 @@ public class ContentProviderUtils {
if (!updatedWaypoint.hasPhoto()) {
deleteWaypointPhoto(context, savedWaypoint);
}
int rows = contentResolver.update(WaypointsColumns.CONTENT_URI, createContentValues(updatedWaypoint), WaypointsColumns._ID + "=?", new String[]{Long.toString(updatedWaypoint.getId())});
int rows = contentResolver.update(WaypointsColumns.CONTENT_URI, createContentValues(updatedWaypoint), WaypointsColumns._ID + "=?", new String[]{Long.toString(updatedWaypoint.getId().getId())});
return rows == 1;
}
ContentValues createContentValues(Waypoint waypoint) {
ContentValues createContentValues(@NonNull Waypoint waypoint) {
ContentValues values = new ContentValues();
// Value < 0 indicates no id is available
if (waypoint.getId() >= 0) {
values.put(WaypointsColumns._ID, waypoint.getId());
if (waypoint.getId() != null && waypoint.getId().isValid()) {
values.put(WaypointsColumns._ID, waypoint.getId().getId());
}
values.put(WaypointsColumns.NAME, waypoint.getName());
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription());
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
values.put(WaypointsColumns.ICON, waypoint.getIcon());
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId());
if (waypoint.getTrackId() != null) {
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId());
}
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
@@ -643,7 +691,7 @@ public class ContentProviderUtils {
* @return the number of trackPoints inserted
*/
//TODO Only used for testing and file import; might be better to replace it.
public int bulkInsertTrackPoint(TrackPoint[] trackPoints, long trackId) {
public int bulkInsertTrackPoint(TrackPoint[] trackPoints, Track.Id trackId) {
ContentValues[] values = new ContentValues[trackPoints.length];
for (int i = 0; i < values.length; i++) {
values[i] = createContentValues(trackPoints[i], trackId);
@@ -653,23 +701,23 @@ public class ContentProviderUtils {
/**
* Gets the first location id for a track.
* Returns -1L if it doesn't exist.
* Returns null if it doesn't exist.
*
* @param trackId the track id
*/
@Deprecated
public long getFirstTrackPointId(long trackId) {
if (trackId < 0) {
return -1L;
public Track.Id getFirstTrackPointId(Track.Id trackId) {
if (!trackId.isValid()) {
return null;
}
String selection = TrackPointsColumns._ID + "=(SELECT MIN(" + TrackPointsColumns._ID + ") FROM " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=?)";
String[] selectionArgs = new String[]{Long.toString(trackId)};
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection, selectionArgs, TrackPointsColumns._ID)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
return new Track.Id(cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID)));
}
}
return -1L;
return null;
}
/**
@@ -679,12 +727,13 @@ public class ContentProviderUtils {
* @param trackId the track id
*/
@Deprecated
public long getLastTrackPointId(long trackId) {
if (trackId < 0) {
public long getLastTrackPointId(Track.Id trackId) {
if (trackId == null || !trackId.isValid()) {
Log.w(TAG, "Fix callers who do this.");
return -1L;
}
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=?)";
String[] selectionArgs = new String[]{Long.toString(trackId)};
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection, selectionArgs, TrackPointsColumns._ID)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
@@ -700,12 +749,12 @@ public class ContentProviderUtils {
* @param location the location
* @return trackPoint id if the location is in the track. -1L otherwise.
*/
public long getTrackPointId(long trackId, Location location) {
if (trackId < 0) {
public long getTrackPointId(Track.Id trackId, Location location) {
if (!trackId.isValid()) {
return -1L;
}
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") FROM " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.TIME + "=?)";
String[] selectionArgs = new String[]{Long.toString(trackId), Long.toString(location.getTime())};
String[] selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(location.getTime())};
try (Cursor cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection, selectionArgs, TrackPointsColumns._ID)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
@@ -731,8 +780,8 @@ public class ContentProviderUtils {
* @param maxLocations maximum number of locations to return. -1 for no limit
* @param descending true to sort the result in descending order (latest location first)
*/
public Cursor getTrackPointCursor(long trackId, long startTrackPointId, int maxLocations, boolean descending) {
if (trackId < 0) {
public Cursor getTrackPointCursor(Track.Id trackId, long startTrackPointId, int maxLocations, boolean descending) {
if (!trackId.isValid()) {
return null;
}
@@ -741,10 +790,10 @@ public class ContentProviderUtils {
if (startTrackPointId >= 0) {
String comparison = descending ? "<=" : ">=";
selection = TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns._ID + comparison + "?";
selectionArgs = new String[]{Long.toString(trackId), Long.toString(startTrackPointId)};
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(startTrackPointId)};
} else {
selection = TrackPointsColumns.TRACKID + "=?";
selectionArgs = new String[]{Long.toString(trackId)};
selectionArgs = new String[]{Long.toString(trackId.getId())};
}
String sortOrder = TrackPointsColumns._ID;
@@ -764,12 +813,12 @@ public class ContentProviderUtils {
* @param trackId the track id
*/
@Deprecated
public TrackPoint getLastValidTrackPoint(long trackId) {
if (trackId < 0) {
public TrackPoint getLastValidTrackPoint(Track.Id trackId) {
if (!trackId.isValid()) {
return null;
}
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") FROM " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")";
String[] selectionArgs = new String[]{Long.toString(trackId)};
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
return findTrackPointBy(selection, selectionArgs);
}
@@ -780,7 +829,7 @@ public class ContentProviderUtils {
* @param trackId the track id
* @return the content provider URI of the inserted trackPoint
*/
public Uri insertTrackPoint(TrackPoint trackPoint, long trackId) {
public Uri insertTrackPoint(TrackPoint trackPoint, Track.Id trackId) {
return contentResolver.insert(TrackPointsColumns.CONTENT_URI_BY_ID, createContentValues(trackPoint, trackId));
}
@@ -790,9 +839,9 @@ public class ContentProviderUtils {
* @param trackPoint the trackPoint
* @param trackId the track id
*/
private ContentValues createContentValues(TrackPoint trackPoint, long trackId) {
private ContentValues createContentValues(TrackPoint trackPoint, Track.Id trackId) {
ContentValues values = new ContentValues();
values.put(TrackPointsColumns.TRACKID, trackId);
values.put(TrackPointsColumns.TRACKID, trackId.getId());
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getLongitude() * 1E6));
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getLatitude() * 1E6));
@@ -833,7 +882,7 @@ public class ContentProviderUtils {
* @param startTrackPointId the starting trackPoint id. -1L to ignore
* @param descending true to sort the result in descending order (latest location first)
*/
public TrackPointIterator getTrackPointLocationIterator(final long trackId, final long startTrackPointId, final boolean descending) {
public TrackPointIterator getTrackPointLocationIterator(final Track.Id trackId, final long startTrackPointId, final boolean descending) {
return new TrackPointIterator(this, trackId, startTrackPointId, descending);
}
@@ -859,7 +908,7 @@ public class ContentProviderUtils {
}
@VisibleForTesting
public List<TrackPoint> getTrackPoints(long trackId) {
public List<TrackPoint> getTrackPoints(Track.Id trackId) {
List<TrackPoint> trackPoints = null;
try (Cursor trackPointCursor = getTrackPointCursor(trackId, -1L, -1, false)) {
@@ -890,13 +939,23 @@ public class ContentProviderUtils {
this.defaultCursorBatchSize = defaultCursorBatchSize;
}
public static String formatIdListForUri(Track.Id... trackIds) {
long[] ids = new long[trackIds.length];
for (int i = 0; i < trackIds.length; i++) {
ids[i] = trackIds[i].getId();
}
return formatIdListForUri(ids);
}
/**
* Formats an array of IDs as comma separated string value
*
* @param ids array with IDs
* @return comma separated list of ids
*/
public static String formatIdListForUri(long[] ids) {
private static String formatIdListForUri(long[] ids) {
StringBuilder idsPathSegment = new StringBuilder();
for (long id : ids) {
if (idsPathSegment.length() > 0) {
@@ -42,7 +42,7 @@ import de.dennisguse.opentracks.content.data.WaypointsColumns;
*
* @author Leif Hendrik Wilden
*/
public abstract class CustomContentProvider extends ContentProvider {
public class CustomContentProvider extends ContentProvider {
private static final String TAG = CustomContentProvider.class.getSimpleName();
@@ -63,6 +63,7 @@ public abstract class CustomContentProvider extends ContentProvider {
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath(), UrlType.WAYPOINTS.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath() + "/#", UrlType.WAYPOINTS_BY_ID.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.WAYPOINTS_BY_TRACKID.ordinal());
}
@Override
@@ -140,6 +141,7 @@ public abstract class CustomContentProvider extends ContentProvider {
case WAYPOINTS:
return WaypointsColumns.CONTENT_TYPE;
case WAYPOINTS_BY_ID:
case WAYPOINTS_BY_TRACKID:
return WaypointsColumns.CONTENT_ITEMTYPE;
default:
throw new IllegalArgumentException("Unknown URL " + url);
@@ -219,6 +221,10 @@ public abstract class CustomContentProvider extends ContentProvider {
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
queryBuilder.appendWhere(WaypointsColumns._ID + "=" + ContentUris.parseId(url));
break;
case WAYPOINTS_BY_TRACKID:
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
queryBuilder.appendWhere(WaypointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
break;
default:
throw new IllegalArgumentException("Unknown url " + url);
}
@@ -382,6 +388,7 @@ public abstract class CustomContentProvider extends ContentProvider {
TRACKS,
TRACKS_BY_ID,
WAYPOINTS,
WAYPOINTS_BY_ID
WAYPOINTS_BY_ID,
WAYPOINTS_BY_TRACKID
}
}
@@ -1,29 +1,34 @@
package de.dennisguse.opentracks.content.provider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.util.UUID;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.util.UUIDUtils;
/**
* Database helper for creating and upgrading the database.
*/
@VisibleForTesting
class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
@VisibleForTesting
private static final int DATABASE_VERSION = 25;
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
@VisibleForTesting
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 26;
@VisibleForTesting
public static final String DATABASE_NAME = "database.db";
public CustomSQLiteOpenHelper(Context context) {
this(context, DATABASE_NAME);
@@ -45,6 +50,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.execSQL(TrackPointsColumns.CREATE_TABLE_INDEX);
db.execSQL(TracksColumns.CREATE_TABLE);
db.execSQL(TracksColumns.CREATE_TABLE_INDEX);
db.execSQL(WaypointsColumns.CREATE_TABLE);
db.execSQL(WaypointsColumns.CREATE_TABLE_INDEX);
@@ -61,6 +67,9 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 25:
upgradeFrom24to25(db);
break;
case 26:
upgradeFrom25to26(db);
break;
default:
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
@@ -79,6 +88,9 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 24:
downgradeFrom25to24(db);
break;
case 25:
downgradeFrom26to25(db);
break;
default:
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
@@ -107,7 +119,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
}
private void downgradeFrom24to23(SQLiteDatabase db) {
//Not needed as the delete columns did not contain any data
//Not needed as the deleted columns did not contain any data
db.beginTransaction();
db.execSQL("ALTER TABLE tracks RENAME TO tracks_old");
db.execSQL("CREATE TABLE tracks (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, starttime INTEGER, stoptime INTEGER, numpoints INTEGER, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, icon TEXT)");
@@ -142,4 +154,40 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.setTransactionSuccessful();
db.endTransaction();
}
private void upgradeFrom25to26(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("ALTER TABLE tracks ADD COLUMN uuid BLOB");
try (Cursor cursor = db.query("tracks", new String[]{"_id"}, null, null, null, null, null)) {
if (cursor.moveToFirst()) {
int trackIdIndex = cursor.getColumnIndexOrThrow("_id");
do {
Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex));
ContentValues cv = new ContentValues();
cv.put("uuid", UUIDUtils.toBytes(UUID.randomUUID()));
db.update("tracks", cv, "_id = ?", new String[]{String.valueOf(trackId)});
} while (cursor.moveToNext());
}
}
db.execSQL("CREATE UNIQUE INDEX tracks_uuid_index ON tracks(uuid)");
db.setTransactionSuccessful();
db.endTransaction();
}
private void downgradeFrom26to25(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("DROP INDEX tracks_uuid_index");
db.execSQL("ALTER TABLE tracks RENAME TO tracks_old");
db.execSQL("CREATE TABLE tracks (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, starttime INTEGER, stoptime INTEGER, numpoints INTEGER, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, icon TEXT)");
db.execSQL("INSERT INTO tracks SELECT _id, name, description, category, starttime, stoptime, numpoints, totaldistance, totaltime, movingtime, avgspeed, avgmovingspeed, maxspeed, minelevation, maxelevation, elevationgain, 0, 0, icon FROM tracks_old");
db.execSQL("DROP TABLE tracks_old");
db.setTransactionSuccessful();
db.endTransaction();
}
}
@@ -6,7 +6,6 @@ import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.util.Log;
@@ -18,8 +17,11 @@ import androidx.annotation.Nullable;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import de.dennisguse.opentracks.android.IContentResolver;
import de.dennisguse.opentracks.content.data.Track;
@@ -30,9 +32,9 @@ import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
/**
* A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) KML-files.
* The actual content of the virtual files is generated by using the functionality defined in {@link CustomContentProvider}.
*
* <p>
* Moreover, it manages access to OpenTrack's database via {@link CustomContentProvider}.
*
* <p>
* Explanation:
* Although a request is handled by a {@link android.content.ContentProvider} (with temporarily granted permission), Android's security infrastructure prevents forwarding queries to non-exported {@link android.content.ContentProvider}.
* Thus, if {@link ShareContentProvider} and {@link CustomContentProvider} would be two different instances, the data would not be accessible to external apps.
@@ -71,14 +73,20 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getName() + "/*/*", URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES);
}
public static Pair<Uri, String> createURI(long[] trackIds, String trackName, @NonNull TrackFileFormat trackFileFormat) {
if (trackIds.length == 0) {
public static Pair<Uri, String> createURI(Track.Id trackId, String trackName, @NonNull TrackFileFormat trackFileFormat) {
Set<Track.Id> trackIds = new HashSet<>(1);
trackIds.add(trackId);
return createURI(trackIds, trackName, trackFileFormat);
}
public static Pair<Uri, String> createURI(Set<Track.Id> trackIds, String trackName, @NonNull TrackFileFormat trackFileFormat) {
if (trackIds.size() == 0) {
throw new UnsupportedOperationException();
}
StringBuilder trackIdBuilder = new StringBuilder();
for (long trackId : trackIds) {
trackIdBuilder.append(trackId).append(TRACKID_DELIMITER);
for (Track.Id trackId : trackIds) {
trackIdBuilder.append(trackId.getId()).append(TRACKID_DELIMITER);
}
trackIdBuilder.deleteCharAt(trackIdBuilder.lastIndexOf(TRACKID_DELIMITER));
@@ -90,18 +98,18 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
return new Pair<>(uri, mime);
}
static long[] parseURI(Uri uri) {
static Set<Track.Id> parseURI(Uri uri) {
List<String> uriPaths = uri.getPathSegments();
if (uriPaths == null || uriPaths.size() < 3) {
Log.d(TAG, "URI does not contain any trackIds.");
return new long[]{};
return new HashSet<>();
}
String[] uriTrackIds = uriPaths.get(2).split(TRACKID_DELIMITER);
long[] trackIds = new long[uriTrackIds.length];
for (int i = 0; i < trackIds.length; i++) {
trackIds[i] = Long.parseLong(uriTrackIds[i]);
Set<Track.Id> trackIds = new HashSet<>();
for (String uriTrackId : uriTrackIds) {
trackIds.add(new Track.Id(Long.parseLong(uriTrackId)));
}
return trackIds;
}
@@ -203,22 +211,19 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
long[] trackIds = parseURI(uri);
final Track[] tracks = new Track[trackIds.length];
for (int i = 0; i < trackIds.length; i++) {
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
Set<Track.Id> trackIds = parseURI(uri);
final ArrayList<Track> tracks = new ArrayList<>();
for (Track.Id trackId : trackIds) {
tracks.add(contentProviderUtils.getTrack(trackId));
}
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext(), tracks);
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext());
PipeDataWriter pipeDataWriter = new PipeDataWriter<String>() {
@Override
public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable String args) {
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
trackExporter.writeTrack(getContext(), fileOutputStream);
} catch (IOException e) {
Log.w(TAG, "there occurred an error while sharing a file: " + e);
}
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
} catch (IOException e) {
Log.w(TAG, "there occurred an error while sharing a file: " + e);
}
};
@@ -6,6 +6,7 @@ import android.util.Log;
import java.util.Iterator;
import java.util.NoSuchElementException;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
/**
@@ -16,14 +17,14 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
private static final String TAG = TrackPointIterator.class.getSimpleName();
private final ContentProviderUtils contentProviderUtils;
private final long trackId;
private final Track.Id trackId;
private final boolean descending;
private final CachedTrackPointsIndexes indexes;
private long lastTrackPointId = -1L;
private Cursor cursor;
public TrackPointIterator(ContentProviderUtils contentProviderUtils, long trackId, long startTrackPointId, boolean descending) {
public TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, long startTrackPointId, boolean descending) {
this.contentProviderUtils = contentProviderUtils;
this.trackId = trackId;
this.descending = descending;
@@ -100,13 +101,4 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
public void remove() {
throw new UnsupportedOperationException();
}
public int getCount() {
if (cursor == null) {
return -1;
}
return cursor.getCount();
}
}
@@ -6,10 +6,10 @@ import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
public class SensorData {
private String sensorAddress;
private String sensorName;
private final String sensorAddress;
private final String sensorName;
private long timestamp_ms;
private final long timestamp_ms;
SensorData(String sensorAddress, String sensorName) {
this(sensorAddress, sensorName, System.currentTimeMillis());
@@ -25,8 +25,8 @@ public final class SensorDataCycling {
public static class Cadence extends SensorData {
private long crankRevolutionsCount; // UINT32
private int crankRevolutionsTime; // UINT16; 1/1024s
private final long crankRevolutionsCount; // UINT32
private final int crankRevolutionsTime; // UINT16; 1/1024s
private float cadence_rpm = INVALID_VALUE_FLOAT;
public Cadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) {
@@ -93,8 +93,8 @@ public final class SensorDataCycling {
public static class Speed extends SensorData {
private int wheelRevolutionsCount; // UINT16
private int wheelRevolutionsTime; // UINT16; 1/1024s
private final int wheelRevolutionsCount; // UINT16
private final int wheelRevolutionsTime; // UINT16; 1/1024s
private float speed_mps = INVALID_VALUE_FLOAT;
public Speed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
@@ -154,8 +154,8 @@ public final class SensorDataCycling {
public static class CadenceAndSpeed extends SensorData {
private Cadence cadence;
private Speed speed;
private final Cadence cadence;
private final Speed speed;
public CadenceAndSpeed(String sensorAddress, String sensorName, @NonNull Cadence cadence, @NonNull Speed speed) {
super(sensorAddress, sensorName);
@@ -4,7 +4,7 @@ import androidx.annotation.NonNull;
public class SensorDataHeartRate extends SensorData {
private float heartRate_bpm;
private final float heartRate_bpm;
public SensorDataHeartRate(String name, String address, float heartRate_bpm) {
super(name, address);
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.fragments;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
@@ -26,6 +25,7 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.util.DialogUtils;
/**
@@ -43,11 +43,11 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
/**
* Create a new instance.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
* @param trackIds list of track ids to delete.
*/
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
public static void showDialog(FragmentManager fragmentManager, Track.Id... trackIds) {
Bundle bundle = new Bundle();
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
bundle.putParcelableArray(KEY_TRACK_IDS, trackIds);
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
deleteTrackDialogFragment.setArguments(bundle);
@@ -67,19 +67,16 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
final Track.Id[] trackIds = (Track.Id[]) getArguments().getParcelableArray(KEY_TRACK_IDS);
int titleId;
int messageId;
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message : R.string.track_delete_one_confirm_message;
int titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
int messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message : R.string.track_delete_one_confirm_message;
return DialogUtils.createConfirmationDialog(
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onConfirmDeleteDone(trackIds);
}
});
getActivity(),
titleId,
getString(messageId),
(dialog, which) -> caller.onConfirmDeleteDone(trackIds)
);
}
/**
@@ -89,12 +86,6 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
*/
public interface ConfirmDeleteCaller {
/**
* Called when confirm delete is done.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1
* with trackIds[0] == -1L
*/
void onConfirmDeleteDone(long[] trackIds);
void onConfirmDeleteDone(Track.Id... trackIds);
}
}
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.fragments;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
@@ -27,6 +26,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.DialogUtils;
@@ -42,9 +42,9 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
private DeleteMarkerCaller caller;
public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
public static void showDialog(FragmentManager fragmentManager, Waypoint.Id... waypointIds) {
Bundle bundle = new Bundle();
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
bundle.putParcelableArray(KEY_MARKER_IDS, waypointIds);
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
deleteMarkerDialogFragment.setArguments(bundle);
@@ -64,31 +64,26 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
final Waypoint.Id[] waypointIds = (Waypoint.Id[]) getArguments().getParcelableArray(KEY_MARKER_IDS);
final FragmentActivity fragmentActivity = getActivity();
int titleId;
int messageId;
if (markerIds.length == 1 && markerIds[0] == -1L) {
if (waypointIds == null) {
titleId = R.string.generic_delete_all_confirm_title;
messageId = R.string.marker_delete_all_confirm_message;
} else {
titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title;
messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message;
titleId = waypointIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title;
messageId = waypointIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message;
}
return DialogUtils.createConfirmationDialog(
fragmentActivity, titleId, getString(messageId), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
for (long markerId : markerIds) {
contentProviderUtils.deleteWaypoint(getContext(), markerId);
}
caller.onDeleteMarkerDone();
}).start();
fragmentActivity, titleId, getString(messageId), (dialog, which) -> new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
for (Waypoint.Id waypointId : waypointIds) {
contentProviderUtils.deleteWaypoint(getContext(), waypointId);
}
});
caller.onDeleteMarkerDone();
}).start());
}
/**

Some files were not shown because too many files have changed in this diff Show More