From c4a4446d05e84d3de7ded696e1e09667bf382d77 Mon Sep 17 00:00:00 2001 From: Chumva Date: Thu, 18 Jul 2019 20:17:58 +0300 Subject: [PATCH 01/17] Move compass to first circle if second is out of screen --- .../osmand/plus/views/RulerControlLayer.java | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java b/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java index 14d3e39105..2973bdfb4d 100644 --- a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java @@ -43,7 +43,7 @@ public class RulerControlLayer extends OsmandMapLayer { private static final long DELAY_BEFORE_DRAW = 500; private static final int TEXT_SIZE = 14; private static final int DISTANCE_TEXT_SIZE = 16; - private static final int COMPASS_CIRCLE_ID = 2; + private static final float COMPASS_CIRCLE_FITTING_RADIUS_COEF = 1.25f; private final MapActivity mapActivity; private OsmandApplication app; @@ -277,9 +277,10 @@ public class RulerControlLayer extends OsmandMapLayer { resetDrawingPaths(); } RenderingLineAttributes attrs = mode == RulerMode.FIRST ? circleAttrs : circleAttrsAlt; + int compassCircleId = getCompassCircleId(tb, center); for (int i = 1; i <= cacheDistances.size(); i++) { - if (showCompass && i == COMPASS_CIRCLE_ID) { - drawCompassCircle(canvas, tb, center, attrs); + if (showCompass && i == compassCircleId) { + drawCompassCircle(canvas, tb, compassCircleId, center, attrs); } else { drawCircle(canvas, tb, i, center, attrs); } @@ -293,6 +294,39 @@ public class RulerControlLayer extends OsmandMapLayer { rightWidgetsPanel.getVisibility() == View.VISIBLE; } + private int getCompassCircleId(RotatedTileBox tileBox, QuadPoint center) { + int compassCircleId = 2; + float radiusLength = radius * compassCircleId; + float top = center.y - radiusLength; + float bottom = center.y + radiusLength; + float left = center.x - radiusLength; + float right = center.x + radiusLength; + + int width = tileBox.getPixWidth(); + int height = tileBox.getPixHeight(); + + if (top < 0) { + top = 0; + } + if (bottom > height) { + bottom = height; + } + if (left < 0) { + left = 0; + } + if (right > width) { + right = width; + } + int horizontal = (int) (bottom - top) / 2; + int vertical = (int) (right - left) / 2; + int minFittingRadius = Math.min(horizontal, vertical); + if (radiusLength > minFittingRadius * COMPASS_CIRCLE_FITTING_RADIUS_COEF) { + compassCircleId = 1; + } + + return compassCircleId; + } + private void updateHeading() { Float heading = mapActivity.getMapViewTrackingUtilities().getHeading(); if (heading != null && heading != cachedHeading) { @@ -512,10 +546,10 @@ public class RulerControlLayer extends OsmandMapLayer { return new float[]{x1, y1, x2, y2}; } - private void drawCompassCircle(Canvas canvas, RotatedTileBox tileBox, QuadPoint center, + private void drawCompassCircle(Canvas canvas, RotatedTileBox tileBox,int circleNumber, QuadPoint center, RenderingLineAttributes attrs) { if (!tileBox.isZoomAnimated()) { - float radiusLength = radius * COMPASS_CIRCLE_ID; + float radiusLength = radius * circleNumber; float innerRadiusLength = radiusLength - attrs.paint.getStrokeWidth() / 2; updateArcShader(radiusLength, center); @@ -541,7 +575,7 @@ public class RulerControlLayer extends OsmandMapLayer { canvas.drawPath(arrow, triangleHeadingPaint); canvas.rotate(-cachedHeading, center.x, center.y); - String distance = cacheDistances.get(COMPASS_CIRCLE_ID - 1); + String distance = cacheDistances.get(circleNumber - 1); String heading = OsmAndFormatter.getFormattedAzimuth(cachedHeading, app) + " " + getCardinalDirectionForDegrees(cachedHeading); float[] textCoords = calculateTextCoords(distance, heading, radiusLength + AndroidUtils.dpToPx(app, 16), center, attrs); canvas.rotate(-tileBox.getRotate(), center.x, center.y); From 1d241433c21be6016a716dcb45357a83478dcaa6 Mon Sep 17 00:00:00 2001 From: Chumva Date: Fri, 19 Jul 2019 09:59:19 +0300 Subject: [PATCH 02/17] Fix #7280 --- OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java b/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java index 2973bdfb4d..37bfc70cf2 100644 --- a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java @@ -450,7 +450,6 @@ public class RulerControlLayer extends OsmandMapLayer { cacheTileX = tb.getCenterTileX(); cacheTileY = tb.getCenterTileY(); cacheMapDensity = mapDensity.get(); - cacheDistances.clear(); updateDistance(tb); } } @@ -484,6 +483,7 @@ public class RulerControlLayer extends OsmandMapLayer { } private void updateText() { + cacheDistances.clear(); double maxCircleRadius = maxRadius; int i = 1; while ((maxCircleRadius -= radius) > 0) { From 806b7374d9a8c787e60dd17e990be406d1d76545 Mon Sep 17 00:00:00 2001 From: crimean Date: Sat, 20 Jul 2019 14:05:50 +0300 Subject: [PATCH 03/17] Fix gpx email writing --- OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java index 4063c55b99..73417f2ed1 100644 --- a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java @@ -1577,6 +1577,7 @@ public class GPXUtilities { serializer.startTag(null, "email"); serializer.attribute(null, "id", idAndDomain[0]); serializer.attribute(null, "domain", idAndDomain[1]); + serializer.endTag(null, "email"); } } writeNotNullTextWithAttribute(serializer, "link", "href", author.link); From c6495204907784262a70e288b9c2b8e3280b4439 Mon Sep 17 00:00:00 2001 From: Chumva Date: Sat, 13 Jul 2019 16:34:25 +0300 Subject: [PATCH 04/17] Add timeline empty state and date control buttons (cherry picked from commit 3788f5183c3488f5893dafd25fad1179a6ef7529) --- .../res/layout/empty_state_timeline.xml | 40 ++++++++++++ .../res/layout/fragment_timeline_tab.xml | 54 +++++++++++++--- OsmAnd-telegram/res/values/strings.xml | 2 + .../osmand/telegram/ui/TimelineTabFragment.kt | 64 ++++++++++++------- 4 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 OsmAnd-telegram/res/layout/empty_state_timeline.xml diff --git a/OsmAnd-telegram/res/layout/empty_state_timeline.xml b/OsmAnd-telegram/res/layout/empty_state_timeline.xml new file mode 100644 index 0000000000..ca2702a578 --- /dev/null +++ b/OsmAnd-telegram/res/layout/empty_state_timeline.xml @@ -0,0 +1,40 @@ + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml b/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml index a26a995ccb..3c46a03a92 100644 --- a/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml +++ b/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml @@ -1,6 +1,7 @@ @@ -20,11 +21,11 @@ android:letterSpacing="@dimen/title_letter_spacing" android:maxLines="1" android:paddingLeft="@dimen/content_padding_standard" + android:paddingTop="@dimen/content_padding_standard" android:paddingRight="@dimen/content_padding_standard" + android:paddingBottom="12dp" android:text="@string/timeline" android:textColor="@color/app_bar_title_light" - android:paddingTop="@dimen/content_padding_standard" - android:paddingBottom="12dp" android:textSize="@dimen/title_text_size" app:typeface="@string/font_roboto_mono_bold" /> @@ -84,6 +85,19 @@ android:paddingLeft="@dimen/content_padding_standard" android:paddingRight="@dimen/content_padding_standard"> + + + + + @@ -112,13 +140,23 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent"> + + + + + + diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index be7c8b5454..0392d839e2 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,5 +1,7 @@ + We don`t have collected data for the selected day + No data Select Minimum logging distance Filter: minimum distance to log a new point diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt index f5c4c44273..0e943c69f9 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt @@ -16,15 +16,16 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView +import android.widget.LinearLayout import android.widget.Switch import android.widget.TextView -import net.osmand.PlatformUtil import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.helpers.LocationMessages import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.telegram.helpers.TelegramUiHelper.ListItem import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder +import net.osmand.telegram.ui.views.EmptyStateRecyclerView import net.osmand.telegram.utils.AndroidUtils import net.osmand.telegram.utils.OsmandFormatter import java.util.* @@ -32,8 +33,6 @@ import java.util.* class TimelineTabFragment : Fragment() { - private val log = PlatformUtil.getLog(TimelineTabFragment::class.java) - private val app: TelegramApplication get() = activity?.application as TelegramApplication @@ -43,11 +42,12 @@ class TimelineTabFragment : Fragment() { private lateinit var adapter: LiveNowListAdapter private lateinit var dateBtn: TextView + private lateinit var previousDateBtn: ImageView + private lateinit var nextDateBtn: ImageView private lateinit var mainView: View private lateinit var switcher: Switch - private var start = 0L - private var end = 0L + private lateinit var calendar: Calendar private var updateEnable: Boolean = false @@ -59,15 +59,16 @@ class TimelineTabFragment : Fragment() { mainView = inflater.inflate(R.layout.fragment_timeline_tab, container, false) val appBarLayout = mainView.findViewById(R.id.app_bar_layout) - val calendar = Calendar.getInstance() - start = getStartOfDay(calendar) - end = getEndOfDay(calendar) + calendar = Calendar.getInstance() AndroidUtils.addStatusBarPadding19v(context!!, appBarLayout) adapter = LiveNowListAdapter() - mainView.findViewById(R.id.recycler_view).apply { + + val emptyView = mainView.findViewById(R.id.empty_view) + mainView.findViewById(R.id.recycler_view).apply { layoutManager = LinearLayoutManager(context) adapter = this@TimelineTabFragment.adapter + setEmptyView(emptyView) } switcher = mainView.findViewById(R.id.monitoring_switcher) @@ -88,7 +89,25 @@ class TimelineTabFragment : Fragment() { } setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_start), null, null, null) setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) - text = OsmandFormatter.getFormattedDate(start / 1000) + } + updateDateButton() + + previousDateBtn = mainView.findViewById(R.id.date_btn_previous).apply { + setImageDrawable(getPressedStateIcon(R.drawable.ic_arrow_back)) + setOnClickListener { + calendar.add(Calendar.DAY_OF_MONTH, -1) + updateList() + updateDateButton() + } + } + + nextDateBtn = mainView.findViewById(R.id.date_btn_next).apply { + setImageDrawable(getPressedStateIcon(R.drawable.ic_arrow_forward)) + setOnClickListener { + calendar.add(Calendar.DAY_OF_MONTH, 1) + updateList() + updateDateButton() + } } mainView.findViewById(R.id.swipe_refresh).apply { @@ -122,25 +141,21 @@ class TimelineTabFragment : Fragment() { fun tabClosed() {} private fun selectDate() { - val dateFromDialog = + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> - val calendar = Calendar.getInstance() + calendar = Calendar.getInstance() calendar.set(Calendar.YEAR, year) calendar.set(Calendar.MONTH, monthOfYear) calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) - start = getStartOfDay(calendar) - end = getEndOfDay(calendar) - updateList() updateDateButton() } - val startCalendar = Calendar.getInstance() - startCalendar.timeInMillis = start - DatePickerDialog(context, dateFromDialog, - startCalendar.get(Calendar.YEAR), - startCalendar.get(Calendar.MONTH), - startCalendar.get(Calendar.DAY_OF_MONTH) + DatePickerDialog( + context, dateSetListener, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) ).show() } @@ -161,7 +176,7 @@ class TimelineTabFragment : Fragment() { } private fun updateDateButton() { - dateBtn.text = OsmandFormatter.getFormattedDate(start / 1000) + dateBtn.text = OsmandFormatter.getFormattedDate(getStartOfDay(calendar) / 1000) } private fun getPressedStateIcon(@DrawableRes iconId: Int): Drawable? { @@ -187,6 +202,8 @@ class TimelineTabFragment : Fragment() { private fun updateList() { val res = mutableListOf() + val start = getStartOfDay(calendar) + val end = getEndOfDay(calendar) app.locationMessages.getIngoingUserLocations(start, end).forEach { TelegramUiHelper.userLocationsToChatItem(telegramHelper, it)?.also { chatItem -> res.add(chatItem) @@ -303,12 +320,13 @@ class TimelineTabFragment : Fragment() { } } - data class UITrackData ( + data class UITrackData( var dist: Float, var points: Int, var minTime: Long, var maxTime: Long ) + companion object { private const val ADAPTER_UPDATE_INTERVAL_MIL = 15 * 1000L // 15 sec } From a5d77a065200a3df9efdc782c3fb656227aeb5bb Mon Sep 17 00:00:00 2001 From: Chumva Date: Mon, 15 Jul 2019 15:09:29 +0300 Subject: [PATCH 05/17] Live track settings initial commit (cherry picked from commit d2e811d6e7306851d80f2b38d6aa8ddceca0d71f) --- .../res/layout/fragment_user_gpx_info.xml | 18 +++++ OsmAnd-telegram/res/values/colors.xml | 2 + .../net/osmand/telegram/TelegramSettings.kt | 70 ++++++++++++++++++- .../osmand/telegram/ui/UserGpxInfoFragment.kt | 40 ++++++++++- 4 files changed, 127 insertions(+), 3 deletions(-) diff --git a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml index 4d7636dc1b..205496fddf 100644 --- a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml +++ b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml @@ -188,6 +188,24 @@ + + #5959FF + #F54522 + diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index fe0c5f2bf1..c90caecd77 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -27,6 +27,8 @@ val ADDITIONAL_ACTIVE_TIME_VALUES_SEC = listOf(15 * 60L, 30 * 60L, 60 * 60L, 180 const val SHARE_DEVICES_KEY = "devices" +const val LIVE_TRACKS_KEY = "live_tracks" + private val SEND_MY_LOC_VALUES_SEC = listOf(1L, 2L, 3L, 5L, 10L, 15L, 30L, 60L, 90L, 2 * 60L, 3 * 60L, 5 * 60L) private val STALE_LOC_VALUES_SEC = @@ -110,6 +112,7 @@ class TelegramSettings(private val app: TelegramApplication) { private var shareChatsInfo = ConcurrentHashMap() private var hiddenOnMapChats: Set = emptySet() private var shareDevices: Set = emptySet() + private var liveTracksInfo: Set = emptySet() var sharingStatusChanges = ConcurrentLinkedQueue() @@ -163,6 +166,19 @@ class TelegramSettings(private val app: TelegramApplication) { fun isShowingChatOnMap(chatId: Long) = !hiddenOnMapChats.contains(chatId) + fun isLiveTrackEnabled(userId: Int, chatId: Long, deviceName: String) = + liveTracksInfo.any { (it.chatId == chatId && it.userId == userId && it.deviceName == deviceName) } + + fun updateLiveTrack(userId: Int, chatId: Long, deviceName: String, enable: Boolean) { + val tracksInfo = liveTracksInfo.toMutableList() + if (enable) { + tracksInfo.add(LiveTrackInfo(userId, chatId, deviceName)) + } else { + tracksInfo.remove(LiveTrackInfo(userId, chatId, deviceName)) + } + liveTracksInfo = tracksInfo.toHashSet() + } + fun removeNonexistingChats(presentChatIds: List) { val hiddenChats = hiddenOnMapChats.toMutableList() hiddenChats.intersect(presentChatIds) @@ -610,6 +626,11 @@ class TelegramSettings(private val app: TelegramApplication) { edit.putString(PROXY_PREFERENCES_KEY, jsonObjectProxy.toString()) } + val jsonArrayLiveTracks = convertLiveTracksInfoToJson() + if (jsonArrayLiveTracks != null) { + edit.putString(LIVE_TRACKS_KEY, jsonArrayLiveTracks.toString()) + } + edit.apply() } @@ -674,7 +695,13 @@ class TelegramSettings(private val app: TelegramApplication) { try { parseProxyPreferences(JSONObject(prefs.getString(PROXY_PREFERENCES_KEY, ""))) } catch (e: JSONException) { - e.printStackTrace() + log.error(e) + } + + try { + parseLiveTracks(JSONArray(prefs.getString(LIVE_TRACKS_KEY, ""))) + } catch (e: JSONException) { + log.error(e) } } @@ -699,6 +726,23 @@ class TelegramSettings(private val app: TelegramApplication) { } } + private fun convertLiveTracksInfoToJson(): JSONArray? { + return try { + JSONArray().apply { + liveTracksInfo.forEach { liveTrackInfo -> + val obj = JSONObject() + obj.put(LiveTrackInfo.USER_ID, liveTrackInfo.userId) + obj.put(LiveTrackInfo.CHAT_ID, liveTrackInfo.chatId) + obj.put(LiveTrackInfo.DEVICE_NAME, liveTrackInfo.deviceName) + put(obj) + } + } + } catch (e: JSONException) { + log.error(e) + null + } + } + private fun convertProxyPrefToJson(): JSONObject? { return try { val proxyPref = currentProxyPref @@ -746,7 +790,7 @@ class TelegramSettings(private val app: TelegramApplication) { } jArray } catch (e: JSONException) { - e.printStackTrace() + log.error(e) null } } @@ -799,6 +843,19 @@ class TelegramSettings(private val app: TelegramApplication) { } } + private fun parseLiveTracks(json: JSONArray) { + val list = mutableListOf() + for (i in 0 until json.length()) { + val obj = json.getJSONObject(i) + val userId = obj.optInt(LiveTrackInfo.USER_ID) + val chatId = obj.optLong(LiveTrackInfo.CHAT_ID) + val deviceName = obj.optString(LiveTrackInfo.DEVICE_NAME) + + list.add(LiveTrackInfo(userId, chatId, deviceName)) + } + liveTracksInfo = list.toHashSet() + } + private fun parseShareDevices(json: String) { shareDevices = OsmandApiUtils.parseJsonContents(json).toHashSet() } @@ -1122,6 +1179,15 @@ class TelegramSettings(private val app: TelegramApplication) { } } + data class LiveTrackInfo(val userId: Int, val chatId: Long, val deviceName: String) { + companion object { + + internal const val USER_ID = "userId" + internal const val CHAT_ID = "chatId" + internal const val DEVICE_NAME = "deviceName" + } + } + enum class ProxyType { MTPROTO, SOCKS5 } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index a2b689cf0d..722c90b35e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.os.Build import android.os.Bundle import android.support.design.widget.Snackbar @@ -46,6 +47,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { private lateinit var timeStartBtn: TextView private lateinit var dateEndBtn: TextView private lateinit var timeEndBtn: TextView + private lateinit var liveBtn: TextView private lateinit var avgElevationTv: TextView private lateinit var avgSpeedTv: TextView @@ -122,6 +124,15 @@ class UserGpxInfoFragment : BaseDialogFragment() { dateEndBtn = mainView.findViewById(R.id.date_end_btn) timeEndBtn = mainView.findViewById(R.id.time_end_btn) + liveBtn = mainView.findViewById(R.id.live_btn).apply { + setOnClickListener { + val enabled = settings.isLiveTrackEnabled(userId, chatId, deviceName) + settings.updateLiveTrack(userId, chatId, deviceName, !enabled) + updateLiveTrackBtn() + } + } + updateLiveTrackBtn() + dateStartBtn.setOnClickListener { selectStartDate() } timeStartBtn.setOnClickListener { selectStartTime() } dateEndBtn.setOnClickListener { selectEndDate() } @@ -241,6 +252,15 @@ class UserGpxInfoFragment : BaseDialogFragment() { textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) } + private fun updateLiveTrackBtn() { + val enabled = settings.isLiveTrackEnabled(userId, chatId, deviceName) + val icon = getLiveTrackBtnIcon(enabled) + val normalTextColor = if (enabled) R.color.ctrl_active_light else R.color.secondary_text_light + + liveBtn.setTextColor(AndroidUtils.createPressedColorStateList(app, true, normalTextColor, R.color.ctrl_light)) + liveBtn.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null) + } + private fun getShareIcon(): Drawable? { val normal = app.uiUtils.getActiveIcon(R.drawable.ic_action_share) if (Build.VERSION.SDK_INT >= 21) { @@ -252,9 +272,27 @@ class UserGpxInfoFragment : BaseDialogFragment() { return normal } + private fun getLiveTrackBtnIcon(enabled: Boolean): Drawable? { + val iconColor = if (enabled) R.color.live_track_active_icon else R.color.icon_light + + val layers = arrayOfNulls(2) + layers[0] = app.uiUtils.getIcon(R.drawable.ic_action_round_shape) + layers[1] = app.uiUtils.getIcon(R.drawable.ic_action_record, iconColor) + + if (Build.VERSION.SDK_INT >= 21 && !enabled) { + val normal = layers[1] + val active = app.uiUtils.getIcon(R.drawable.ic_action_record, R.color.live_track_active_icon) + if (normal != null && active != null) { + layers[1] = AndroidUtils.createPressedStateListDrawable(normal, active) + } + } + + return LayerDrawable(layers) + } + private fun updateGpxInfo() { checkTime() - locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId,deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis) + locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis) gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages).firstOrNull()?:GPXUtilities.GPXFile() updateGPXStatisticRow() From c2cf37113b3650eadc35da739ce56db518c9e942 Mon Sep 17 00:00:00 2001 From: Chumva Date: Mon, 15 Jul 2019 17:09:28 +0300 Subject: [PATCH 06/17] Add auto map updates in UserGpxInfoFragment (cherry picked from commit 44caece1d44afd57f9bbcd12c4d057ae364d3d48) --- OsmAnd-telegram/AndroidManifest.xml | 1 + .../telegram/helpers/LocationMessages.kt | 5 ++- .../osmand/telegram/ui/UserGpxInfoFragment.kt | 43 +++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/OsmAnd-telegram/AndroidManifest.xml b/OsmAnd-telegram/AndroidManifest.xml index f5f012c063..eca282af51 100644 --- a/OsmAnd-telegram/AndroidManifest.xml +++ b/OsmAnd-telegram/AndroidManifest.xml @@ -3,6 +3,7 @@ package="net.osmand.telegram"> + diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt index ff46e36e6a..6ccc1b6c20 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt @@ -76,6 +76,9 @@ class LocationMessages(val app: TelegramApplication) { return dbHelper.getMessagesForUser(userId, start, end) } + fun getLastLocationInfoForUserInChat(userId: Int, chatId: Long, deviceName: String) = + lastLocationPoints.sortedByDescending { it.time }.firstOrNull { it.userId == userId && it.chatId == chatId && it.deviceName == deviceName } + fun addBufferedMessage(message: BufferMessage) { log.debug("addBufferedMessage $message") val messages = mutableListOf(*this.bufferedMessages.toTypedArray()) @@ -91,7 +94,7 @@ class LocationMessages(val app: TelegramApplication) { val content = OsmandLocationUtils.parseMessageContent(message, app.telegramHelper) if (content != null) { val deviceName = if (content is OsmandLocationUtils.MessageOsmAndBotLocation) content.deviceName else "" - val previousLocationMessage = lastLocationPoints.sortedBy { it.time }.firstOrNull { + val previousLocationMessage = lastLocationPoints.sortedByDescending { it.time }.firstOrNull { it.userId == senderId && it.chatId == message.chatId && it.deviceName == deviceName && it.type == type } if (previousLocationMessage == null || content.lastUpdated * 1000L > previousLocationMessage.time) { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index 722c90b35e..3b38aac634 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -10,6 +10,8 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Build import android.os.Bundle +import android.os.Handler +import android.os.Message import android.support.design.widget.Snackbar import android.support.v4.app.FragmentManager import android.util.DisplayMetrics @@ -63,6 +65,10 @@ class UserGpxInfoFragment : BaseDialogFragment() { private var chatId = -1L private var deviceName = "" + private var handler: Handler = Handler() + + private var endTimeChanged: Boolean = false + override fun onCreateView( inflater: LayoutInflater, parent: ViewGroup?, @@ -126,9 +132,12 @@ class UserGpxInfoFragment : BaseDialogFragment() { liveBtn = mainView.findViewById(R.id.live_btn).apply { setOnClickListener { - val enabled = settings.isLiveTrackEnabled(userId, chatId, deviceName) - settings.updateLiveTrack(userId, chatId, deviceName, !enabled) + val enabled = !liveTrackEnabled() + settings.updateLiveTrack(userId, chatId, deviceName, enabled) updateLiveTrackBtn() + if (enabled) { + startHandler() + } } } updateLiveTrackBtn() @@ -215,6 +224,21 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } + private fun startHandler() { + log.debug("startHandler") + if (!handler.hasMessages(TRACK_UPDATE_MSG_ID)) { + val msg = Message.obtain(handler) { + log.debug("Handler postDelayed") + if (isResumed && liveTrackEnabled()) { + updateGpxInfo() + startHandler() + } + } + msg.what = TRACK_UPDATE_MSG_ID + handler.sendMessageDelayed(msg, UPDATE_TRACK_INTERVAL_MS) + } + } + private fun canOsmandCreateBitmap(): Boolean { val version = AndroidUtils.getAppVersionCode(app, app.settings.appToConnectPackage) return version >= MIN_OSMAND_BITMAP_VERSION_CODE @@ -248,12 +272,14 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } + private fun liveTrackEnabled() = settings.isLiveTrackEnabled(userId, chatId, deviceName) + private fun setupBtnTextColor(textView: TextView) { textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) } private fun updateLiveTrackBtn() { - val enabled = settings.isLiveTrackEnabled(userId, chatId, deviceName) + val enabled = liveTrackEnabled() val icon = getLiveTrackBtnIcon(enabled) val normalTextColor = if (enabled) R.color.ctrl_active_light else R.color.secondary_text_light @@ -301,6 +327,13 @@ class UserGpxInfoFragment : BaseDialogFragment() { } private fun checkTime() { + val enabled = liveTrackEnabled() + if (enabled && !endTimeChanged) { + val locationMessage = app.locationMessages.getLastLocationInfoForUserInChat(userId, chatId, deviceName) + if (locationMessage != null && locationMessage.time > endCalendar.timeInMillis) { + endCalendar.timeInMillis = locationMessage.time + } + } if (startCalendar.timeInMillis > endCalendar.timeInMillis) { val time = startCalendar.timeInMillis startCalendar.timeInMillis = endCalendar.timeInMillis @@ -393,6 +426,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { endCalendar.set(Calendar.YEAR, year) endCalendar.set(Calendar.MONTH, monthOfYear) endCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + endTimeChanged = true updateGpxInfo() } DatePickerDialog(context, dateFromDialog, @@ -406,6 +440,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { TimePickerDialog.OnTimeSetListener { _, hours, minutes -> endCalendar.set(Calendar.HOUR_OF_DAY, hours) endCalendar.set(Calendar.MINUTE, minutes) + endTimeChanged = true updateGpxInfo() }, 0, 0, true).show() } @@ -421,6 +456,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { private const val GPX_TRACK_COLOR = -65536 private const val MIN_OSMAND_BITMAP_VERSION_CODE = 330 + private const val UPDATE_TRACK_INTERVAL_MS = 30 * 1000L // 30 sec + private const val TRACK_UPDATE_MSG_ID = 1001 fun showInstance(fm: FragmentManager,userId:Int,chatId:Long,deviceName:String, start: Long, end: Long): Boolean { return try { From a33be766188f42a53748b31dffc7611bfec2f7cc Mon Sep 17 00:00:00 2001 From: Chumva Date: Tue, 16 Jul 2019 14:16:53 +0300 Subject: [PATCH 07/17] Remove unnecessary copy of GpxUtilities (cherry picked from commit f2c63eeb2aa2a5ab5685270fcf640c613b41c9ad) --- .../osmand/telegram/ui/UserGpxInfoFragment.kt | 26 +- .../osmand/telegram/utils/GPXUtilities.java | 1808 ----------------- .../osmand/telegram/utils/LocationPoint.java | 24 - .../telegram/utils/OsmandLocationUtils.kt | 55 +- 4 files changed, 47 insertions(+), 1866 deletions(-) delete mode 100644 OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java delete mode 100644 OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index 3b38aac634..de3652f8bb 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -23,6 +23,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast +import net.osmand.GPXUtilities import net.osmand.PlatformUtil import net.osmand.aidl.gpx.AGpxBitmap import net.osmand.telegram.R @@ -30,7 +31,10 @@ import net.osmand.telegram.TelegramSettings import net.osmand.telegram.helpers.LocationMessages import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.TelegramUiHelper -import net.osmand.telegram.utils.* +import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.OsmandFormatter +import net.osmand.telegram.utils.OsmandLocationUtils +import net.osmand.telegram.utils.TRACKS_DIR import net.osmand.util.Algorithms import java.io.File import java.text.SimpleDateFormat @@ -42,7 +46,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { private val uiUtils get() = app.uiUtils - private var gpxFile = GPXUtilities.GPXFile() + private var gpxFile = GPXUtilities.GPXFile("") private lateinit var mainView: View private lateinit var dateStartBtn: TextView @@ -101,8 +105,10 @@ class UserGpxInfoFragment : BaseDialogFragment() { openGpx(path) } - override fun onSavingGpxError(warnings: List?) { - Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show() + override fun onSavingGpxError(warning: String?) { + warning?.also { + Toast.makeText(app, it, Toast.LENGTH_LONG).show() + } } }) } @@ -198,8 +204,10 @@ class UserGpxInfoFragment : BaseDialogFragment() { (activity as MainActivity).shareGpx(path) } - override fun onSavingGpxError(warnings: List?) { - Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show() + override fun onSavingGpxError(warning: String?) { + warning?.also { + Toast.makeText(app, it, Toast.LENGTH_LONG).show() + } } }) } @@ -320,7 +328,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { checkTime() locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis) - gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages).firstOrNull()?:GPXUtilities.GPXFile() + gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app.packageName, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName) updateGPXStatisticRow() updateDateAndTimeButtons() updateGPXMap() @@ -390,8 +398,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } - override fun onSavingGpxError(warnings: List?) { - log.debug("onSavingGpxError ${warnings?.firstOrNull()}") + override fun onSavingGpxError(warning: String?) { + log.debug("onSavingGpxError $warning") } }) } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java b/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java deleted file mode 100644 index b27293c40b..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java +++ /dev/null @@ -1,1808 +0,0 @@ -package net.osmand.telegram.utils; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import net.osmand.Location; -import net.osmand.PlatformUtil; -import net.osmand.data.QuadRect; -import net.osmand.data.RotatedTileBox; -import net.osmand.telegram.TelegramApplication; -import net.osmand.util.Algorithms; - -import org.apache.commons.logging.Log; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.TimeZone; - -// copy from net.osmand.plus.GPXUtilities and changes done to WptPt and GPXFile (userId,chatId) - -public class GPXUtilities { - public final static Log log = PlatformUtil.getLog(GPXUtilities.class); - - private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$ - private final static String GPX_TIME_FORMAT_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; //$NON-NLS-1$ - - private final static NumberFormat latLonFormat = new DecimalFormat("0.00#####", new DecimalFormatSymbols( - new Locale("EN", "US"))); - private final static NumberFormat decimalFormat = new DecimalFormat("#.###", new DecimalFormatSymbols( - new Locale("EN", "US"))); - - public static class GPXExtensions { - Map extensions = null; - - public Map getExtensionsToRead() { - if (extensions == null) { - return Collections.emptyMap(); - } - return extensions; - } - - @ColorInt - public int getColor(@ColorInt int defColor) { - String clrValue = null; - if (extensions != null) { - clrValue = extensions.get("color"); - if (clrValue == null) { - clrValue = extensions.get("colour"); - } - if (clrValue == null) { - clrValue = extensions.get("displaycolor"); - } - } - if (clrValue != null && clrValue.length() > 0) { - try { - return Color.parseColor(clrValue.toUpperCase()); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - return defColor; - } - - public void setColor(int color) { - getExtensionsToWrite().put("color", Algorithms.colorToString(color)); - } - - public void removeColor() { - getExtensionsToWrite().remove("color"); - } - - public Map getExtensionsToWrite() { - if (extensions == null) { - extensions = new LinkedHashMap<>(); - } - return extensions; - } - - } - - public static class Elevation { - public float distance; - public int time; - public float elevation; - } - - public static class Speed { - public float distance; - public int time; - public float speed; - } - - public static class WptPt extends GPXExtensions implements LocationPoint { - public boolean firstPoint = false; - public boolean lastPoint = false; - public int userId; - public long chatId; - public double lat; - public double lon; - public String name = null; - public String link = null; - // previous undocumented feature 'category' ,now 'type' - public String category = null; - public String desc = null; - public String comment = null; - // by default - public long time = 0; - public double ele = Double.NaN; - public double speed = 0; - public double hdop = Double.NaN; - public boolean deleted = false; - public int colourARGB = 0; // point colour (used for altitude/speed colouring) - public double distance = 0.0; // cumulative distance, if in a track - - public WptPt() { - } - - public WptPt(WptPt wptPt) { - this.lat = wptPt.lat; - this.lon = wptPt.lon; - this.name = wptPt.name; - this.link = wptPt.link; - - this.category = wptPt.category; - this.desc = wptPt.desc; - this.comment = wptPt.comment; - - this.time = wptPt.time; - this.ele = wptPt.ele; - this.speed = wptPt.speed; - this.hdop = wptPt.hdop; - this.deleted = wptPt.deleted; - this.colourARGB = wptPt.colourARGB; - this.distance = wptPt.distance; - } - - public void setDistance(double dist) { - distance = dist; - } - - public double getDistance() { - return distance; - } - - @Override - public int getColor() { - return getColor(0); - } - - @Override - public double getLatitude() { - return lat; - } - - @Override - public double getLongitude() { - return lon; - } - - -// @Override -// public PointDescription getPointDescription(Context ctx) { -// return new PointDescription(PointDescription.POINT_TYPE_WPT, name); -// } - - public WptPt(double lat, double lon, long time, double ele, double speed, double hdop) { - this.lat = lat; - this.lon = lon; - this.time = time; - this.ele = ele; - this.speed = speed; - this.hdop = hdop; - } - - @Override - public boolean isVisible() { - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((category == null) ? 0 : category.hashCode()); - result = prime * result + ((desc == null) ? 0 : desc.hashCode()); - result = prime * result + ((comment == null) ? 0 : comment.hashCode()); - result = prime * result + ((lat == 0) ? 0 : Double.valueOf(lat).hashCode()); - result = prime * result + ((lon == 0) ? 0 : Double.valueOf(lon).hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null || getClass() != obj.getClass()) - return false; - WptPt other = (WptPt) obj; - return Algorithms.objectEquals(other.name, name) - && Algorithms.objectEquals(other.category, category) - && Algorithms.objectEquals(other.lat, lat) - && Algorithms.objectEquals(other.lon, lon) - && Algorithms.objectEquals(other.desc, desc); - } - } - - public static class TrkSegment extends GPXExtensions { - public boolean generalSegment = false; - - public List points = new ArrayList<>(); - -// public List renders = new ArrayList<>(); - - public List splitByDistance(double meters) { - return split(getDistanceMetric(), getTimeSplit(), meters); - } - - public List splitByTime(int seconds) { - return split(getTimeSplit(), getDistanceMetric(), seconds); - } - - private List split(SplitMetric metric, SplitMetric secondaryMetric, double metricLimit) { - List splitSegments = new ArrayList<>(); - splitSegment(metric, secondaryMetric, metricLimit, splitSegments, this); - return convert(splitSegments); - } - -// public void drawRenderers(double zoom, Paint p, Canvas c, RotatedTileBox tb) { -// for (Renderable.RenderableSegment rs : renders) { -// rs.drawSegment(zoom, p, c, tb); -// } -// } - } - - public static class Track extends GPXExtensions { - public String name = null; - public String desc = null; - public List segments = new ArrayList<>(); - public boolean generalTrack = false; - - } - - public static class Route extends GPXExtensions { - public String name = null; - public String desc = null; - public List points = new ArrayList<>(); - - } - - public static class Metadata extends GPXExtensions { - public String desc; - - @Nullable - public String getArticleTitle() { - return getExtensionsToRead().get("article_title"); - } - - @Nullable - public String getArticleLang() { - return getExtensionsToRead().get("article_lang"); - } - } - - public static class GPXTrackAnalysis { - public float totalDistance = 0; - public int totalTracks = 0; - public long startTime = Long.MAX_VALUE; - public long endTime = Long.MIN_VALUE; - public long timeSpan = 0; - //Next few lines for Issue 3222 heuristic testing only - //public long timeMoving0 = 0; - //public float totalDistanceMoving0 = 0; - public long timeMoving = 0; - public float totalDistanceMoving = 0; - - public double diffElevationUp = 0; - public double diffElevationDown = 0; - public double avgElevation = 0; - public double minElevation = 99999; - public double maxElevation = -100; - - public float minSpeed = Float.MAX_VALUE; - public float maxSpeed = 0; - public float avgSpeed; - - public int points; - public int wptPoints = 0; - - public Set wptCategoryNames; - - public double metricEnd; - public double secondaryMetricEnd; - public WptPt locationStart; - public WptPt locationEnd; - - public double left = 0; - public double right = 0; - public double top = 0; - public double bottom = 0; - - public boolean isTimeSpecified() { - return startTime != Long.MAX_VALUE && startTime != 0; - } - - public boolean isTimeMoving() { - return timeMoving != 0; - } - - public boolean isElevationSpecified() { - return maxElevation != -100; - } - - public boolean hasSpeedInTrack() { - return hasSpeedInTrack; - } - - public boolean isBoundsCalculated() { - return left != 0 && right != 0 && top != 0 && bottom != 0; - } - - public List elevationData; - public List speedData; - - public boolean hasElevationData; - public boolean hasSpeedData; - public boolean hasSpeedInTrack = false; - - public boolean isSpeedSpecified() { - return avgSpeed > 0; - } - - - public static GPXTrackAnalysis segment(long filetimestamp, TrkSegment segment) { - return new GPXTrackAnalysis().prepareInformation(filetimestamp, new SplitSegment(segment)); - } - - public GPXTrackAnalysis prepareInformation(long filestamp, SplitSegment... splitSegments) { - float[] calculations = new float[1]; - - long startTimeOfSingleSegment = 0; - long endTimeOfSingleSegment = 0; - - float totalElevation = 0; - int elevationPoints = 0; - int speedCount = 0; - int timeDiff = 0; - double totalSpeedSum = 0; - points = 0; - - double channelThresMin = 10; // Minimum oscillation amplitude considered as relevant or as above noise for accumulated Ascent/Descent analysis - double channelThres = channelThresMin; // Actual oscillation amplitude considered as above noise (dynamic channel adjustment, accomodates depedency on current VDOP/getAccuracy if desired) - double channelBase; - double channelTop; - double channelBottom; - boolean climb = false; - - elevationData = new ArrayList<>(); - speedData = new ArrayList<>(); - - for (SplitSegment s : splitSegments) { - final int numberOfPoints = s.getNumberOfPoints(); - - channelBase = 99999; - channelTop = channelBase; - channelBottom = channelBase; - //channelThres = channelThresMin; //only for dynamic channel adjustment - - float segmentDistance = 0f; - metricEnd += s.metricEnd; - secondaryMetricEnd += s.secondaryMetricEnd; - points += numberOfPoints; - for (int j = 0; j < numberOfPoints; j++) { - WptPt point = s.get(j); - if (j == 0 && locationStart == null) { - locationStart = point; - } - if (j == numberOfPoints - 1) { - locationEnd = point; - } - long time = point.time; - if (time != 0) { - if (s.metricEnd == 0) { - if (s.segment.generalSegment) { - if (point.firstPoint) { - startTimeOfSingleSegment = time; - } else if (point.lastPoint) { - endTimeOfSingleSegment = time; - } - if (startTimeOfSingleSegment != 0 && endTimeOfSingleSegment != 0) { - timeSpan += endTimeOfSingleSegment - startTimeOfSingleSegment; - startTimeOfSingleSegment = 0; - endTimeOfSingleSegment = 0; - } - } - } - startTime = Math.min(startTime, time); - endTime = Math.max(endTime, time); - } - - if (left == 0 && right == 0) { - left = point.getLongitude(); - right = point.getLongitude(); - top = point.getLatitude(); - bottom = point.getLatitude(); - } else { - left = Math.min(left, point.getLongitude()); - right = Math.max(right, point.getLongitude()); - top = Math.max(top, point.getLatitude()); - bottom = Math.min(bottom, point.getLatitude()); - } - - double elevation = point.ele; - Elevation elevation1 = new Elevation(); - if (!Double.isNaN(elevation)) { - totalElevation += elevation; - elevationPoints++; - minElevation = Math.min(elevation, minElevation); - maxElevation = Math.max(elevation, maxElevation); - - elevation1.elevation = (float) elevation; - } else { - elevation1.elevation = Float.NaN; - } - - float speed = (float) point.speed; - if (speed > 0) { - hasSpeedInTrack = true; - } - - // Trend channel analysis for elevation gain/loss, Hardy 2015-09-22, LPF filtering added 2017-10-26: - // - Detect the consecutive elevation trend channels: Only use the net elevation changes of each trend channel (i.e. between the turnarounds) to accumulate the Ascent/Descent values. - // - Perform the channel evaluation on Low Pass Filter (LPF) smoothed ele data instead of on the raw ele data - // Parameters: - // - channelThresMin (in meters): defines the channel turnaround detection, i.e. oscillations smaller than this are ignored as irrelevant or noise. - // - smoothWindow (number of points): is the LPF window - // NOW REMOVED, as no relevant examples found: Dynamic channel adjustment: To suppress unreliable measurement points, could relax the turnaround detection from the constant channelThresMin to channelThres which is e.g. based on the maximum VDOP of any point which contributed to the current trend. (Good assumption is VDOP=2*HDOP, which accounts for invisibility of lower hemisphere satellites.) - - // LPF smooting of ele data, usually smooth over odd number of values like 5 - final int smoothWindow = 5; - double eleSmoothed = Double.NaN; - int j2 = 0; - for (int j1 = -smoothWindow + 1; j1 <= 0; j1++) { - if ((j + j1 >= 0) && !Double.isNaN(s.get(j + j1).ele)) { - j2++; - if (!Double.isNaN(eleSmoothed)) { - eleSmoothed = eleSmoothed + s.get(j + j1).ele; - } else { - eleSmoothed = s.get(j + j1).ele; - } - } - } - if (!Double.isNaN(eleSmoothed)) { - eleSmoothed = eleSmoothed / j2; - } - - if (!Double.isNaN(eleSmoothed)) { - // Init channel - if (channelBase == 99999) { - channelBase = eleSmoothed; - channelTop = channelBase; - channelBottom = channelBase; - //channelThres = channelThresMin; //only for dynamic channel adjustment - } - // Channel maintenance - if (eleSmoothed > channelTop) { - channelTop = eleSmoothed; - //if (!Double.isNaN(point.hdop)) { - // channelThres = Math.max(channelThres, 2.0 * point.hdop); //only for dynamic channel adjustment - //} - } else if (eleSmoothed < channelBottom) { - channelBottom = eleSmoothed; - //if (!Double.isNaN(point.hdop)) { - // channelThres = Math.max(channelThres, 2.0 * point.hdop); //only for dynamic channel adjustment - //} - } - // Turnaround (breakout) detection - if ((eleSmoothed <= (channelTop - channelThres)) && (climb == true)) { - if ((channelTop - channelBase) >= channelThres) { - diffElevationUp += channelTop - channelBase; - } - channelBase = channelTop; - channelBottom = eleSmoothed; - climb = false; - //channelThres = channelThresMin; //only for dynamic channel adjustment - } else if ((eleSmoothed >= (channelBottom + channelThres)) && (climb == false)) { - if ((channelBase - channelBottom) >= channelThres) { - diffElevationDown += channelBase - channelBottom; - } - channelBase = channelBottom; - channelTop = eleSmoothed; - climb = true; - //channelThres = channelThresMin; //only for dynamic channel adjustment - } - // End detection without breakout - if (j == (numberOfPoints - 1)) { - if ((channelTop - channelBase) >= channelThres) { - diffElevationUp += channelTop - channelBase; - } - if ((channelBase - channelBottom) >= channelThres) { - diffElevationDown += channelBase - channelBottom; - } - } - } - - if (j > 0) { - WptPt prev = s.get(j - 1); - - // Old complete summation approach for elevation gain/loss - //if (!Double.isNaN(point.ele) && !Double.isNaN(prev.ele)) { - // double diff = point.ele - prev.ele; - // if (diff > 0) { - // diffElevationUp += diff; - // } else { - // diffElevationDown -= diff; - // } - //} - - // totalDistance += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon); - // using ellipsoidal 'distanceBetween' instead of spherical haversine (MapUtils.getDistance) is - // a little more exact, also seems slightly faster: - net.osmand.Location.distanceBetween(prev.lat, prev.lon, point.lat, point.lon, calculations); - totalDistance += calculations[0]; - segmentDistance += calculations[0]; - point.distance = segmentDistance; - timeDiff = (int) ((point.time - prev.time) / 1000); - - //Last resort: Derive speed values from displacement if track does not originally contain speed - if (!hasSpeedInTrack && speed == 0 && timeDiff > 0) { - speed = calculations[0] / timeDiff; - } - - // Motion detection: - // speed > 0 uses GPS chipset's motion detection - // calculations[0] > minDisplacment * time is heuristic needed because tracks may be filtered at recording time, so points at rest may not be present in file at all - if ((speed > 0) && (calculations[0] > 0.1 / 1000f * (point.time - prev.time)) && point.time != 0 && prev.time != 0) { - timeMoving = timeMoving + (point.time - prev.time); - totalDistanceMoving += calculations[0]; - } - - //Next few lines for Issue 3222 heuristic testing only - // if (speed > 0 && point.time != 0 && prev.time != 0) { - // timeMoving0 = timeMoving0 + (point.time - prev.time); - // totalDistanceMoving0 += calculations[0]; - // } - } - - elevation1.time = timeDiff; - elevation1.distance = (j > 0) ? calculations[0] : 0; - elevationData.add(elevation1); - if (!hasElevationData && !Float.isNaN(elevation1.elevation) && totalDistance > 0) { - hasElevationData = true; - } - - minSpeed = Math.min(speed, minSpeed); - if (speed > 0) { - totalSpeedSum += speed; - maxSpeed = Math.max(speed, maxSpeed); - speedCount++; - } - - Speed speed1 = new Speed(); - speed1.speed = speed; - speed1.time = timeDiff; - speed1.distance = elevation1.distance; - speedData.add(speed1); - if (!hasSpeedData && speed1.speed > 0 && totalDistance > 0) { - hasSpeedData = true; - } - } - } - if (totalDistance < 0) { - hasElevationData = false; - hasSpeedData = false; - } - if (!isTimeSpecified()) { - startTime = filestamp; - endTime = filestamp; - } - - // OUTPUT: - // 1. Total distance, Start time, End time - // 2. Time span - if (timeSpan == 0) { - timeSpan = endTime - startTime; - } - - // 3. Time moving, if any - // 4. Elevation, eleUp, eleDown, if recorded - if (elevationPoints > 0) { - avgElevation = totalElevation / elevationPoints; - } - - - // 5. Max speed and Average speed, if any. Average speed is NOT overall (effective) speed, but only calculated for "moving" periods. - // Averaging speed values is less precise than totalDistanceMoving/timeMoving - if (speedCount > 0) { - if (timeMoving > 0) { - avgSpeed = (float) totalDistanceMoving / (float) timeMoving * 1000f; - } else { - avgSpeed = (float) totalSpeedSum / (float) speedCount; - } - } else { - avgSpeed = -1; - } - return this; - } - - } - - private static class SplitSegment { - TrkSegment segment; - double startCoeff = 0; - int startPointInd; - double endCoeff = 0; - int endPointInd; - double metricEnd; - double secondaryMetricEnd; - - public SplitSegment(TrkSegment s) { - startPointInd = 0; - startCoeff = 0; - endPointInd = s.points.size() - 2; - endCoeff = 1; - this.segment = s; - } - - public SplitSegment(TrkSegment s, int pointInd, double cf) { - this.segment = s; - this.startPointInd = pointInd; - this.startCoeff = cf; - } - - - public int getNumberOfPoints() { - return endPointInd - startPointInd + 2; - } - - public WptPt get(int j) { - final int ind = j + startPointInd; - if (j == 0) { - if (startCoeff == 0) { - return segment.points.get(ind); - } - return approx(segment.points.get(ind), segment.points.get(ind + 1), startCoeff); - } - if (j == getNumberOfPoints() - 1) { - if (endCoeff == 1) { - return segment.points.get(ind); - } - return approx(segment.points.get(ind - 1), segment.points.get(ind), endCoeff); - } - return segment.points.get(ind); - } - - - private WptPt approx(WptPt w1, WptPt w2, double cf) { - long time = value(w1.time, w2.time, 0, cf); - double speed = value(w1.speed, w2.speed, 0, cf); - double ele = value(w1.ele, w2.ele, 0, cf); - double hdop = value(w1.hdop, w2.hdop, 0, cf); - double lat = value(w1.lat, w2.lat, -360, cf); - double lon = value(w1.lon, w2.lon, -360, cf); - return new WptPt(lat, lon, time, ele, speed, hdop); - } - - private double value(double vl, double vl2, double none, double cf) { - if (vl == none || Double.isNaN(vl)) { - return vl2; - } else if (vl2 == none || Double.isNaN(vl2)) { - return vl; - } - return vl + cf * (vl2 - vl); - } - - private long value(long vl, long vl2, long none, double cf) { - if (vl == none) { - return vl2; - } else if (vl2 == none) { - return vl; - } - return vl + ((long) (cf * (vl2 - vl))); - } - - - public double setLastPoint(int pointInd, double endCf) { - endCoeff = endCf; - endPointInd = pointInd; - return endCoeff; - } - - } - - private static SplitMetric getDistanceMetric() { - return new SplitMetric() { - - private float[] calculations = new float[1]; - - @Override - public double metric(WptPt p1, WptPt p2) { - net.osmand.Location.distanceBetween(p1.lat, p1.lon, p2.lat, p2.lon, calculations); - return calculations[0]; - } - }; - } - - private static SplitMetric getTimeSplit() { - return new SplitMetric() { - - @Override - public double metric(WptPt p1, WptPt p2) { - if (p1.time != 0 && p2.time != 0) { - return (int) Math.abs((p2.time - p1.time) / 1000l); - } - return 0; - } - }; - } - - private abstract static class SplitMetric { - - public abstract double metric(WptPt p1, WptPt p2); - - } - - private static void splitSegment(SplitMetric metric, SplitMetric secondaryMetric, - double metricLimit, List splitSegments, - TrkSegment segment) { - double currentMetricEnd = metricLimit; - double secondaryMetricEnd = 0; - SplitSegment sp = new SplitSegment(segment, 0, 0); - double total = 0; - WptPt prev = null; - for (int k = 0; k < segment.points.size(); k++) { - WptPt point = segment.points.get(k); - if (k > 0) { - double currentSegment = metric.metric(prev, point); - secondaryMetricEnd += secondaryMetric.metric(prev, point); - while (total + currentSegment > currentMetricEnd) { - double p = currentMetricEnd - total; - double cf = (p / currentSegment); - sp.setLastPoint(k - 1, cf); - sp.metricEnd = currentMetricEnd; - sp.secondaryMetricEnd = secondaryMetricEnd; - splitSegments.add(sp); - - sp = new SplitSegment(segment, k - 1, cf); - currentMetricEnd += metricLimit; - prev = sp.get(0); - } - total += currentSegment; - } - prev = point; - } - if (segment.points.size() > 0 - && !(sp.endPointInd == segment.points.size() - 1 && sp.startCoeff == 1)) { - sp.metricEnd = total; - sp.secondaryMetricEnd = secondaryMetricEnd; - sp.setLastPoint(segment.points.size() - 2, 1); - splitSegments.add(sp); - } - } - - private static List convert(List splitSegments) { - List ls = new ArrayList<>(); - for (SplitSegment s : splitSegments) { - GPXTrackAnalysis a = new GPXTrackAnalysis(); - a.prepareInformation(0, s); - ls.add(a); - } - return ls; - } - - public static class GPXFile extends GPXExtensions { - public String author; - public Metadata metadata; - public List tracks = new ArrayList<>(); - private List points = new ArrayList<>(); - public List routes = new ArrayList<>(); - - public String warning = null; - public String path = ""; - public boolean showCurrentTrack; - public long modifiedTime = 0; - public int userId; - public long chatId; - - private Track generalTrack; - private TrkSegment generalSegment; - - public List getPoints() { - return Collections.unmodifiableList(points); - } - - public Map> getPointsByCategories() { - Map> res = new HashMap<>(); - for (WptPt pt : points) { - String category = pt.category == null ? "" : pt.category; - List list = res.get(category); - if (list != null) { - list.add(pt); - } else { - list = new ArrayList<>(); - list.add(pt); - res.put(category, list); - } - } - return res; - } - - public boolean isPointsEmpty() { - return points.isEmpty(); - } - - public int getPointsSize() { - return points.size(); - } - - boolean containsPoint(WptPt point) { - return points.contains(point); - } - - void clearPoints() { - points.clear(); - modifiedTime = System.currentTimeMillis(); - } - - public void addPoint(WptPt point) { - points.add(point); - modifiedTime = System.currentTimeMillis(); - } - - public void addPoint(int position, WptPt point) { - points.add(position, point); - modifiedTime = System.currentTimeMillis(); - } - - void addPoints(Collection collection) { - points.addAll(collection); - modifiedTime = System.currentTimeMillis(); - } - - public boolean isCloudmadeRouteFile() { - return "cloudmade".equalsIgnoreCase(author); - } - - public void addGeneralTrack() { - Track generalTrack = getGeneralTrack(); - if (generalTrack != null && !tracks.contains(generalTrack)) { - tracks.add(0, generalTrack); - } - } - - public Track getGeneralTrack() { - TrkSegment generalSegment = getGeneralSegment(); - if (generalTrack == null && generalSegment != null) { - Track track = new Track(); - track.segments = new ArrayList<>(); - track.segments.add(generalSegment); - generalTrack = track; - track.generalTrack = true; - } - return generalTrack; - } - - public TrkSegment getGeneralSegment() { - if (generalSegment == null && getNonEmptySegmentsCount() > 1) { - buildGeneralSegment(); - } - return generalSegment; - } - - private void buildGeneralSegment() { - TrkSegment segment = new TrkSegment(); - for (Track track : tracks) { - for (TrkSegment s : track.segments) { - if (s.points.size() > 0) { - List waypoints = new ArrayList<>(s.points.size()); - for (WptPt wptPt : s.points) { - waypoints.add(new WptPt(wptPt)); - } - waypoints.get(0).firstPoint = true; - waypoints.get(waypoints.size() - 1).lastPoint = true; - segment.points.addAll(waypoints); - } - } - } - if (segment.points.size() > 0) { - segment.generalSegment = true; - generalSegment = segment; - } - } - - public GPXTrackAnalysis getAnalysis(long fileTimestamp) { - GPXTrackAnalysis g = new GPXTrackAnalysis(); - g.wptPoints = points.size(); - g.wptCategoryNames = getWaypointCategories(true); - List splitSegments = new ArrayList(); - for (int i = 0; i < tracks.size(); i++) { - Track subtrack = tracks.get(i); - for (TrkSegment segment : subtrack.segments) { - if (!segment.generalSegment) { - g.totalTracks++; - if (segment.points.size() > 1) { - splitSegments.add(new SplitSegment(segment)); - } - } - } - } - g.prepareInformation(fileTimestamp, splitSegments.toArray(new SplitSegment[splitSegments.size()])); - return g; - } - - public List getRoutePoints() { - List points = new ArrayList<>(); - for (int i = 0; i < routes.size(); i++) { - Route rt = routes.get(i); - points.addAll(rt.points); - } - return points; - } - - public boolean hasRtePt() { - for (Route r : routes) { - if (r.points.size() > 0) { - return true; - } - } - return false; - } - - public boolean hasWptPt() { - return points.size() > 0; - } - - public boolean hasTrkPt() { - for (Track t : tracks) { - for (TrkSegment ts : t.segments) { - if (ts.points.size() > 0) { - return true; - } - } - } - return false; - } - - public WptPt addWptPt(double lat, double lon, long time, String description, String name, String category, int color) { - double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); - double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); - final WptPt pt = new WptPt(latAdjusted, lonAdjusted, time, Double.NaN, 0, Double.NaN); - pt.name = name; - pt.category = category; - pt.desc = description; - if (color != 0) { - pt.setColor(color); - } - - points.add(pt); - - modifiedTime = System.currentTimeMillis(); - - return pt; - } - - public WptPt addRtePt(double lat, double lon, long time, String description, String name, String category, int color) { - double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); - double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); - final WptPt pt = new WptPt(latAdjusted, lonAdjusted, time, Double.NaN, 0, Double.NaN); - pt.name = name; - pt.category = category; - pt.desc = description; - if (color != 0) { - pt.setColor(color); - } - - if (routes.size() == 0) { - routes.add(new Route()); - } - Route currentRoute = routes.get(routes.size() - 1); - currentRoute.points.add(pt); - - modifiedTime = System.currentTimeMillis(); - - return pt; - } - - public void addTrkSegment(List points) { - removeGeneralTrackIfExists(); - - TrkSegment segment = new TrkSegment(); - segment.points.addAll(points); - - if (tracks.size() == 0) { - tracks.add(new Track()); - } - Track lastTrack = tracks.get(tracks.size() - 1); - lastTrack.segments.add(segment); - - modifiedTime = System.currentTimeMillis(); - } - - public boolean replaceSegment(TrkSegment oldSegment, TrkSegment newSegment) { - removeGeneralTrackIfExists(); - - for (int i = 0; i < tracks.size(); i++) { - Track currentTrack = tracks.get(i); - for (int j = 0; j < currentTrack.segments.size(); j++) { - int segmentIndex = currentTrack.segments.indexOf(oldSegment); - if (segmentIndex != -1) { - currentTrack.segments.remove(segmentIndex); - currentTrack.segments.add(segmentIndex, newSegment); - addGeneralTrack(); - modifiedTime = System.currentTimeMillis(); - return true; - } - } - } - - addGeneralTrack(); - return false; - } - - public void addRoutePoints(List points) { - if (routes.size() == 0) { - Route route = new Route(); - routes.add(route); - } - - Route lastRoute = routes.get(routes.size() - 1); - lastRoute.points.addAll(points); - modifiedTime = System.currentTimeMillis(); - } - - public void replaceRoutePoints(List points) { - routes.clear(); - routes.add(new Route()); - Route currentRoute = routes.get(routes.size() - 1); - currentRoute.points.addAll(points); - modifiedTime = System.currentTimeMillis(); - } - - public void updateWptPt(WptPt pt, double lat, double lon, long time, String description, String name, String category, int color) { - int index = points.indexOf(pt); - double latAdjusted = Double.parseDouble(latLonFormat.format(lat)); - double lonAdjusted = Double.parseDouble(latLonFormat.format(lon)); - pt.lat = latAdjusted; - pt.lon = lonAdjusted; - pt.time = time; - pt.desc = description; - pt.name = name; - pt.category = category; - if (color != 0) { - pt.setColor(color); - } - - if (index != -1) { - points.set(index, pt); - } - modifiedTime = System.currentTimeMillis(); - } - - private void removeGeneralTrackIfExists() { - if (generalTrack != null) { - tracks.remove(generalTrack); - this.generalTrack = null; - this.generalSegment = null; - } - } - - public boolean removeTrkSegment(TrkSegment segment) { - removeGeneralTrackIfExists(); - - for (int i = 0; i < tracks.size(); i++) { - Track currentTrack = tracks.get(i); - for (int j = 0; j < currentTrack.segments.size(); j++) { - if (currentTrack.segments.remove(segment)) { - addGeneralTrack(); - modifiedTime = System.currentTimeMillis(); - return true; - } - } - } - addGeneralTrack(); - return false; - } - - public boolean deleteWptPt(WptPt pt) { - modifiedTime = System.currentTimeMillis(); - return points.remove(pt); - } - - public boolean deleteRtePt(WptPt pt) { - modifiedTime = System.currentTimeMillis(); - for (Route route : routes) { - if (route.points.remove(pt)) { - return true; - } - } - return false; - } - - public List processRoutePoints() { - List tpoints = new ArrayList(); - if (routes.size() > 0) { - for (Route r : routes) { - int routeColor = r.getColor(getColor(0)); - if (r.points.size() > 0) { - TrkSegment sgmt = new TrkSegment(); - tpoints.add(sgmt); - sgmt.points.addAll(r.points); - sgmt.setColor(routeColor); - } - } - } - return tpoints; - } - - public List proccessPoints() { - List tpoints = new ArrayList(); - for (Track t : tracks) { - int trackColor = t.getColor(getColor(0)); - for (TrkSegment ts : t.segments) { - if (!ts.generalSegment && ts.points.size() > 0) { - TrkSegment sgmt = new TrkSegment(); - tpoints.add(sgmt); - sgmt.points.addAll(ts.points); - sgmt.setColor(trackColor); - } - } - } - return tpoints; - } - - public WptPt getLastPoint() { - if (tracks.size() > 0) { - Track tk = tracks.get(tracks.size() - 1); - if (tk.segments.size() > 0) { - TrkSegment ts = tk.segments.get(tk.segments.size() - 1); - if (ts.points.size() > 0) { - return ts.points.get(ts.points.size() - 1); - } - } - } - return null; - } - - public WptPt findPointToShow() { - for (Track t : tracks) { - for (TrkSegment s : t.segments) { - if (s.points.size() > 0) { - return s.points.get(0); - } - } - } - for (Route s : routes) { - if (s.points.size() > 0) { - return s.points.get(0); - } - } - if (points.size() > 0) { - return points.get(0); - } - return null; - } - - public boolean isEmpty() { - for (Track t : tracks) { - if (t.segments != null) { - for (TrkSegment s : t.segments) { - boolean tracksEmpty = s.points.isEmpty(); - if (!tracksEmpty) { - return false; - } - } - } - } - return points.isEmpty() && routes.isEmpty(); - } - - public int getNonEmptySegmentsCount() { - int count = 0; - for (Track t : tracks) { - for (TrkSegment s : t.segments) { - if (s.points.size() > 0) { - count++; - } - } - } - return count; - } - - public Set getWaypointCategories(boolean withDefaultCategory) { - Set categories = new HashSet<>(); - for (WptPt pt : points) { - String category = pt.category == null ? "" : pt.category; - if (withDefaultCategory || !TextUtils.isEmpty(category)) { - categories.add(category); - } - } - return categories; - } - - public Map getWaypointCategoriesWithColors(boolean withDefaultCategory) { - Map categories = new HashMap<>(); - for (WptPt pt : points) { - String category = pt.category == null ? "" : pt.category; - int color = pt.category == null ? 0 : pt.getColor(); - boolean emptyCategory = TextUtils.isEmpty(category); - if (!emptyCategory) { - Integer existingColor = categories.get(category); - if (existingColor == null || (existingColor == 0 && color != 0)) { - categories.put(category, color); - } - } else if (withDefaultCategory) { - categories.put(category, 0); - } - } - return categories; - } - - public QuadRect getRect() { - double left = 0, right = 0; - double top = 0, bottom = 0; - for (Track track : tracks) { - for (TrkSegment segment : track.segments) { - for (WptPt p : segment.points) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - } - } - for (WptPt p : points) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - for (GPXUtilities.Route route : routes) { - for (WptPt p : route.points) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - } - return new QuadRect(left, top, right, bottom); - } - } - - public static String asString(GPXFile file, TelegramApplication ctx) { - final Writer writer = new StringWriter(); - GPXUtilities.writeGpx(writer, file, ctx); - return writer.toString(); - } - - public static String writeGpxFile(File fout, GPXFile file, TelegramApplication ctx) { - Writer output = null; - try { - if (fout.getParentFile() != null) { - fout.getParentFile().mkdirs(); - } - output = new OutputStreamWriter(new FileOutputStream(fout), "UTF-8"); //$NON-NLS-1$ - if (Algorithms.isEmpty(file.path)) { - file.path = fout.getAbsolutePath(); - } - String msg = writeGpx(output, file, ctx); - return msg; - } catch (IOException e) { - log.error("Error saving gpx", e); //$NON-NLS-1$ - return "Error saving gpx"; - } finally { - if (output != null) { - try { - output.close(); - } catch (IOException ignore) { - // ignore - } - } - } - } - - public static String writeGpx(Writer output, GPXFile file, TelegramApplication ctx) { - try { - SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - XmlSerializer serializer = PlatformUtil.newSerializer(); - serializer.setOutput(output); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$ - serializer.startDocument("UTF-8", true); //$NON-NLS-1$ - serializer.startTag(null, "gpx"); //$NON-NLS-1$ - serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$ - if (file.author == null) { - serializer.attribute(null, "creator", ctx.getPackageName()); //$NON-NLS-1$ - } else { - serializer.attribute(null, "creator", file.author); //$NON-NLS-1$ - } - serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$ - serializer.attribute(null, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); - serializer.attribute(null, "xsi:schemaLocation", - "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); - - String trackName = getFilename(file.path); - serializer.startTag(null, "metadata"); - writeNotNullText(serializer, "name", trackName); - if (file.metadata != null) { - writeNotNullText(serializer, "desc", file.metadata.desc); - writeExtensions(serializer, file.metadata); - } - serializer.endTag(null, "metadata"); - - - for (Track track : file.tracks) { - if (!track.generalTrack) { - serializer.startTag(null, "trk"); //$NON-NLS-1$ - writeNotNullText(serializer, "name", track.name); - writeNotNullText(serializer, "desc", track.desc); - for (TrkSegment segment : track.segments) { - serializer.startTag(null, "trkseg"); //$NON-NLS-1$ - for (WptPt p : segment.points) { - serializer.startTag(null, "trkpt"); //$NON-NLS-1$ - writeWpt(format, serializer, p); - serializer.endTag(null, "trkpt"); //$NON-NLS-1$ - } - serializer.endTag(null, "trkseg"); //$NON-NLS-1$ - } - writeExtensions(serializer, track); - serializer.endTag(null, "trk"); //$NON-NLS-1$ - } - } - - for (Route track : file.routes) { - serializer.startTag(null, "rte"); //$NON-NLS-1$ - writeNotNullText(serializer, "name", track.name); - writeNotNullText(serializer, "desc", track.desc); - - for (WptPt p : track.points) { - serializer.startTag(null, "rtept"); //$NON-NLS-1$ - writeWpt(format, serializer, p); - serializer.endTag(null, "rtept"); //$NON-NLS-1$ - } - writeExtensions(serializer, track); - serializer.endTag(null, "rte"); //$NON-NLS-1$ - } - - for (WptPt l : file.points) { - serializer.startTag(null, "wpt"); //$NON-NLS-1$ - writeWpt(format, serializer, l); - serializer.endTag(null, "wpt"); //$NON-NLS-1$ - } - - serializer.endTag(null, "gpx"); //$NON-NLS-1$ - serializer.endDocument(); - serializer.flush(); - - - } catch (RuntimeException e) { - log.error("Error saving gpx", e); //$NON-NLS-1$ - return "Error saving gpx"; - } catch (IOException e) { - log.error("Error saving gpx", e); //$NON-NLS-1$ - return "Error saving gpx"; - } - return null; - } - - private static String getFilename(String path) { - if (path != null) { - int i = path.lastIndexOf('/'); - if (i > 0) { - path = path.substring(i + 1); - } - i = path.lastIndexOf('.'); - if (i > 0) { - path = path.substring(0, i); - } - } - return path; - } - - private static void writeNotNullText(XmlSerializer serializer, String tag, String value) throws IOException { - if (value != null) { - serializer.startTag(null, tag); - serializer.text(value); - serializer.endTag(null, tag); - } - } - - private static void writeExtensions(XmlSerializer serializer, GPXExtensions p) throws IOException { - if (!p.getExtensionsToRead().isEmpty()) { - serializer.startTag(null, "extensions"); - for (Map.Entry s : p.getExtensionsToRead().entrySet()) { - writeNotNullText(serializer, s.getKey(), s.getValue()); - } - serializer.endTag(null, "extensions"); - } - } - - private static void writeWpt(SimpleDateFormat format, XmlSerializer serializer, WptPt p) throws IOException { - serializer.attribute(null, "lat", latLonFormat.format(p.lat)); //$NON-NLS-1$ //$NON-NLS-2$ - serializer.attribute(null, "lon", latLonFormat.format(p.lon)); //$NON-NLS-1$ //$NON-NLS-2$ - - if (!Double.isNaN(p.ele)) { - writeNotNullText(serializer, "ele", decimalFormat.format(p.ele)); - } - if (p.time != 0) { - writeNotNullText(serializer, "time", format.format(new Date(p.time))); - } - writeNotNullText(serializer, "name", p.name); - writeNotNullText(serializer, "desc", p.desc); - if (p.link != null) { - serializer.startTag(null, "link"); - serializer.attribute(null, "href", p.link); - serializer.endTag(null, "link"); - } - writeNotNullText(serializer, "type", p.category); - if (p.comment != null) { - writeNotNullText(serializer, "cmt", p.comment); - } - if (!Double.isNaN(p.hdop)) { - writeNotNullText(serializer, "hdop", decimalFormat.format(p.hdop)); - } - if (p.speed > 0) { - p.getExtensionsToWrite().put("speed", decimalFormat.format(p.speed)); - } - writeExtensions(serializer, p); - } - - public static class GPXFileResult { - public ArrayList> locations = new ArrayList>(); - public ArrayList wayPoints = new ArrayList<>(); - // special case for cloudmate gpx : they discourage common schema - // by using waypoint as track points and rtept are not very close to real way - // such as wpt. However they provide additional information into gpx. - public boolean cloudMadeFile; - public String error; - - public Location findFistLocation() { - for (List l : locations) { - for (Location ls : l) { - if (ls != null) { - return ls; - } - } - } - return null; - } - } - - private static String readText(XmlPullParser parser, String key) throws XmlPullParserException, IOException { - int tok; - String text = null; - while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (tok == XmlPullParser.END_TAG && parser.getName().equals(key)) { - break; - } else if (tok == XmlPullParser.TEXT) { - if (text == null) { - text = parser.getText(); - } else { - text += parser.getText(); - } - } - - } - return text; - } - - public static GPXFile loadGPXFile(Context ctx, File f) { - FileInputStream fis = null; - try { - fis = new FileInputStream(f); - GPXFile file = loadGPXFile(ctx, fis); - file.path = f.getAbsolutePath(); - try { - fis.close(); - } catch (IOException e) { - } - return file; - } catch (IOException e) { - GPXFile res = new GPXFile(); - res.path = f.getAbsolutePath(); - log.error("Error reading gpx " + res.path, e); //$NON-NLS-1$ - res.warning = "Error reading gpx "; - return res; - } finally { - try { - if (fis != null) - fis.close(); - } catch (IOException ignore) { - // ignore - } - } - } - - public static GPXFile loadGPXFile(Context ctx, InputStream f) { - GPXFile res = new GPXFile(); - SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - SimpleDateFormat formatMillis = new SimpleDateFormat(GPX_TIME_FORMAT_MILLIS, Locale.US); - formatMillis.setTimeZone(TimeZone.getTimeZone("UTC")); - try { - XmlPullParser parser = PlatformUtil.newXMLPullParser(); - parser.setInput(getUTF8Reader(f)); - Stack parserState = new Stack<>(); - boolean extensionReadMode = false; - parserState.push(res); - int tok; - while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (tok == XmlPullParser.START_TAG) { - Object parse = parserState.peek(); - String tag = parser.getName(); - if (extensionReadMode && parse instanceof GPXExtensions) { - String value = readText(parser, tag); - if (value != null) { - ((GPXExtensions) parse).getExtensionsToWrite().put(tag.toLowerCase(), value); - if (tag.equals("speed") && parse instanceof WptPt) { - try { - ((WptPt) parse).speed = Float.parseFloat(value); - } catch (NumberFormatException e) { - } - } - } - - } else if (parse instanceof GPXExtensions && tag.equals("extensions")) { - extensionReadMode = true; - } else { - if (parse instanceof GPXFile) { - if (tag.equals("gpx")) { - ((GPXFile) parse).author = parser.getAttributeValue("", "creator"); - } - if (tag.equals("metadata")) { - Metadata metadata = new Metadata(); - ((GPXFile) parse).metadata = metadata; - parserState.push(metadata); - } - if (tag.equals("trk")) { - Track track = new Track(); - ((GPXFile) parse).tracks.add(track); - parserState.push(track); - } - if (tag.equals("rte")) { - Route route = new Route(); - ((GPXFile) parse).routes.add(route); - parserState.push(route); - } - if (tag.equals("wpt")) { - WptPt wptPt = parseWptAttributes(parser); - ((GPXFile) parse).points.add(wptPt); - parserState.push(wptPt); - } - } else if (parse instanceof Metadata) { - if (tag.equals("desc")) { - ((Metadata) parse).desc = readText(parser, "desc"); - } - } else if (parse instanceof Route) { - if (tag.equals("name")) { - ((Route) parse).name = readText(parser, "name"); - } - if (tag.equals("desc")) { - ((Route) parse).desc = readText(parser, "desc"); - } - if (tag.equals("rtept")) { - WptPt wptPt = parseWptAttributes(parser); - ((Route) parse).points.add(wptPt); - parserState.push(wptPt); - } - } else if (parse instanceof Track) { - if (tag.equals("name")) { - ((Track) parse).name = readText(parser, "name"); - } - if (tag.equals("desc")) { - ((Track) parse).desc = readText(parser, "desc"); - } - if (tag.equals("trkseg")) { - TrkSegment trkSeg = new TrkSegment(); - ((Track) parse).segments.add(trkSeg); - parserState.push(trkSeg); - } - } else if (parse instanceof TrkSegment) { - if (tag.equals("trkpt")) { - WptPt wptPt = parseWptAttributes(parser); - ((TrkSegment) parse).points.add(wptPt); - parserState.push(wptPt); - } - if (tag.equals("csvattributes")) { - String segmentPoints = readText(parser, "csvattributes"); - String[] pointsArr = segmentPoints.split("\n"); - for (int i = 0; i < pointsArr.length; i++) { - String[] pointAttrs = pointsArr[i].split(","); - try { - int arrLength = pointsArr.length; - if (arrLength > 1) { - WptPt wptPt = new WptPt(); - wptPt.lon = Double.parseDouble(pointAttrs[0]); - wptPt.lat = Double.parseDouble(pointAttrs[1]); - ((TrkSegment) parse).points.add(wptPt); - if (arrLength > 2) { - wptPt.ele = Double.parseDouble(pointAttrs[2]); - } - } - } catch (NumberFormatException e) { - } - } - } - // main object to parse - } else if (parse instanceof WptPt) { - if (tag.equals("name")) { - ((WptPt) parse).name = readText(parser, "name"); - } else if (tag.equals("desc")) { - ((WptPt) parse).desc = readText(parser, "desc"); - } else if (tag.equals("cmt")) { - ((WptPt) parse).comment = readText(parser, "cmt"); - } else if (tag.equals("speed")) { - try { - String value = readText(parser, "speed"); - ((WptPt) parse).speed = Float.parseFloat(value); - ((WptPt) parse).getExtensionsToWrite().put("speed", value); - } catch (NumberFormatException e) { - } - } else if (tag.equals("link")) { - ((WptPt) parse).link = parser.getAttributeValue("", "href"); - } else if (tag.equals("category")) { - ((WptPt) parse).category = readText(parser, "category"); - } else if (tag.equals("type")) { - if (((WptPt) parse).category == null) { - ((WptPt) parse).category = readText(parser, "type"); - } - } else if (tag.equals("ele")) { - String text = readText(parser, "ele"); - if (text != null) { - try { - ((WptPt) parse).ele = Float.parseFloat(text); - } catch (NumberFormatException e) { - } - } - } else if (tag.equals("hdop")) { - String text = readText(parser, "hdop"); - if (text != null) { - try { - ((WptPt) parse).hdop = Float.parseFloat(text); - } catch (NumberFormatException e) { - } - } - } else if (tag.equals("time")) { - String text = readText(parser, "time"); - if (text != null) { - try { - ((WptPt) parse).time = format.parse(text).getTime(); - } catch (ParseException e1) { - try { - ((WptPt) parse).time = formatMillis.parse(text).getTime(); - } catch (ParseException e2) { - - } - } - } - } - } - } - - } else if (tok == XmlPullParser.END_TAG) { - Object parse = parserState.peek(); - String tag = parser.getName(); - if (parse instanceof GPXExtensions && tag.equals("extensions")) { - extensionReadMode = false; - } - - if (tag.equals("metadata")) { - Object pop = parserState.pop(); - assert pop instanceof Metadata; - } else if (tag.equals("trkpt")) { - Object pop = parserState.pop(); - assert pop instanceof WptPt; - } else if (tag.equals("wpt")) { - Object pop = parserState.pop(); - assert pop instanceof WptPt; - } else if (tag.equals("rtept")) { - Object pop = parserState.pop(); - assert pop instanceof WptPt; - } else if (tag.equals("trk")) { - Object pop = parserState.pop(); - assert pop instanceof Track; - } else if (tag.equals("rte")) { - Object pop = parserState.pop(); - assert pop instanceof Route; - } else if (tag.equals("trkseg")) { - Object pop = parserState.pop(); - assert pop instanceof TrkSegment; - } - } - } - } catch (RuntimeException e) { - log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = "Error reading gpx" + " " + e.getMessage(); - } catch (XmlPullParserException e) { - log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = "Error reading gpx" + " " + e.getMessage(); - } catch (IOException e) { - log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = "Error reading gpx" + " " + e.getMessage(); - } - - return res; - } - - private static Reader getUTF8Reader(InputStream f) throws IOException { - BufferedInputStream bis = new BufferedInputStream(f); - assert bis.markSupported(); - bis.mark(3); - boolean reset = true; - byte[] t = new byte[3]; - bis.read(t); - if (t[0] == ((byte) 0xef) && t[1] == ((byte) 0xbb) && t[2] == ((byte) 0xbf)) { - reset = false; - } - if (reset) { - bis.reset(); - } - return new InputStreamReader(bis, "UTF-8"); - } - - private static WptPt parseWptAttributes(XmlPullParser parser) { - WptPt wpt = new WptPt(); - try { - wpt.lat = Double.parseDouble(parser.getAttributeValue("", "lat")); //$NON-NLS-1$ //$NON-NLS-2$ - wpt.lon = Double.parseDouble(parser.getAttributeValue("", "lon")); //$NON-NLS-1$ //$NON-NLS-2$ - } catch (NumberFormatException e) { - } - return wpt; - } - - public static void mergeGPXFileInto(GPXFile to, GPXFile from) { - if (from == null) { - return; - } - if (from.showCurrentTrack) { - to.showCurrentTrack = true; - } - if (from.points != null) { - to.points.addAll(from.points); - } - if (from.tracks != null) { - to.tracks.addAll(from.tracks); - } - if (from.routes != null) { - to.routes.addAll(from.routes); - } - if (from.warning != null) { - to.warning = from.warning; - } - } -} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java b/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java deleted file mode 100644 index 220896e389..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/LocationPoint.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.osmand.telegram.utils; - - -import android.content.Context; - -/** - */ -public interface LocationPoint { - - public double getLatitude(); - - public double getLongitude(); - - public int getColor(); - - public boolean isVisible(); - -// public PointDescription getPointDescription(Context ctx); - -// public String getSpeakableName(); - - //public void prepareCommandPlayer(CommandBuilder cmd, String names); - -} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt index dd346e7896..569701e0d6 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt @@ -1,6 +1,7 @@ package net.osmand.telegram.utils import android.os.AsyncTask +import net.osmand.GPXUtilities import net.osmand.Location import net.osmand.data.LatLon import net.osmand.telegram.TelegramApplication @@ -15,6 +16,7 @@ import org.drinkless.td.libcore.telegram.TdApi import java.io.File import java.text.SimpleDateFormat import java.util.* +import kotlin.math.abs const val TRACKS_DIR = "tracker/" @@ -412,7 +414,7 @@ object OsmandLocationUtils { return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true) } - fun convertLocationMessagesToGpxFiles(items: List, newGpxPerChat: Boolean = true): List { + fun convertLocationMessagesToGpxFiles(author:String, items: List, newGpxPerChat: Boolean = true): List { val dataTracks = ArrayList() var previousTime: Long = -1 @@ -432,24 +434,25 @@ object OsmandLocationUtils { } countedLocations++ if (previousUserId != userId || (newGpxPerChat && previousChatId != chatId)) { - gpx = GPXUtilities.GPXFile() - gpx!!.chatId = chatId - gpx!!.userId = userId + gpx = GPXUtilities.GPXFile(author).apply { + metadata = GPXUtilities.Metadata().apply { + extensionsToWrite["chatId"] = chatId.toString() + extensionsToWrite["userId"] = userId.toString() + } + } previousTime = 0 track = null segment = null dataTracks.add(gpx!!) } val pt = GPXUtilities.WptPt() - pt.userId = userId - pt.chatId = chatId pt.lat = it.lat pt.lon = it.lon pt.ele = it.altitude pt.speed = it.speed pt.hdop = it.hdop pt.time = time - val currentInterval = Math.abs(time - previousTime) + val currentInterval = abs(time - previousTime) if (track != null) { if (currentInterval < 30 * 60 * 1000) { // 30 minute - same segment @@ -524,44 +527,46 @@ object OsmandLocationUtils { private val app: TelegramApplication, private val listener: SaveGpxListener?, private val gpxFile: GPXUtilities.GPXFile, private val dir: File, private val userId: Int ) : - AsyncTask>() { + AsyncTask() { - override fun doInBackground(vararg params: Void): List { - val warnings = ArrayList() + override fun doInBackground(vararg params: Void): Exception? { dir.mkdirs() if (dir.parentFile.canWrite()) { if (dir.exists()) { // save file - var fout = File(dir, "$userId.gpx") if (!gpxFile.isEmpty) { val pt = gpxFile.findPointToShow() - - val user = app.telegramHelper.getUser(pt!!.userId) + val userId = gpxFile.metadata.extensionsToRead["userId"] + val user = if (!userId.isNullOrEmpty()) { + try { + app.telegramHelper.getUser(userId.toInt()) + } catch (e: NumberFormatException) { + null + } + } else { + null + } val fileName: String fileName = if (user != null) { (TelegramUiHelper.getUserName(user) + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time))) } else { userId.toString() + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time)) } - fout = File(dir, "$fileName.gpx") - } - val warn = GPXUtilities.writeGpxFile(fout, gpxFile, app) - if (warn != null) { - warnings.add(warn) - return warnings + gpxFile.metadata?.name = fileName + val fout = File(dir, "$fileName.gpx") + return GPXUtilities.writeGpxFile(fout, gpxFile) } } } - - return warnings + return null } - override fun onPostExecute(warnings: List?) { + override fun onPostExecute(warning: Exception?) { if (listener != null) { - if (warnings != null && warnings.isEmpty()) { + if (warning == null) { listener.onSavingGpxFinish(gpxFile.path) } else { - listener.onSavingGpxError(warnings) + listener.onSavingGpxError(warning.message) } } } @@ -571,6 +576,6 @@ object OsmandLocationUtils { fun onSavingGpxFinish(path: String) - fun onSavingGpxError(warnings: List?) + fun onSavingGpxError(warning: String?) } } \ No newline at end of file From 0e965dd7960d2477fe2bd9234ff5529fe9e95731 Mon Sep 17 00:00:00 2001 From: Chumva Date: Tue, 16 Jul 2019 15:58:59 +0300 Subject: [PATCH 08/17] Fix live track auto update in UserGpxInfoFragment (cherry picked from commit 1c4322ab0aa9bed29b60b366ded0107b7298963f) --- .../src/net/osmand/telegram/ui/UserGpxInfoFragment.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index de3652f8bb..f110abf262 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -232,6 +232,13 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } + override fun onResume() { + super.onResume() + if (liveTrackEnabled()) { + startHandler() + } + } + private fun startHandler() { log.debug("startHandler") if (!handler.hasMessages(TRACK_UPDATE_MSG_ID)) { From e7bb115e294e43504cbcd1fd50458cea29248fdd Mon Sep 17 00:00:00 2001 From: Chumva Date: Wed, 17 Jul 2019 17:20:38 +0300 Subject: [PATCH 09/17] Fix import gpx files to custom folders (cherry picked from commit caac552822fc47e1018f58aa029fd63048e52b46) --- .../src/main/java/net/osmand/util/Algorithms.java | 11 ++++++++--- OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java index 3e6caffafa..cd32d5e0f3 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java @@ -2,8 +2,6 @@ package net.osmand.util; import net.osmand.IProgress; import net.osmand.PlatformUtil; -import net.osmand.router.GeneralRouter; -import net.osmand.router.RoutingConfiguration; import org.apache.commons.logging.Log; import org.xmlpull.v1.XmlPullParser; @@ -31,7 +29,6 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.Stack; import java.util.zip.GZIPInputStream; @@ -370,6 +367,14 @@ public class Algorithms { return ""; } + public static void createParentDirsForFile(File file) { + if (file != null && !file.exists()) { + File parent = file.getParentFile(); + if (parent != null && !parent.exists()) { + parent.mkdirs(); + } + } + } @SuppressWarnings("TryFinallyCanBeTryWithResources") public static void fileCopy(File src, File dst) throws IOException { diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 07da3f1642..c67f63d588 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -74,7 +74,6 @@ import net.osmand.plus.audionotes.AudioVideoNotesPlugin; import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.helpers.ColorDialogs; import net.osmand.plus.helpers.ExternalApiHelper; -import net.osmand.plus.helpers.LockHelper; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.other.IContextMenuButtonListener; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; @@ -1315,6 +1314,9 @@ public class OsmandAidlApi { File destination = app.getAppPath(IndexConstants.GPX_INDEX_DIR + destinationPath); if (destination.getParentFile().canWrite()) { boolean destinationExists = destination.exists(); + if (!destinationExists) { + Algorithms.createParentDirsForFile(destination); + } try { Algorithms.fileCopy(source, destination); finishGpxImport(destinationExists, destination, color, show); @@ -1336,6 +1338,9 @@ public class OsmandAidlApi { gpxParcelDescriptor = app.getContentResolver().openFileDescriptor(gpxUri, "r"); if (gpxParcelDescriptor != null) { boolean destinationExists = destination.exists(); + if (!destinationExists) { + Algorithms.createParentDirsForFile(destination); + } FileDescriptor fileDescriptor = gpxParcelDescriptor.getFileDescriptor(); InputStream is = new FileInputStream(fileDescriptor); FileOutputStream fout = new FileOutputStream(destination); @@ -1370,6 +1375,9 @@ public class OsmandAidlApi { InputStream is = new ByteArrayInputStream(sourceRawData.getBytes()); FileOutputStream fout = new FileOutputStream(destination); boolean destinationExists = destination.exists(); + if (!destinationExists) { + Algorithms.createParentDirsForFile(destination); + } try { Algorithms.streamCopy(is, fout); finishGpxImport(destinationExists, destination, color, show); From 0d770e4f8a19cc2d183110e6f533766683587756 Mon Sep 17 00:00:00 2001 From: Chumva Date: Wed, 17 Jul 2019 17:23:27 +0300 Subject: [PATCH 10/17] Add live tracks via gpx aidl import (cherry picked from commit 43797bae3ca35c58405993ed01f3b17338918850) --- .../net/osmand/telegram/TelegramService.kt | 19 ++++ .../net/osmand/telegram/TelegramSettings.kt | 13 +++ .../telegram/helpers/LocationMessages.kt | 2 + .../telegram/helpers/OsmandAidlHelper.kt | 19 ++++ .../telegram/helpers/ShowLocationHelper.kt | 86 +++++++++++++++++++ .../osmand/telegram/ui/UserGpxInfoFragment.kt | 24 ++---- .../telegram/utils/OsmandLocationUtils.kt | 54 ++++++------ 7 files changed, 173 insertions(+), 44 deletions(-) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt index 480766d85e..a448ef714a 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt @@ -21,6 +21,7 @@ import org.drinkless.td.libcore.telegram.TdApi import java.util.* private const val UPDATE_LIVE_MESSAGES_INTERVAL_MS = 10000L // 10 sec +private const val UPDATE_LIVE_TRACKS_INTERVAL_MS = 30000L // 30 sec class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener, TelegramOutgoingMessagesListener { @@ -32,6 +33,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis private var updateShareInfoHandler: Handler? = null private var mHandlerThread = HandlerThread("SharingServiceThread") + private var updateTracksHandler: Handler? = null + private var tracksHandlerThread = HandlerThread("TracksUpdateServiceThread") + var handler: Handler? = null private set var usedBy = 0 @@ -53,7 +57,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis override fun onCreate() { super.onCreate() mHandlerThread.start() + tracksHandlerThread.start() updateShareInfoHandler = Handler(mHandlerThread.looper) + updateTracksHandler = Handler(tracksHandlerThread.looper) } override fun onBind(intent: Intent): IBinder? { @@ -103,6 +109,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis } if (isUsedByUsersLocations(usedBy)) { app.telegramHelper.startLiveMessagesUpdates(app.settings.sendMyLocInterval) + startTracksUpdates() } val locationNotification = app.notificationHelper.locationNotification @@ -130,6 +137,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis app.telegramHelper.removeOutgoingMessagesListener(this) app.settings.save() app.telegramService = null + tracksHandlerThread.quit() mHandlerThread.quit() usedBy = 0 @@ -190,6 +198,17 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis } }, UPDATE_LIVE_MESSAGES_INTERVAL_MS) } + + private fun startTracksUpdates() { + updateTracksHandler?.postDelayed({ + if (isUsedByUsersLocations(usedBy)) { + if (app().settings.hasAnyLiveTracksToShowOnMap()) { + app().showLocationHelper.startUpdateTracksTask() + } + startTracksUpdates() + } + }, UPDATE_LIVE_TRACKS_INTERVAL_MS) + } @SuppressLint("MissingPermission") private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index c90caecd77..760a28a35d 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -164,6 +164,19 @@ class TelegramSettings(private val app: TelegramApplication) { fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats()) + fun hasAnyLiveTracksToShowOnMap(): Boolean { + val time = System.currentTimeMillis() - locHistoryTime * 1000 + val locations = app.locationMessages.getLastLocationMessagesSinceTime(time) + locations.forEach { loc -> + if (liveTracksInfo.any { it.userId == loc.userId && it.chatId == loc.chatId && it.deviceName == loc.deviceName }) { + return true + } + } + return false + } + + fun getLiveTracksInfo() = liveTracksInfo + fun isShowingChatOnMap(chatId: Long) = !hiddenOnMapChats.contains(chatId) fun isLiveTrackEnabled(userId: Int, chatId: Long, deviceName: String) = diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt index 6ccc1b6c20..1ada05fd45 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt @@ -79,6 +79,8 @@ class LocationMessages(val app: TelegramApplication) { fun getLastLocationInfoForUserInChat(userId: Int, chatId: Long, deviceName: String) = lastLocationPoints.sortedByDescending { it.time }.firstOrNull { it.userId == userId && it.chatId == chatId && it.deviceName == deviceName } + fun getLastLocationMessagesSinceTime(time: Long) = lastLocationPoints.filter { it.time > time } + fun addBufferedMessage(message: BufferMessage) { log.debug("addBufferedMessage $message") val messages = mutableListOf(*this.bufferedMessages.toTypedArray()) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt index d708d49b7b..66ccd1bad8 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt @@ -211,6 +211,25 @@ class OsmandAidlHelper(private val app: TelegramApplication) { return null } + /** + * Get list of all imported GPX files. + * + * @return list of imported gpx files. + */ + val importedGpxFiles: List? + get() { + if (mIOsmAndAidlInterface != null) { + try { + val files = mutableListOf() + mIOsmAndAidlInterface!!.getImportedGpx(files) + return files + } catch (e: RemoteException) { + e.printStackTrace() + } + } + return null + } + init { connectOsmand() } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index d7bb3054b7..e52c80ed2e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -4,6 +4,9 @@ import android.content.Intent import android.graphics.Color import android.os.AsyncTask import android.text.TextUtils +import net.osmand.GPXUtilities +import net.osmand.PlatformUtil +import net.osmand.aidl.gpx.AGpxFile import net.osmand.aidl.map.ALatLon import net.osmand.aidl.maplayer.point.AMapPoint import net.osmand.aidl.mapmarker.AMapMarker @@ -24,6 +27,8 @@ import java.util.concurrent.Executors class ShowLocationHelper(private val app: TelegramApplication) { + private val log = PlatformUtil.getLog(ShowLocationHelper::class.java) + companion object { const val MAP_LAYER_ID = "telegram_layer" @@ -32,11 +37,14 @@ class ShowLocationHelper(private val app: TelegramApplication) { const val MAP_CONTEXT_MENU_BUTTON_ID = 1 const val MAP_CONTEXT_MENU_BUTTONS_PARAMS_ID = "DIRECTION" const val DIRECTION_ICON_ID = "ic_action_start_navigation" + + const val LIVE_TRACKS_DIR = "livetracks" } private val telegramHelper = app.telegramHelper private val osmandAidlHelper = app.osmandAidlHelper private val executor = Executors.newSingleThreadExecutor() + private val liveTracksExecutor = Executors.newSingleThreadExecutor() private val points = ConcurrentHashMap() private val markers = ConcurrentHashMap() @@ -246,6 +254,72 @@ class ShowLocationHelper(private val app: TelegramApplication) { osmandAidlHelper.removeContextMenuButtons(MAP_CONTEXT_MENU_BUTTONS_PARAMS_ID) } + private fun updateTracksOnMap() { + val startTime = System.currentTimeMillis() + osmandAidlHelper.execOsmandApi { + val gpxFiles = getLiveGpxFiles() + if (gpxFiles.isEmpty()) { + return@execOsmandApi + } + + val importedGpxFiles = osmandAidlHelper.importedGpxFiles + gpxFiles.forEach { + if (!isGpxAlreadyImported(importedGpxFiles, it)) { + val listener = object : OsmandLocationUtils.SaveGpxListener { + + override fun onSavingGpxFinish(path: String) { + log.debug("LiveTracks onSavingGpxFinish $path time ${startTime - System.currentTimeMillis()}") + val uri = AndroidUtils.getUriForFile(app, File(path)) + val destinationPath = "$LIVE_TRACKS_DIR/${it.metadata.name}.gpx" + osmandAidlHelper.importGpxFromUri(uri, destinationPath, GPXUtilities.GPXColor.AQUA.name, true) + log.debug("LiveTracks importGpxFromUri finish time ${startTime - System.currentTimeMillis()}") + } + + override fun onSavingGpxError(error: Exception) { + log.error(error) + } + } + OsmandLocationUtils.saveGpx(app, it, listener) + } + } + } + } + + private fun isGpxAlreadyImported(importedGpxFiles: List?, gpxFile: GPXUtilities.GPXFile): Boolean { + if (importedGpxFiles != null && importedGpxFiles.isNotEmpty()) { + val name = "${gpxFile.metadata.name}.gpx" + val aGpxFile = importedGpxFiles.firstOrNull { it.fileName == name } + + if (aGpxFile != null) { + val startTimeImported = aGpxFile.details?.startTime + val endTimeImported = aGpxFile.details?.endTime + if (startTimeImported != null && endTimeImported != null) { + val startTime = gpxFile.findPointToShow()?.time + val endTime = gpxFile.lastPoint?.time + if (aGpxFile.details?.startTime == startTime && endTimeImported == endTime) { + return true + } + } + } + } + return false + } + + private fun getLiveGpxFiles(): List { + val currentTime = System.currentTimeMillis() + val start = currentTime - app.settings.locHistoryTime * 1000 + val locationMessages = mutableListOf() + + app.settings.getLiveTracksInfo().forEach { + val messages = app.locationMessages.getMessagesForUserInChat(it.userId, it.chatId, it.deviceName, start, currentTime) + if (messages.isNotEmpty()) { + locationMessages.addAll(messages) + } + } + + return OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages) + } + fun startShowingLocation() { if (!showingLocation && !forcedStop) { showingLocation = if (isUseOsmandCallback() && !app.settings.monitoringEnabled) { @@ -316,6 +390,10 @@ class ShowLocationHelper(private val app: TelegramApplication) { fun startUpdateMessagesTask() { UpdateMessagesTask(app).executeOnExecutor(executor) } + + fun startUpdateTracksTask() { + UpdateTracksTask(app).executeOnExecutor(liveTracksExecutor) + } private class ShowMessagesTask(private val app: TelegramApplication) : AsyncTask() { @@ -345,6 +423,14 @@ class ShowLocationHelper(private val app: TelegramApplication) { } } + private class UpdateTracksTask(private val app: TelegramApplication) : AsyncTask() { + + override fun doInBackground(vararg params: Void?): Void? { + app.showLocationHelper.updateTracksOnMap() + return null + } + } + private fun generatePointDetails(bearing: Float?, altitude: Float?, precision: Float?): List { val details = mutableListOf() if (bearing != null && bearing != 0.0f) { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index f110abf262..b37cbdfa8f 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -34,7 +34,6 @@ import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.telegram.utils.AndroidUtils import net.osmand.telegram.utils.OsmandFormatter import net.osmand.telegram.utils.OsmandLocationUtils -import net.osmand.telegram.utils.TRACKS_DIR import net.osmand.util.Algorithms import java.io.File import java.text.SimpleDateFormat @@ -105,10 +104,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { openGpx(path) } - override fun onSavingGpxError(warning: String?) { - warning?.also { - Toast.makeText(app, it, Toast.LENGTH_LONG).show() - } + override fun onSavingGpxError(error: Exception) { + Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() } }) } @@ -204,10 +201,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { (activity as MainActivity).shareGpx(path) } - override fun onSavingGpxError(warning: String?) { - warning?.also { - Toast.makeText(app, it, Toast.LENGTH_LONG).show() - } + override fun onSavingGpxError(error: Exception) { + Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() } }) } @@ -271,10 +266,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { } private fun saveCurrentGpxToFile(listener: OsmandLocationUtils.SaveGpxListener) { - if (!gpxFile.isEmpty) { - val file = File(app.getExternalFilesDir(null), TRACKS_DIR) - OsmandLocationUtils.saveGpx(app, listener, file, gpxFile) - } + OsmandLocationUtils.saveGpx(app, gpxFile, listener) } private fun readFromBundle(bundle: Bundle?) { @@ -335,7 +327,7 @@ class UserGpxInfoFragment : BaseDialogFragment() { checkTime() locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis) - gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app.packageName, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName) + gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName) updateGPXStatisticRow() updateDateAndTimeButtons() updateGPXMap() @@ -405,8 +397,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } - override fun onSavingGpxError(warning: String?) { - log.debug("onSavingGpxError $warning") + override fun onSavingGpxError(error: Exception) { + log.error(error) } }) } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt index 569701e0d6..839dee7652 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt @@ -414,7 +414,7 @@ object OsmandLocationUtils { return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true) } - fun convertLocationMessagesToGpxFiles(author:String, items: List, newGpxPerChat: Boolean = true): List { + fun convertLocationMessagesToGpxFiles(app: TelegramApplication, items: List, newGpxPerChat: Boolean = true): List { val dataTracks = ArrayList() var previousTime: Long = -1 @@ -434,10 +434,9 @@ object OsmandLocationUtils { } countedLocations++ if (previousUserId != userId || (newGpxPerChat && previousChatId != chatId)) { - gpx = GPXUtilities.GPXFile(author).apply { + gpx = GPXUtilities.GPXFile(app.packageName).apply { metadata = GPXUtilities.Metadata().apply { - extensionsToWrite["chatId"] = chatId.toString() - extensionsToWrite["userId"] = userId.toString() + name = getGpxFileNameForUserId(app, userId, time) } } previousTime = 0 @@ -478,13 +477,27 @@ object OsmandLocationUtils { return dataTracks } - fun saveGpx(app: TelegramApplication, listener: SaveGpxListener, dir: File, gpxFile: GPXUtilities.GPXFile) { + fun saveGpx(app: TelegramApplication, gpxFile: GPXUtilities.GPXFile, listener: SaveGpxListener) { if (!gpxFile.isEmpty) { - val task = SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0) + val dir = File(app.getExternalFilesDir(null), TRACKS_DIR) + val task = SaveGPXTrackToFileTask(listener, gpxFile, dir) task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) } } + fun getGpxFileNameForUserId(app: TelegramApplication, userId: Int, time: Long): String { + var userName = userId.toString() + try { + val user = app.telegramHelper.getUser(userId) + if (user != null) { + userName = TelegramUiHelper.getUserName(user) + } + } catch (e: NumberFormatException) { + //ignore + } + return "${userName}_${UTC_DATE_FORMAT.format(Date(time))}" + } + abstract class MessageLocation : TdApi.MessageContent() { var lat: Double = Double.NaN @@ -524,8 +537,9 @@ object OsmandLocationUtils { } private class SaveGPXTrackToFileTask internal constructor( - private val app: TelegramApplication, private val listener: SaveGpxListener?, - private val gpxFile: GPXUtilities.GPXFile, private val dir: File, private val userId: Int + private val listener: SaveGpxListener?, + private val gpxFile: GPXUtilities.GPXFile, + private val dir: File ) : AsyncTask() { @@ -535,25 +549,9 @@ object OsmandLocationUtils { if (dir.exists()) { // save file if (!gpxFile.isEmpty) { - val pt = gpxFile.findPointToShow() - val userId = gpxFile.metadata.extensionsToRead["userId"] - val user = if (!userId.isNullOrEmpty()) { - try { - app.telegramHelper.getUser(userId.toInt()) - } catch (e: NumberFormatException) { - null - } - } else { - null - } - val fileName: String - fileName = if (user != null) { - (TelegramUiHelper.getUserName(user) + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time))) - } else { - userId.toString() + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time)) - } - gpxFile.metadata?.name = fileName + val fileName = gpxFile.metadata.name val fout = File(dir, "$fileName.gpx") + return GPXUtilities.writeGpxFile(fout, gpxFile) } } @@ -566,7 +564,7 @@ object OsmandLocationUtils { if (warning == null) { listener.onSavingGpxFinish(gpxFile.path) } else { - listener.onSavingGpxError(warning.message) + listener.onSavingGpxError(warning) } } } @@ -576,6 +574,6 @@ object OsmandLocationUtils { fun onSavingGpxFinish(path: String) - fun onSavingGpxError(warning: String?) + fun onSavingGpxError(error: Exception) } } \ No newline at end of file From 550a2fb433d7ab26698b037b4b6240509b9707c1 Mon Sep 17 00:00:00 2001 From: Chumva Date: Fri, 19 Jul 2019 13:04:57 +0300 Subject: [PATCH 11/17] Add colors to live tracks and add group name to gpx file name (cherry picked from commit 960a231201f925635556d8bf28d210ccb95100a0) --- .../net/osmand/telegram/TelegramSettings.kt | 22 ++++++++---- .../telegram/helpers/LocationMessages.kt | 2 +- .../telegram/helpers/ShowLocationHelper.kt | 11 +++++- .../telegram/utils/OsmandLocationUtils.kt | 36 +++++++++++-------- 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index 760a28a35d..ac68e16384 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -9,6 +9,7 @@ import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan import net.osmand.PlatformUtil import net.osmand.telegram.helpers.OsmandAidlHelper +import net.osmand.telegram.helpers.ShowLocationHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.utils.AndroidUtils import net.osmand.telegram.utils.OsmandApiUtils @@ -112,7 +113,7 @@ class TelegramSettings(private val app: TelegramApplication) { private var shareChatsInfo = ConcurrentHashMap() private var hiddenOnMapChats: Set = emptySet() private var shareDevices: Set = emptySet() - private var liveTracksInfo: Set = emptySet() + private var liveTracksInfo = emptyList() var sharingStatusChanges = ConcurrentLinkedQueue() @@ -177,6 +178,9 @@ class TelegramSettings(private val app: TelegramApplication) { fun getLiveTracksInfo() = liveTracksInfo + fun getLiveTrackInfo(userId: Int, chatId: Long, deviceName: String) = + liveTracksInfo.firstOrNull { it.userId == userId && it.chatId == chatId && it.deviceName == deviceName } + fun isShowingChatOnMap(chatId: Long) = !hiddenOnMapChats.contains(chatId) fun isLiveTrackEnabled(userId: Int, chatId: Long, deviceName: String) = @@ -185,11 +189,12 @@ class TelegramSettings(private val app: TelegramApplication) { fun updateLiveTrack(userId: Int, chatId: Long, deviceName: String, enable: Boolean) { val tracksInfo = liveTracksInfo.toMutableList() if (enable) { - tracksInfo.add(LiveTrackInfo(userId, chatId, deviceName)) + val colorIndex = if (tracksInfo.size > 0) (tracksInfo.last().colorIndex + 1) % ShowLocationHelper.GPX_COLORS_COUNT else 0 + tracksInfo.add(LiveTrackInfo(userId, chatId, deviceName, colorIndex)) } else { - tracksInfo.remove(LiveTrackInfo(userId, chatId, deviceName)) + tracksInfo.removeAll { it.userId == userId && it.chatId == chatId && it.deviceName == deviceName } } - liveTracksInfo = tracksInfo.toHashSet() + liveTracksInfo = tracksInfo.toList() } fun removeNonexistingChats(presentChatIds: List) { @@ -747,6 +752,7 @@ class TelegramSettings(private val app: TelegramApplication) { obj.put(LiveTrackInfo.USER_ID, liveTrackInfo.userId) obj.put(LiveTrackInfo.CHAT_ID, liveTrackInfo.chatId) obj.put(LiveTrackInfo.DEVICE_NAME, liveTrackInfo.deviceName) + obj.put(LiveTrackInfo.COLOR_INDEX, liveTrackInfo.colorIndex) put(obj) } } @@ -863,10 +869,11 @@ class TelegramSettings(private val app: TelegramApplication) { val userId = obj.optInt(LiveTrackInfo.USER_ID) val chatId = obj.optLong(LiveTrackInfo.CHAT_ID) val deviceName = obj.optString(LiveTrackInfo.DEVICE_NAME) + val colorIndex = obj.optInt(LiveTrackInfo.COLOR_INDEX) - list.add(LiveTrackInfo(userId, chatId, deviceName)) + list.add(LiveTrackInfo(userId, chatId, deviceName, colorIndex)) } - liveTracksInfo = list.toHashSet() + liveTracksInfo = list.toList() } private fun parseShareDevices(json: String) { @@ -1192,12 +1199,13 @@ class TelegramSettings(private val app: TelegramApplication) { } } - data class LiveTrackInfo(val userId: Int, val chatId: Long, val deviceName: String) { + data class LiveTrackInfo(val userId: Int, val chatId: Long, val deviceName: String, val colorIndex: Int) { companion object { internal const val USER_ID = "userId" internal const val CHAT_ID = "chatId" internal const val DEVICE_NAME = "deviceName" + internal const val COLOR_INDEX = "colorIndex" } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt index 1ada05fd45..43f307bc0e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt @@ -69,7 +69,7 @@ class LocationMessages(val app: TelegramApplication) { } fun getMessagesForUserInChat(userId: Int, chatId: Long, deviceName: String, start: Long, end: Long): List { - return dbHelper.getMessagesForUserInChat(userId, chatId,deviceName, start, end) + return dbHelper.getMessagesForUserInChat(userId, chatId, deviceName, start, end) } fun getMessagesForUser(userId: Int, start: Long, end: Long): List { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index e52c80ed2e..d5da914902 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -39,6 +39,14 @@ class ShowLocationHelper(private val app: TelegramApplication) { const val DIRECTION_ICON_ID = "ic_action_start_navigation" const val LIVE_TRACKS_DIR = "livetracks" + + const val GPX_COLORS_COUNT = 10 + + val GPX_COLORS = arrayOf( + "red", "orange", "lightblue", "blue", "purple", + "translucent_red", "translucent_orange", "translucent_lightblue", + "translucent_blue", "translucent_purple" + ) } private val telegramHelper = app.telegramHelper @@ -271,7 +279,8 @@ class ShowLocationHelper(private val app: TelegramApplication) { log.debug("LiveTracks onSavingGpxFinish $path time ${startTime - System.currentTimeMillis()}") val uri = AndroidUtils.getUriForFile(app, File(path)) val destinationPath = "$LIVE_TRACKS_DIR/${it.metadata.name}.gpx" - osmandAidlHelper.importGpxFromUri(uri, destinationPath, GPXUtilities.GPXColor.AQUA.name, true) + val color = it.extensionsToRead["color"] ?: "" + osmandAidlHelper.importGpxFromUri(uri, destinationPath, color, true) log.debug("LiveTracks importGpxFromUri finish time ${startTime - System.currentTimeMillis()}") } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt index 839dee7652..9bbd0b29e2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandLocationUtils.kt @@ -8,6 +8,7 @@ import net.osmand.telegram.TelegramApplication import net.osmand.telegram.helpers.LocationMessages import net.osmand.telegram.helpers.LocationMessages.BufferMessage import net.osmand.telegram.helpers.LocationMessages.LocationMessage +import net.osmand.telegram.helpers.ShowLocationHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.util.GeoPointParserUtil @@ -420,6 +421,7 @@ object OsmandLocationUtils { var previousTime: Long = -1 var previousChatId: Long = -1 var previousUserId = -1 + var previousDeviceName = "" var segment: GPXUtilities.TrkSegment? = null var track: GPXUtilities.Track? = null var gpx: GPXUtilities.GPXFile? = null @@ -428,15 +430,16 @@ object OsmandLocationUtils { items.forEach { val userId = it.userId val chatId = it.chatId + val deviceName = it.deviceName val time = it.time - if (previousTime >= time) { - return@forEach - } - countedLocations++ - if (previousUserId != userId || (newGpxPerChat && previousChatId != chatId)) { + if (previousUserId != userId || previousDeviceName != deviceName || (newGpxPerChat && previousChatId != chatId)) { gpx = GPXUtilities.GPXFile(app.packageName).apply { metadata = GPXUtilities.Metadata().apply { - name = getGpxFileNameForUserId(app, userId, time) + name = getGpxFileNameForUserId(app, userId, chatId, time) + } + val colorIndex = app.settings.getLiveTrackInfo(userId, chatId, deviceName)?.colorIndex ?: -1 + if (colorIndex != -1) { + extensionsToWrite["color"] = ShowLocationHelper.GPX_COLORS[colorIndex] } } previousTime = 0 @@ -444,6 +447,10 @@ object OsmandLocationUtils { segment = null dataTracks.add(gpx!!) } + if (previousTime >= time) { + return@forEach + } + countedLocations++ val pt = GPXUtilities.WptPt() pt.lat = it.lat pt.lon = it.lon @@ -472,6 +479,7 @@ object OsmandLocationUtils { previousTime = time previousUserId = userId previousChatId = chatId + previousDeviceName = deviceName } return dataTracks @@ -485,15 +493,15 @@ object OsmandLocationUtils { } } - fun getGpxFileNameForUserId(app: TelegramApplication, userId: Int, time: Long): String { + fun getGpxFileNameForUserId(app: TelegramApplication, userId: Int, chatId: Long, time: Long): String { var userName = userId.toString() - try { - val user = app.telegramHelper.getUser(userId) - if (user != null) { - userName = TelegramUiHelper.getUserName(user) - } - } catch (e: NumberFormatException) { - //ignore + val user = app.telegramHelper.getUser(userId) + if (user != null) { + userName = TelegramUiHelper.getUserName(user) + } + val chat = app.telegramHelper.getChat(chatId) + if (chat != null && app.telegramHelper.isGroup(chat)) { + return "${userName}_${chat.title}_${UTC_DATE_FORMAT.format(Date(time))}" } return "${userName}_${UTC_DATE_FORMAT.format(Date(time))}" } From d8d4505e06534c1bba1c9662975c8e3bbdfc30d8 Mon Sep 17 00:00:00 2001 From: Chumva Date: Fri, 19 Jul 2019 15:46:22 +0300 Subject: [PATCH 12/17] Add check for already selected gpx color (cherry picked from commit a13367c0cccd85166d467892d4c7f0ef166fb014) --- .../src/net/osmand/aidl/gpx/AGpxFile.java | 15 ++++++++--- .../telegram/helpers/LocationMessages.kt | 5 ++-- .../telegram/helpers/ShowLocationHelper.kt | 8 ++++-- OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java | 7 +++++- OsmAnd/src/net/osmand/aidl/gpx/AGpxFile.java | 18 +++++++------ .../osmand/plus/dialogs/ConfigureMapMenu.java | 25 +++++++++++++++++-- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java index 9572057fdc..fb357c79b1 100644 --- a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java @@ -11,13 +11,15 @@ public class AGpxFile implements Parcelable { private long modifiedTime; private long fileSize; private boolean active; + private String color; private AGpxFileDetails details; - public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, @Nullable AGpxFileDetails details) { + public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, String color, @Nullable AGpxFileDetails details) { this.fileName = fileName; this.modifiedTime = modifiedTime; this.fileSize = fileSize; this.active = active; + this.color = color; this.details = details; } @@ -52,6 +54,10 @@ public class AGpxFile implements Parcelable { return active; } + public String getColor() { + return color; + } + public AGpxFileDetails getDetails() { return details; } @@ -66,6 +72,7 @@ public class AGpxFile implements Parcelable { if (details != null) { out.writeParcelable(details, flags); } + out.writeString(color); } private void readFromParcel(Parcel in) { @@ -74,16 +81,16 @@ public class AGpxFile implements Parcelable { fileSize = in.readLong(); active = in.readByte() != 0; - boolean hasDetails= in.readByte() != 0; + boolean hasDetails = in.readByte() != 0; if (hasDetails) { details = in.readParcelable(AGpxFileDetails.class.getClassLoader()); } else { details = null; } + color = in.readString(); } public int describeContents() { return 0; } -} - +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt index 43f307bc0e..59eb0e5961 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt @@ -11,6 +11,7 @@ import net.osmand.telegram.TelegramApplication import net.osmand.telegram.utils.OsmandLocationUtils import net.osmand.util.MapUtils import org.drinkless.td.libcore.telegram.TdApi +import java.util.concurrent.ConcurrentLinkedQueue class LocationMessages(val app: TelegramApplication) { @@ -18,7 +19,7 @@ class LocationMessages(val app: TelegramApplication) { private var bufferedMessages = emptyList() - private var lastLocationPoints = mutableListOf() + private var lastLocationPoints = ConcurrentLinkedQueue() private val dbHelper: SQLiteHelper @@ -144,7 +145,7 @@ class LocationMessages(val app: TelegramApplication) { } private fun readLastMessages() { - this.lastLocationPoints = dbHelper.getLastMessages() + this.lastLocationPoints.addAll(dbHelper.getLastMessages()) } private class SQLiteHelper(context: Context) : diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index d5da914902..501133778f 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -272,7 +272,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { val importedGpxFiles = osmandAidlHelper.importedGpxFiles gpxFiles.forEach { - if (!isGpxAlreadyImported(importedGpxFiles, it)) { + if (!checkAlreadyImportedGpx(importedGpxFiles, it)) { val listener = object : OsmandLocationUtils.SaveGpxListener { override fun onSavingGpxFinish(path: String) { @@ -294,12 +294,16 @@ class ShowLocationHelper(private val app: TelegramApplication) { } } - private fun isGpxAlreadyImported(importedGpxFiles: List?, gpxFile: GPXUtilities.GPXFile): Boolean { + private fun checkAlreadyImportedGpx(importedGpxFiles: List?, gpxFile: GPXUtilities.GPXFile): Boolean { if (importedGpxFiles != null && importedGpxFiles.isNotEmpty()) { val name = "${gpxFile.metadata.name}.gpx" val aGpxFile = importedGpxFiles.firstOrNull { it.fileName == name } if (aGpxFile != null) { + val color = aGpxFile.color + if (!color.isNullOrEmpty()) { + gpxFile.extensionsToWrite["color"] = color + } val startTimeImported = aGpxFile.details?.startTime val endTimeImported = aGpxFile.details?.endTime if (startTimeImported != null && endTimeImported != null) { diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index c67f63d588..a50813d692 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -1477,12 +1477,17 @@ public class OsmandAidlApi { boolean active = app.getSelectedGpxHelper().getSelectedFileByPath(file.getAbsolutePath()) != null; long modifiedTime = dataItem.getFileLastModifiedTime(); long fileSize = file.length(); + int color = dataItem.getColor(); + String colorName = ""; + if (color != 0) { + colorName = ConfigureMapMenu.GpxAppearanceAdapter.parseTrackColorName(app.getRendererRegistry().getCurrentSelectedRenderer(), color); + } AGpxFileDetails details = null; GPXTrackAnalysis analysis = dataItem.getAnalysis(); if (analysis != null) { details = createGpxFileDetails(analysis); } - files.add(new AGpxFile(fileName, modifiedTime, fileSize, active, details)); + files.add(new AGpxFile(fileName, modifiedTime, fileSize, active, colorName, details)); } //} } diff --git a/OsmAnd/src/net/osmand/aidl/gpx/AGpxFile.java b/OsmAnd/src/net/osmand/aidl/gpx/AGpxFile.java index 7183f3024c..fb357c79b1 100644 --- a/OsmAnd/src/net/osmand/aidl/gpx/AGpxFile.java +++ b/OsmAnd/src/net/osmand/aidl/gpx/AGpxFile.java @@ -4,9 +4,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.text.TextUtils; - -import java.io.File; public class AGpxFile implements Parcelable { @@ -14,13 +11,15 @@ public class AGpxFile implements Parcelable { private long modifiedTime; private long fileSize; private boolean active; + private String color; private AGpxFileDetails details; - public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, @Nullable AGpxFileDetails details) { + public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, String color, @Nullable AGpxFileDetails details) { this.fileName = fileName; this.modifiedTime = modifiedTime; this.fileSize = fileSize; this.active = active; + this.color = color; this.details = details; } @@ -55,6 +54,10 @@ public class AGpxFile implements Parcelable { return active; } + public String getColor() { + return color; + } + public AGpxFileDetails getDetails() { return details; } @@ -69,6 +72,7 @@ public class AGpxFile implements Parcelable { if (details != null) { out.writeParcelable(details, flags); } + out.writeString(color); } private void readFromParcel(Parcel in) { @@ -77,16 +81,16 @@ public class AGpxFile implements Parcelable { fileSize = in.readLong(); active = in.readByte() != 0; - boolean hasDetails= in.readByte() != 0; + boolean hasDetails = in.readByte() != 0; if (hasDetails) { details = in.readParcelable(AGpxFileDetails.class.getClassLoader()); } else { details = null; } + color = in.readString(); } public int describeContents() { return 0; } -} - +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index 2180b33fc3..a283715c7a 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -23,7 +23,6 @@ import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; -import java.io.File; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities; import net.osmand.PlatformUtil; @@ -58,6 +57,7 @@ import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1415,7 +1415,7 @@ public class ConfigureMapMenu { public static int parseTrackColor(RenderingRulesStorage renderer, String colorName) { int defaultColor = -1; RenderingRule gpxRule = null; - if(renderer!=null) { + if (renderer != null) { gpxRule = renderer.getRenderingAttributeRule("gpx"); } if (gpxRule != null && gpxRule.getIfElseChildren().size() > 0) { @@ -1433,6 +1433,27 @@ public class ConfigureMapMenu { return defaultColor; } + public static String parseTrackColorName(RenderingRulesStorage renderer, int color) { + int defaultColor = -1; + RenderingRule gpxRule = null; + if (renderer != null) { + gpxRule = renderer.getRenderingAttributeRule("gpx"); + } + if (gpxRule != null && gpxRule.getIfElseChildren().size() > 0) { + List rules = renderer.getRenderingAttributeRule("gpx").getIfElseChildren().get(0).getIfElseChildren(); + for (RenderingRule r : rules) { + String cName = r.getStringPropertyValue(CURRENT_TRACK_COLOR_ATTR); + if (!Algorithms.isEmpty(cName) && color == r.getIntPropertyValue(COLOR_ATTR)) { + return cName; + } + if (cName == null && defaultColor == -1) { + defaultColor = r.getIntPropertyValue(COLOR_ATTR); + } + } + } + return Algorithms.colorToString(color); + } + @NonNull @Override public View getView(int position, View convertView, @NonNull ViewGroup parent) { From 02ace7b6b86912eb59dc665f89c82d95878458fb Mon Sep 17 00:00:00 2001 From: Chumva Date: Sat, 20 Jul 2019 13:40:05 +0300 Subject: [PATCH 13/17] Remove unnecessary call to search for renderingAttributeRule (cherry picked from commit 2b69d0908c35d035fef57896df5848b245dd7e86) --- OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index a283715c7a..accb54a146 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -1419,7 +1419,7 @@ public class ConfigureMapMenu { gpxRule = renderer.getRenderingAttributeRule("gpx"); } if (gpxRule != null && gpxRule.getIfElseChildren().size() > 0) { - List rules = renderer.getRenderingAttributeRule("gpx").getIfElseChildren().get(0).getIfElseChildren(); + List rules = gpxRule.getIfElseChildren().get(0).getIfElseChildren(); for (RenderingRule r : rules) { String cName = r.getStringPropertyValue(CURRENT_TRACK_COLOR_ATTR); if (!Algorithms.isEmpty(cName) && cName.equals(colorName)) { @@ -1440,7 +1440,7 @@ public class ConfigureMapMenu { gpxRule = renderer.getRenderingAttributeRule("gpx"); } if (gpxRule != null && gpxRule.getIfElseChildren().size() > 0) { - List rules = renderer.getRenderingAttributeRule("gpx").getIfElseChildren().get(0).getIfElseChildren(); + List rules = gpxRule.getIfElseChildren().get(0).getIfElseChildren(); for (RenderingRule r : rules) { String cName = r.getStringPropertyValue(CURRENT_TRACK_COLOR_ATTR); if (!Algorithms.isEmpty(cName) && color == r.getIntPropertyValue(COLOR_ATTR)) { From 7d96509d7400831063e088f0a039bb120461fbd5 Mon Sep 17 00:00:00 2001 From: Chumva Date: Sat, 20 Jul 2019 13:41:13 +0300 Subject: [PATCH 14/17] Remove unnecessary check for default color (cherry picked from commit 58e02f14e668ac19ada463b6094ac6c3ee30b8a3) --- OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index accb54a146..1b4dbbc8f0 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -1434,7 +1434,6 @@ public class ConfigureMapMenu { } public static String parseTrackColorName(RenderingRulesStorage renderer, int color) { - int defaultColor = -1; RenderingRule gpxRule = null; if (renderer != null) { gpxRule = renderer.getRenderingAttributeRule("gpx"); @@ -1446,9 +1445,6 @@ public class ConfigureMapMenu { if (!Algorithms.isEmpty(cName) && color == r.getIntPropertyValue(COLOR_ATTR)) { return cName; } - if (cName == null && defaultColor == -1) { - defaultColor = r.getIntPropertyValue(COLOR_ATTR); - } } } return Algorithms.colorToString(color); From fca2f0dee3c767ea94f688aae60e69050cec7a3a Mon Sep 17 00:00:00 2001 From: crimean Date: Sat, 20 Jul 2019 16:59:29 +0300 Subject: [PATCH 15/17] Fix lat/lon images on widget --- .../plus/views/mapwidgets/MapInfoWidgetsFactory.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index c91b499e84..3385f2eb82 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -1160,8 +1160,12 @@ public class MapInfoWidgetsFactory { } catch (RuntimeException e) { e.printStackTrace(); } - latitudeIcon.setImageDrawable(iconsCache.getIcon(nightMode ? R.drawable.widget_coordinates_latitude_night : R.drawable.widget_coordinates_latitude_day)); - longitudeIcon.setImageDrawable(iconsCache.getIcon(nightMode ? R.drawable.widget_coordinates_longitude_night : R.drawable.widget_coordinates_longitude_day)); + int latDayImgId = lat >= 0 ? R.drawable.widget_coordinates_latitude_north_day : R.drawable.widget_coordinates_latitude_south_day; + int latNightImgId = lat >= 0 ? R.drawable.widget_coordinates_latitude_north_night : R.drawable.widget_coordinates_latitude_south_night; + int lonDayImgId = lon >= 0 ? R.drawable.widget_coordinates_longitude_east_day : R.drawable.widget_coordinates_longitude_west_day; + int lonNightImgId = lon >= 0 ? R.drawable.widget_coordinates_longitude_east_night : R.drawable.widget_coordinates_longitude_west_night; + latitudeIcon.setImageDrawable(iconsCache.getIcon(nightMode ? latDayImgId : latNightImgId)); + longitudeIcon.setImageDrawable(iconsCache.getIcon(nightMode ? lonDayImgId : lonNightImgId)); latitudeText.setText(latitude); longitudeText.setText(longitude); } From dffbd491538117ea7b3ef55cc16f39268bc63862 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Mon, 22 Jul 2019 12:58:33 +0200 Subject: [PATCH 16/17] Fix issue with live maps --- .../osmand/plus/liveupdates/LiveUpdatesHelper.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java index 1b3d8bfd3b..9796a715aa 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java @@ -34,37 +34,37 @@ public class LiveUpdatesHelper { public static OsmandSettings.CommonPreference preferenceForLocalIndex( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; - return settings.registerBooleanPreference(settingId, false); + return settings.registerBooleanPreference(settingId, false).makeGlobal(); } public static OsmandSettings.CommonPreference preferenceLiveUpdatesOn( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; - return settings.registerBooleanPreference(settingId, false); + return settings.registerBooleanPreference(settingId, false).makeGlobal(); } public static OsmandSettings.CommonPreference preferenceDownloadViaWiFi( String fileName, OsmandSettings settings) { final String settingId = fileName + DOWNLOAD_VIA_WIFI_POSTFIX; - return settings.registerBooleanPreference(settingId, false); + return settings.registerBooleanPreference(settingId, false).makeGlobal(); } public static OsmandSettings.CommonPreference preferenceUpdateFrequency( String fileName, OsmandSettings settings) { final String settingId = fileName + UPDATE_TIMES_POSTFIX; - return settings.registerIntPreference(settingId, UpdateFrequency.HOURLY.ordinal()); + return settings.registerIntPreference(settingId, UpdateFrequency.HOURLY.ordinal()).makeGlobal(); } public static OsmandSettings.CommonPreference preferenceTimeOfDayToUpdate( String fileName, OsmandSettings settings) { final String settingId = fileName + TIME_OF_DAY_TO_UPDATE_POSTFIX; - return settings.registerIntPreference(settingId, TimeOfDay.NIGHT.ordinal()); + return settings.registerIntPreference(settingId, TimeOfDay.NIGHT.ordinal()).makeGlobal(); } public static OsmandSettings.CommonPreference preferenceLastCheck( String fileName, OsmandSettings settings) { final String settingId = fileName + LAST_UPDATE_ATTEMPT_ON_POSTFIX; - return settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK); + return settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK).makeGlobal(); } public static String getNameToDisplay(String fileName, OsmandActionBarActivity activity) { From 804bc025bb5fabb459e3f085240036279723d085 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Mon, 22 Jul 2019 13:08:12 +0200 Subject: [PATCH 17/17] Restore default vl --- .../plus/liveupdates/LiveUpdatesHelper.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java index 9796a715aa..f570390f97 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java @@ -31,40 +31,50 @@ public class LiveUpdatesHelper { public static final int DEFAULT_LAST_CHECK = -1; + private static OsmandSettings.CommonPreference checkPref(OsmandSettings.CommonPreference p) { + if(p.isSet()) { + T vl = p.get(); + p = p.makeGlobal(); + p.set(vl); + } else { + p = p.makeGlobal(); + } + return p; + } public static OsmandSettings.CommonPreference preferenceForLocalIndex( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; - return settings.registerBooleanPreference(settingId, false).makeGlobal(); + return checkPref(settings.registerBooleanPreference(settingId, false)); } public static OsmandSettings.CommonPreference preferenceLiveUpdatesOn( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; - return settings.registerBooleanPreference(settingId, false).makeGlobal(); + return checkPref(settings.registerBooleanPreference(settingId, false)); } public static OsmandSettings.CommonPreference preferenceDownloadViaWiFi( String fileName, OsmandSettings settings) { final String settingId = fileName + DOWNLOAD_VIA_WIFI_POSTFIX; - return settings.registerBooleanPreference(settingId, false).makeGlobal(); + return checkPref(settings.registerBooleanPreference(settingId, false)); } public static OsmandSettings.CommonPreference preferenceUpdateFrequency( String fileName, OsmandSettings settings) { final String settingId = fileName + UPDATE_TIMES_POSTFIX; - return settings.registerIntPreference(settingId, UpdateFrequency.HOURLY.ordinal()).makeGlobal(); + return checkPref(settings.registerIntPreference(settingId, UpdateFrequency.HOURLY.ordinal())); } public static OsmandSettings.CommonPreference preferenceTimeOfDayToUpdate( String fileName, OsmandSettings settings) { final String settingId = fileName + TIME_OF_DAY_TO_UPDATE_POSTFIX; - return settings.registerIntPreference(settingId, TimeOfDay.NIGHT.ordinal()).makeGlobal(); + return checkPref(settings.registerIntPreference(settingId, TimeOfDay.NIGHT.ordinal())); } public static OsmandSettings.CommonPreference preferenceLastCheck( String fileName, OsmandSettings settings) { final String settingId = fileName + LAST_UPDATE_ATTEMPT_ON_POSTFIX; - return settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK).makeGlobal(); + return checkPref(settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK)); } public static String getNameToDisplay(String fileName, OsmandActionBarActivity activity) {