Compare commits

...

275 Commits

Author SHA1 Message Date
Dennis Guse b3117e6410 Release: v3.11.0 2020-09-26 20:23:32 +02:00
Dennis Guse 47ab109c2b F-Droid now signs APKs with v2, so installing on Android 11 works.
More info: #421
This reverts commit ee22fd8e
2020-09-26 19:56:22 +02:00
Dennis Guse bc04efbaac Bugfix: for export all write permissions are required.
Android 11 enforces this (and it is good practice).
Fixes #439.
2020-09-26 19:52:48 +02:00
Román fbb9d21e73 TrackRecordingActivity keeps Track.Id value instead of track object. Every time the track is needed it gets it. Fixes #435. 2020-09-26 16:47:51 +02:00
dependabot[bot] f5308e4287 Bump mockito-android from 3.5.11 to 3.5.13
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.11 to 3.5.13.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.11...v3.5.13)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-25 18:20:32 +02:00
Dennis Guse 15b5920e3b README: add F-Droid version shield 2020-09-25 12:37:58 +02:00
Dennis Guse d09aaab398 Theming: import/export finished dialog needs to be AndroidX. 2020-09-24 22:45:20 +02:00
pappapisshu f7008e2362 Update strings.xml
Fixed italian translation.
2020-09-24 17:16:54 +02:00
Dennis Guse e939778beb Refactor: use marker instead of waypoint.
Fixes #389.
2020-09-23 17:16:34 +02:00
Dennis Guse 8643b50bd1 Bugfix: application context does not provide access to theme attributes. 2020-09-23 12:12:34 +02:00
Dennis Guse 74e42525d4 Cleanup. 2020-09-23 08:11:10 +02:00
Dennis Guse 833a387574 Theming: splashscreen uses dark background color. 2020-09-23 08:05:40 +02:00
Dennis Guse e574c67ad6 Theming: activity icons are now in textColorPrimary.
And replaced spinner in StatisticsRecordedFragment.
2020-09-23 07:36:15 +02:00
Dennis Guse ce38d5e891 Theming: define colorAccent. 2020-09-23 07:36:15 +02:00
Dennis Guse 3a44da4a9d Theming: stats labels use color accent. 2020-09-23 07:36:15 +02:00
Dennis Guse eba9106db5 Theming: delete dialog needs to be Androidx for proper theming. 2020-09-23 07:36:15 +02:00
Dennis Guse 4501226562 Theming: reduce font size of lists first line. 2020-09-23 07:36:15 +02:00
Dennis Guse b974193564 Theming: delete dialog uses delete icon. 2020-09-23 07:36:15 +02:00
Dennis Guse 0b3e447258 Theming: charts use theme text colors. 2020-09-23 07:36:15 +02:00
Dennis Guse 9908cc02a2 Theming: added color.xml for night styling. 2020-09-23 07:36:15 +02:00
Dennis Guse 4e67011d7c Theming: style selected list items with transparent color. 2020-09-23 07:36:15 +02:00
Dennis Guse 5f93c0bab3 Theming: tabs use default indicator color. 2020-09-23 07:36:15 +02:00
Dennis Guse caec5e7c45 Theming: chart use default design background color. 2020-09-23 07:36:15 +02:00
Dennis Guse 240cb4b363 Theming: chart colors extracted. 2020-09-23 07:36:15 +02:00
Dennis Guse 91e8eb66b1 Theming: chart text is now rendered as FILL_AND_STROKE. 2020-09-23 07:36:15 +02:00
Dennis Guse 27bd69ec62 Code cleanup. 2020-09-23 07:36:15 +02:00
Dennis Guse 553fd39e21 Theming: make SplashScreen fullscreen. 2020-09-23 07:36:15 +02:00
Dennis Guse 733ee1c6e6 Theming: move TrackEdit to material design text fields. 2020-09-23 07:36:15 +02:00
Dennis Guse ef61c9597d Theming: activity chooser matches parent height. 2020-09-23 07:36:15 +02:00
Dennis Guse e874af791e Theming: removed all custom modifications to Holo theme. 2020-09-23 07:36:15 +02:00
Dennis Guse a4b2bb1aa0 Theming: tint all icons with colorControlNormal. 2020-09-23 07:36:15 +02:00
Dennis Guse bc9b20ffc8 Theming: marker detail uses default background. 2020-09-23 07:36:15 +02:00
Dennis Guse 6f212328a1 Theming: merged edit buttons into layout. 2020-09-23 07:36:15 +02:00
Dennis Guse a68cde7ac9 Theming: listView main item is now TextLarge. 2020-09-23 07:36:15 +02:00
Dennis Guse 5cab331058 Theming: removed custom spinner style. 2020-09-23 07:36:15 +02:00
Dennis Guse 6d25a10abd Theming: use default text color. 2020-09-23 07:36:15 +02:00
Dennis Guse d2516947f2 Theming: removed custom radio button style - use Material design. 2020-09-23 07:36:15 +02:00
Dennis Guse 4150d060ee Theming: enable day/night mode incl. app-wide setting. 2020-09-23 07:36:15 +02:00
Dennis Guse 1688076457 Theming: use AppCompat themes instead of Holo. 2020-09-23 07:36:15 +02:00
Román 5c1f669b2b Bugfix: spinner adapter needed notifyDataSetChanged to reload intervals with unit when user change from km to mi and the other way around. 2020-09-21 19:12:57 +02:00
Dennis Guse cc1e080ce1 Interval statistics: show unit in dropdown. 2020-09-20 19:05:32 +02:00
Dennis Guse 0dfd31a308 Code cleanup via Android Studio analyze.
Notably change: TrackPoint's copy constructor missed to copy the elevationGain.
2020-09-20 18:46:05 +02:00
dependabot[bot] bd13535d3c Bump mockito-android from 3.5.10 to 3.5.11
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.10 to 3.5.11.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.10...v3.5.11)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-18 08:07:00 +02:00
Dennis Guse 27386bb4f0 Localization: update via Transifex CLI. 2020-09-17 23:44:37 +02:00
Dennis Guse c6c07f03bf Localization: add configuration for Transifex CLI. 2020-09-17 23:43:28 +02:00
Dennis Guse b0ad08f7c5 Release: v3.10.0 2020-09-14 18:38:48 +02:00
Román d1957331b8 Intervals refactoring: added stack mode to the intervals adapter with two possibilities: stack from top and stack from bottom.
Stack from top for recorded intervals (the first lap in the top).
Stack from bottom for recording intervals (the last lap in the top).
2020-09-14 18:31:12 +02:00
Román ffedc04777 Intervals refactoring: avoid duplication code: IntervalsRecordingFragment is a version of IntervalsFragment. 2020-09-14 18:31:12 +02:00
Román 684762db58 Intervals refactoring: ListView instead of LinearLayout with a list of views. 2020-09-14 18:31:12 +02:00
thrillfall 5a44da3fb0 extract logic for creating DocumentFile for export to align naming 2020-09-14 17:25:29 +02:00
Dennis Guse 88cf46d999 Bugfix: another missed toString() in the id-refactoring. 2020-09-14 12:29:31 +02:00
Román 906b9413d1 Improved and simplified lap time/speed announcement message. 2020-09-13 19:30:17 +02:00
Dennis Guse 060c4f1529 Theming: add a SplashScreen for TrackListActivity. 2020-09-12 00:03:42 +02:00
Dennis Guse 7902aea849 Disable flacky test. 2020-09-12 00:01:56 +02:00
Sudeep Duggal ee22fd8ee8 Added workaround for allowing Android 11 install with only apk v1 signature 2020-09-10 08:28:55 +02:00
Román a6e275f8aa Several hidden bugfixes around Track.Id 2020-09-09 17:53:07 +02:00
Román a61468d600 Bugfix: String.valueOf doesn't work with Track.Id. Fixes #422. 2020-09-09 17:53:07 +02:00
thrillfall 78c26cb669 unify export filename logic 2020-09-09 17:50:00 +02:00
Dennis Guse 0d493c410e Merge branch 'barometer#10' into main 2020-09-06 22:34:54 +02:00
Dennis Guse 80dbbbc1ed Release: v3.9.5 2020-09-06 19:51:34 +02:00
Dennis Guse 81e8872a11 Added script for creating releases. 2020-09-06 19:49:42 +02:00
Román 6e1aba6fa0 Added barriers in stats recorded layout and stats recording layout. 2020-09-06 18:17:46 +02:00
Román 4220c62ac0 Bugfix: interval recording fragment has to keep select interval. 2020-09-06 12:21:39 +02:00
Dennis Guse 4b73567075 Bugfix: ClassCastException of Marker.Id 2020-09-06 08:06:29 +02:00
Dennis Guse fdff0d255a Autoformated drawables. 2020-09-06 00:09:39 +02:00
Dennis Guse e956cf14c4 Present speed only with one fractional digit. 2020-09-05 20:53:55 +02:00
Román a1646e1b76 Bugfix: bad equal comparison between Track.Id objects. 2020-09-05 15:46:49 +02:00
Dennis Guse 5099118dd7 Elevation gain: make it optional as Float - and if null, then the UI just shows "-".
In TrackStatistics the elevation gain is now nullable.
2020-09-05 12:39:23 +02:00
Dennis Guse 6845ce4984 Elevation gain: make it optional as Float.
So if no sensor data is provided, it is not stored in the database and not exported.
2020-09-05 12:39:23 +02:00
Dennis Guse e0d1ad4b52 Squash: fixed tests. 2020-09-05 12:39:23 +02:00
Dennis Guse 04b8d0f4a8 Import/Export: elevation gain per TrackPoint. 2020-09-05 12:39:23 +02:00
Román fe3bf61acb Squash: load TrackPoint elevationGain. 2020-09-05 12:39:23 +02:00
Román 577b9b4635 Alter table trackpoints: added elevation gain to the trackpoints table. 2020-09-05 12:39:23 +02:00
Dennis Guse 4b7de188fd Elevation gain adheres to visibility setting. 2020-09-05 12:39:23 +02:00
Román 3bfb212dc9 Bugfix: reset elevation every time track point is inserted. 2020-09-05 12:39:22 +02:00
Dennis Guse be7eb41cf3 Bugfix: previous total elevation gain was added twice. 2020-09-05 12:39:22 +02:00
Dennis Guse c7db4f528e Elevation gain: apply exponential smoothing and limit changes (defined change diff per sensor value). 2020-09-05 12:39:22 +02:00
Dennis Guse 8a14bc46f6 Implement elevation gain via barometer. 2020-09-05 12:39:22 +02:00
Dennis Guse 01ee1a1e49 Renamed DoubleBuffer to DoubleRingBuffer. 2020-09-05 12:39:22 +02:00
Dennis Guse 9d6c9676ab Refactoring to marker: content-package and fragments.
#389
2020-09-05 10:37:41 +02:00
Dennis Guse 19d8563a76 Refactoring to marker: TrackRecordingService.
#389
2020-09-05 10:20:16 +02:00
Dennis Guse 74e09e1fa9 Refactoring to marker: data class and ContentProviderUtils.
#389
2020-09-05 10:20:16 +02:00
Román 6533e3e170 Recording intervals received a null trackId because trackId is created in a Runnable.
Unit changed needs refreshing the intervals.
Fixes #406.
2020-09-05 10:18:01 +02:00
Dennis Guse bfeca771f8 Release v3.9.4 2020-09-05 01:41:01 +02:00
Dennis Guse f128111650 Code cleanup. 2020-09-05 01:19:59 +02:00
Dennis Guse 38fadda762 Bugfix: show on map via GPX/KML/KMZ was failing.
We were sending the trackId as intent extra (actually unused) and this trackId could not be unparceled.
Fixes #411.
2020-09-05 01:19:59 +02:00
Dennis Guse d6d40b01e6 Refactoring.
Also fixes #406.
2020-09-05 01:17:40 +02:00
dependabot[bot] bc051f5d81 Bump mockito-android from 3.5.9 to 3.5.10
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.9 to 3.5.10.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.9...v3.5.10)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 23:45:30 +02:00
dependabot[bot] 39bf2190a1 Bump material from 1.2.0 to 1.2.1
Bumps [material](https://github.com/material-components/material-components-android) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.2.0...1.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-03 16:55:31 +02:00
Román b88f81726b Bugfix: location handler didn't remove updates. Fixes #403. 2020-09-03 15:32:44 +02:00
Dennis Guse 24e6cdec62 Interval Stats: fixed NPE introduced in 25ef0ecad1.
Includes some refactoring.
2020-09-02 23:12:52 +02:00
Dennis Guse 951671b2f3 Update PULL_REQUEST_TEMPLATE.md 2020-09-02 22:20:50 +02:00
dependabot[bot] 21e30545a0 Bump mockito-android from 3.4.6 to 3.5.9
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.4.6 to 3.5.9.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.6...v3.5.9)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-02 19:26:45 +02:00
Dennis Guse 946edb7e16 Update CONTRIBUTING.md 2020-09-02 19:24:33 +02:00
Román 89c44af374 Recorded stats: natural order for intervals. 2020-09-02 14:32:32 +02:00
Dennis Guse faad451e22 Added contribution guidelines 2020-09-02 08:10:55 +02:00
Dennis Guse 25ef0ecad1 Interval Stats: use a tab for recorded tracks as well. 2020-09-02 07:52:33 +02:00
Román efa6caca66 New style for spinner with text. 2020-09-02 07:52:33 +02:00
Román 5be4c107f4 Announcement: while recording report current interval pace or speed.
Fixes #134.
2020-09-02 07:52:33 +02:00
Román 65f42dd3e5 Interval Stats: improve code and fixed some UI issues. 2020-09-02 07:52:33 +02:00
Román 287d246777 Interval Stats: track's id it's not long anymore. 2020-09-02 07:52:33 +02:00
Román d56f652d0a Interval stats: while user is recording a track intervals are refreshed every 5 seconds.
Interval stats: completed the list of split intervals: 1, 2, 3, 4, 5, 10, 20 or 50 km or mi.
Fixes #87.
2020-09-02 07:52:33 +02:00
Román 1f1ef4445f Interval stats: added IntervalStatisticsTest. 2020-09-02 07:52:33 +02:00
Román 22a669a573 Interval stats: added a new tab in TrackRecordingActivity that shows the intervals on live. 2020-09-02 07:52:33 +02:00
Román f55cce5614 Interval stats: improve code. 2020-09-02 07:52:33 +02:00
Román a6c3cf1c28 Interval statistics: listen preferred units change (metric/imperial) and recreate intervals. 2020-09-02 07:52:33 +02:00
Román cbc803d038 Interval statistics: retain interval value after change orientation and recreation. 2020-09-02 07:52:33 +02:00
Román a8cefcd0fd Interval statistics: show accumulate in distance column. 2020-09-02 07:52:33 +02:00
Román 2b95dbab22 Adjusted intervals. 2020-09-02 07:52:33 +02:00
Román 7a923a947e Added intervals in StatisticsRecordedFragment with distance, speed and pace for all activity types.
Users can select the length of the interval (in a spinner with several options).
The intervals will be in km or mi depending on settings.
2020-09-02 07:52:33 +02:00
Román 19e35a7609 StatisticsRecordedFragment doesn't need TrackDataHub.
TrackRecordedActivity saves Track object instead of track's id.
2020-09-02 07:52:33 +02:00
Dennis Guse b8088169ee Settings: animate the Bluetooth icon.
Fixes #314.
2020-09-01 22:19:19 +02:00
Román a5594fbb7a Release v3.9.3 2020-08-31 12:47:33 +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
Dennis Guse 23f47f10f8 Release v3.7.5 2020-06-14 20:09:27 +02:00
Román Martínez 251fa17487 Merge pull request #271 from OpenTracksApp/all_statistics#112
Aggregated statistics by activity type.
2020-06-14 20:03:29 +02:00
Román bae3373fe9 Aggregated statistics by activity type. Fixes #112. 2020-06-14 19:57:07 +02:00
Dennis Guse 0a685a66e3 Added list of tested Bluetooth LE sensors.
Fixes #202.
2020-06-14 11:58:45 +02:00
Dennis Guse 5ca9ca857a Cleanup. 2020-06-13 23:07:58 +02:00
Dennis Guse d9dfa6d746 Dashboard API: add data transfer as GPX and removed unnecessary IntentChooser. 2020-06-13 17:56:59 +02:00
Dennis Guse 3a5220546e Dashboard API: fallback to "open as KMZ" implemented as proxy activity.
We are not allowed to query for installed packages as of Android R (API 30).
2020-06-13 17:56:59 +02:00
Dennis Guse 047d5c2340 Manifest reordered to fix warning. 2020-06-13 17:56:59 +02:00
Dennis Guse 725c21c0a1 Bugfix: parse URI with trackIds correctly. 2020-06-13 17:56:59 +02:00
Dennis Guse 7939e39fc1 Go for Android R (API level 30). 2020-06-13 17:56:59 +02:00
Dennis Guse a133bd8f9e Bugfix: adaptive icon was too large.
We now use the same as OSMDashboard.
Thanks Peter.
2020-06-13 00:08:00 +02:00
transifex-integration[bot] 1f9790cba8 Translate /src/main/res/values/strings.xml in gl
translation completed for the source file '/src/main/res/values/strings.xml'
on the 'gl' language.
2020-06-10 22:20:23 +02:00
Dennis Guse a3ba1e1490 Cleanup: use lambda instead of runnable. 2020-06-09 23:01:09 +02:00
Dennis Guse accb30152f Translations fixed. 2020-06-09 19:58:23 +02:00
Dennis Guse dc7daa6e8c App icon: be complaint with Android requirements. (#253)
Fixes #121.
2020-06-09 19:37:47 +02:00
Dennis Guse 1d8e788d53 Documentation: add internal data flow. 2020-06-09 19:36:54 +02:00
Dennis Guse 568b908b26 Removed unused strings. 2020-06-09 19:35:57 +02:00
transifex-integration[bot] 3ab63adb34 Translate '/src/main/res/values/strings.xml' in 'de' [manual sync] (#269)
* Translate /src/main/res/values/strings.xml in de

at least 95% translated 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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2020-06-08 23:42:16 +02:00
transifex-integration[bot] 91692f345e Translate /src/main/res/values/strings.xml in fr
at least 95% translated 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-06-08 23:32:34 +02:00
transifex-integration[bot] dbdb98a682 Translate /src/main/res/values/strings.xml in eu
at least 95% translated 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-06-08 23:32:21 +02:00
transifex-integration[bot] df66cd5753 Translate /src/main/res/values/strings.xml in cs
at least 95% translated 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-06-08 23:31:19 +02:00
transifex-integration[bot] 6e39dd37f8 Translate /src/main/res/values/strings.xml in da
at least 95% translated 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-06-08 23:31:08 +02:00
transifex-integration[bot] 7212f7be94 Translate /src/main/res/values/strings.xml in nl
at least 95% translated 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-06-08 23:31:00 +02:00
transifex-integration[bot] 77fe494e6c Translate /src/main/res/values/strings.xml in et
at least 95% translated 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-06-08 23:30:55 +02:00
transifex-integration[bot] 2f491e8c10 Translate /src/main/res/values/strings.xml in es
at least 95% translated 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-06-08 23:30:48 +02:00
Dennis Guse f4fe7b1ab3 Import: convert all failed date parsing into SAXExceptions. 2020-06-08 22:51:01 +02:00
Peter Storch 7e444cbf31 Merge pull request #248 from OpenTracksApp/keep_screen_on_dashboard
forward keep screen on while recording preference to dashboard
2020-06-06 23:12:24 +02:00
Dennis Guse ed6ab9c53c Import: be lenient with missing timezone information.
Fixes #249.
2020-06-06 22:58:02 +02:00
Peter Storch f91ca3154c forward show on lockscreen to dashboard 2020-06-05 20:52:00 +02:00
Peter Storch bbfab1b201 forward keep screen on while recording preference to dashboard 2020-06-05 20:17:24 +02:00
Dennis Guse 1f2e3bcf70 Upgrade to Android Gradle plugin 4.0.0 2020-05-30 20:25:36 +02:00
Dennis Guse d1f3791f97 Release v3.7.4 2020-05-27 08:21:37 +02:00
Román 490b95a001 Merge branch 'split_trackdetail#171' 2020-05-25 13:28:36 +02:00
Román 12e6f03098 Recorded/Recording nested layouts converted to constraint layout. Fixes #15. 2020-05-25 13:06:36 +02:00
Román d86f42e2e9 Animation between recording and recorded activities. Update PlantUML with new changes. 2020-05-25 13:06:17 +02:00
Román 360d59d3de Split detail and record to simplify the code. Fixes #171. 2020-05-25 13:06:17 +02:00
Dennis Guse b617420290 Export: do not show progress of exported TrackPoints.
Only the overall number of tracks is shown now (similar to import).
2020-05-24 10:58:43 +02:00
Dennis Guse 53b267150b Export/Import: TrackFileFormat is now selected via settings only. 2020-05-24 10:58:43 +02:00
Dennis Guse 0dfd55ed52 Settings: added default sharing track file format.
Fixes #225.
2020-05-24 10:58:43 +02:00
Román daabd5381d Links in help and about are clickable. Also added scroll in about activity. Fixes #232. 2020-05-22 16:01:33 +02:00
Dennis Guse a842129e37 Bugfix: wheel circumference was not stored in correct preference key. 2020-05-13 21:46:48 +02:00
Dennis Guse e8b9c2bbc3 Release v3.7.3 2020-05-13 21:03:52 +02:00
Dennis Guse e55924dae3 Bugfix: Cadence/Speed computation incorrectly truncated time to long instead of float. 2020-05-13 20:34:16 +02:00
Dennis Guse e3a8143d58 Cleanup. 2020-05-13 20:34:16 +02:00
Dennis Guse 30310bb874 Localization: removed unused translations.
Fixed German labels for speed (were too long on Stats screen).
2020-05-13 20:34:16 +02:00
Dennis Guse 9f377b172d Upgraded to Gradle wrapper 6.3 2020-05-10 15:28:42 +02:00
473 changed files with 20360 additions and 8079 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
+12 -1
View File
@@ -1,5 +1,13 @@
# Thanks for your contribution.
## PLEASE REMOVE
To support us in providing a nice (and fast) open-source experience:
1. Verify that the tests are passing
2. Check that the code is properly formatted (using AndroidStudio's autoformatter)
3. Provide write access to the [branch](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork)
4. If the PR is not ready for review, please submit it as a [draft](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests)
## PLEASE REMOVE
**Describe the pull request**
A clear and concise description of what the pull request changes/adds.
@@ -7,4 +15,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
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
+10
View File
@@ -0,0 +1,10 @@
[main]
host = https://www.transifex.com
[opentracks.src-main-res-values-strings-xml--main]
file_filter = src/main/res/values-<lang>/strings.xml
minimum_perc = 0
source_file = src/main/res/values/strings.xml
source_lang = en
type = ANDROID
+35
View File
@@ -0,0 +1,35 @@
# How to contribute?
## Testing
Just use OpenTracks and tell us what can be improved by opening an issue [here](https://github.com/OpenTracksApp/OpenTracks/issues/new/choose)
If we broke things (happens now and then), please tell us also.
## Coding new features
You have a cool idea for a feature that is missing in OpenTracks?
And you would like to help by implementing it?
If not, you can just take a look at the [issue tracker](https://github.com/OpenTracksApp/OpenTracks/issues).
Advices:
* creating an issue beforehand might be a good idea
Sometimes it is helpful to discuss your idea and also your implementation approach first.
We may be able to give you some hints on how to tackle the problem.
It also makes merging your changes easier and, usually, faster.
* please only add new dependencies if is absolutely necessary
If you need to:
* tell us why those are required,
* what alternatives are existing, and
* why you choose this dependency.
And also verify that the license(s) are compatible with _Apache 2.0_ as well as open-source.
## Coding conventions
None so far.
Just keep it simple.
And follow the _scout rule_.
You can take a look at existing code and get a feeling for it.
Please autoformat your code using AndroidStudio's code formatter.
+12 -4
View File
@@ -13,6 +13,8 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
<td>
<a href="https://f-droid.org/packages/de.dennisguse.opentracks">
<img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
<br />
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
</a>
</td>
<td>
@@ -21,7 +23,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>
@@ -51,13 +53,19 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
__Only required permissions:__
* _ACCESS_FINE_LOCATION_: required to use the GPS.
An overview of Bluetooth LE sensors that are known to work with OpenTracks is in [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md).
_Please note:_
_OpenTracks_ does not contain a _map_ (as this would required Internet access).
To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)) needs to be installed.
## Custom Dashboards
## Custom Dashboards (incl. Map)
As of v3.3.1 OpenTracks enables to use custom dashboards.
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
The developer documentation is in [README_API.md](README_API.md).
Alternatively, recorded tracks can be shared as KMZ/GPX to installed applications (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
However, this is rather slow and does not provide updates while recording.
## Screenshots
<div>
@@ -87,7 +95,7 @@ Rework of _OpenTracks_ included:
* removing calorie estimation and activity estimation,
* removing support for ANT+ and Classic Bluetooth,
* adding support for Bluetooth LE heart rate sensors,
* removing Protobuf (store sensor data in SQLite columns directly), and
* removing Protobuf (store sensosensorr data in SQLite columns directly), and
* removing Android Service API for other apps.
Artwork, logos and user interface remained more or less unchanged.
+1 -4
View File
@@ -7,14 +7,11 @@ Although this might be (mostly) sufficient, it has some limitations.
This include limited configurability (e.g., ordering of shown elements) and the lack of Internet access.
In fact, the later is considered a feature but severely limits the implementation presenting a map (at least in an easy way).
_NOTE:_ for showing a track on a map a workaround was implemented in OpenTracks.
Data could be exported as KMZ and presented in 3rd-party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
For the time being, the OpenTracks Data API can be used to implement mainly custom dashboards.
## 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`)
+20
View File
@@ -0,0 +1,20 @@
# Overview of tested Bluetooth LE sensors
## HRS: Heartrate Service
* Decathlon|Geonaute|Kalenji Dual HRM Belt
* Polar H7
* Polar OH1
Has updatable firmware that requires a user account at polar.com
* Wahoo Tickr (Model: WFBTHR02)
## CSCP: Cycling Cadence and Speed Service
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
However, often only one value is provided.
### Speed
* Wahoo Speed (Model: WFRPMSPD)
### Cadence
* Wahoo Cadence (Model: WFPODCAD2)
This sensor reports cadence data as speed.
A workaround is in place.
Executable
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
set -e
CHANGELOG_DIR="fastlane/metadata/android/en-US/changelogs/"
echo "Checking main branch"
git checkout main
VERSIONID=$(git rev-list HEAD --count main)
CHANGELOG_FILE="$CHANGELOG_DIR/$VERSIONID.txt"
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
#TODO Execute all tests: we are destroying data, running on a real device.
# ./gradlew connectedCheck
# Changelog
echo -e "\nPrevious changelog\n------------"
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
sed 's/^/>/' "$PREVIOUS_CHANGELOG"
#For F-Droid, a version starts with a 'v'
read -p $'\n\nPlease enter the versionname (e.g., v3.9.5):\n> v' VERSIONNAME
VERSIONNAME="v$VERSIONNAME"
cp "$CHANGELOG_DIR/next_release.txt" "$CHANGELOG_FILE"
sed -i "s/(versionName)/$VERSIONNAME/" "$CHANGELOG_FILE"
echo -e "\nGit commit since last release\n------------"
git log $(git describe --tags --abbrev=0)..HEAD --no-merges --oneline
echo -e "\nPlease edit the changelog for the new release"
gedit -w "$CHANGELOG_FILE"
git add "$CHANGELOG_FILE"
echo "Updating build.gradle"
sed -i s/versionCode\ .*/versionCode\ "$VERSIONID"/g build.gradle
sed -i s/versionName\ .*/versionName\ "\"$VERSIONNAME\""/g build.gradle
git diff
COMMIT_MESSAGE="Release: $VERSIONNAME"
echo -e "\nCommit message will be: '$COMMIT_MESSAGE'"
echo -e "\nPress ENTER, if everything looks good."
read
git add -u
git commit -m "$COMMIT_MESSAGE"
echo -e "Next steps:\n1. Push to Github\n2. Create release on Github: https://github.com/OpenTracksApp/OpenTracks/releases"
+23 -42
View File
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
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()
@@ -50,25 +36,26 @@ def getVersionName = { ->
}
android {
compileSdkVersion 29
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 30
defaultConfig {
applicationId "de.dennisguse.opentracks"
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_color_24dp",
applicationName: "@string/app_name"
]
versionCode 3381
//versionCode getVersionCode() //Useful for debugging.
versionName "v3.7.2"
versionCode 3656
versionName "v3.11.0"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 30
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -77,10 +64,6 @@ android {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_black_24dp",
applicationName: "OpenTracks (Debug)"
]
}
release {
@@ -99,19 +82,17 @@ android {
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference: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.2.1'
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'
implementation 'com.google.android.material:material:1.1.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.5.13'
}
+28 -13
View File
@@ -9,23 +9,35 @@ from every activity showing a menu.
endlegend
[*] --> TrackListActivity
TrackListActivity --> TrackDetailActivity : Start recording
TrackListActivity --> TrackRecordingActivity : Start recording
TrackListActivity --> SearchListActivity
TrackListActivity --> MarkerListActivity : Show all markers
TrackListActivity --> TrackDetailActivity : Show track
TrackListActivity --> AggregatedStatisticsActivity : Aggregated stats
TrackListActivity --> TrackRecordedActivity : Show track
TrackListActivity --> TrackDeleteActivity
SearchListActivity --> MarkerDetailActivity : Search result
SearchListActivity --> TrackDetailActivity : Search result
SearchListActivity --> TrackRecordedActivity : Search result
TrackDetailActivity --> TrackDeleteActivity
TrackDetailActivity --> TrackEditActivity
TrackDetailActivity --> MarkerListActivity
TrackDetailActivity --> MarkerEditActivity : Add marker
TrackDetailActivity --> CameraIntent : Add marker (with photo)
TrackDetailActivity --> ShareFileIntent
TrackDetailActivity --> DashboardIntent : Database cursor
TrackDetailActivity --> TrackOnMapIntent : KMZ
TrackRecordedActivity --> TrackDeleteActivity
TrackRecordedActivity --> TrackEditActivity
TrackRecordedActivity --> MarkerListActivity
TrackRecordedActivity --> MarkerEditActivity : Add marker
TrackRecordedActivity --> CameraIntent : Add marker (with photo)
TrackRecordedActivity --> ShareFileIntent
TrackRecordedActivity --> DashboardIntent : Database cursor
TrackRecordedActivity --> TrackOnMapIntent : KMZ
TrackRecordedActivity --> TrackRecordingActivity
TrackRecordedActivity --> SettingsActivity
TrackRecordedActivity --> TrackListActivity : Back button
TrackRecordingActivity --> TrackOnMapIntent
TrackRecordingActivity --> MarkerEditActivity : Add marker
TrackRecordingActivity --> MarkerListActivity
TrackRecordingActivity --> TrackEditActivity
TrackRecordingActivity --> SettingsActivity
TrackRecordingActivity --> TrackRecordedActivity : Stop recording
TrackRecordingActivity --> TrackListActivity : Back button
MarkerListActivity --> MarkerDetailActivity
@@ -35,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
@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
+18
View File
@@ -0,0 +1,18 @@
@startuml
SensorManager --> ElevationSumManager
LocationManager --> TrackRecordingService
ElevationSumManager --> TrackRecordingService
TrackRecordingService --> TrackRecordingServiceConnection
TrackRecordingService --> Database
Database --> CustomContentProvider
CustomContentProvider --> TrackDataHub
CustomContentProvider --> ShareContentProvider
TrackDataHub --> StatisticsRecordingFragment : TrackPoints, TrackStatistics, Waypoints (push)
TrackDataHub --> StatisticsRecordedFragment : TrackPoints, TrackStatistics, Waypoints (push)
TrackDataHub --> ChartFragment --> ChartView
TrackRecordingServiceConnection --> StatisticsRecordingFragment : Sensor data (heartrate, cadence, elevation gain; pull 1s)
TrackRecordingServiceConnection --> TrackController : Start time (pull, onStart)
@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,7 @@
v3.7.3: OpenTracks
Bugfix:
- Fixed computation of cadence and speed (via Bluetooth LE sensor)
Developer:
- Removed unused translations
@@ -0,0 +1,12 @@
v3.7.4: OpenTracks
Changes:
- Sharing/export format configurable
Bugfix:
- Wheel diameter not stored correctly (for speed sensor)
- Help: links are clickable
Developer:
- Split TrackDetailActivity
- StatsFragement successor uses ConstraintLayout
@@ -0,0 +1,11 @@
v3.7.5: OpenTracks
Changes:
- Added aggregated statistics summary (from main the screen's menu)
- Dashboard API: pass "show on lock screen"- and "keep screen on"-setting
- Updated translations
- Import: be lenient with ISO8601 date format
- Show track on map: file format can be selected and opening dashboard works on Android R (API 30+)
Bugfix:
- App icon now correctly implemented
@@ -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.
@@ -0,0 +1,10 @@
v3.9.4: OpenTracks
Changes:
- Added interval computation
- Voice announcements present last interval statistics
- Settings: Bluetooth icon is animated
Bugfix:
- GPS was not turned off when stopping a recording
- Show on map via KML/KMZ/GPX was broken due to refactoring
@@ -0,0 +1,8 @@
v3.9.5: OpenTracks
Changes:
- UI shows speed only with one fractional digit
Bugfix:
- App crash in search (due to Track.Id refactoring)
- Track list did now show if a recording was ongoing (due to Track.Id refactoring)
@@ -0,0 +1,10 @@
v3.10.0: OpenTracks
Changes:
- Elevation gain via barometer
- Add splash screen
- Android 11/F-Droid: downgrade to targetSDK 29 (v1 signature)
see https://gitlab.com/fdroid/fdroidserver/-/issues/827
Bugfix:
- Several fixes due to trackId refactoring
@@ -0,0 +1,10 @@
v3.11.0: OpenTracks
Changes:
- Theming: day/night mode (default: night)
- Theming: use Material Design instead of custom element theming
- Use targetSDK 30 again (F-Droid signs now with V2)
Bugfix:
- Android 11: export all crashed
- TrackDetail not updated in UI after data was changed by user
@@ -1,4 +1,4 @@
(tbd): OpenTracks
(versionName): OpenTracks
Changes:
-
@@ -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.
Binary file not shown.
+1 -2
View File
@@ -1,6 +1,5 @@
#Sun Apr 12 18:41:12 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
Vendored
+14 -17
View File
@@ -82,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -154,19 +156,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -175,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
Vendored
+104 -100
View File
@@ -1,100 +1,104 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -19,13 +19,16 @@ import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests {@link ChartValueSeries}.
*
@@ -60,16 +63,16 @@ public class ChartValueSeriesTest {
@Test
public void testInitialConditions() {
Assert.assertEquals(1, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(5, series.getMaxMarkerValue());
Assert.assertTrue(series.isEnabled());
assertEquals(1, series.getInterval());
assertEquals(0, series.getMinMarkerValue());
assertEquals(5, series.getMaxMarkerValue());
assertTrue(series.isEnabled());
}
@Test
public void testEnabled() {
series.setEnabled(false);
Assert.assertFalse(series.isEnabled());
assertFalse(series.isEnabled());
}
@Test
@@ -77,9 +80,9 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(0));
series.update(new ChartPoint(10));
series.updateDimension();
Assert.assertEquals(100, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(500, series.getMaxMarkerValue());
assertEquals(100, series.getInterval());
assertEquals(0, series.getMinMarkerValue());
assertEquals(500, series.getMaxMarkerValue());
}
@Test
@@ -87,9 +90,9 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(0));
series.update(new ChartPoint(901));
series.updateDimension();
Assert.assertEquals(1000, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(5000, series.getMaxMarkerValue());
assertEquals(1000, series.getInterval());
assertEquals(0, series.getMinMarkerValue());
assertEquals(5000, series.getMaxMarkerValue());
}
@Test
@@ -97,8 +100,8 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(220));
series.update(new ChartPoint(250));
series.updateDimension();
Assert.assertEquals(100, series.getInterval());
Assert.assertEquals(200, series.getMinMarkerValue());
Assert.assertEquals(700, series.getMaxMarkerValue());
assertEquals(100, series.getInterval());
assertEquals(200, series.getMinMarkerValue());
assertEquals(700, series.getMaxMarkerValue());
}
}
@@ -21,7 +21,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +30,8 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.StringUtils;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link DescriptionGenerator}.
*
@@ -67,7 +68,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>"
@@ -85,7 +86,7 @@ public class DescriptionGeneratorTest {
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
@Test
@@ -103,7 +104,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>"
@@ -119,7 +120,7 @@ public class DescriptionGeneratorTest {
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
@@ -130,7 +131,7 @@ public class DescriptionGeneratorTest {
public void testWriteDistance() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
Assert.assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
}
/**
@@ -140,7 +141,7 @@ public class DescriptionGeneratorTest {
public void testWriteTime() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
Assert.assertEquals("Total time: 00:01<br>", builder.toString());
assertEquals("Total time: 00:01<br>", builder.toString());
}
/**
@@ -150,7 +151,7 @@ public class DescriptionGeneratorTest {
public void testWriteSpeed() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
Assert.assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
}
/**
@@ -160,7 +161,7 @@ public class DescriptionGeneratorTest {
public void testWriteElevation() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
Assert.assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
}
/**
@@ -170,6 +171,6 @@ public class DescriptionGeneratorTest {
public void testWritePace() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
Assert.assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
}
}
@@ -17,18 +17,16 @@
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;
@@ -36,11 +34,17 @@ import java.util.List;
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
import de.dennisguse.opentracks.content.data.Marker;
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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Tests for {@link SearchEngine}.
* These are not meant to be quality tests, but instead feature-by-feature tests.
@@ -48,29 +52,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,47 +91,47 @@ 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 Marker.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);
if (hoursAgo >= 0) {
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
}
Waypoint waypoint = new Waypoint(location);
Marker waypoint = new Marker(location);
waypoint.setName(title);
waypoint.setDescription(description);
waypoint.setCategory(category);
waypoint.setTrackId(trackId);
Uri uri = providerUtils.insertWaypoint(waypoint);
return ContentUris.parseId(uri);
Uri uri = providerUtils.insertMarker(waypoint);
return new Marker.Id(ContentUris.parseId(uri));
}
private long insertWaypoint(String title, String description, String category) {
return insertWaypoint(title, description, category, 0.0, -1, -1);
private Marker.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 Marker.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 Marker.Id insertWaypoint(String title, long hoursAgo) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, null);
}
private long insertWaypoint(String title, long hoursAgo, long trackId) {
private Marker.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
}
@@ -139,14 +146,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 +171,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");
Marker.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc");
Marker.Id categoryMatchId = insertWaypoint("bb", "cc", "aa");
Marker.Id titleMatchId = insertWaypoint("aa", "bb", "cc");
Marker.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
Marker.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
Marker.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,17 +193,17 @@ 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");
Marker.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
insertWaypoint("bb", "cc", "dd");
long titleWaypointId = insertWaypoint("aa", "bb", "cc");
Marker.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.
Assert.assertEquals(results.toString(), 3, results.size());
assertEquals(results.toString(), 3, results.size());
assertTrackResult(trackId, results.get(0));
assertWaypointResult(titleWaypointId, results.get(1));
assertWaypointResult(descriptionWaypointId, results.get(2));
@@ -205,11 +212,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);
Marker.Id farFarAwayId = insertWaypoint("aa", 0.3);
Marker.Id nearId = insertWaypoint("ab", 0.1);
Marker.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 +225,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 +239,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);
Marker.Id oldestId = insertWaypoint("aa", 2);
Marker.Id recentId = insertWaypoint("ab", 0);
Marker.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 +253,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,47 +266,57 @@ 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);
Marker.Id otherId = insertWaypoint("aa", 1, new Track.Id(456));
Marker.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) {
Assert.assertNotNull("Not a track", result.track);
Assert.assertNull("Ambiguous result", result.waypoint);
Assert.assertEquals(trackId, result.track.getId());
private void assertTrackResult(Track.Id trackId, ScoredResult result) {
assertNotNull("Not a track", result.track);
assertNull("Ambiguous result", result.marker);
assertEquals(trackId, result.track.getId());
}
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
Assert.assertEquals(results.size(), trackIds.length);
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;
assertEquals(results.size(), trackIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
Assert.assertNotNull(errMsg, result.track);
Assert.assertNull(errMsg, result.waypoint);
Assert.assertEquals(errMsg, trackIds[i], result.track.getId());
assertNotNull(errMsg, result.track);
assertNull(errMsg, result.marker);
assertEquals(errMsg, trackIds[i], result.track.getId());
}
}
private void assertWaypointResult(long waypointId, ScoredResult result) {
Assert.assertNotNull("Not a waypoint", result.waypoint);
Assert.assertNull("Ambiguous result", result.track);
Assert.assertEquals(waypointId, result.waypoint.getId());
private void assertWaypointResult(Marker.Id waypointId, ScoredResult result) {
assertNotNull("Not a waypoint", result.marker);
assertNull("Ambiguous result", result.track);
assertEquals(waypointId, result.marker.getId());
}
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
Assert.assertEquals(results.size(), waypointIds.length);
private void assertWaypointResults(List<ScoredResult> results, Marker.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;
assertEquals(results.size(), waypointIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
Assert.assertNotNull(errMsg, result.waypoint);
Assert.assertNull(errMsg, result.track);
Assert.assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
assertNotNull(errMsg, result.marker);
assertNull(errMsg, result.track);
assertEquals(errMsg, waypointIds[i], result.marker.getId());
}
}
}
@@ -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(300f + i);
trackPoint.setPower(400f + i);
trackPoint.setElevationGain(500f + i);
return trackPoint;
}
/**
@@ -88,13 +93,12 @@ 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 Marker 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);
return new Marker("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl);
}
}
@@ -22,13 +22,15 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.dennisguse.opentracks.content.data.MarkerColumns;
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(MarkerColumns.CONTENT_TYPE, customContentProvider.getType(MarkerColumns.CONTENT_URI));
assertEquals(MarkerColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), 1).build()));
}
}
@@ -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;
@@ -16,9 +18,13 @@ import org.junit.runner.RunWith;
import java.util.HashMap;
import java.util.Map;
import de.dennisguse.opentracks.content.data.MarkerColumns;
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,9 +42,10 @@ 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")) {
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + MarkerColumns.TABLE_NAME + "')", null, null, null, "name")) {
if (cursor != null) {
while (cursor.moveToNext()) {
tableSQL.put(cursor.getString(0), cursor.getString(1));
@@ -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, MarkerColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, MarkerColumns.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(MarkerColumns.TABLE_NAME), tableByUpgrade.get(MarkerColumns.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(MarkerColumns.TABLE_NAME), indicesByCreate.get(MarkerColumns.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();
}
}
@@ -3,26 +3,43 @@ package de.dennisguse.opentracks.content.provider;
import android.net.Uri;
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;
import static org.junit.Assert.assertEquals;
public class ShareContentProviderTest {
@Test
public void testCreateandParseURI() {
long[] trackIds = {1, 3, 5};
public void testCreateAndParseURI_invalid() {
Uri uri = Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/1");
assertEquals(0, ShareContentProvider.parseURI(uri).size());
}
@Test
public void testCreateAndParseURI_valid() {
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));
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);
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
}
}
@@ -2,17 +2,19 @@ package de.dennisguse.opentracks.content.sensor;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.util.UintUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@RunWith(AndroidJUnit4.class)
public class SensorDataCyclingTest {
@Test
public void compute_cadence() {
public void compute_cadence_1() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024); // 1s
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 2048); // 2s
@@ -21,7 +23,20 @@ public class SensorDataCyclingTest {
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_cadence_2() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 6184);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 8016);
// when
current.compute(previous);
// then
assertEquals(33.53, current.getCadence_rpm(), 0.01);
}
@Test
@@ -34,7 +49,7 @@ public class SensorDataCyclingTest {
current.compute(previous);
// then
Assert.assertEquals(0, current.getCadence_rpm(), 0.01);
assertEquals(0, current.getCadence_rpm(), 0.01);
}
@@ -48,7 +63,7 @@ public class SensorDataCyclingTest {
current.compute(previous);
// then
Assert.assertFalse(current.hasCadence_rpm());
assertFalse(current.hasCadence_rpm());
}
@Test
@@ -61,7 +76,7 @@ public class SensorDataCyclingTest {
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
@@ -74,7 +89,20 @@ public class SensorDataCyclingTest {
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_speed() {
// given
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", 1, 6184);
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 2, 8016);
// when
current.compute(previous, 2150);
// then
assertEquals(1.20, current.getSpeed_mps(), 0.01);
}
@Test
@@ -87,6 +115,6 @@ public class SensorDataCyclingTest {
current.compute(previous, 2000);
// then
Assert.assertEquals(2, current.getSpeed_mps(), 0.01);
assertEquals(2, current.getSpeed_mps(), 0.01);
}
}
@@ -23,7 +23,6 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -35,6 +34,8 @@ import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
/**
* Tests {@link ChartFragment}.
*
@@ -78,9 +79,9 @@ public class ChartFragmentTest {
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
assertEquals(Float.NaN, point.getHeartRate(), 0.01);
assertEquals(Float.NaN, point.getCadence(), 0.01);
assertEquals(Float.NaN, point.getPower(), 0.01);
}
/**
@@ -98,9 +99,9 @@ public class ChartFragmentTest {
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
Assert.assertEquals(100.0, point.getHeartRate(), 0.01);
Assert.assertEquals(101.0, point.getCadence(), 0.01);
Assert.assertEquals(102.0, point.getPower(), 0.01);
assertEquals(100.0, point.getHeartRate(), 0.01);
assertEquals(101.0, point.getCadence(), 0.01);
assertEquals(102.0, point.getPower(), 0.01);
}
/**
@@ -112,12 +113,12 @@ public class ChartFragmentTest {
// Resets last location and writes first location.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second is a same location, just different time.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use metric.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
@@ -129,7 +130,7 @@ public class ChartFragmentTest {
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The fourth location is a new location, and use metric.
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
@@ -140,7 +141,7 @@ public class ChartFragmentTest {
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
}
/**
@@ -156,7 +157,7 @@ public class ChartFragmentTest {
// The first is a same location, just different time.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second location is a new location, and use imperial.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -170,7 +171,7 @@ public class ChartFragmentTest {
float[] results = new float[4];
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use imperial.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
@@ -183,7 +184,7 @@ public class ChartFragmentTest {
*/
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
}
/**
@@ -194,12 +195,12 @@ public class ChartFragmentTest {
chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
long timeSpan = 222;
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
}
/**
@@ -214,7 +215,7 @@ public class ChartFragmentTest {
* Then only one value INITIAL_ALTITUDE in buffer.
*/
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
/*
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
@@ -222,7 +223,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
}
/**
@@ -239,7 +240,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(128.5f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
assertEquals(0.0, point.getSpeed(), 0.01);
/*
* Tests the logic when both metricUnits and reportSpeed are true.
@@ -253,7 +254,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);
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
}
/**
@@ -267,7 +268,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -278,7 +279,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);
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
}
/**
@@ -292,7 +293,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -303,7 +304,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);
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
}
/**
@@ -315,6 +316,6 @@ public class ChartFragmentTest {
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setSpeed(0);
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
Assert.assertEquals(0.0, point.getPace(), 0.01);
assertEquals(0.0, point.getPace(), 0.01);
}
}
@@ -24,21 +24,21 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
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.MarkerColumns;
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;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
@@ -64,19 +64,19 @@ 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();
private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
protected final Context context = ApplicationProvider.getApplicationContext();
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.insertMarker(any())).thenReturn(WAYPOINT_ID_O_URI);
if (lastTrack) {
// Return null to not add waypoints
when(contentProviderUtils.getTrack(trackId)).thenReturn(null);
@@ -122,10 +122,10 @@ public abstract class AbstractTestFileTrackImporter {
}
protected void verifyTrack(Track track, String name, String description, long time) {
Assert.assertEquals(name, track.getName());
Assert.assertEquals(description, track.getDescription());
assertEquals(name, track.getName());
assertEquals(description, track.getDescription());
if (time != -1L) {
Assert.assertEquals(time, track.getTrackStatistics().getStartTime_ms());
assertEquals(time, track.getTrackStatistics().getStartTime_ms());
}
}
}
@@ -17,19 +17,23 @@ 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.Marker;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
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<Marker> markers = new ArrayList<>();
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,20 +68,23 @@ public class ExportImportTest {
contentProviderUtils.insertTrack(track.first);
contentProviderUtils.bulkInsertTrackPoint(track.second, track.first.getId());
for (int i = 0; i < 3; i++) {
Waypoint waypoint = new Waypoint(track.second[i].getLocation());
waypoint.setName("the waypoint " + i);
waypoint.setDescription("the waypoint description " + i);
waypoint.setCategory("the waypoint category" + i);
waypoint.setIcon("the waypoing icon" + i);
waypoint.setPhotoUrl("the photo url" + i);
waypoint.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint);
trackPoints.clear();
trackPoints.addAll(Arrays.asList(track.second));
waypoints.add(waypoint);
for (int i = 0; i < 3; i++) {
Marker marker = new Marker(track.second[i].getLocation());
marker.setName("the marker " + i);
marker.setDescription("the marker description " + i);
marker.setCategory("the marker category" + i);
marker.setIcon("the marker icon" + i);
marker.setPhotoUrl("the photo url" + i);
marker.setTrackId(trackId);
contentProviderUtils.insertMarker(marker);
markers.add(marker);
}
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(trackId));
assertEquals(markers.size(), contentProviderUtils.getMarkerCount(trackId));
}
@After
@@ -96,42 +106,93 @@ public class ExportImportTest {
// given
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, null);
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);
//TODO assertEquals(track.getTrackPoints(), importedTrack.getTrackPoints());
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();
// 2. markers
assertMarkers();
//TODO Check absolute time of trackpoints
// 3. trackpoints
assertTrackpoints(false, false, false, false);
}
@LargeTest
@Test
public void kml_with_trackdetail_and_sensordata() {
// TODO
Log.e(TAG, "Test not implemented.");
// 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);
assertEquals(track.getCategory(), importedTrack.getCategory());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.getIcon(), importedTrack.getIcon());
// 2. markers
assertMarkers();
// 3. trackpoints
assertTrackpoints(true, true, true, true);
}
@LargeTest
@Test
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}, null);
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());
@@ -193,28 +253,86 @@ public class ExportImportTest {
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
//assertEquals(track.getIcon(), trackImported.getIcon());
// 2. waypoints
assertWaypoints();
// 2. markers
assertMarkers();
//TODO Check absolute time of trackpoints
// 3. trackpoints
assertTrackpoints(false, true, true, false);
}
private void assertWaypoints() {
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(importTrackId));
@LargeTest
@Test
public void gpx_duplicate_trackUUID() {
// given
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
Track track = contentProviderUtils.getTrack(trackId);
List<Waypoint> importedWaypoints = contentProviderUtils.getWaypoints(importTrackId);
for (int i = 0; i < waypoints.size(); i++) {
Waypoint waypoint = waypoints.get(i);
Waypoint importedWaypoint = importedWaypoints.get(i);
assertEquals(waypoint.getCategory(), importedWaypoint.getCategory());
assertEquals(waypoint.getDescription(), importedWaypoint.getDescription());
// assertEquals(waypoint.getIcon(), importedWaypoint.getIcon()); // TODO for KML
assertEquals(waypoint.getName(), importedWaypoint.getName());
assertEquals("", importedWaypoint.getPhotoUrl());
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
assertEquals(waypoint.getLocation().getLatitude(), importedWaypoint.getLocation().getLatitude(), 0.001);
assertEquals(waypoint.getLocation().getLongitude(), importedWaypoint.getLocation().getLongitude(), 0.001);
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001);
// 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 assertMarkers() {
assertEquals(markers.size(), contentProviderUtils.getMarkerCount(importTrackId));
List<Marker> importedMarkers = contentProviderUtils.getMarkers(importTrackId);
for (int i = 0; i < markers.size(); i++) {
Marker marker = markers.get(i);
Marker importMarker = importedMarkers.get(i);
assertEquals(marker.getCategory(), importMarker.getCategory());
assertEquals(marker.getDescription(), importMarker.getDescription());
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(marker.getName(), importMarker.getName());
assertEquals("", importMarker.getPhotoUrl());
assertEquals(marker.getLocation().getLatitude(), importMarker.getLocation().getLatitude(), 0.001);
assertEquals(marker.getLocation().getLongitude(), importMarker.getLocation().getLongitude(), 0.001);
assertEquals(marker.getLocation().getAltitude(), importMarker.getLocation().getAltitude(), 0.001);
}
}
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain) {
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);
}
if (verifyElevationGain) {
assertEquals(trackPoint.getElevationGain(), importedTrackPoint.getElevationGain(), 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());
}
}
@@ -15,12 +15,15 @@
*/
package de.dennisguse.opentracks.services;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
import static org.junit.Assert.assertEquals;
/**
* Tests the {@link AdaptiveLocationListenerPolicy}.
*
@@ -55,17 +58,17 @@ public class AdaptiveLocationListenerPolicyTest {
@Test
public void testGetDesiredPollingInterval() {
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
Assert.assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
Assert.assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
}
/**
@@ -73,6 +76,6 @@ public class AdaptiveLocationListenerPolicyTest {
*/
@Test
public void testGetMinDistance() {
Assert.assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
}
}
@@ -21,18 +21,19 @@ 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;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.rule.GrantPermissionRule;
import androidx.test.rule.ServiceTestRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,15 +44,22 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
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.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for the track recording service.
*
@@ -69,10 +77,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);
@@ -111,22 +126,22 @@ public class TrackRecordingServiceTest {
@Test
public void testStartable() throws TimeoutException {
mServiceRule.startService(createStartIntent(context));
Assert.assertNotNull(mServiceRule.bindService(createStartIntent(context)));
assertNotNull(mServiceRule.bindService(createStartIntent(context)));
}
@MediumTest
@Test
public void testBindable() throws TimeoutException {
IBinder service = mServiceRule.bindService(createStartIntent(context));
Assert.assertNotNull(service);
assertNotNull(service);
}
@MediumTest
@Test
public void testRecording_noTracks() throws Exception {
// given
List<Track> tracks = contentProviderUtils.getAllTracks();
Assert.assertTrue(tracks.isEmpty());
List<Track> tracks = contentProviderUtils.getTracks();
assertTrue(tracks.isEmpty());
// when
Intent startIntent = createStartIntent(context);
@@ -135,8 +150,8 @@ public class TrackRecordingServiceTest {
// then
// Test if we start in no-recording mode by default.
Assert.assertFalse(service.isRecording());
Assert.assertEquals(-1L, service.getRecordingTrackId());
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -149,8 +164,8 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// then
Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -163,7 +178,7 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// then
Assert.assertTrue(service.isRecording());
assertTrue(service.isRecording());
}
@MediumTest
@@ -172,27 +187,27 @@ public class TrackRecordingServiceTest {
// given
createDummyTrack(trackId, -1L, true);
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
insertLocation(service);
newTrackPoint(service);
// when
service.pauseCurrentTrack();
// then
Assert.assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
//when
service.resumeTrack(trackId);
insertLocation(service);
newTrackPoint(service);
// then
Assert.assertTrue(service.isRecording());
Assert.assertEquals(trackId, service.getRecordingTrackId());
assertTrue(service.isRecording());
assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(5, trackPoints.size());
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
assertEquals(5, trackPoints.size());
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
}
@MediumTest
@@ -201,26 +216,26 @@ 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());
assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
// when
service.resumeTrack(trackId);
insertLocation(service);
newTrackPoint(service);
// then
Assert.assertTrue(service.isRecording());
Assert.assertEquals(trackId, service.getRecordingTrackId());
assertTrue(service.isRecording());
assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(4, trackPoints.size());
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
assertEquals(4, trackPoints.size());
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
}
@FlakyTest(detail = "Sometimes fails on CI.")
@Ignore("Sometimes fails on CI.")
@MediumTest
@Test
public void testRecording_orphanedRecordingTrack() throws Exception {
@@ -233,8 +248,8 @@ public class TrackRecordingServiceTest {
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L);
// then
Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -243,18 +258,18 @@ public class TrackRecordingServiceTest {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Assert.assertTrue(service.isRecording());
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);
assertNull(newTrackId);
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId());
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
assertEquals(trackId, service.getRecordingTrackId());
}
@MediumTest
@@ -262,15 +277,15 @@ public class TrackRecordingServiceTest {
public void testEndCurrentTrack_noRecording() throws Exception {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
// when
// Ending the current track when there is no recording should not result in any error.
service.endCurrentTrack();
// then
Assert.assertFalse(PreferencesUtils.isRecording(context));
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
assertFalse(PreferencesUtils.isRecording(context));
assertNull(service.getRecordingTrackId());
}
@MediumTest
@@ -278,13 +293,13 @@ public class TrackRecordingServiceTest {
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
// when
long waypointId = service.insertWaypoint(null, null, null, null);
Marker.Id waypointId = service.insertMarker(null, null, null, null);
// then
Assert.assertEquals(-1L, waypointId);
assertNull(waypointId);
}
@MediumTest
@@ -293,35 +308,35 @@ public class TrackRecordingServiceTest {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Assert.assertTrue(service.isRecording());
insertLocation(service);
long trackId = service.getRecordingTrackId();
assertTrue(service.isRecording());
newTrackPoint(service);
Track.Id trackId = service.getRecordingTrackId();
// when
long waypointId = service.insertWaypoint(null, null, null, null);
Marker.Id waypointId = service.insertMarker(null, null, null, null);
// then
Assert.assertNotEquals(-1L, waypointId);
Waypoint wpt = contentProviderUtils.getWaypoint(waypointId);
Assert.assertEquals(context.getString(R.string.marker_waypoint_icon_url), wpt.getIcon());
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
Assert.assertEquals(trackId, wpt.getTrackId());
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
Assert.assertNotNull(wpt.getLocation());
assertNotEquals(-1L, waypointId);
Marker wpt = contentProviderUtils.getMarker(waypointId);
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
assertEquals(trackId, wpt.getTrackId());
assertEquals(0.0, wpt.getLength(), 0.01);
assertNotNull(wpt.getLocation());
service.endCurrentTrack();
}
private void addTrack(Track track, boolean isRecording) {
Assert.assertTrue(track.getId() >= 0);
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);
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
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 +346,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 +365,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);
@@ -12,7 +12,6 @@ import androidx.test.rule.GrantPermissionRule;
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;
@@ -23,6 +22,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;
@@ -32,6 +32,9 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
* Tests insert location.
* <p>
@@ -72,14 +75,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,71 +92,48 @@ 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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(6, trackPoints.size());
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);
assertEquals(6, trackPoints.size());
assertEquals(45.0005, trackPoints.get(5).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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(2, trackPoints.size());
Assert.assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
assertEquals(2, trackPoints.size());
assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
}
// @MediumTest
@@ -181,53 +158,53 @@ 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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(3, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
assertEquals(3, trackPoints.size());
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
}
@MediumTest
@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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(4, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
Assert.assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
assertEquals(4, trackPoints.size());
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
}
@@ -235,7 +212,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,75 +230,54 @@ 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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(6, trackPoints.size());
Assert.assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
Assert.assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
assertEquals(6, trackPoints.size());
assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
}
@MediumTest
@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();
// then
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(7, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
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);
assertEquals(7, trackPoints.size());
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
}
}
@@ -13,7 +13,6 @@ import androidx.test.rule.ServiceTestRule;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -26,11 +25,18 @@ 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;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for the track recording service, which require a {@link Looper}.
*
@@ -186,24 +192,24 @@ public class TrackRecordingServiceTestLooper {
@MediumTest
@Test
public void testIntegration_completeRecordingSession() throws TimeoutException {
List<Track> tracks = contentProviderUtils.getAllTracks();
Assert.assertTrue(tracks.isEmpty());
List<Track> tracks = contentProviderUtils.getTracks();
assertTrue(tracks.isEmpty());
fullRecordingSession();
}
private void fullRecordingSession() throws TimeoutException {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
Assert.assertFalse(service.isRecording());
assertFalse(service.isRecording());
// Start a track.
long id = service.startNewTrack();
Assert.assertTrue(id >= 0);
Assert.assertTrue(service.isRecording());
Track track = contentProviderUtils.getTrack(id);
Assert.assertNotNull(track);
Assert.assertEquals(id, track.getId());
Assert.assertEquals(id, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(id, service.getRecordingTrackId());
Track.Id trackId = service.startNewTrack();
assertTrue(trackId.isValid());
assertTrue(service.isRecording());
Track track = contentProviderUtils.getTrack(trackId);
assertNotNull(track);
assertEquals(trackId, track.getId());
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
assertEquals(trackId, service.getRecordingTrackId());
// Insert a few points, markers and statistics.
long startTime = System.currentTimeMillis();
@@ -215,23 +221,25 @@ 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);
service.insertMarker(null, null, null, null);
}
}
// 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.assertNotNull(track);
Assert.assertEquals(id, track.getId());
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
track = contentProviderUtils.getTrack(trackId);
assertNotNull(track);
assertEquals(trackId, track.getId());
TrackStatistics trackStatistics = track.getTrackStatistics();
Assert.assertNotNull(trackStatistics);
Assert.assertTrue(trackStatistics.getStartTime_ms() > 0);
Assert.assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
assertNotNull(trackStatistics);
assertTrue(trackStatistics.getStartTime_ms() > 0);
assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
}
}
@@ -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;
}
}
@@ -0,0 +1,50 @@
package de.dennisguse.opentracks.services.sensors;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* NOTE: Test data is completely artificial.
*/
public class ElevationSumManagerTest {
private ElevationSumManager elevationSumManager = new ElevationSumManager();
private static void addSensorValue(ElevationSumManager elevationSumManager, float[] values) {
for (float f : values) {
elevationSumManager.onSensorValueChanged(f);
}
}
@Before
public void setUp() {
elevationSumManager.reset();
}
@Test
public void getElevationGainLoss_downhill() {
// given
elevationSumManager.setConnected(true);
// then
addSensorValue(elevationSumManager, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
// then
Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01);
Assert.assertEquals(-48.0, elevationSumManager.getElevationLoss_m(), 0.01);
}
@Test
public void sensorUnavailable() {
// given
elevationSumManager.setConnected(false);
// then
elevationSumManager.onSensorValueChanged(999f);
// then
Assert.assertNull(elevationSumManager.getElevationGain_m());
Assert.assertNull(elevationSumManager.getElevationLoss_m());
}
}
@@ -23,11 +23,12 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link AnnouncementPeriodicTaskFactory}.
*
@@ -53,6 +54,6 @@ public class AnnouncementPeriodicTaskFactoryTest {
public void testCreate() {
PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory();
PeriodicTask task = factory.create(context);
Assert.assertTrue(task instanceof AnnouncementPeriodicTask);
assertTrue(task instanceof AnnouncementPeriodicTask);
}
}
@@ -15,12 +15,13 @@
*/
package de.dennisguse.opentracks.stats;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link TrackStatistics}.
* This only tests non-trivial pieces of that class.
@@ -50,8 +51,8 @@ public class TrackStatisticsTest {
statistics2.setMovingTime(600L); // Result: 700+600
statistics.setTotalDistance(750.0);
statistics2.setTotalDistance(350.0); // Result: 750+350
statistics.setTotalElevationGain(50.0);
statistics2.setTotalElevationGain(850.0); // Result: 850+50
statistics.setTotalElevationGain(50.0f);
statistics2.setTotalElevationGain(850.0f); // Result: 850+50
statistics.setMaxSpeed(60.0); // Resulting max speed
statistics2.setMaxSpeed(30.0);
statistics.setMaxElevation(1250.0);
@@ -61,28 +62,28 @@ public class TrackStatisticsTest {
statistics.merge(statistics2);
Assert.assertEquals(1000L, statistics.getStartTime_ms());
Assert.assertEquals(4000L, statistics.getStopTime_ms());
Assert.assertEquals(2500L, statistics.getTotalTime());
Assert.assertEquals(1300L, statistics.getMovingTime());
Assert.assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
Assert.assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
Assert.assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
Assert.assertEquals(1200.0, statistics.getMinElevation(), 0.001);
Assert.assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
assertEquals(1000L, statistics.getStartTime_ms());
assertEquals(4000L, statistics.getStopTime_ms());
assertEquals(2500L, statistics.getTotalTime());
assertEquals(1300L, statistics.getMovingTime());
assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
assertEquals(1200.0, statistics.getMinElevation(), 0.001);
assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
}
@Test
public void testGetAverageSpeed() {
statistics.setTotalDistance(1000.0);
statistics.setTotalTime(50000); // in milliseconds
Assert.assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
}
@Test
public void testGetAverageMovingSpeed() {
statistics.setTotalDistance(1000.0);
statistics.setMovingTime(20000); // in milliseconds
Assert.assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
}
}
@@ -5,11 +5,20 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
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;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class AnnouncementUtilsTest {
@@ -26,9 +35,50 @@ public class AnnouncementUtilsTest {
stats.setTotalElevationGain(6000);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane");
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", null);
// then
Assert.assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
}
@Test
public void getAnnouncement_withInterval() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setMaxSpeed(100);
stats.setTotalElevationGain(6000);
IntervalStatistics intervalStatistics = new IntervalStatistics();
List<TrackPoint> trackPoints = Arrays.asList(TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second);
intervalStatistics.build(trackPoints, 1000);
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
int speedId = R.plurals.voiceSpeedKilometersPerHour;
double kmPerHour = lastInterval.getSpeed_ms() * UnitConversions.MPS_TO_KMH;
String firstPartMsg = "OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour";
String rateMsg = " Lap speed of " + context.getResources().getQuantityString(speedId, getQuantityCount(kmPerHour), kmPerHour);
String msg = firstPartMsg + rateMsg;
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", lastInterval);
// then
assertEquals(msg, announcement);
}
private int getQuantityCount(double d) {
if (d == 0) {
return 0;
} else if (d == 1) {
return 1;
} else if (d == 2) {
return 2;
} else {
int count = (int) d;
return Math.max(count, 3);
}
}
}
@@ -2,11 +2,13 @@ package de.dennisguse.opentracks.util;
import android.bluetooth.BluetoothGattCharacteristic;
import org.junit.Assert;
import org.junit.Test;
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class BluetoothUtilsTest {
@Test
@@ -19,7 +21,7 @@ public class BluetoothUtilsTest {
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
// then
Assert.assertEquals(60, heartRate);
assertEquals(60, heartRate);
}
@Test
@@ -32,7 +34,7 @@ public class BluetoothUtilsTest {
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
// then
Assert.assertEquals(257, heartRate);
assertEquals(257, heartRate);
}
@Test
@@ -44,8 +46,8 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
Assert.assertNull(sensor.getSpeed());
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
assertNull(sensor.getSpeed());
}
@Test
@@ -57,8 +59,8 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
Assert.assertNull(sensor.getCadence());
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
assertNull(sensor.getCadence());
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
}
@Test
@@ -70,7 +72,7 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
}
}
@@ -15,7 +15,6 @@
*/
package de.dennisguse.opentracks.util;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -23,6 +22,8 @@ import org.junit.runners.JUnit4;
import java.io.File;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link FileUtils}.
*
@@ -37,7 +38,7 @@ public class FileUtilsTest {
@Test
public void testBuildUniqueFileName_new() {
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
Assert.assertEquals("Filename.ext", filename);
assertEquals("Filename.ext", filename);
}
/**
@@ -47,7 +48,7 @@ public class FileUtilsTest {
public void testBuildUniqueFileName_exist() {
// Expect "/default.prop" to exist on the phone/emulator
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
Assert.assertEquals("default(1).prop", filename);
assertEquals("default(1).prop", filename);
}
/**
@@ -58,13 +59,13 @@ public class FileUtilsTest {
public void testSanitizeFileName() {
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
String expected = "Swim_ming-^across_the_ pacific (ocean).";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, FileUtils.sanitizeFileName(name));
}
@Test
public void testSanitizeFileNameWithSuffix() {
String name = "FileName.jpeg";
Assert.assertEquals(name, FileUtils.sanitizeFileName(name));
assertEquals(name, FileUtils.sanitizeFileName(name));
}
/**
@@ -75,7 +76,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_i18n() {
String name = "您好-привет";
String expected = "您好-привет";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, FileUtils.sanitizeFileName(name));
}
/**
@@ -86,7 +87,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_special_characters() {
String name = "$%'-_@~`!(){}^#&+,;=[] ";
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, FileUtils.sanitizeFileName(name));
}
/**
@@ -97,7 +98,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_collapse() {
String name = "hello//there";
String expected = "hello_there";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
assertEquals(expected, FileUtils.sanitizeFileName(name));
}
/**
@@ -114,8 +115,8 @@ public class FileUtilsTest {
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
for (int i = 0; i < truncated.length(); i++) {
Assert.assertEquals('a', truncated.charAt(i));
assertEquals('a', truncated.charAt(i));
}
Assert.assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
}
}
@@ -0,0 +1,63 @@
package de.dennisguse.opentracks.util;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class PreferencesUtilsTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Test
public void ExportTrackFileFormat_ok() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KMZ_WITH_TRACKDETAIL.name());
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
}
@Test
public void ExportTrackFileFormat_invalid() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
}
@Test
public void ExportTrackFileFormat_noValue() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.clear();
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
}
}
@@ -0,0 +1,49 @@
package de.dennisguse.opentracks.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class PressureSensorUtilsTest {
// real data at 500Hz stationary
@Test
public void elevationChanges_none() {
// given
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
float firstSensorValue = sensorValues_hPa[0];
// when // then
for (float v : sensorValues_hPa) {
assertNull(PressureSensorUtils.computeChanges_m(firstSensorValue, v));
}
}
// Simulate a sudden drop of elevation.
// test data
@Test
public void computeChanges_downhill() {
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
float firstSensorValue = sensorValues_hPa[0];
// when
float elevationGain_m = 0;
float elevationLoss_m = 0;
float lastUsedPressureValue_hPa = firstSensorValue;
for (float v : sensorValues_hPa) {
PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, v);
if (elevationChange != null) {
elevationGain_m += elevationChange.getElevationGain_m();
elevationLoss_m += elevationChange.getElevationLoss_m();
lastUsedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa();
}
}
// then
assertEquals(0f, elevationGain_m, 0.01);
assertEquals(-15.0f, elevationLoss_m, 0.01);
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
}
}
@@ -21,13 +21,15 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link StringUtils}.
*
@@ -44,7 +46,7 @@ public class StringUtilsTest {
*/
@Test
public void testFormatDateTimeIso8601() {
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
}
/**
@@ -53,19 +55,19 @@ public class StringUtilsTest {
@Test
public void testformatElapsedTime() {
// 1 second
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000));
assertEquals("00:01", StringUtils.formatElapsedTime(1000));
// 10 seconds
Assert.assertEquals("00:10", StringUtils.formatElapsedTime(10000));
assertEquals("00:10", StringUtils.formatElapsedTime(10000));
// 1 minute
Assert.assertEquals("01:00", StringUtils.formatElapsedTime(60000));
assertEquals("01:00", StringUtils.formatElapsedTime(60000));
// 10 minutes
Assert.assertEquals("10:00", StringUtils.formatElapsedTime(600000));
assertEquals("10:00", StringUtils.formatElapsedTime(600000));
// 1 hour
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
// 10 hours
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
// 100 hours
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
}
/**
@@ -74,19 +76,19 @@ public class StringUtilsTest {
@Test
public void testformatElapsedTimeWithHour() {
// 1 second
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
// 10 seconds
Assert.assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
// 1 minute
Assert.assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
// 10 minutes
Assert.assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
// 1 hour
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
// 10 hours
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
// 100 hours
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
}
/**
@@ -96,13 +98,13 @@ public class StringUtilsTest {
@Test
public void testFormatDistance() {
// A large number in metric
Assert.assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
// A large number in imperial
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
// A small number in metric
Assert.assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
// A small number in imperial
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
}
/**
@@ -110,8 +112,8 @@ public class StringUtilsTest {
*/
@Test
public void testFormatCData() {
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
}
/**
@@ -128,6 +130,7 @@ public class StringUtilsTest {
*/
@Test
public void testGetTime_timezone() {
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
@@ -167,7 +170,7 @@ public class StringUtilsTest {
// This comparision tends to be flaky (difference of 1ms)
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
Assert.assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
}
/**
@@ -176,9 +179,9 @@ public class StringUtilsTest {
@Test
public void testGetTimeParts_positive() {
int[] parts = StringUtils.getTimeParts(61000);
Assert.assertEquals(1, parts[0]);
Assert.assertEquals(1, parts[1]);
Assert.assertEquals(0, parts[2]);
assertEquals(1, parts[0]);
assertEquals(1, parts[1]);
assertEquals(0, parts[2]);
}
/**
@@ -187,28 +190,40 @@ public class StringUtilsTest {
@Test
public void testGetTimeParts_negative() {
int[] parts = StringUtils.getTimeParts(-61000);
Assert.assertEquals(-1, parts[0]);
Assert.assertEquals(-1, parts[1]);
Assert.assertEquals(0, parts[2]);
assertEquals(-1, parts[0]);
assertEquals(-1, parts[1]);
assertEquals(0, parts[2]);
}
@Test
public void testFormatDecimal() {
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(0.1, 0));
Assert.assertEquals("1", StringUtils.formatDecimal(1.1, 0));
Assert.assertEquals("10", StringUtils.formatDecimal(10, 0));
Assert.assertEquals("10", StringUtils.formatDecimal(10.1, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(-0.1, 0));
assertEquals("0", StringUtils.formatDecimal(0.0, 0));
assertEquals("0", StringUtils.formatDecimal(0.1, 0));
assertEquals("1", StringUtils.formatDecimal(1.1, 0));
assertEquals("10", StringUtils.formatDecimal(10, 0));
assertEquals("10", StringUtils.formatDecimal(10.1, 0));
assertEquals("-0", StringUtils.formatDecimal(-0.1, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 2));
Assert.assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
Assert.assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
Assert.assertEquals("10", StringUtils.formatDecimal(10, 2));
Assert.assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
Assert.assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
Assert.assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
assertEquals("0", StringUtils.formatDecimal(0.0, 2));
assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
assertEquals("10", StringUtils.formatDecimal(10, 2));
assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
Assert.assertEquals("1", StringUtils.formatDecimal(0.99, 1));
assertEquals("1", StringUtils.formatDecimal(0.99, 1));
}
@Test
public void testGetSpeedParts() {
assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
assertEquals("11.9", StringUtils.getSpeedParts(context, 3.31, true, true).first);
assertEquals("7.5", StringUtils.getSpeedParts(context, 3.34, false, true).first);
assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
}
}
@@ -21,7 +21,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,6 +29,9 @@ import java.util.Date;
import java.util.Locale;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import static org.junit.Assert.assertEquals;
/**
* Tests {@link TrackNameUtils}.
@@ -39,7 +41,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();
@@ -50,7 +52,7 @@ public class TrackNameUtilsTest {
@Test
public void testTrackName_date_local() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
}
/**
@@ -60,7 +62,7 @@ public class TrackNameUtilsTest {
public void testTrackName_date_iso_8601() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
}
/**
@@ -69,6 +71,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));
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
}
}
@@ -1,10 +1,11 @@
package de.dennisguse.opentracks.util;
import org.junit.Assert;
import org.junit.Test;
import de.dennisguse.opentracks.content.data.TrackPoint;
import static org.junit.Assert.assertEquals;
public class TrackPointUtilsTest {
@Test
@@ -18,7 +19,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint);
// then
Assert.assertEquals(time, trackPoint.getTime());
assertEquals(time, trackPoint.getTime());
}
@Test
@@ -32,7 +33,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint);
// then
Assert.assertEquals(time, trackPoint.getTime(), 1000);
assertEquals(time, trackPoint.getTime(), 1000);
}
@Test
@@ -46,7 +47,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint);
// then
Assert.assertEquals(time, trackPoint.getTime());
assertEquals(time, trackPoint.getTime());
}
}
@@ -0,0 +1,24 @@
package de.dennisguse.opentracks.util;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class UUIDUtilsTest {
@Test
public void test() {
UUID uuid = UUID.randomUUID();
byte[] bytes = UUIDUtils.toBytes(uuid);
UUID output = UUIDUtils.fromBytes(bytes);
assertEquals(uuid, output);
}
}
@@ -0,0 +1,259 @@
package de.dennisguse.opentracks.viewmodels;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.TrackIconUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(JUnit4.class)
public class AggregatedStatisticsTest {
private final Context context = ApplicationProvider.getApplicationContext();
/**
* Create a TrackStatistics object.
*
* @param totalDistance distance in meters.
* @param totalTime total time in milliseconds.
* @return TrackStatistics object.
*/
private static Track createTrack(Context context, long totalDistance, long totalTime, String category) {
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime_ms(1000L); // Resulting start time
statistics.setStopTime_ms(1000L + totalTime);
statistics.setTotalTime(totalTime);
statistics.setMovingTime(totalTime);
statistics.setTotalDistance(totalDistance);
statistics.setTotalElevationGain(50.0f);
statistics.setMaxSpeed(50.0); // Resulting max speed
statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0); // Resulting min elevation
Track track = new Track();
track.setIcon(TrackIconUtils.getIconValue(context, category));
track.setCategory(category);
track.setTrackStatistics(statistics);
return track;
}
@Test
public void testAggregate() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
Track track = createTrack(context, totalDistance, totalTime, biking);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
assertEquals(1, aggregatedStatistics.getCount());
assertNotNull(aggregatedStatistics.get(biking));
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
}
@Test
public void testAggregate_mountainBiking() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String mountainBiking = context.getString(R.string.activity_type_mountain_biking);
Track track = createTrack(context, totalDistance, totalTime, mountainBiking);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
assertNotNull(aggregatedStatistics.get(mountainBiking));
}
@Test
public void testAggregate_trailRunning() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String trailRunning = context.getString(R.string.activity_type_trail_running);
Track track = createTrack(context, totalDistance, totalTime, trailRunning);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
assertNotNull(aggregatedStatistics.get(trailRunning));
}
@Test
public void testAggregate_twoBikingTracks() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
Track[] tracks = new Track[2];
for (int i = 0; i < 2; i++) {
tracks[i] = createTrack(context, totalDistance, totalTime, biking);
}
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(tracks[0]); // biking activity 1.
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
// then
assertEquals(1, aggregatedStatistics.getCount());
assertNotNull(aggregatedStatistics.get(biking));
assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2L, statistics2.getMovingTime());
}
@Test
public void testAggregate_threeDifferentTracks() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
String biking = context.getString(R.string.activity_type_biking);
String running = context.getString(R.string.activity_type_running);
String walking = context.getString(R.string.activity_type_walking);
long totalTime = 2400000;
Track[] tracks = new Track[]{
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking)
};
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(tracks[0]); // biking activity 1.
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
aggregatedStatistics.aggregate(tracks[2]); // biking activity 3.
// then
assertEquals(3, aggregatedStatistics.getCount());
assertNotNull(aggregatedStatistics.get(biking));
assertNotNull(aggregatedStatistics.get(running));
assertNotNull(aggregatedStatistics.get(walking));
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
}
}
@Test
public void testAggregate_severalTracksWithSeveralActivities() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
String running = context.getString(R.string.activity_type_running);
String walking = context.getString(R.string.activity_type_walking);
String driving = context.getString(R.string.activity_type_driving);
Track[] tracks = new Track[]{
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, driving),
};
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
for (Track track : tracks) {
aggregatedStatistics.aggregate(track);
}
// then
// 4 sports.
assertEquals(4, aggregatedStatistics.getCount());
// There is a map for every sport.
assertNotNull(aggregatedStatistics.get(biking));
assertNotNull(aggregatedStatistics.get(running));
assertNotNull(aggregatedStatistics.get(walking));
assertNotNull(aggregatedStatistics.get(driving));
// Number of tracks by sport.
assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running.
assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking.
assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving.
// Biking.
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 5, statistics2.getMovingTime());
}
// Running.
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2, statistics2.getMovingTime());
}
// Walking.
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2, statistics2.getMovingTime());
}
// Driving.
{
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
}
}
}
@@ -0,0 +1,144 @@
package de.dennisguse.opentracks.viewmodels;
import android.util.Pair;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Arrays;
import java.util.List;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
@RunWith(JUnit4.class)
public class IntervalStatisticsTest {
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
private List<TrackPoint> buildTrackPoints(int numberOfTrackPoints) {
Pair<Track, TrackPoint[]> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints);
return Arrays.asList(pair.second);
}
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater(trackPoints.get(0).getTime());
for (TrackPoint tp : trackPoints) {
trackStatisticsUpdater.addTrackPoint(tp, 0);
}
return trackStatisticsUpdater.getTrackStatistics();
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_1() {
// With 50 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(50);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_2() {
// With 200 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_3() {
// With 200 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_4() {
// With 1000 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(1000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_5() {
// With 10000 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(10000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
private void whenAndThen(List<TrackPoint> trackPoints, TrackStatistics trackStatistics, float distanceInterval, IntervalStatistics intervalStatistics) {
intervalStatistics.build(trackPoints, distanceInterval);
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
double totalDistance = 0d;
long totalTime = 0L;
for (IntervalStatistics.Interval i : intervalList) {
totalDistance += i.getDistance_m();
totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS);
}
// then
assertEquals(trackStatistics.getTotalDistance(), totalDistance, 0.01);
assertEquals(trackStatistics.getTotalTime() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.1);
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval));
for (int i = 0; i < intervalList.size() - 1; i++) {
assertEquals(intervalList.get(i).getDistance_m(), distanceInterval, 0.001);
totalDistance -= intervalList.get(i).getDistance_m();
}
assertEquals(intervalList.get(intervalList.size() - 1).getDistance_m(), totalDistance, 0.01);
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_logo_color_108dp" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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>
+91 -47
View File
@@ -16,46 +16,99 @@ limitations under the License.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.dennisguse.opentracks"
android:installLocation="auto">
<!-- Permission to initialize services -->
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
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" />
@@ -78,13 +131,15 @@ limitations under the License.
android:resource="@xml/searchable" />
</activity>
<activity
android:name=".TrackDetailActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackRecordedActivity" />
<activity android:name=".TrackRecordingActivity" />
<activity android:name=".TrackEditActivity" />
<activity android:name=".TrackListActivity">
<activity
android:name=".TrackListActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -94,12 +149,29 @@ limitations under the License.
android:value=".SearchListActivity" />
</activity>
<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" />
<activity android:name=".ShowOnMapProxyActivity$KMZ">
<intent-filter android:label="@string/open_track_as_KMZ">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".ShowOnMapProxyActivity$GPX">
<intent-filter android:label="@string/open_track_as_GPX">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<provider
android:name=".content.provider.ShareContentProvider"
android:authorities="${applicationId}.content"
@@ -124,32 +196,4 @@ limitations under the License.
android:icon="@drawable/ic_logo_color_24dp"
android:label="@string/recording_service" />
</application>
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<!-- 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>
@@ -7,6 +7,7 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.util.SystemUtils;
import de.dennisguse.opentracks.util.ViewUtils;
public class AboutActivity extends AbstractActivity {
@@ -27,6 +28,8 @@ 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(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;
@@ -26,7 +30,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An abstract class for the following common tasks across
* {@link TrackListActivity}, {@link TrackDetailActivity}, and
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and
* {@link SearchListActivity}:
* <p>
* - share track <br>
@@ -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);
}
/**
@@ -0,0 +1,37 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import android.widget.ListView;
import androidx.lifecycle.ViewModelProvider;
import de.dennisguse.opentracks.adapters.AggregatedStatisticsAdapter;
import de.dennisguse.opentracks.viewmodels.AggregatedStatisticsModel;
public class AggregatedStatisticsActivity extends AbstractActivity {
private ListView listView;
private AggregatedStatisticsAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
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, aggregatedStatistics -> {
if (aggregatedStatistics != null) {
adapter = new AggregatedStatisticsAdapter(this, aggregatedStatistics);
listView.setAdapter(adapter);
}
adapter.notifyDataSetChanged();
});
}
@Override
protected int getLayoutResId() {
return R.layout.aggregated_stats;
}
}
@@ -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,19 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.util.ViewUtils;
public class HelpActivity extends AbstractActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
}
@Override
protected int getLayoutResId() {
return R.layout.help;
@@ -30,7 +30,7 @@ import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.fragments.MarkerDetailFragment;
@@ -48,31 +48,32 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
private List<Long> markerIds;
private List<Marker.Id> markerIds;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId == -1L) {
Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
if (markerId == null) {
Log.d(TAG, "invalid marker id");
finish();
return;
}
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
Marker marker = contentProviderUtils.getMarker(markerId);
markerIds = new ArrayList<>();
int markerIndex = -1;
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), -1L, -1)) {
//TODO Load only markerIds, not the whole marker
try (Cursor cursor = contentProviderUtils.getMarkerCursor(marker.getTrackId(), null, -1)) {
if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) {
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
Marker currentMarker = contentProviderUtils.createMarker(cursor);
markerIds.add(currentMarker.getId());
if (currentMarker.getId() == markerId) {
if (markerId.equals(currentMarker.getId())) {
markerIndex = markerIds.size() - 1;
}
@@ -109,12 +110,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override
public void onDeleteMarkerDone() {
runOnUiThread(new Runnable() {
@Override
public void run() {
finish();
}
});
runOnUiThread(this::finish);
}
/**
@@ -125,7 +121,7 @@ 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
@@ -29,21 +29,22 @@ 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;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.google.android.material.textfield.TextInputEditText;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.util.FileUtils;
@@ -59,14 +60,15 @@ 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 Waypoint waypoint;
private Track.Id trackId;
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
private Marker marker;
private MenuItem insertPhotoMenuItem;
private MenuItem insertGalleryImgMenuItem;
@@ -75,52 +77,91 @@ public class MarkerEditActivity extends AbstractActivity {
private boolean hasCamera;
// UI elements
private EditText waypointName;
private AutoCompleteTextView waypointMarkerType;
private EditText waypointDescription;
private ImageView waypointPhoto;
private ImageView waypointDeletePhotoBtn;
private Button done;
private TextInputEditText markerName;
private AutoCompleteTextView markerType;
private TextInputEditText markerDescription;
private ImageView markerPhoto;
private ImageView markerDeletePhotoBtn;
@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);
Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
// Setup UI elements
waypointName = findViewById(R.id.marker_edit_waypoint_name);
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
waypointMarkerType.setAdapter(adapter);
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
waypointPhoto = findViewById(R.id.marker_edit_waypoint_photo);
markerName = findViewById(R.id.marker_edit_name);
markerType = findViewById(R.id.marker_edit_marker_type);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.marker_types, android.R.layout.simple_dropdown_item_1line);
markerType.setAdapter(adapter);
markerDescription = findViewById(R.id.marker_edit_description);
markerPhoto = findViewById(R.id.marker_edit_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();
markerDeletePhotoBtn = findViewById(R.id.marker_edit_photo_delete);
markerDeletePhotoBtn.setOnClickListener(v -> {
if (marker != null && marker.hasPhoto()) {
marker.setPhotoUrl(null);
}
markerPhoto.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 = markerId == 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 nextMarkerNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextMarkerNumber(trackId);
if (nextMarkerNumber == -1) {
nextMarkerNumber = 0;
}
markerName.setText(getString(R.string.marker_name_format, nextMarkerNumber));
markerName.selectAll();
markerType.setText("");
markerDescription.setText("");
} else {
marker = new ContentProviderUtils(this).getMarker(markerId);
if (marker == null) {
Log.d(TAG, "marker is null");
finish();
return;
}
markerName.setText(marker.getName());
markerType.setText(marker.getCategory());
markerDescription.setText(marker.getDescription());
if (marker.hasPhoto()) {
photoUri = marker.getPhotoURI();
}
}
if (savedInstanceState != null) {
photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI);
if (marker != null) {
marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
}
}
if (photoUri != null) {
setMarkerImageView(photoUri);
}
hideAndShowOptions();
}
@Override
@@ -129,6 +170,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();
@@ -156,10 +203,10 @@ public class MarkerEditActivity extends AbstractActivity {
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.marker_edit_insert_photo:
createWaypointWithPicture();
createMarkerWithPicture();
return true;
case R.id.marker_edit_insert_gallery_img:
createWaypointWithGalleryImage();
createMarkerWithGalleryImage();
return true;
default:
return super.onOptionsItemSelected(item);
@@ -173,7 +220,7 @@ public class MarkerEditActivity extends AbstractActivity {
Toast.makeText(this, R.string.marker_add_photo_canceled, Toast.LENGTH_LONG).show();
return;
} else if (resultCode == RESULT_OK) {
setWaypointImageView(photoUri);
setMarkerImageView(photoUri);
}
} else if (requestCode == GALLERY_IMG_REQUEST_CODE) {
if (resultCode == RESULT_CANCELED) {
@@ -187,7 +234,7 @@ public class MarkerEditActivity extends AbstractActivity {
FileUtils.copy(srcFd, dstFile);
photoUri = FileUtils.getUriForFile(this, dstFile);
setWaypointImageView(photoUri);
setMarkerImageView(photoUri);
} catch(Exception e) {
Log.e(TAG, e.getMessage());
Toast.makeText(this, R.string.marker_add_canceled, Toast.LENGTH_LONG).show();
@@ -198,123 +245,64 @@ 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.
*
* If a photo is set then one's options are shown, otherwise another ones are shown.
*/
private void hideAndShowOptions() {
boolean isPhotoSet = (waypoint != null && waypoint.hasPhoto()) || photoUri != null;
boolean isPhotoSet = (marker != null && marker.hasPhoto()) || photoUri != null;
if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) {
insertPhotoMenuItem.setVisible(!isPhotoSet);
insertGalleryImgMenuItem.setVisible(!isPhotoSet);
}
waypointDeletePhotoBtn.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE);
markerDeletePhotoBtn.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE);
}
/**
* Returns the trackId. If trackId == -1 then get it from waypoint object.
* Returns the trackId; either from track or marker.
*/
private long getTrackId() {
return trackId == -1 ? waypoint.getTrackId() : trackId;
private Track.Id getTrackId() {
return trackId == null ? marker.getTrackId() : trackId;
}
/**
* Sets the ImageView waypointPhoto with the uri photo.
*
* @param uri the uri photo.
*/
private void setWaypointImageView(Uri uri) {
private void setMarkerImageView(@NonNull Uri uri) {
try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r")) {
FileDescriptor fd = pfd.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd);
waypointPhoto.setImageBitmap(bitmap);
markerPhoto.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();
}
}
private void createWaypointWithPicture() {
private void createMarkerWithPicture() {
Pair<Intent, Uri> intentAndPhotoUri = IntentUtils.createTakePictureIntent(this, getTrackId());
photoUri = intentAndPhotoUri.second;
startActivityForResult(intentAndPhotoUri.first, CAMERA_REQUEST_CODE);
}
private void createWaypointWithGalleryImage() {
private void createMarkerWithGalleryImage() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, GALLERY_IMG_REQUEST_CODE);
}
/**
* Adds a marker.
*/
private void addMarker() {
trackRecordingServiceConnection.addMarker(this,
waypointName.getText().toString(),
waypointMarkerType.getText().toString(),
waypointDescription.getText().toString(),
markerName.getText().toString(),
markerType.getText().toString(),
markerDescription.getText().toString(),
photoUri != null ? photoUri.toString() : null);
}
/**
* Saves a marker.
*/
private void saveMarker() {
waypoint.setName(waypointName.getText().toString());
waypoint.setCategory(waypointMarkerType.getText().toString());
waypoint.setDescription(waypointDescription.getText().toString());
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
marker.setName(markerName.getText().toString());
marker.setCategory(markerType.getText().toString());
marker.setDescription(markerDescription.getText().toString());
marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
new ContentProviderUtils(this).updateWaypoint(this, waypoint);
new ContentProviderUtils(this).updateMarker(this, marker);
}
}

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