Custom Filter -> Edit Categories Bug

Merge branch 'master' of github.com:osmandapp/Osmand into kolomiets_branch
This commit is contained in:
madwasp79 2019-01-14 12:15:09 +02:00
commit 67ef319294
25 changed files with 3907 additions and 179 deletions

17
AUTHORS
View file

@ -1,17 +0,0 @@
Copyright © OsmAnd 20102014
### Credits to all major contributors/developers:
* Victor Shcherb all parts of the project, originator
* Pavol Zibrita first contributor and developer of some utilities
* Dusan Kazik one of the first contributors
* Andre Van Atten project supporter, active forum participant, one of the first users.
* Dr. Hardy Mueller map appearance concept and base renderers, international consistency and testing, usability, app scoping, concepts, documentation, wiki, market research.
* Yvecai main contributor to Contour Lines and Hillshade maps
* Alexey Pelykh C++ developer, created native library and made application much snappier.
* Max (Zahnstocher) Java contributor, active forum participant.
* Harry van der Wolf contributor to country boundaries, configuration files, address files, and much else; active forum participant.
* Robin ypid Schneider opening hours contributor
### Other Pull requests
Copyright © All authors of translations and pull requests could be found in commits history:
- Translations are under special “contributor” name weblate
- Pull requests have two committers, first is original contributor and second is project maintainer

7
AUTHORS.md Normal file
View file

@ -0,0 +1,7 @@
### Credits to all major contributors/developers:
Major contributors /developers listed here https://github.com/osmandapp/osmandapp.github.io/blob/master/website/help/about.html#L8
### Other Pull requests
Copyright © All authors of translations and pull requests could be found in commits history:
- Translations are under special “contributor” name weblate
- Pull requests have two committers, first is original contributor and second is project maintainer

View file

@ -5,17 +5,20 @@ import gnu.trove.map.hash.TIntObjectHashMap;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.OsmandOdb.TransportRouteSchedule;
import net.osmand.data.TransportSchedule;
import net.osmand.data.TransportStop;
import net.osmand.data.TransportStopExit;
import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.Way;
import net.osmand.util.MapUtils;
import net.sf.junidecode.Junidecode;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.WireFormat;
@ -238,6 +241,11 @@ public class BinaryMapTransportReaderAdapter {
stringTable.putIfAbsent(i, "");
return ((char) i)+"";
}
private String regStr(TIntObjectHashMap<String> stringTable, int i) throws IOException{
stringTable.putIfAbsent(i, "");
return ((char) i)+"";
}
public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TIntObjectHashMap<String> stringTable,
boolean onlyDescription) throws IOException {
@ -446,12 +454,26 @@ public class BinaryMapTransportReaderAdapter {
}
protected void initializeNames(TIntObjectHashMap<String> stringTable, TransportStop s) {
for (TransportStopExit exit : s.getExits()) {
if (exit.getRef().length() > 0) {
exit.setRef(stringTable.get(exit.getRef().charAt(0)));
}
}
if (s.getName().length() > 0) {
s.setName(stringTable.get(s.getName().charAt(0)));
}
if (s.getEnName(false).length() > 0) {
s.setEnName(stringTable.get(s.getEnName(false).charAt(0)));
}
Map<String, String> namesMap = new HashMap<>(s.getNamesMap(false));
if (!s.getNamesMap(false).isEmpty()) {
s.getNamesMap(false).clear();
}
Iterator<Map.Entry<String, String>> it = namesMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> e = it.next();
s.setName(stringTable.get(e.getKey().charAt(0)),stringTable.get(e.getValue().charAt(0)));
}
}
@ -517,6 +539,7 @@ public class BinaryMapTransportReaderAdapter {
TransportStop dataObject = new TransportStop();
dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x, y);
dataObject.setFileOffset(shift);
List<String> names = null;
while(true){
int t = codedIS.readTag();
tag = WireFormat.getTagFieldNumber(t);
@ -543,16 +566,76 @@ public class BinaryMapTransportReaderAdapter {
} else {
skipUnknownField(t);
}
break;
case OsmandOdb.TransportStop.ADDITIONALNAMEPAIRS_FIELD_NUMBER :
if (req.stringTable != null) {
int sizeL = codedIS.readRawVarint32();
int oldRef = codedIS.pushLimit(sizeL);
while (codedIS.getBytesUntilLimit() > 0) {
dataObject.setName(regStr(req.stringTable,codedIS.readRawVarint32()),
regStr(req.stringTable,codedIS.readRawVarint32()));
}
codedIS.popLimit(oldRef);
} else {
skipUnknownField(t);
}
break;
case OsmandOdb.TransportStop.ID_FIELD_NUMBER :
dataObject.setId(codedIS.readSInt64());
break;
case OsmandOdb.TransportStop.EXITS_FIELD_NUMBER :
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
TransportStopExit transportStopExit = readTransportStopExit(cleft, ctop, req);
dataObject.addExit(transportStopExit);
codedIS.popLimit(oldLimit);
break;
default:
skipUnknownField(t);
break;
}
}
}
private TransportStopExit readTransportStopExit(int cleft, int ctop, SearchRequest<TransportStop> req) throws IOException {
TransportStopExit dataObject = new TransportStopExit();
int x = 0;
int y = 0;
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
if (dataObject.getName("en").length() == 0) {
dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
}
if (x != 0 || y != 0) {
dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x, y);
}
return dataObject;
case OsmandOdb.TransportStopExit.REF_FIELD_NUMBER:
if (req.stringTable != null) {
dataObject.setRef(regStr(req.stringTable));
} else {
skipUnknownField(t);
}
break;
case OsmandOdb.TransportStopExit.DX_FIELD_NUMBER:
x = codedIS.readSInt32() + cleft;
break;
case OsmandOdb.TransportStopExit.DY_FIELD_NUMBER:
y = codedIS.readSInt32() + ctop;
break;
default:
skipUnknownField(t);
break;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -77,6 +77,15 @@ public abstract class MapObject implements Comparable<MapObject> {
}
}
public void setNames(Map<String, String> name) {
if (name != null) {
if (names == null) {
names = new HashMap<String, String>();
}
names.putAll(name);
}
}
public Map<String, String> getNamesMap(boolean includeEn) {
if (!includeEn || Algorithms.isEmpty(enName)) {
if (names == null) {

View file

@ -2,13 +2,20 @@ package net.osmand.data;
import net.osmand.util.MapUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TransportStop extends MapObject {
private int[] referencesToRoutes = null;
private Amenity amenity;
public int distance;
public int x31;
public int y31;
private List<TransportStopExit> exits;
private HashMap<String,String> names;
public TransportStop(){
}
@ -39,4 +46,35 @@ public class TransportStop extends MapObject {
y31 = dy << (31 - zoom);
setLocation(MapUtils.getLatitudeFromTile(zoom, dy), MapUtils.getLongitudeFromTile(zoom, dx));
}
public void addExit(TransportStopExit transportStopExit) {
if (exits == null) {
exits = new ArrayList<>();
}
exits.add(transportStopExit);
}
public List<TransportStopExit> getExits () {
if (exits == null) {
return Collections.emptyList();
}
return this.exits;
}
public String getExitsString () {
String exitsString = "";
String refString = "";
if (this.exits != null) {
int i = 1;
exitsString = exitsString + " Exits: [";
for (TransportStopExit e : this.exits ) {
if (e.getRef() != null) {
refString = " [ref:" + e.getRef() + "] ";
}
exitsString = exitsString + " " + i + ")" + refString + e.getName() + " " + e.getLocation() + " ]";
i++;
}
}
return exitsString;
}
}

View file

@ -0,0 +1,27 @@
package net.osmand.data;
import net.osmand.util.MapUtils;
public class TransportStopExit extends MapObject {
public int x31;
public int y31;
public String ref = null;
@Override
public void setLocation(double latitude, double longitude) {
super.setLocation(latitude, longitude);
}
public void setLocation(int zoom, int dx, int dy) {
x31 = dx << (31 - zoom);
y31 = dy << (31 - zoom);
setLocation(MapUtils.getLatitudeFromTile(zoom, dy), MapUtils.getLongitudeFromTile(zoom, dx));
}
public void setRef (String ref) {
this.ref = ref;
}
public String getRef() {
if (ref != null) {
return ref;
}
return "";
}
}

View file

@ -13,6 +13,7 @@ public class OSMSettings {
BOUNDARY("boundary"), //$NON-NLS-1$
POSTAL_CODE("postal_code"), //$NON-NLS-1$
RAILWAY("railway"), //$NON-NLS-1$
STATION("subway"), //$NON-NLS-1$
ONEWAY("oneway"), //$NON-NLS-1$
LAYER("layer"), //$NON-NLS-1$
BRIDGE("bridge"), //$NON-NLS-1$

View file

@ -28,7 +28,7 @@ public class TransportRoutePlanner {
public List<TransportRouteResult> buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException {
public List<TransportRouteResult> buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException, InterruptedException {
ctx.startCalcTime = System.currentTimeMillis();
List<TransportRouteSegment> startStops = ctx.getTransportStops(start);
List<TransportRouteSegment> endStops = ctx.getTransportStops(end);
@ -45,7 +45,7 @@ public class TransportRoutePlanner {
}
double finishTime = ctx.cfg.maxRouteTime;
List<TransportRouteSegment> results = new ArrayList<TransportRouteSegment>();
initProgressBar(ctx, start, end);
while (!queue.isEmpty()) {
TransportRouteSegment segment = queue.poll();
TransportRouteSegment ex = ctx.visitedSegments.get(segment.getId());
@ -56,7 +56,6 @@ public class TransportRoutePlanner {
continue;
}
ctx.visitedRoutesCount++;
System.out.println(segment);
ctx.visitedSegments.put(segment.getId(), segment);
if (segment.getDepth() > ctx.cfg.maxNumberOfChanges) {
continue;
@ -137,10 +136,36 @@ public class TransportRoutePlanner {
results.add(finish);
}
}
if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) {
throw new InterruptedException("Route calculation interrupted");
}
updateCalculationProgress(ctx, queue);
}
return prepareResults(ctx, results);
}
private void initProgressBar(TransportRoutingContext ctx, LatLon start, LatLon end) {
ctx.calculationProgress.distanceFromEnd = 0;
ctx.calculationProgress.reverseSegmentQueueSize = 0;
ctx.calculationProgress.directSegmentQueueSize = 0;
float speed = (float) ctx.cfg.travelSpeed + 1; // assume
ctx.calculationProgress.totalEstimatedDistance = (float) (MapUtils.getDistance(start, end)/ speed);
}
private void updateCalculationProgress(TransportRoutingContext ctx, PriorityQueue<TransportRouteSegment> queue) {
if (ctx.calculationProgress != null) {
ctx.calculationProgress.directSegmentQueueSize = queue.size();
if (queue.size() > 0) {
TransportRouteSegment peek = queue.peek();
ctx.calculationProgress.distanceFromBegin = (float) Math.max(peek.distFromStart,
ctx.calculationProgress.distanceFromBegin);
}
}
}
private List<TransportRouteResult> prepareResults(TransportRoutingContext ctx, List<TransportRouteSegment> results) {
Collections.sort(results, new SegmentsComparator(ctx));

View file

@ -146,4 +146,6 @@ dependencies {
implementation("com.github.HITGIF:TextFieldBoxes:1.4.4") {
exclude group: 'com.android.support'
}
implementation 'net.sf.kxml:kxml2:2.1.8'
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -1,6 +1,9 @@
package net.osmand;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
public class PlatformUtil {
@ -143,4 +146,11 @@ public class PlatformUtil {
return getLog(cl.getName());
}
public static XmlPullParser newXMLPullParser() throws XmlPullParserException {
return new org.kxml2.io.KXmlParser();
}
public static XmlSerializer newSerializer() {
return new org.kxml2.io.KXmlSerializer();
}
}

View file

@ -25,6 +25,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
lateinit var osmandAidlHelper: OsmandAidlHelper private set
lateinit var locationProvider: TelegramLocationProvider private set
lateinit var messagesDbHelper: MessagesDbHelper private set
lateinit var savingTracksDbHelper: SavingTracksDbHelper private set
var telegramService: TelegramService? = null
@ -68,6 +69,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
notificationHelper = NotificationHelper(this)
locationProvider = TelegramLocationProvider(this)
messagesDbHelper = MessagesDbHelper(this)
savingTracksDbHelper = SavingTracksDbHelper(this)
if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) {
shareLocationHelper.startSharingLocation()

View file

@ -105,7 +105,7 @@ class TelegramSettings(private val app: TelegramApplication) {
var appToConnectPackage = ""
private set
var liveNowSortType = LiveNowSortType.SORT_BY_GROUP
var liveNowSortType = LiveNowSortType.SORT_BY_DISTANCE
val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref())
@ -487,7 +487,7 @@ class TelegramSettings(private val app: TelegramApplication) {
appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "")
liveNowSortType = LiveNowSortType.valueOf(
prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_GROUP.name)
prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_DISTANCE.name)
)
batteryOptimisationAsked = prefs.getBoolean(BATTERY_OPTIMISATION_ASKED,false)

View file

@ -0,0 +1,311 @@
package net.osmand.telegram.helpers;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import net.osmand.PlatformUtil;
import net.osmand.telegram.TelegramApplication;
import net.osmand.telegram.ui.LiveNowTabFragment;
import net.osmand.telegram.utils.GPXUtilities;
import net.osmand.telegram.utils.GPXUtilities.GPXFile;
import net.osmand.telegram.utils.GPXUtilities.Track;
import net.osmand.telegram.utils.GPXUtilities.TrkSegment;
import net.osmand.telegram.utils.GPXUtilities.WptPt;
import org.apache.commons.logging.Log;
import org.drinkless.td.libcore.telegram.TdApi;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class SavingTracksDbHelper extends SQLiteOpenHelper {
private final static String DATABASE_NAME = "tracks";
private final static int DATABASE_VERSION = 3;
private final static String TRACK_NAME = "track"; //$NON-NLS-1$
private final static String TRACK_COL_USER_ID = "user_id"; //$NON-NLS-1$
private final static String TRACK_COL_CHAT_ID = "chat_id"; //$NON-NLS-1$
private final static String TRACK_COL_DATE = "date"; //$NON-NLS-1$
private final static String TRACK_COL_LAT = "lat"; //$NON-NLS-1$
private final static String TRACK_COL_LON = "lon"; //$NON-NLS-1$
private final static String TRACK_COL_ALTITUDE = "altitude"; //$NON-NLS-1$
private final static String TRACK_COL_SPEED = "speed"; //$NON-NLS-1$
private final static String TRACK_COL_HDOP = "hdop"; //$NON-NLS-1$
private final static String TRACK_COL_TEXT_INFO = "text_info"; // 1 = true, 0 = false //$NON-NLS-1$
private final static String INSERT_SCRIPT = "INSERT INTO " + TRACK_NAME + " (" + TRACK_COL_USER_ID + ", " + TRACK_COL_CHAT_ID + ", " + TRACK_COL_LAT + ", " + TRACK_COL_LON + ", "
+ TRACK_COL_ALTITUDE + ", " + TRACK_COL_SPEED + ", " + TRACK_COL_HDOP + ", " + TRACK_COL_DATE + ", " + TRACK_COL_TEXT_INFO + ")"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
private final static String CREATE_SCRIPT = "CREATE TABLE " + TRACK_NAME + " (" + TRACK_COL_USER_ID + " long," + TRACK_COL_CHAT_ID + " long," + TRACK_COL_LAT + " double, " + TRACK_COL_LON + " double, " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$
+ TRACK_COL_ALTITUDE + " double, " + TRACK_COL_SPEED + " double, " //$NON-NLS-1$ //$NON-NLS-2$
+ TRACK_COL_HDOP + " double, " + TRACK_COL_DATE + " long, " + TRACK_COL_TEXT_INFO + " int )";
public final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class);
private final TelegramApplication app;
public SavingTracksDbHelper(TelegramApplication app) {
super(app, DATABASE_NAME, null, DATABASE_VERSION);
this.app = app;
app.getTelegramHelper().addIncomingMessagesListener(new TelegramHelper.TelegramIncomingMessagesListener() {
@Override
public void onReceiveChatLocationMessages(long chatId, @NotNull TdApi.Message... messages) {
for (TdApi.Message message : messages) {
updateLocationMessage(message);
}
}
@Override
public void onDeleteChatLocationMessages(long chatId, @NotNull List<? extends TdApi.Message> messages) {
}
@Override
public void updateLocationMessages() {
}
});
app.getTelegramHelper().addOutgoingMessagesListener(new TelegramHelper.TelegramOutgoingMessagesListener() {
@Override
public void onUpdateMessages(@NotNull List<? extends TdApi.Message> messages) {
for (TdApi.Message message : messages) {
updateLocationMessage(message);
}
}
@Override
public void onDeleteMessages(long chatId, @NotNull List<Long> messages) {
}
@Override
public void onSendLiveLocationError(int code, @NotNull String message) {
}
});
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_SCRIPT);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 3) {
db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_TEXT_INFO + " int");
}
}
public void saveAsyncUserDataToGpx(LiveNowTabFragment fragment, File dir, int userId, long interval) {
GPXFile gpxFile = app.getSavingTracksDbHelper().collectRecordedDataForUser(userId, interval);
if (gpxFile != null && !gpxFile.isEmpty()) {
LiveUpdatesPurchaseTask task = new LiveUpdatesPurchaseTask(fragment, gpxFile, dir, userId);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void updateLocationMessage(TdApi.Message message) {
TdApi.MessageContent content = message.content;
if (content instanceof TdApi.MessageLocation) {
long lastTextMessageUpdate = getLastTextTrackPointTimeForUser(message.senderUserId);
long currentTime = System.currentTimeMillis();
if (lastTextMessageUpdate == 0 || currentTime - lastTextMessageUpdate < 10 * 1000) {
log.debug("Add map message" + message.senderUserId);
TdApi.MessageLocation messageLocation = (TdApi.MessageLocation) content;
insertData(message.senderUserId, message.chatId, messageLocation.location.latitude,
messageLocation.location.longitude, 0.0, 0.0, 0.0,
Math.max(message.date, message.editDate), 0);
} else {
log.debug("Skip map message");
}
} else if (content instanceof TelegramHelper.MessageLocation) {
log.debug("Add text message " + message.senderUserId);
TelegramHelper.MessageLocation messageLocation = (TelegramHelper.MessageLocation) content;
insertData(message.senderUserId, message.chatId, messageLocation.getLat(), messageLocation.getLon(),
messageLocation.getAltitude(), messageLocation.getSpeed(), messageLocation.getHdop(),
messageLocation.getLastUpdated() * 1000L, 1);
}
}
private void insertData(int userId, long chatId, double lat, double lon, double alt, double speed, double hdop, long time, int textMessage) {
execWithClose(INSERT_SCRIPT, new Object[]{userId, chatId, lat, lon, alt, speed, hdop, time, textMessage});
}
private synchronized void execWithClose(String script, Object[] objects) {
SQLiteDatabase db = getWritableDatabase();
try {
if (db != null) {
db.execSQL(script, objects);
}
} catch (RuntimeException e) {
log.error(e.getMessage(), e);
} finally {
if (db != null) {
db.close();
}
}
}
private long getLastTextTrackPointTimeForUser(int userId) {
long res = 0;
try {
SQLiteDatabase db = getWritableDatabase();
if (db != null) {
try {
Cursor query = db.rawQuery("SELECT " + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ? AND "
+ TRACK_COL_TEXT_INFO + " = ?" + " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(1)});
if (query.moveToFirst()) {
res = query.getLong(0);
}
query.close();
} finally {
db.close();
}
}
} catch (RuntimeException e) {
}
return res;
}
private GPXFile collectRecordedDataForUser(int userId, long interval) {
GPXFile gpxFile = null;
SQLiteDatabase db = getReadableDatabase();
if (db != null && db.isOpen()) {
try {
gpxFile = collectDBTracksForUser(db, userId, interval);
} finally {
db.close();
}
}
return gpxFile;
}
private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long interval) {
Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ TRACK_COL_SPEED + "," + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME +
" WHERE " + TRACK_COL_USER_ID + " = ? ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
GPXFile gpxFile = new GPXFile();
long previousTime = 0;
TrkSegment segment = null;
Track track = null;
if (query.moveToFirst()) {
do {
long time = query.getLong(7);
long curTime = System.currentTimeMillis();
if (curTime - time > interval) {
continue;
}
WptPt pt = new WptPt();
pt.userId = query.getInt(0);
pt.chatId = query.getLong(1);
pt.lat = query.getDouble(2);
pt.lon = query.getDouble(3);
pt.ele = query.getDouble(4);
pt.speed = query.getDouble(5);
pt.hdop = query.getDouble(6);
pt.time = time;
long currentInterval = Math.abs(time - previousTime);
if (track != null) {
if (currentInterval < 30 * 60 * 1000) {
// 30 minute - same segment
segment.points.add(pt);
} else {
segment = new TrkSegment();
segment.points.add(pt);
track.segments.add(segment);
}
} else {
track = new Track();
segment = new TrkSegment();
track.segments.add(segment);
segment.points.add(pt);
gpxFile.tracks.add(track);
}
previousTime = time;
} while (query.moveToNext());
}
query.close();
return gpxFile;
}
private static class LiveUpdatesPurchaseTask extends AsyncTask<Void, Void, List<String>> {
private TelegramApplication app;
private WeakReference<LiveNowTabFragment> fragmentRef;
private final GPXFile gpxFile;
private File dir;
private int userId;
LiveUpdatesPurchaseTask(LiveNowTabFragment fragment, GPXFile gpxFile, File dir, int userId) {
this.gpxFile = gpxFile;
this.fragmentRef = new WeakReference<>(fragment);
this.app = (TelegramApplication) fragment.getActivity().getApplication();
this.dir = dir;
this.userId = userId;
}
@Override
protected List<String> doInBackground(Void... params) {
List<String> warnings = new ArrayList<String>();
dir.mkdirs();
if (dir.getParentFile().canWrite()) {
if (dir.exists()) {
// save file
File fout = new File(dir, userId + ".gpx"); //$NON-NLS-1$
if (!gpxFile.isEmpty()) {
WptPt pt = gpxFile.findPointToShow();
TdApi.User user = app.getTelegramHelper().getUser(pt.userId);
String fileName;
if (user != null) {
fileName = TelegramUiHelper.INSTANCE.getUserName(user)
+ "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
} else {
fileName = userId + "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
}
fout = new File(dir, fileName + ".gpx"); //$NON-NLS-1$
int ind = 1;
while (fout.exists()) {
fout = new File(dir, fileName + "_" + (++ind) + ".gpx"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String warn = GPXUtilities.writeGpxFile(fout, gpxFile, app);
if (warn != null) {
warnings.add(warn);
return warnings;
}
}
}
return warnings;
}
@Override
protected void onPostExecute(List<String> warnings) {
if (warnings != null && warnings.isEmpty()) {
LiveNowTabFragment fragment = fragmentRef.get();
if (fragment != null && fragment.isResumed()) {
fragment.shareGpx(gpxFile.path);
}
}
}
}
}

View file

@ -221,10 +221,10 @@ class TelegramHelper private constructor() {
is MessageUserTextLocation -> content.lastUpdated
else -> Math.max(message.editDate, message.date)
}
}
}
fun isPrivateChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypePrivate
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
private fun isChannel(chat: TdApi.Chat): Boolean {
@ -384,7 +384,7 @@ class TelegramHelper private constructor() {
is TdApi.ChatTypeSecret -> type.userId
else -> 0
}
fun isOsmAndBot(userId: Int) = users[userId]?.username == OSMAND_BOT_USERNAME
fun isBot(userId: Int) = users[userId]?.type is TdApi.UserTypeBot
@ -685,10 +685,16 @@ class TelegramHelper private constructor() {
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
message.content = parseOsmAndBotLocation(message)
}
removeOldMessages(message, fromBot, viaBot)
usersLocationMessages[message.id] = message
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
if (message.isOutgoing) {
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(message))
}
} else {
removeOldMessages(message, fromBot, viaBot)
usersLocationMessages[message.id] = message
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
}
}
}
}
@ -753,7 +759,7 @@ class TelegramHelper private constructor() {
stopSendingLiveLocationToChat(chatInfo)
}
}
fun getActiveLiveLocationMessages(onComplete: (() -> Unit)?) {
requestingActiveLiveLocationMessages = true
client?.send(TdApi.GetActiveLiveLocationMessages()) { obj ->
@ -1145,19 +1151,20 @@ class TelegramHelper private constructor() {
if (isChannelPost) {
return false
}
val content = content
val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE)
val isOsmAndBot = isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId)
if (isOutgoing && !isOsmAndBot) {
if (!(isUserTextLocation || content is TdApi.MessageLocation || isOsmAndBot)) {
return false
}
val lastEdited = Math.max(date, editDate)
if (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - lastEdited > messageActiveTimeSec) {
return false
}
val content = content
val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE)
return when (content) {
is TdApi.MessageLocation -> true
is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || (isUserTextLocation && senderUserId != currentUser?.id)
is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || isUserTextLocation
else -> false
}
}
@ -1234,6 +1241,33 @@ class TelegramHelper private constructor() {
}
}
}
s.startsWith(ALTITUDE_PREFIX) -> {
val altStr = s.removePrefix(ALTITUDE_PREFIX)
try {
val alt = altStr.split(" ").first()
res.altitude = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(SPEED_PREFIX) -> {
val altStr = s.removePrefix(SPEED_PREFIX)
try {
val alt = altStr.split(" ").first()
res.speed = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(HDOP_PREFIX) -> {
val altStr = s.removePrefix(HDOP_PREFIX)
try {
val alt = altStr.split(" ").first()
res.hdop = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(UPDATED_PREFIX) -> {
if (res.lastUpdated == 0) {
val updatedStr = s.removePrefix(UPDATED_PREFIX)
@ -1291,6 +1325,12 @@ class TelegramHelper private constructor() {
internal set
var lastUpdated: Int = 0
internal set
var speed: Double = 0.0
internal set
var altitude: Double = 0.0
internal set
var hdop: Double = 0.0
internal set
override fun getConstructor() = -1
@ -1510,7 +1550,7 @@ class TelegramHelper private constructor() {
lastTelegramUpdateTime = Math.max(message.date, message.editDate)
}
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
it.updateLocationMessages()
}
}
}
@ -1536,6 +1576,7 @@ class TelegramHelper private constructor() {
newContent
}
}
log.debug("UpdateMessageContent " + message.senderUserId)
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
}

View file

@ -33,6 +33,7 @@ import net.osmand.telegram.utils.OsmandFormatter
import net.osmand.telegram.utils.UiUtils.UpdateLocationViewCache
import net.osmand.util.MapUtils
import org.drinkless.td.libcore.telegram.TdApi
import java.io.File
private const val CHAT_VIEW_TYPE = 0
private const val LOCATION_ITEM_VIEW_TYPE = 1
@ -232,6 +233,15 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
stopLocationUpdate()
}
fun shareGpx(path: String) {
val fileUri = AndroidUtils.getUriForFile(app, File(path))
val sendIntent = Intent(Intent.ACTION_SEND)
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
sendIntent.type = "application/gpx+xml"
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(sendIntent)
}
private fun chooseOsmAnd() {
val ctx = context ?: return
val installedApps = TelegramSettings.AppConnect.getInstalledApps(ctx)
@ -446,6 +456,15 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
app.showLocationHelper.showLocationOnMap(item, staleLocation)
}
}
openOnMapView?.setOnLongClickListener {
app.savingTracksDbHelper.saveAsyncUserDataToGpx(
this@LiveNowTabFragment,
app.getExternalFilesDir(null),
item.userId,
60 * 60 * 6 * 1000
)
true
}
} else {
openOnMapView?.setOnClickListener(null)
}
@ -466,6 +485,17 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
if (lastItem) {
holder.lastTelegramUpdateTime?.visibility = View.VISIBLE
holder.lastTelegramUpdateTime?.text = OsmandFormatter.getListItemLiveTimeDescr(app, telegramHelper.lastTelegramUpdateTime, lastTelegramUpdateStr)
holder.lastTelegramUpdateTime?.setOnClickListener {
val currentUserId = telegramHelper.getCurrentUser()?.id
if (currentUserId != null) {
app.savingTracksDbHelper.saveAsyncUserDataToGpx(
this@LiveNowTabFragment,
app.getExternalFilesDir(null),
currentUserId,
60 * 60 * 6 * 1000
)
}
}
} else {
holder.lastTelegramUpdateTime?.visibility = View.GONE
}

View file

@ -77,7 +77,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
private var actionButtonsListener: ActionButtonsListener? = null
private var sharingMode = false
private var updateEnable: Boolean = false
override fun onCreateView(
@ -96,7 +96,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
searchBoxSidesMargin = resources.getDimensionPixelSize(R.dimen.content_padding_half)
sharingMode = settings.hasAnyChatToShareLocation()
savedInstanceState?.apply {
val chatsArray = getLongArray(SELECTED_CHATS_KEY)
val usersArray = getLongArray(SELECTED_CHATS_KEY)
@ -137,7 +137,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
setupOptionsBtn(optionsBtn)
setupOptionsBtn(mainView.findViewById<ImageView>(R.id.options_title))
}
imageContainer = mainView.findViewById<FrameLayout>(R.id.image_container)
titleContainer = mainView.findViewById<LinearLayout>(R.id.title_container).apply {
AndroidUtils.addStatusBarPadding19v(context, this)
@ -146,8 +146,10 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
mainView.findViewById<TextView>(R.id.status_title).apply {
val sharingStatus = getString(R.string.sharing_enabled)
val spannable = SpannableString(sharingStatus)
spannable.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()),
sharingStatus.indexOf(" "), sharingStatus.length, 0)
spannable.setSpan(
ForegroundColorSpan(app.uiUtils.getActiveColor()),
sharingStatus.indexOf(" "), sharingStatus.length, 0
)
text = spannable
}
@ -222,7 +224,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
updateContent()
}
}
return mainView
}
@ -239,7 +241,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
super.onPause()
updateEnable = false
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray())
@ -349,7 +351,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
}, ADAPTER_UPDATE_INTERVAL_MIL)
}
private fun animateStartSharingBtn(show: Boolean) {
if (startSharingBtn.visibility == View.VISIBLE) {
val scale = if (show) 1f else 0f
@ -361,7 +363,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
.start()
}
}
private fun clearSelection() {
selectedChats.clear()
selectedUsers.clear()
@ -450,7 +452,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
textContainer.visibility = if (sharingMode) View.GONE else View.VISIBLE
titleContainer.visibility = if (sharingMode) View.VISIBLE else View.GONE
startSharingBtn.visibility = if (sharingMode) View.VISIBLE else View.GONE
headerParams.scrollFlags = if (sharingMode) 0 else AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
headerParams.scrollFlags =
if (sharingMode) 0 else AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
stopSharingSwitcher.isChecked = true
appBarScrollRange = -1
}
@ -460,7 +463,12 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
settings.updateSharingStatusHistory()
val sharingStatus = settings.sharingStatusChanges.last()
sharingStatusTitle.text = sharingStatus.getTitle(app)
sharingStatusIcon.setImageDrawable(app.uiUtils.getIcon(sharingStatus.statusType.iconId, sharingStatus.statusType.iconColorRes))
sharingStatusIcon.setImageDrawable(
app.uiUtils.getIcon(
sharingStatus.statusType.iconId,
sharingStatus.statusType.iconColorRes
)
)
}
}
@ -490,7 +498,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
items.addAll(chats)
if (!sharingMode) {
for (user in contacts.values) {
val containsInChats = chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id }
val containsInChats =
chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id }
if ((!sharingMode && settings.isSharingLocationToUser(user.id)) || user.id == currentUser?.id || containsInChats) {
continue
}
@ -520,8 +529,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
})
return list
}
inner class MyLocationListAdapter : RecyclerView.Adapter<MyLocationListAdapter.BaseViewHolder>() {
inner class MyLocationListAdapter :
RecyclerView.Adapter<MyLocationListAdapter.BaseViewHolder>() {
var items = mutableListOf<TdApi.Object>()
set(value) {
field = value
@ -569,7 +579,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
val lastItem = position == itemCount - 1
val placeholderId = if (isChat && telegramHelper.isGroup(item as TdApi.Chat)) R.drawable.img_group_picture else R.drawable.img_user_picture
val placeholderId =
if (isChat && telegramHelper.isGroup(item as TdApi.Chat)) R.drawable.img_group_picture else R.drawable.img_user_picture
val live = (isChat && settings.isSharingLocationToChat(itemId))
val shareInfo = if (isChat) settings.getChatsShareInfo()[itemId] else null
@ -588,7 +599,12 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
holder.title?.text = title
holder.icon?.setOnClickListener {
app.forceUpdateMyLocation()
val curUser = telegramHelper.getCurrentUser()
val text = "${curUser?.id} ${curUser?.firstName} ${curUser?.lastName}"
Toast.makeText(app, text, Toast.LENGTH_LONG).show()
}
if (holder is ChatViewHolder) {
holder.description?.visibility = View.GONE
if (live) {
@ -648,7 +664,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
val duration = shareInfo?.userSetLivePeriod
if (duration != null && duration > 0) {
holder.descriptionDuration?.text = OsmandFormatter.getFormattedDuration(context!!, duration)
holder.descriptionDuration?.text =
OsmandFormatter.getFormattedDuration(context!!, duration)
holder.description?.apply {
visibility = View.VISIBLE
text = "${getText(R.string.sharing_time)}:"
@ -656,19 +673,31 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
val expiresIn = shareInfo?.getChatLiveMessageExpireTime() ?: 0
holder.textInArea?.apply {
val time = shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
val time =
shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
visibility = View.VISIBLE
text = "+ ${OsmandFormatter.getFormattedDuration(context!!, time)}"
setOnClickListener {
val expireTime = shareInfo?.getChatLiveMessageExpireTime() ?: 0
val newLivePeriod = expireTime + (shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0])
val nextAdditionalActiveTime = shareInfo?.getNextAdditionalActiveTime() ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[1]
val newLivePeriod = expireTime + (shareInfo?.additionalActiveTime
?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0])
val nextAdditionalActiveTime = shareInfo?.getNextAdditionalActiveTime()
?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[1]
if (isChat) {
settings.shareLocationToChat(itemId, true, newLivePeriod, nextAdditionalActiveTime)
settings.shareLocationToChat(
itemId,
true,
newLivePeriod,
nextAdditionalActiveTime
)
} else {
settings.shareLocationToUser(itemId.toInt(), newLivePeriod, nextAdditionalActiveTime)
settings.shareLocationToUser(
itemId.toInt(),
newLivePeriod,
nextAdditionalActiveTime
)
}
notifyItemChanged(position)
}
@ -686,13 +715,16 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
holder.stopSharingSecondPart?.apply {
visibility = getStopSharingVisibility(expiresIn)
text = "(${getString(R.string.in_time,
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true))})"
text = "(${getString(
R.string.in_time,
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true)
)})"
}
}
}
private fun getStopSharingVisibility(expiresIn: Long) = if (expiresIn > 0) View.VISIBLE else View.INVISIBLE
private fun getStopSharingVisibility(expiresIn: Long) =
if (expiresIn > 0) View.VISIBLE else View.INVISIBLE
private fun removeItem(chat: TdApi.Object) {
items.remove(chat)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
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);
}

View file

@ -3850,24 +3850,24 @@
<string name="poi_service_car">Servicio automotriz</string>
<string name="poi_service_vehicle_car_repair_yes">Taller mecánico</string>
<string name="poi_service_vehicle_car_repair_yes">Taller para automóviles</string>
<string name="poi_service_vehicle_oil_change_yes">Cambio de aceite</string>
<string name="poi_service_vehicle_used_car_sales_yes">Venta de autos usados</string>
<string name="poi_service_vehicle_brakes_yes">Frenos</string>
<string name="poi_service_vehicle_new_car_sales_yes">Venta de autos nuevos</string>
<string name="poi_service_vehicle_diagnostics_yes">Diagnóstico</string>
<string name="poi_service_vehicle_diagnostics_yes">Diagnósticos</string>
<string name="poi_service_vehicle_car_parts_yes">Autopartes</string>
<string name="poi_service_vehicle_batteries_yes">Baterías</string>
<string name="poi_service_vehicle_air_conditioning_yes">Aire acondicionado</string>
<string name="poi_service_vehicle_body_repair_yes">Reparación de carrocería</string>
<string name="poi_service_vehicle_body_repair_yes">Reparación de carrocerías</string>
<string name="poi_service_vehicle_electrical_yes">Eléctrico</string>
<string name="poi_service_vehicle_wheels_yes">Ruedas</string>
<string name="poi_service_vehicle_glass_yes">Vidrio</string>
<string name="poi_service_vehicle_truck_repair_yes">Taller mecánico para camiones</string>
<string name="poi_service_vehicle_glass_yes">Vidrios</string>
<string name="poi_service_vehicle_truck_repair_yes">Taller para camiones</string>
<string name="poi_service_vehicle_muffler_yes">Silenciador</string>
<string name="poi_service_vehicle_alignment_yes">Alineación</string>
<string name="poi_service_vehicle_transmission_repair_yes">Reparación de la transmisión</string>
<string name="poi_service_vehicle_motor_yes">Motor</string>
<string name="poi_service_vehicle_motor_yes">Motores</string>
<string name="poi_service_vehicle_insurance_yes">Aseguradora</string>
<string name="poi_service_vehicle_tyres_yes">Neumáticos</string>

View file

@ -2370,7 +2370,7 @@
<string name="marker_moved_to_active">Kartmarkør flyttet til aktive</string>
<string name="shared_string_list">Liste</string>
<string name="passed">Sist brukt: %1$s</string>
<string name="mapillary_menu_filter_description">Filtrer bilder etter innsender eller etter dato. Kun aktiv på høyere zoomnivåer.</string>
<string name="mapillary_menu_filter_description">Filtrer bilder etter innsender, dato eller type. Kun aktivt på nærgående forstørrelsesnivå.</string>
<string name="improve_coverage_install_mapillary_desc">Installer Mapillary for å legge til et eller flere bilder til denne kartposisjonen.</string>
<string name="plugin_mapillary_descr">Foto på gatenivå for alle. Oppdag plasser, samarbeid, fang inn verden.</string>
<string name="quick_action_showhide_osmbugs_title">Vis/skjul OSM-notater</string>

View file

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="poi_shoes">Skoaffär</string>
<string name="poi_computer">Datorbutik</string>
<string name="poi_shop">Butik</string>
@ -3105,4 +3105,199 @@
<string name="poi_payment_troika_yes">Troika</string>
<string name="poi_support_ceiling">Stöd: tak</string>
</resources>
<string name="poi_bulk_purchase">Massköp</string>
<string name="poi_substation_type">Typ</string>
<string name="poi_books_type">Bok</string>
<string name="poi_car_pooling">Bilpoolplats</string>
<string name="poi_atm">Bankomat</string>
<string name="poi_pump_type_beam_pump">Pumptyp: domkraftspump</string>
<string name="poi_pump_type_india_mk_2_3">Pumptyp: India Mk II eller III&gt;</string>
<string name="poi_payment_troika_no">Troika-kort accepteras ej</string>
<string name="poi_support_billboard">Stöd: billboard</string>
<string name="poi_support_suspended">Stöd: upphängd</string>
<string name="poi_support_roof">Stöd: tak</string>
<string name="poi_support_tower">Stöd: torn</string>
<string name="poi_passenger_information_display_yes">Skärm för passagerarinformation: ja</string>
<string name="poi_passenger_information_display_no">Skärm för passagerarinformation: nej</string>
<string name="poi_aquaculture">Vattenbruk</string>
<string name="poi_aquaculture_shrimp">Vattenbruk: räkor</string>
<string name="poi_aquaculture_fish">Vattenbruk: fisk</string>
<string name="poi_aquaculture_mussels">Vattenbruk: musslor</string>
<string name="poi_min_age">Lägsta ålder</string>
<string name="poi_organic_yes">Ja</string>
<string name="poi_organic_no">Nej</string>
<string name="poi_organic_only">Endast</string>
<string name="poi_traffic_mirror">Trafikspegel</string>
<string name="poi_diplomatic_consulate">Konsulat</string>
<string name="poi_diplomatic_consulate_general">Generalkonsulat</string>
<string name="poi_diplomatic_honorary_consulate">Honorärkonsulat</string>
<string name="poi_diplomatic_delegation">Delegation</string>
<string name="poi_diplomatic_ambassadors_residence">Ambassadörsresidens</string>
<string name="poi_water_tank">Vattentank</string>
<string name="poi_length">Längd</string>
<string name="poi_wiki_link">Wikipedia</string>
<string name="poi_xmas">Jul</string>
<string name="poi_xmas_event">Julhändelse</string>
<string name="poi_xmas_market">Julmarknad</string>
<string name="poi_xmas_pyramid">Julpyramid</string>
<string name="poi_xmas_shop">Julbutik</string>
<string name="poi_xmas_shop_christmas_tree">Julgranshandel</string>
<string name="poi_xmas_tree">Julgran</string>
<string name="poi_cuisine">Kök</string>
<string name="poi_cuisine_pizza">Pizza</string>
<string name="poi_cuisine_burger">Hamburgare</string>
<string name="poi_cuisine_coffee">Kaffe</string>
<string name="poi_cuisine_sandwich">Smörgås</string>
<string name="poi_cuisine_kebab">Kebab</string>
<string name="poi_cuisine_doner">Dönerkebab (shawarma)</string>
<string name="poi_cuisine_chicken">Kyckling</string>
<string name="poi_cuisine_ice_cream">Glass</string>
<string name="poi_cuisine_sushi">Sushi</string>
<string name="poi_cuisine_snack">Mellanmål</string>
<string name="poi_cuisine_yogurt">Yoghurt</string>
<string name="poi_cuisine_gyros">Gyros</string>
<string name="poi_cuisine_empanada">Empanada</string>
<string name="poi_cuisine_crepes">Crêpes</string>
<string name="poi_cuisine_yakiniku">Yakiniku</string>
<string name="poi_cuisine_suki">Suki</string>
<string name="poi_cuisine_udon">Udon</string>
<string name="poi_cuisine_brasserie">Brasserie</string>
<string name="poi_cuisine_bubble_tea">Bubbelte</string>
<string name="poi_cuisine_yakitori">Yakitori</string>
<string name="poi_cuisine_sagardotegia">Sagardotegia</string>
<string name="poi_cuisine_meat">Kött</string>
<string name="poi_cuisine_wings">Vingar</string>
<string name="poi_cuisine_waffle">Våfflor</string>
<string name="poi_cuisine_chocolate">Choklad</string>
<string name="poi_cuisine_wine">Vin</string>
<string name="poi_cuisine_potato">Potatis</string>
<string name="poi_cuisine_brunch">Brunch</string>
<string name="poi_cuisine_sub">Sub</string>
<string name="poi_cuisine_pita">Pita</string>
<string name="poi_cuisine_fondue">Fondue</string>
<string name="poi_cuisine_baguette">Baguette</string>
<string name="poi_cuisine_pastel">Pastell</string>
<string name="poi_cuisine_burrito">Burrito</string>
<string name="poi_cuisine_teriyaki">Teriyaki</string>
<string name="poi_cuisine_shawarma">Shawarma</string>
<string name="poi_cuisine_regional">Regionalt</string>
<string name="poi_cuisine_italian">Italienskt</string>
<string name="poi_cuisine_chinese">Kinesiskt</string>
<string name="poi_cuisine_mexican">Mexikanskt</string>
<string name="poi_cuisine_japanese">Japanskt</string>
<string name="poi_cuisine_german">Tyskt</string>
<string name="poi_cuisine_indian">Indiskt</string>
<string name="poi_cuisine_american">Amerikanskt</string>
<string name="poi_cuisine_asian">Asiatiskt</string>
<string name="poi_cuisine_french">Franskt</string>
<string name="poi_cuisine_greek">Grekiskt</string>
<string name="poi_cuisine_thai">Thailändskt</string>
<string name="poi_cuisine_international">Internationellt</string>
<string name="poi_cuisine_turkish">Turkiskt</string>
<string name="poi_cuisine_spanish">Spanskt</string>
<string name="poi_cuisine_vietnamese">Vietnamesiskt</string>
<string name="poi_cuisine_korean">Koreanskt</string>
<string name="poi_cuisine_mediterranean">Medelhavs</string>
<string name="poi_cuisine_bavarian">Bayerskt</string>
<string name="poi_cuisine_lebanese">"Libanesiskt "</string>
<string name="poi_cuisine_russian">Ryskt</string>
<string name="poi_cuisine_filipino">Filippinskt</string>
<string name="poi_cuisine_portuguese">Portugisiskt</string>
<string name="poi_cuisine_georgian">Georgiskt</string>
<string name="poi_cuisine_polish">Polskt</string>
<string name="poi_cuisine_brazilian">Brasilianskt</string>
<string name="poi_cuisine_arab">Arabiskt</string>
<string name="poi_cuisine_danish">Danskt</string>
<string name="poi_cuisine_indonesian">Indonesiskt</string>
<string name="poi_cuisine_african">Afrikanskt</string>
<string name="poi_cuisine_caribbean">Karibiskt</string>
<string name="poi_cuisine_argentinian">Argentinskt</string>
<string name="poi_cuisine_balkan">Balkan</string>
<string name="poi_cuisine_peruvian">Peruanskt</string>
<string name="poi_cuisine_croatian">Kroatiskt</string>
<string name="poi_cuisine_bolivian">Bolivianskt</string>
<string name="poi_cuisine_malagasy">Malagassiskt</string>
<string name="poi_cuisine_persian">Persiskt</string>
<string name="poi_cuisine_moroccan">Marockanskt</string>
<string name="poi_cuisine_austrian">Österrikiskt</string>
<string name="poi_cuisine_malaysian">Malajiskt</string>
<string name="poi_cuisine_irish">Irländskt</string>
<string name="poi_cuisine_ethiopian">Etiopiskt</string>
<string name="poi_cuisine_hungarian">Ungerskt</string>
<string name="poi_cuisine_lao">Laotiskt</string>
<string name="poi_cuisine_european">Europeiskt</string>
<string name="poi_cuisine_uzbek">Uzbekiskt</string>
<string name="poi_cuisine_czech">Tjeckiskt</string>
<string name="poi_cuisine_cuban">Kubanskt</string>
<string name="poi_cuisine_british">Brittiskt</string>
<string name="poi_cuisine_latin_american">Latinamerikanskt</string>
<string name="poi_cuisine_nepalese">Nepalesiskt</string>
<string name="poi_cuisine_mongolian">Mongoliskt</string>
<string name="poi_cuisine_middle_eastern">Mellanöstern</string>
<string name="poi_cuisine_ukrainian">Ukrainskt</string>
<string name="poi_cuisine_afghan">Afghanskt</string>
<string name="poi_cuisine_belgian">Belgiskt</string>
<string name="poi_cuisine_basque">Baskiskt</string>
<string name="poi_cuisine_swiss">"Schweiziskt "</string>
<string name="poi_cuisine_cantonese">Kantonesiskt</string>
<string name="poi_cuisine_swedish">Svenskt</string>
<string name="poi_cuisine_jamaican">Jamaicanskt</string>
<string name="poi_cuisine_armenian">Armeniskt</string>
<string name="poi_cuisine_hawaiian">"Hawaiianskt "</string>
<string name="poi_cuisine_english">Engelskt</string>
<string name="poi_cuisine_pakistani">Pakistanskt</string>
<string name="poi_cuisine_taiwanese">Taiwanesiskt</string>
<string name="poi_cuisine_tex_mex">Tex-mex</string>
<string name="poi_cuisine_dutch">Holländskt</string>
<string name="poi_cuisine_syrian">Syrianskt</string>
<string name="poi_cuisine_australian">Australienskt</string>
<string name="poi_cuisine_cajun">Cajun</string>
<string name="poi_cuisine_egyptian">Egyptiskt</string>
<string name="poi_cuisine_senegalese">Senegalesiskt</string>
<string name="poi_cuisine_jewish">Judiskt</string>
<string name="poi_cuisine_bulgarian">Bulgariskt</string>
<string name="poi_cuisine_tibetan">Tibetanskt</string>
<string name="poi_feeding_place">Plats för utfodring av djur</string>
<string name="poi_party">Festutrustning</string>
<string name="poi_electrical">Elbutik</string>
<string name="poi_locksmith">Låssmed</string>
<string name="poi_lighting">Belysning</string>
<string name="poi_lottery">Lotterier</string>
<string name="poi_gambling_type">Typ</string>
<string name="poi_gambling_lottery">Lotteri</string>
<string name="poi_gambling_pachinko">Pachinko</string>
<string name="poi_gambling_slot_machines">Spelautomater</string>
<string name="poi_gambling_betting">Betting</string>
<string name="poi_gambling_bingo">Bingo</string>
<string name="poi_e_cigarette">E-cigarettbutik</string>
<string name="poi_locomotive">Lok</string>
<string name="poi_nutrition_supplements">Kosttillskott</string>
<string name="poi_photo_studio">Fotostudio</string>
<string name="poi_cliff">Klippa</string>
<string name="poi_animal_keeping">Djurhållning</string>
<string name="poi_animal_keeping_horse">Djurhållning: häst</string>
<string name="poi_animal_keeping_sheep">Djurhållning: får</string>
<string name="poi_animal_keeping_type_paddock">Typ: paddock</string>
<string name="poi_animal_keeping_type_open_stable">Typ: öppet stall</string>
<string name="poi_tower_construction_lattice">Konstruktion: fackverk</string>
<string name="poi_tower_construction_freestanding">Konstruktion: fristående</string>
<string name="poi_tower_construction_dish">Konstruktion: disk</string>
<string name="poi_tower_construction_dome">Konstruktion: dom</string>
<string name="poi_tower_construction_concealed">Konstruktion: dold</string>
<string name="poi_railway_yard">Fraktstation</string>
<string name="poi_fast_food_cafeteria">Ja</string>
<string name="poi_drink_wine_yes">Vin: ja</string>
<string name="poi_drink_wine_retail">Vin: detaljhandel</string>
<string name="poi_drink_wine_served">Vin: serveras</string>
<string name="poi_energy_supplier">Energileverantör</string>
<string name="poi_electronics_repair_computer">Reparation av elektronik: datorer</string>
<string name="poi_electronics_repair_appliance">Elektronik reparationer: apparater</string>
<string name="poi_electronics_repair_phone">Reparation av elektronik: telefon</string>
<string name="poi_electronics_repair_tv">Reparation av elektronik: tv</string>
<string name="poi_resort_kids_camp">Barnläger</string>
<string name="poi_music_school">Musikskola</string>
<string name="poi_language_school">Språkskola</string>
<string name="poi_life_ring">Livboj</string>
<string name="poi_zoo_safari_park">Safaripark</string>
<string name="poi_zoo_birds">Fåglar</string>
</resources>

View file

@ -364,6 +364,7 @@ public class MapActivityActions implements DialogProvider {
adapter.addItem(new ContextMenuItem.ItemBuilder()
.setTitleId(R.string.context_menu_item_add_waypoint, mapActivity)
.setTitle(MAP_CONTEXT_MENU_ADD_GPX_WAYPOINT)
.setId(POINT_ADD_GPX_WAYPOINT)
.setIcon(R.drawable.ic_action_gnew_label_dark)
.setOrder(ADD_GPX_WAYPOINT_ITEM_ORDER)
.setListener(listener).createItem());

View file

@ -233,7 +233,7 @@ public class DownloadActivityType {
}
public String getBaseUrl(OsmandApplication ctx, String fileName) {
String url = "http://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&"
String url = "https://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&"
+ Version.getVersionAsURLParam(ctx) + "&file=" + encode(fileName);
if(this == LIVE_UPDATES_FILE && fileName.length() > 16) {
// DATE_AND_EXT_STR_LEN = "_18_06_02.obf.gz".length()