diff --git a/OsmAnd/src/net/osmand/plus/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java similarity index 84% rename from OsmAnd/src/net/osmand/plus/GPXUtilities.java rename to OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java index 5e755f8bb0..339f91e3fd 100644 --- a/OsmAnd/src/net/osmand/plus/GPXUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java @@ -1,26 +1,5 @@ -package net.osmand.plus; - -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.LocationPoint; -import net.osmand.data.PointDescription; -import net.osmand.data.RotatedTileBox; -import net.osmand.plus.views.Renderable; -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; +package net.osmand; import java.io.BufferedInputStream; import java.io.File; @@ -48,10 +27,19 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import java.util.TimeZone; +import net.osmand.data.QuadRect; +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; + public class GPXUtilities { public final static Log log = PlatformUtil.getLog(GPXUtilities.class); @@ -63,6 +51,47 @@ public class GPXUtilities { private final static NumberFormat decimalFormat = new DecimalFormat("#.###", new DecimalFormatSymbols( new Locale("EN", "US"))); + public enum GPXColor { + BLACK(0xFF000000), + DARKGRAY(0xFF444444), + GRAY(0xFF888888), + LIGHTGRAY(0xFFCCCCCC), + WHITE(0xFFFFFFFF), + RED(0xFFFF0000), + GREEN(0xFF00FF00), + BLUE(0xFF0000FF), + YELLOW(0xFFFFFF00), + CYAN(0xFF00FFFF), + MAGENTA(0xFFFF00FF), + AQUA(0xFF00FFFF), + FUCHSIA(0xFFFF00FF), + DARKGREY(0xFF444444), + GREY(0xFF888888), + LIGHTGREY(0xFFCCCCCC), + LIME(0xFF00FF00), + MAROON(0xFF800000), + NAVY(0xFF000080), + OLIVE(0xFF808000), + PURPLE(0xFF800080), + SILVER(0xFFC0C0C0), + TEAL(0xFF008080); + + int color; + + GPXColor(int color) { + this.color = color; + } + + public static GPXColor getColorFromName(String s) { + for (GPXColor c : values()) { + if (c.name().equalsIgnoreCase(s)) { + return c; + } + } + return null; + } + } + public static class GPXExtensions { Map extensions = null; @@ -73,8 +102,7 @@ public class GPXUtilities { return extensions; } - @ColorInt - public int getColor(@ColorInt int defColor) { + public int getColor(int defColor) { String clrValue = null; if (extensions != null) { clrValue = extensions.get("color"); @@ -84,15 +112,11 @@ public class GPXUtilities { if (clrValue == null) { clrValue = extensions.get("displaycolor"); } - } - if (clrValue != null && clrValue.length() > 0) { - try { - return Color.parseColor(clrValue.toUpperCase()); - } catch (IllegalArgumentException e) { - e.printStackTrace(); + if (clrValue == null) { + clrValue = extensions.get("displaycolour"); } } - return defColor; + return parseColor(clrValue, defColor); } public void setColor(int color) { @@ -110,6 +134,25 @@ public class GPXUtilities { return extensions; } + private int parseColor(String colorString, int defColor) { + if (!Algorithms.isEmpty(colorString)) { + if (colorString.charAt(0) == '#') { + long color = Long.parseLong(colorString.substring(1), 16); + if (colorString.length() == 7) { + color |= 0x00000000ff000000; + } else if (colorString.length() != 9) { + return defColor; + } + return (int) color; + } else { + GPXColor c = GPXColor.getColorFromName(colorString); + if (c != null) { + return c.color; + } + } + } + return defColor; + } } public static class Elevation { @@ -124,7 +167,7 @@ public class GPXUtilities { public float speed; } - public static class WptPt extends GPXExtensions implements LocationPoint { + public static class WptPt extends GPXExtensions { public boolean firstPoint = false; public boolean lastPoint = false; public double lat; @@ -174,27 +217,19 @@ public class GPXUtilities { 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; @@ -204,7 +239,6 @@ public class GPXUtilities { this.hdop = hdop; } - @Override public boolean isVisible() { return true; } @@ -235,6 +269,10 @@ public class GPXUtilities { && Algorithms.objectEquals(other.lon, lon) && Algorithms.objectEquals(other.desc, desc); } + + public boolean hasLocation() { + return (lat != 0 && lon != 0); + } } public static class TrkSegment extends GPXExtensions { @@ -242,7 +280,8 @@ public class GPXUtilities { public List points = new ArrayList<>(); - public List renders = new ArrayList<>(); + public Object renderer; + public List splitByDistance(double meters) { return split(getDistanceMetric(), getTimeSplit(), meters); @@ -258,11 +297,6 @@ public class GPXUtilities { 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 { @@ -283,12 +317,10 @@ public class GPXUtilities { 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"); } @@ -300,9 +332,9 @@ public class GPXUtilities { 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; + //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; @@ -756,8 +788,8 @@ public class GPXUtilities { } private static void splitSegment(SplitMetric metric, SplitMetric secondaryMetric, - double metricLimit, List splitSegments, - TrkSegment segment) { + double metricLimit, List splitSegments, + TrkSegment segment) { double currentMetricEnd = metricLimit; double secondaryMetricEnd = 0; SplitSegment sp = new SplitSegment(segment, 0, 0); @@ -810,14 +842,32 @@ public class GPXUtilities { private List points = new ArrayList<>(); public List routes = new ArrayList<>(); - public String warning = null; + public Exception error = null; public String path = ""; public boolean showCurrentTrack; + public boolean hasAltitude; public long modifiedTime = 0; private Track generalTrack; private TrkSegment generalSegment; + public GPXFile(String author) { + this.author = author; + } + + public GPXFile(String title, String lang, String description) { + this.metadata = new Metadata(); + if(description != null) { + metadata.extensions.put("desc", description); + } + if(lang != null) { + metadata.extensions.put("article_lang", lang); + } + if(title != null) { + metadata.extensions.put("article_title", title); + } + } + public List getPoints() { return Collections.unmodifiableList(points); } @@ -846,11 +896,11 @@ public class GPXUtilities { return points.size(); } - boolean containsPoint(WptPt point) { + public boolean containsPoint(WptPt point) { return points.contains(point); } - void clearPoints() { + public void clearPoints() { points.clear(); modifiedTime = System.currentTimeMillis(); } @@ -864,8 +914,8 @@ public class GPXUtilities { points.add(position, point); modifiedTime = System.currentTimeMillis(); } - - void addPoints(Collection collection) { + + public void addPoints(Collection collection) { points.addAll(collection); modifiedTime = System.currentTimeMillis(); } @@ -1223,7 +1273,7 @@ public class GPXUtilities { Set categories = new HashSet<>(); for (WptPt pt : points) { String category = pt.category == null ? "" : pt.category; - if (withDefaultCategory || !TextUtils.isEmpty(category)) { + if (withDefaultCategory || !Algorithms.isEmpty(category)) { categories.add(category); } } @@ -1235,7 +1285,7 @@ public class GPXUtilities { for (WptPt pt : points) { String category = pt.category == null ? "" : pt.category; int color = pt.category == null ? 0 : pt.getColor(); - boolean emptyCategory = TextUtils.isEmpty(category); + boolean emptyCategory = Algorithms.isEmpty(category); if (!emptyCategory) { Integer existingColor = categories.get(category); if (existingColor == null || (existingColor == 0 && color != 0)) { @@ -1247,29 +1297,79 @@ public class GPXUtilities { } 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, OsmandApplication ctx) { + public static String asString(GPXFile file) { final Writer writer = new StringWriter(); - GPXUtilities.writeGpx(writer, file, ctx); + GPXUtilities.writeGpx(writer, file); return writer.toString(); } - public static String writeGpxFile(File fout, GPXFile file, OsmandApplication ctx) { + public static Exception writeGpxFile(File fout, GPXFile file) { Writer output = null; try { - if(fout.getParentFile() != null) { + if (fout.getParentFile() != null) { fout.getParentFile().mkdirs(); } output = new OutputStreamWriter(new FileOutputStream(fout), "UTF-8"); //$NON-NLS-1$ - if(Algorithms.isEmpty(file.path)) { + if (Algorithms.isEmpty(file.path)) { file.path = fout.getAbsolutePath(); } - String msg = writeGpx(output, file, ctx); - return msg; - } catch (IOException e) { + return writeGpx(output, file); + } catch (Exception e) { log.error("Error saving gpx", e); //$NON-NLS-1$ - return ctx.getString(R.string.error_occurred_saving_gpx); + return e; } finally { if (output != null) { try { @@ -1281,7 +1381,7 @@ public class GPXUtilities { } } - public static String writeGpx(Writer output, GPXFile file, OsmandApplication ctx) { + public static Exception writeGpx(Writer output, GPXFile file) { try { SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); format.setTimeZone(TimeZone.getTimeZone("UTC")); @@ -1291,9 +1391,7 @@ public class GPXUtilities { 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", Version.getAppName(ctx)); //$NON-NLS-1$ - } else { + if (file.author != null) { 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$ @@ -1353,29 +1451,24 @@ public class GPXUtilities { serializer.endTag(null, "gpx"); //$NON-NLS-1$ serializer.endDocument(); serializer.flush(); - - - } catch (RuntimeException e) { + } catch (Exception e) { log.error("Error saving gpx", e); //$NON-NLS-1$ - return ctx.getString(R.string.error_occurred_saving_gpx); - } catch (IOException e) { - log.error("Error saving gpx", e); //$NON-NLS-1$ - return ctx.getString(R.string.error_occurred_saving_gpx); + return e; } 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); - } - } + 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; } @@ -1390,7 +1483,7 @@ public class GPXUtilities { 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()) { + for (Entry s : p.getExtensionsToRead().entrySet()) { writeNotNullText(serializer, s.getKey(), s.getValue()); } serializer.endTag(null, "extensions"); @@ -1450,27 +1543,54 @@ public class GPXUtilities { private static String readText(XmlPullParser parser, String key) throws XmlPullParserException, IOException { int tok; - String text = null; + StringBuilder 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(); + text = new StringBuilder(parser.getText()); } else { - text += parser.getText(); + text.append(parser.getText()); } } - } - return text; + return text == null ? null : text.toString(); } - public static GPXFile loadGPXFile(Context ctx, File f) { + private static Map readTextMap(XmlPullParser parser, String key) + throws XmlPullParserException, IOException { + int tok; + StringBuilder text = null; + Map result = new HashMap<>(); + while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (tok == XmlPullParser.END_TAG) { + String tag = parser.getName(); + if (text != null && !Algorithms.isEmpty(text.toString().trim())) { + result.put(tag, text.toString()); + } + if (tag.equals(key)) { + break; + } + text = null; + } else if (tok == XmlPullParser.START_TAG) { + text = null; + } else if (tok == XmlPullParser.TEXT) { + if (text == null) { + text = new StringBuilder(parser.getText()); + } else { + text.append(parser.getText()); + } + } + } + return result; + } + + public static GPXFile loadGPXFile(File f) { FileInputStream fis = null; try { fis = new FileInputStream(f); - GPXFile file = loadGPXFile(ctx, fis); + GPXFile file = loadGPXFile(fis); file.path = f.getAbsolutePath(); try { fis.close(); @@ -1478,10 +1598,10 @@ public class GPXUtilities { } return file; } catch (IOException e) { - GPXFile res = new GPXFile(); + GPXFile res = new GPXFile(null); res.path = f.getAbsolutePath(); log.error("Error reading gpx " + res.path, e); //$NON-NLS-1$ - res.warning = ctx.getString(R.string.error_reading_gpx); + res.error = e; return res; } finally { try { @@ -1493,8 +1613,8 @@ public class GPXUtilities { } } - public static GPXFile loadGPXFile(Context ctx, InputStream f) { - GPXFile res = new GPXFile(); + public static GPXFile loadGPXFile(InputStream f) { + GPXFile res = new GPXFile(null); SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); format.setTimeZone(TimeZone.getTimeZone("UTC")); SimpleDateFormat formatMillis = new SimpleDateFormat(GPX_TIME_FORMAT_MILLIS, Locale.US); @@ -1504,23 +1624,47 @@ public class GPXUtilities { parser.setInput(getUTF8Reader(f)); Stack parserState = new Stack<>(); boolean extensionReadMode = false; + boolean parseExtension = false; + boolean endOfTrkSegment = false; parserState.push(res); int tok; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { - Object parse = parserState.peek(); + GPXExtensions parse = parserState.peek(); String tag = parser.getName(); - if (extensionReadMode && parse != null) { - 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) {} - } + if (extensionReadMode && parse != null && !parseExtension) { + switch (tag.toLowerCase()) { + case "routepointextension": + parseExtension = true; + Track track = new Track(); + res.tracks.add(track); + GPXExtensions parent = parserState.size() > 1 ? parserState.get(parserState.size() - 2) : null; + if (parse instanceof WptPt && parent instanceof Route) { + track.getExtensionsToWrite().putAll(parent.getExtensionsToRead()); + track.getExtensionsToWrite().putAll(parse.getExtensionsToRead()); + } + parserState.push(track); + break; + + default: + Map values = readTextMap(parser, tag); + if (values.size() > 0) { + for (Entry entry : values.entrySet()) { + String t = entry.getKey().toLowerCase(); + String value = entry.getValue(); + parse.getExtensionsToWrite().put(t, value); + if (tag.equals("speed") && parse instanceof WptPt) { + try { + ((WptPt) parse).speed = Float.parseFloat(value); + } catch (NumberFormatException e) { + log.debug(e.getMessage(), e); + } + } + } + } + break; } - } else if (parse instanceof GPXExtensions && tag.equals("extensions")) { + } else if (parse != null && tag.equals("extensions")) { extensionReadMode = true; } else { if (parse instanceof GPXFile) { @@ -1575,8 +1719,18 @@ public class GPXUtilities { ((Track) parse).segments.add(trkSeg); parserState.push(trkSeg); } + if (tag.equals("rpt")) { + endOfTrkSegment = false; + TrkSegment trkSeg = new TrkSegment(); + ((Track) parse).segments.add(trkSeg); + parserState.push(trkSeg); + WptPt wptPt = parseWptAttributes(parser); + parse = parserState.peek(); + ((TrkSegment) parse).points.add(wptPt); + parserState.push(wptPt); + } } else if (parse instanceof TrkSegment) { - if (tag.equals("trkpt")) { + if (tag.equals("trkpt") || tag.equals("rpt")) { WptPt wptPt = parseWptAttributes(parser); ((TrkSegment) parse).points.add(wptPt); parserState.push(wptPt); @@ -1613,7 +1767,7 @@ public class GPXUtilities { try { String value = readText(parser, "speed"); ((WptPt) parse).speed = Float.parseFloat(value); - ((WptPt) parse).getExtensionsToWrite().put("speed", value); + parse.getExtensionsToWrite().put("speed", value); } catch (NumberFormatException e) { } } else if (tag.equals("link")) { @@ -1653,6 +1807,8 @@ public class GPXUtilities { } } } + } else if (tag.toLowerCase().equals("subclass")) { + endOfTrkSegment = true; } } } @@ -1660,7 +1816,13 @@ public class GPXUtilities { } else if (tok == XmlPullParser.END_TAG) { Object parse = parserState.peek(); String tag = parser.getName(); - if (parse instanceof GPXExtensions && tag.equals("extensions")) { + + if (tag.toLowerCase().equals("routepointextension")) { + parseExtension = false; + Object pop = parserState.pop(); + assert pop instanceof Track; + } + if (parse != null && tag.equals("extensions")) { extensionReadMode = false; } @@ -1685,18 +1847,26 @@ public class GPXUtilities { } else if (tag.equals("trkseg")) { Object pop = parserState.pop(); assert pop instanceof TrkSegment; + } else if (tag.equals("rpt")) { + Object pop = parserState.pop(); + assert pop instanceof WptPt; + if (endOfTrkSegment) { + Object popSegment = parserState.pop(); + if (popSegment instanceof TrkSegment) { + List segments = res.tracks.get(res.tracks.size() - 1).segments; + int last = segments.size() - 1; + if (!Algorithms.isEmpty(segments) && segments.get(last).points.size() < 2) { + segments.remove(last); + } + } + endOfTrkSegment = false; + } } } } - } catch (RuntimeException e) { + } catch (Exception e) { + res.error = e; log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = ctx.getString(R.string.error_reading_gpx) + " " + e.getMessage(); - } catch (XmlPullParserException e) { - log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = ctx.getString(R.string.error_reading_gpx) + " " + e.getMessage(); - } catch (IOException e) { - log.error("Error reading gpx", e); //$NON-NLS-1$ - res.warning = ctx.getString(R.string.error_reading_gpx) + " " + e.getMessage(); } return res; @@ -1744,8 +1914,8 @@ public class GPXUtilities { if (from.routes != null) { to.routes.addAll(from.routes); } - if (from.warning != null) { - to.warning = from.warning; + if (from.error != null) { + to.error = from.error; } } -} +} \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java b/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java index 0bde0dca08..9670a029f5 100644 --- a/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java +++ b/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java @@ -68,4 +68,5 @@ public class IndexConstants { public static final String RENDERERS_DIR = "rendering/"; //$NON-NLS-1$ public static final String ROUTING_XML_FILE= "routing.xml"; public static final String SETTINGS_DIR = "settings/"; //$NON-NLS-1$ + public static final String TEMP_DIR = "temp/"; } diff --git a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java index 3eaebd56e5..ed2ed17c17 100644 --- a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java +++ b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java @@ -130,10 +130,10 @@ public class NativeLibrary { public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config, RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection, - boolean basemap) { + boolean basemap, boolean publicTransport, boolean startTransportStop, boolean targetTransportStop) { // config.router.printRules(System.out); return nativeRouting(new int[] { sx31, sy31, ex31, ey31 }, config, config.initialDirection == null ? -360 : config.initialDirection.floatValue(), - regions, progress, precalculatedRouteDirection, basemap); + regions, progress, precalculatedRouteDirection, basemap, publicTransport, startTransportStop, targetTransportStop); } @@ -156,7 +156,9 @@ public class NativeLibrary { protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31); protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, RoutingConfiguration r, - float initDirection, RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap); + float initDirection, RouteRegion[] regions, RouteCalculationProgress progress, + PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap, + boolean publicTransport, boolean startTransportStop, boolean targetTransportStop); protected static native void deleteSearchResult(long searchResultHandle); diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java index 75c7dac5c2..65493a75ed 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java @@ -2078,14 +2078,14 @@ public class BinaryMapIndexReader { private static boolean testAddressSearch = false; private static boolean testAddressSearchName = false; private static boolean testAddressJustifySearch = false; - private static boolean testPoiSearch = true; + private static boolean testPoiSearch = false; private static boolean testPoiSearchOnPath = false; private static boolean testTransportSearch = true; - private static int sleft = MapUtils.get31TileNumberX(4.7495); - private static int sright = MapUtils.get31TileNumberX(4.8608); - private static int stop = MapUtils.get31TileNumberY(52.3395); - private static int sbottom = MapUtils.get31TileNumberY(52.2589); + private static int sleft = MapUtils.get31TileNumberX(27.55079); + private static int sright = MapUtils.get31TileNumberX(27.55317); + private static int stop = MapUtils.get31TileNumberY(53.89378); + private static int sbottom = MapUtils.get31TileNumberY(53.89276); private static int szoom = 15; private static void println(String s) { @@ -2094,7 +2094,7 @@ public class BinaryMapIndexReader { public static void main(String[] args) throws IOException { File fl = new File(System.getProperty("maps") + "/Synthetic_test_rendering.obf"); - fl = new File(System.getProperty("maps") + "/Map.obf"); + fl = new File(System.getProperty("maps") + "/Belarus_europe_2.obf"); RandomAccessFile raf = new RandomAccessFile(fl, "r"); @@ -2284,6 +2284,9 @@ public class BinaryMapIndexReader { println(" " + route.getRef() + " " + route.getName() + " " + route.getDistance() + " " + route.getAvgBothDistance()); StringBuilder b = new StringBuilder(); + if(route.getForwardWays() == null) { + continue; + } for(Way w : route.getForwardWays()) { b.append(w.getNodes()).append(" "); } diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java index aa564321d7..fce39c5c55 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java @@ -472,7 +472,7 @@ public class BinaryMapTransportReaderAdapter { Iterator> it = namesMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = it.next(); - s.setName(stringTable.get(e.getKey().charAt(0)),stringTable.get(e.getValue().charAt(0))); + s.setName(stringTable.get(e.getKey().charAt(0)), stringTable.get(e.getValue().charAt(0))); } } diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java index a1e10eae7a..5cbb1cd0cc 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java @@ -578,7 +578,21 @@ public class RouteDataObject { public boolean loop(){ return pointsX[0] == pointsX[pointsX.length - 1] && pointsY[0] == pointsY[pointsY.length - 1] ; } - + + public boolean platform(){ + int sz = types.length; + for(int i=0; i forwardStops = new ArrayList(); private String ref; @@ -209,4 +209,16 @@ public class TransportRoute extends MapObject { return d; } + public String getAdjustedRouteRef() { + if (ref != null) { + int charPos = ref.lastIndexOf(':'); + if (charPos != -1) { + ref = ref.substring(0, charPos); + } + if (ref.length() > 4) { + ref = ref.substring(0, 4); + } + } + return ref; + } } \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/map/TileSourceManager.java b/OsmAnd-java/src/main/java/net/osmand/map/TileSourceManager.java index d464f881f4..409b188b93 100644 --- a/OsmAnd-java/src/main/java/net/osmand/map/TileSourceManager.java +++ b/OsmAnd-java/src/main/java/net/osmand/map/TileSourceManager.java @@ -435,7 +435,6 @@ public class TileSourceManager { public static java.util.List getKnownSourceTemplates() { java.util.List list = new ArrayList(); list.add(getMapnikSource()); - list.add(getCycleMapSource()); list.add(getMapillaryRasterSource()); list.add(getMapillaryVectorSource()); return list; @@ -445,9 +444,6 @@ public class TileSourceManager { return MAPNIK_SOURCE; } - public static TileSourceTemplate getCycleMapSource(){ - return CYCLE_MAP_SOURCE; - } public static TileSourceTemplate getMapillaryRasterSource() { return MAPILLARY_RASTER_SOURCE; diff --git a/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java b/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java index b1ebbc9b30..e195c42d42 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/GeneralRouter.java @@ -32,6 +32,7 @@ public class GeneralRouter implements VehicleRouter { public static final String AVOID_UNPAVED = "avoid_unpaved"; public static final String PREFER_MOTORWAYS = "prefer_motorway"; public static final String ALLOW_PRIVATE = "allow_private"; + public static final String ALLOW_MOTORWAYS = "allow_motorway"; private final RouteAttributeContext[] objectAttributes; public final Map attributes; diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java index cad29d75e3..c2d5776de4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java @@ -55,6 +55,10 @@ public class RoutePlannerFrontEnd { } public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List list) throws IOException { + return findRouteSegment(lat, lon, ctx, list, false); + } + + public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List list, boolean transportStop) throws IOException { int px = MapUtils.get31TileNumberX(lon); int py = MapUtils.get31TileNumberY(lat); ArrayList dataObjects = new ArrayList(); @@ -92,7 +96,26 @@ public class RoutePlannerFrontEnd { } }); if (list.size() > 0) { - RouteSegmentPoint ps = list.get(0); + RouteSegmentPoint ps = null; + if (ctx.publicTransport) { + for (RouteSegmentPoint p : list) { + if (transportStop && p.distSquare > 100) { + break; + } + boolean platform = p.road.platform(); + if (transportStop && platform) { + ps = p; + break; + } + if (!transportStop && !platform) { + ps = p; + break; + } + } + } + if (ps == null) { + ps = list.get(0); + } ps.others = list; return ps; } @@ -185,17 +208,17 @@ public class RoutePlannerFrontEnd { } int indexNotFound = 0; List points = new ArrayList(); - if (!addSegment(start, ctx, indexNotFound++, points)) { + if (!addSegment(start, ctx, indexNotFound++, points, ctx.startTransportStop)) { return null; } if (intermediates != null) { for (LatLon l : intermediates) { - if (!addSegment(l, ctx, indexNotFound++, points)) { + if (!addSegment(l, ctx, indexNotFound++, points, false)) { return null; } } } - if (!addSegment(end, ctx, indexNotFound++, points)) { + if (!addSegment(end, ctx, indexNotFound++, points, ctx.targetTransportStop)) { return null; } ctx.calculationProgress.nextIteration(); @@ -315,8 +338,8 @@ public class RoutePlannerFrontEnd { } - private boolean addSegment(LatLon s, RoutingContext ctx, int indexNotFound, List res) throws IOException { - RouteSegmentPoint f = findRouteSegment(s.getLatitude(), s.getLongitude(), ctx, null); + private boolean addSegment(LatLon s, RoutingContext ctx, int indexNotFound, List res, boolean transportStop) throws IOException { + RouteSegmentPoint f = findRouteSegment(s.getLatitude(), s.getLongitude(), ctx, null, transportStop); if (f == null) { ctx.calculationProgress.segmentNotFound = indexNotFound; return false; @@ -405,7 +428,8 @@ public class RoutePlannerFrontEnd { long time = System.currentTimeMillis(); RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY, - ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE); + ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE, + ctx.publicTransport, ctx.startTransportStop, ctx.targetTransportStop); log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds"); ArrayList result = new ArrayList(Arrays.asList(res)); if (recalculationEnd != null) { diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java index 2c975ac500..167dfbebe9 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java @@ -41,7 +41,6 @@ public class RouteResultPreparation { */ List prepareResult(RoutingContext ctx, FinalRouteSegment finalSegment) throws IOException { List result = convertFinalSegmentToResults(ctx, finalSegment); - combineWayPointsForAreaRouting(ctx, result); prepareResult(ctx, result); return result; } @@ -158,6 +157,7 @@ public class RouteResultPreparation { } List prepareResult(RoutingContext ctx, List result) throws IOException { + combineWayPointsForAreaRouting(ctx, result); validateAllPointsConnected(result); splitRoadsAndAttachRoadSegments(ctx, result); calculateTimeSpeed(ctx, result); @@ -1718,4 +1718,6 @@ public class RouteResultPreparation { return MapUtils.getDistance(MapUtils.get31LatitudeY(y1), MapUtils.get31LongitudeX(x1), MapUtils.get31LatitudeY(y2), MapUtils.get31LongitudeX(x2)); } + + } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java index 6ee9fbbb52..6a014ad5fe 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteSegmentResult.java @@ -2,6 +2,7 @@ package net.osmand.router; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -43,19 +44,28 @@ public class RouteSegmentResult { int st = Math.min(startPointIndex, endPointIndex); int end = Math.max(startPointIndex, endPointIndex); float[] res = new float[(end - st + 1) * 2]; - for (int k = 0; k < res.length / 2; k++) { - int ind = reverse ? (2 * (end - k)) : (2 * (k + st)); - if (k == 0) { - res[2 * k] = 0; - } else { - if(ind < pf.length) { - res[2 * k] = pf[k]; + if (reverse) { + for (int k = 1; k <= res.length / 2; k++) { + int ind = (2 * (end--)); + if (ind < pf.length && k < res.length / 2) { + res[2 * k] = pf[ind]; + } + if (ind < pf.length) { + res[2 * (k - 1) + 1] = pf[ind + 1]; } } - if(ind < pf.length) { - res[2 * k + 1] = pf[ind + 1]; + } else { + for (int k = 0; k < res.length / 2; k++) { + int ind = (2 * (st + k)); + if (k > 0 && ind < pf.length) { + res[2 * k] = pf[ind]; + } + if (ind < pf.length) { + res[2 * k + 1] = pf[ind + 1]; + } } } + return res; } @@ -217,5 +227,16 @@ public class RouteSegmentResult { public String toString() { return object.toString() + ": " + startPointIndex + "-" + endPointIndex; } - + + public String getSurface() { + return object.getValue("surface"); + } + + public String getSmoothness() { + return object.getValue("smoothness"); + } + + public String getHighway() { + return object.getHighway(); + } } \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java new file mode 100644 index 0000000000..6435e5a45d --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteStatistics.java @@ -0,0 +1,530 @@ +package net.osmand.router; + +import java.util.*; + +public class RouteStatistics { + + private final List route; + + private RouteStatistics(List route) { + this.route = route; + } + + public static RouteStatistics newRouteStatistic(List route) { + return new RouteStatistics(route); + } + + public Statistics getRouteSurfaceStatistic() { + RouteStatisticComputer statisticComputer = new RouteSurfaceStatisticComputer(route); + return statisticComputer.computeStatistic(); + } + + public Statistics getRouteSmoothnessStatistic() { + RouteStatisticComputer statisticComputer = new RouteSmoothnessStatisticComputer(route); + return statisticComputer.computeStatistic(); + } + + public Statistics getRouteClassStatistic() { + RouteStatisticComputer statisticComputer = new RouteClassStatisticComputer(route); + return statisticComputer.computeStatistic(); + } + + public Statistics getRouteSteepnessStatistic(List inclines) { + RouteStatisticComputer statisticComputer = new RouteSteepnessStatisticComputer(inclines); + return statisticComputer.computeStatistic(); + } + + + private abstract static class RouteStatisticComputer> { + + private final List route; + + public RouteStatisticComputer(List route) { + this.route = route; + } + + protected Map> makePartition(List> routeAttributes) { + Map> partition = new TreeMap<>(); + for (RouteSegmentAttribute attribute : routeAttributes) { + E key = attribute.getAttribute(); + RouteSegmentAttribute pattr = partition.get(key); + if (pattr == null) { + pattr = new RouteSegmentAttribute<>(attribute.getIndex(), attribute.getAttribute(), attribute.getColorAttrName()); + partition.put(key, pattr); + } + pattr.incrementDistanceBy(attribute.getDistance()); + } + return partition; + } + + private float computeTotalDistance(List> attributes) { + float distance = 0f; + for (RouteSegmentAttribute attribute : attributes) { + distance += attribute.getDistance(); + } + return distance; + } + + protected List getRoute() { + return route; + } + + protected List> processRoute() { + int index = 0; + List> routes = new ArrayList<>(); + E prev = null; + for (RouteSegmentResult segment : getRoute()) { + E current = getAttribute(segment); + if (prev != null && !prev.equals(current)) { + index++; + } + if (index >= routes.size()) { + String colorAttrName = determineColor(current); + routes.add(new RouteSegmentAttribute<>(index, current, colorAttrName)); + } + RouteSegmentAttribute surface = routes.get(index); + surface.incrementDistanceBy(segment.getDistance()); + prev = current; + } + return routes; + } + + public Statistics computeStatistic() { + List> routeAttributes = processRoute(); + Map> partition = makePartition(routeAttributes); + float totalDistance = computeTotalDistance(routeAttributes); + return new Statistics<>(routeAttributes, partition, totalDistance); + } + + public abstract E getAttribute(RouteSegmentResult segment); + + public abstract String determineColor(E attribute); + + } + + private static class RouteSurfaceStatisticComputer extends RouteStatisticComputer { + + public RouteSurfaceStatisticComputer(List route) { + super(route); + } + + @Override + public String getAttribute(RouteSegmentResult segment) { + String segmentSurface = segment.getSurface(); + if (segmentSurface == null) { + return RoadSurface.UNDEFINED.name().toLowerCase(); + } + for (RoadSurface roadSurface : RoadSurface.values()) { + if (roadSurface.contains(segmentSurface)) { + return roadSurface.name().toLowerCase(); + } + } + return RoadSurface.UNDEFINED.name().toLowerCase(); + } + + @Override + public String determineColor(String attribute) { + RoadSurface roadSurface = RoadSurface.valueOf(attribute.toUpperCase()); + return roadSurface.getColorAttrName(); + } + } + + private static class RouteSmoothnessStatisticComputer extends RouteStatisticComputer { + + public RouteSmoothnessStatisticComputer(List route) { + super(route); + } + + @Override + public String getAttribute(RouteSegmentResult segment) { + String segmentSmoothness = segment.getSurface(); + if (segmentSmoothness == null) { + return RoadSmoothness.UNDEFINED.name().toLowerCase(); + } + for (RoadSmoothness roadSmoothness : RoadSmoothness.values()) { + if (roadSmoothness.contains(segmentSmoothness)) { + return roadSmoothness.name().toLowerCase(); + } + } + return RoadSmoothness.UNDEFINED.name().toLowerCase(); + } + + @Override + public String determineColor(String attribute) { + RoadSmoothness roadSmoothness = RoadSmoothness.valueOf(attribute.toUpperCase()); + return roadSmoothness.getColorAttrName(); + } + } + + + private static class RouteClassStatisticComputer extends RouteStatisticComputer { + + public RouteClassStatisticComputer(List route) { + super(route); + } + + @Override + public String getAttribute(RouteSegmentResult segment) { + String segmentClass = segment.getHighway(); + if (segmentClass == null) { + return RoadClass.UNDEFINED.name().toLowerCase(); + } + for (RoadClass roadClass : RoadClass.values()) { + if (roadClass.contains(segmentClass)) { + return roadClass.name().toLowerCase(); + } + } + return RoadClass.UNDEFINED.name().toLowerCase(); + } + + @Override + public String determineColor(String attribute) { + RoadClass roadClass = RoadClass.valueOf(attribute.toUpperCase()); + return roadClass.getColorAttrName(); + } + } + + + private static class RouteSteepnessStatisticComputer extends RouteStatisticComputer { + + private static final String POSITIVE_INCLINE_COLOR_ATTR_NAME = "greenColor"; + private static final String NEGATIVE_INCLINE_COLOR_ATTR_NAME = "redColor"; + + private final List inclines; + + public RouteSteepnessStatisticComputer(List inclines) { + super(null); + this.inclines = inclines; + } + + @Override + public List> processRoute() { + List> routeInclines = new ArrayList<>(); + int index = 0; + Boundaries prev = null; + Incline prevIncline = null; + for (Incline incline : inclines) { + Boundaries current = incline.getBoundaries(); + if (prev != null && !prev.equals(current)) { + index++; + } + if (index >= routeInclines.size()) { + String colorAttrName = determineColor(current); + RouteSegmentAttribute attribute = new RouteSegmentAttribute<>(index, current, colorAttrName); + if (prevIncline != null) { + attribute.setInitDistance(prevIncline.getDistance()); + } + routeInclines.add(attribute); + } + RouteSegmentAttribute routeIncline = routeInclines.get(index); + routeIncline.relativeSum(incline.getDistance()); + prev = current; + prevIncline = incline; + } + return routeInclines; + } + + @Override + public Boundaries getAttribute(RouteSegmentResult segment) { + /* + no-op + */ + return null; + } + + @Override + public String determineColor(Boundaries attribute) { + return attribute.getLowerBoundary() >= 0 ? POSITIVE_INCLINE_COLOR_ATTR_NAME : NEGATIVE_INCLINE_COLOR_ATTR_NAME; + } + } + + + public static class RouteSegmentAttribute { + + private final int index; + private final E attribute; + private final String colorAttrName; + + private float distance; + private float initDistance; + + public RouteSegmentAttribute(int index, E attribute, String colorAttrName) { + this.index = index; + this.attribute = attribute; + this.colorAttrName = colorAttrName; + } + + public int getIndex() { + return index; + } + + public E getAttribute() { + return attribute; + } + + public float getDistance() { + return distance; + } + + public void setInitDistance(float initDistance) { + this.initDistance = initDistance; + } + + public void incrementDistanceBy(float distance) { + this.distance += distance; + } + + public void relativeSum(float distance) { + this.distance = this.distance + ((distance - this.initDistance) - this.distance); + } + + public String getColorAttrName() { + return colorAttrName; + } + + @Override + public String toString() { + return "RouteSegmentAttribute{" + + "index=" + index + + ", attribute='" + attribute + '\'' + + ", colorAttrName='" + colorAttrName + '\'' + + ", distance=" + distance + + '}'; + } + } + + public static class Incline { + + private float inclineValue; + private final float distance; + private final Boundaries boundaries; + + public Incline(float inclineValue, float distance) { + this.inclineValue = inclineValue; + this.distance = distance; + this.boundaries = Boundaries.newBoundariesFor(inclineValue); + } + + public float getValue() { + return inclineValue; + } + + public float getDistance() { + return distance; + } + + public Boundaries getBoundaries() { + return this.boundaries; + } + + @Override + public String toString() { + return "Incline{" + + ", incline=" + inclineValue + + ", distance=" + distance + + '}'; + } + } + + public static class Boundaries implements Comparable { + + private static final int MIN_INCLINE = -100; + private static final int MAX_INCLINE = 100; + private static final int STEP = 4; + private static final int NUM; + private static final int[] BOUNDARIES_ARRAY; + + static { + NUM = ((MAX_INCLINE - MIN_INCLINE) / STEP + 1); + BOUNDARIES_ARRAY = new int[NUM]; + for (int i = 0; i < NUM; i++) { + BOUNDARIES_ARRAY[i] = MIN_INCLINE + i * STEP; + } + } + + private final float upperBoundary; + private final float lowerBoundary; + + private Boundaries(float upperBoundary, float lowerBoundary) { + this.upperBoundary = upperBoundary; + this.lowerBoundary = lowerBoundary; + } + + public static Boundaries newBoundariesFor(float incline) { + if (incline > MAX_INCLINE) { + return new Boundaries(MAX_INCLINE, MAX_INCLINE - STEP); + } + if (incline < MIN_INCLINE) { + return new Boundaries(MIN_INCLINE + STEP, MIN_INCLINE); + } + for (int i = 1; i < NUM; i++) { + if (incline >= BOUNDARIES_ARRAY[i - 1] && incline < BOUNDARIES_ARRAY[i]) { + return new Boundaries(BOUNDARIES_ARRAY[i], BOUNDARIES_ARRAY[i - 1]); + } + } + return null; + } + + public float getUpperBoundary() { + return upperBoundary; + } + + public float getLowerBoundary() { + return lowerBoundary; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Boundaries that = (Boundaries) o; + + if (Float.compare(that.upperBoundary, upperBoundary) != 0) return false; + return Float.compare(that.lowerBoundary, lowerBoundary) == 0; + } + + @Override + public int hashCode() { + int result = (upperBoundary != +0.0f ? Float.floatToIntBits(upperBoundary) : 0); + result = 31 * result + (lowerBoundary != +0.0f ? Float.floatToIntBits(lowerBoundary) : 0); + return result; + } + + @Override + public int compareTo(Boundaries boundaries) { + return (int) (getLowerBoundary() - boundaries.getLowerBoundary()); + } + + @Override + public String toString() { + return String.format("%d-%d", Math.round(getLowerBoundary()), Math.round(getUpperBoundary())); + } + } + + public static class Statistics { + + private final List> elements; + private final Map> partition; + private final float totalDistance; + + private Statistics(List> elements, + Map> partition, + float totalDistance) { + this.elements = elements; + this.partition = partition; + this.totalDistance = totalDistance; + } + + public float getTotalDistance() { + return totalDistance; + } + + public List> getElements() { + return elements; + } + + public Map> getPartition() { + return partition; + } + } + + public enum RoadClass { + UNDEFINED("whitewaterSectionGrade0Color", "undefined"), + MOTORWAY("motorwayRoadColor", "motorway", "motorway_link"), + STATE_ROAD("trunkRoadColor" , "trunk", "trunk_link", "primary", "primary_link"), + ROAD("secondaryRoadColor", "secondary", "secondary_link", "tertiary", "tertiary_link", "unclassified"), + STREET("residentialRoadColor" ,"residential", "living_street"), + SERVICE("serviceRoadColor", "service"), + TRACK("trackColor", "track", "road"), + FOOTWAY("footwayColor", "footway"), + PATH("pathColor", "path"), + CYCLE_WAY("cyclewayColor", "cycleway"); + + final Set roadClasses = new TreeSet<>(); + final String colorAttrName; + + RoadClass(String colorAttrName, String... classes) { + roadClasses.addAll(Arrays.asList(classes)); + this.colorAttrName = colorAttrName; + } + + boolean contains(String roadClass) { + return roadClasses.contains(roadClass); + } + + String getColorAttrName() { + return colorAttrName; + } + } + + public enum RoadSurface { + UNDEFINED("whitewaterSectionGrade0Color", "undefined"), + PAVED("motorwayRoadColor", "paved"), + UNPAVED("motorwayRoadShadowColor", "unpaved"), + ASPHALT("trunkRoadColor", "asphalt"), + CONCRETE("primaryRoadColor", "concrete"), + COMPACTED("secondaryRoadColor", "compacted"), + GRAVEL("tertiaryRoadColor", "gravel"), + FINE_GRAVEL("residentialRoadColor", "fine_gravel"), + PAVING_STONES("serviceRoadColor", "paving_stones"), + SETT("roadRoadColor", "sett"), + COBBLESTONE("pedestrianRoadColor", "cobblestone"), + PEBBLESTONE("racewayColor", "pebblestone"), + STONE("trackColor", "stone"), + METAL("footwayColor", "metal"), + GROUND("pathColor", "ground", "mud"), + WOOD("cycleRouteColor", "wood"), + GRASS_PAVER("osmcBlackColor", "grass_paver"), + GRASS("osmcBlueColor", "grass"), + SAND("osmcGreenColor", "sand"), + SALT("osmcRedColor", "salt"), + SNOW("osmcYellowColor", "snow"), + ICE("osmcOrangeColor", "ice"), + CLAY("osmcBrownColor", "clay"); + + final Set surfaces = new TreeSet<>(); + final String colorAttrName; + + RoadSurface(String colorAttrName, String... surfaces) { + this.surfaces.addAll(Arrays.asList(surfaces)); + this.colorAttrName = colorAttrName; + } + + boolean contains(String surface) { + return surfaces.contains(surface); + } + + public String getColorAttrName() { + return this.colorAttrName; + } + } + + public enum RoadSmoothness { + UNDEFINED("redColor", "undefined"), + EXCELLENT("orangeColor", "excellent"), + GOOD("brownColor", "good"), + INTERMEDIATE("darkyellowColor", "intermediate"), + BAD("yellowColor", "bad"), + VERY_BAD("lightgreenColor", "very_bad"), + HORRIBLE("greenColor", "horrible"), + VERY_HORRIBLE("lightblueColor", "very_horrible"), + IMPASSABLE("blueColor", "impassable"); + + final Set surfaces = new TreeSet<>(); + final String colorAttrName; + + RoadSmoothness(String colorAttrName, String... surfaces) { + this.surfaces.addAll(Arrays.asList(surfaces)); + this.colorAttrName = colorAttrName; + } + + boolean contains(String surface) { + return surfaces.contains(surface); + } + + public String getColorAttrName() { + return this.colorAttrName; + } + } +} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java index ddd44e738c..91a4fd0eff 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java @@ -57,8 +57,11 @@ public class RoutingContext { // 1. Initial variables public int startX; public int startY; + public boolean startTransportStop; public int targetX; public int targetY; + public boolean targetTransportStop; + public boolean publicTransport; // deprecated public long firstRoadId; public int firstRoadDirection; diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java index 91abce36d8..e278e44656 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java @@ -1,19 +1,5 @@ package net.osmand.router; -import gnu.trove.iterator.TIntIterator; -import gnu.trove.list.array.TIntArrayList; -import gnu.trove.map.hash.TIntObjectHashMap; -import gnu.trove.map.hash.TLongObjectHashMap; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; - import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.data.LatLon; @@ -24,6 +10,20 @@ import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Way; import net.osmand.util.MapUtils; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +import gnu.trove.iterator.TIntIterator; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.map.hash.TLongObjectHashMap; + public class TransportRoutePlanner { @@ -37,7 +37,7 @@ public class TransportRoutePlanner { for(TransportRouteSegment s : endStops) { endSegments.put(s.getId(), s); } - PriorityQueue queue = new PriorityQueue(new SegmentsComparator(ctx)); + PriorityQueue queue = new PriorityQueue(startStops.size(), new SegmentsComparator(ctx)); for(TransportRouteSegment r : startStops){ r.walkDist = (float) MapUtils.getDistance(r.getLocation(), start); r.distFromStart = r.walkDist / ctx.cfg.walkSpeed; @@ -47,6 +47,9 @@ public class TransportRoutePlanner { List results = new ArrayList(); initProgressBar(ctx, start, end); while (!queue.isEmpty()) { + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } TransportRouteSegment segment = queue.poll(); TransportRouteSegment ex = ctx.visitedSegments.get(segment.getId()); if(ex != null) { @@ -71,7 +74,10 @@ public class TransportRoutePlanner { TransportStop prevStop = segment.getStop(segment.segStart); List sgms = new ArrayList(); for (int ind = 1 + segment.segStart; ind < segment.getLength(); ind++) { - segmentId ++; + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } + segmentId ++; ctx.visitedSegments.put(segmentId, segment); TransportStop stop = segment.getStop(ind); // could be geometry size @@ -87,6 +93,9 @@ public class TransportRoutePlanner { sgms.clear(); sgms = ctx.getTransportStops(stop.x31, stop.y31, true, sgms); for (TransportRouteSegment sgm : sgms) { + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } if (segment.wasVisited(sgm)) { continue; } @@ -174,11 +183,17 @@ public class TransportRoutePlanner { (System.currentTimeMillis() - ctx.startCalcTime) / 1000.0, results.size(), ctx.visitedRoutesCount, ctx.quadTree.size(), ctx.readTime / (1000 * 1000), ctx.loadTime / (1000 * 1000))); for(TransportRouteSegment res : results) { + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } TransportRouteResult route = new TransportRouteResult(ctx); route.routeTime = res.distFromStart; route.finishWalkDist = res.walkDist; TransportRouteSegment p = res; while (p != null) { + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } if (p.parentRoute != null) { TransportRouteResultSegment sg = new TransportRouteResultSegment(p.parentRoute.road, p.parentRoute.segStart, p.parentStop, p.parentRoute.walkDist, @@ -190,6 +205,9 @@ public class TransportRoutePlanner { // test if faster routes fully included boolean include = false; for(TransportRouteResult s : lst) { + if (ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { + return null; + } if(includeRoute(s, route)) { include = true; break; @@ -280,7 +298,16 @@ public class TransportRoutePlanner { public TransportStop getEnd() { return route.getForwardStops().get(end); } - + + public List getNodes() { + List nodes = new ArrayList<>(); + List ways = getGeometry(); + for (Way way : ways) { + nodes.addAll(way.getNodes()); + } + return nodes; + } + public List getGeometry() { List list = new ArrayList(); route.mergeForwardWays(); @@ -352,7 +379,7 @@ public class TransportRoutePlanner { public List getSegments() { return segments; } - + public double getWalkDist() { double d = finishWalkDist; for (TransportRouteResultSegment s : segments) { @@ -360,7 +387,15 @@ public class TransportRoutePlanner { } return d; } - + + public double getFinishWalkDist() { + return finishWalkDist; + } + + public double getWalkSpeed() { + return cfg.walkSpeed; + } + public double getRouteTime() { return routeTime; } diff --git a/OsmAnd-java/src/test/java/net/osmand/search/SearchCoreUITest.java b/OsmAnd-java/src/test/java/net/osmand/search/SearchCoreUITest.java index 80d92b7bf1..a130e8b65c 100644 --- a/OsmAnd-java/src/test/java/net/osmand/search/SearchCoreUITest.java +++ b/OsmAnd-java/src/test/java/net/osmand/search/SearchCoreUITest.java @@ -222,7 +222,7 @@ public class SearchCoreUITest { }); if (files != null) { for (File f : files) { - testSearchImpl(f); + //testSearchImpl(f); } } } diff --git a/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java b/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java index 05138ea91e..a2e7469cfc 100644 --- a/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java @@ -11,7 +11,7 @@ import org.junit.Test; import net.osmand.util.GeoPointParserUtil.GeoParsedPoint; public class GeoPointParserUtilTest { - + @Test public void testGeoPointUrlDecode() { // bug in get scheme getSchemeSpecificPart() @@ -19,14 +19,14 @@ public class GeoPointParserUtilTest { GeoParsedPoint test = GeoPointParserUtil.parse("geo:0,0?q=86HJV99P%2B29"); Assert.assertEquals(test.getQuery(), "86HJV99P+29"); } - + @Test public void testGoogleMaps() { // https://www.google.com/maps?daddr=Bahnhofplatz+3,+7000+Chur@46.853582,9.529903 GeoParsedPoint actual = GeoPointParserUtil.parse( "https://www.google.com/maps?daddr=Bahnhofplatz+3,+7000+Chur"); assertGeoPoint(actual, new GeoParsedPoint("Bahnhofplatz 3, 7000 Chur")); - + actual = GeoPointParserUtil.parse( "https://www.google.com/maps?daddr=Bahnhofplatz+3,+7000+Chur@46.853582,9.529903"); System.out.println(actual); @@ -97,8 +97,8 @@ public class GeoPointParserUtilTest { System.out.println(" Passed!"); } - - + + // geo:34,-106 url = "geo:" + ilat + "," + ilon; System.out.println("url: " + url); @@ -111,7 +111,7 @@ public class GeoPointParserUtilTest { System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(dlat, dlon)); - + // geo:34.99393,-106.61568 url = "geo:" + dlat + "," + dlon; System.out.println("url: " + url); @@ -271,12 +271,12 @@ public class GeoPointParserUtilTest { actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(dlat, -Math.abs(dlon))); - url = "http://maps.google.com/maps?f=d&saddr=" + dlat +"," +dlon +"&daddr=" +dlat +"," +dlon+"&hl=en"; + url = "http://maps.google.com/maps?f=d&saddr=" + dlat +"," +dlon +"&daddr=" +dlat +"," +dlon+"&hl=en"; System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(dlat, dlon)); - - url = "http://maps.google.com/maps?f=d&saddr=My+Location&daddr=" +dlat +"," +dlon+"&hl=en"; + + url = "http://maps.google.com/maps?f=d&saddr=My+Location&daddr=" +dlat +"," +dlon+"&hl=en"; System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(dlat, dlon)); @@ -407,7 +407,7 @@ public class GeoPointParserUtilTest { System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(ilat, ilon, z)); - + // https://maps.google.com/maps?q=loc:-21.8835112,-47.7838932 (Name) url = "https://maps.google.com/maps?q=loc:" + dlat + "," + dlon + " (Name)" ; System.out.println("url: " + url); @@ -843,7 +843,7 @@ public class GeoPointParserUtilTest { } - - + + } diff --git a/OsmAnd-telegram/build.gradle b/OsmAnd-telegram/build.gradle index f7976040af..aa13df28b0 100644 --- a/OsmAnd-telegram/build.gradle +++ b/OsmAnd-telegram/build.gradle @@ -135,7 +135,9 @@ dependencies { implementation project(path: ':OsmAnd-java', configuration: 'android') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation( "org.jetbrains.kotlin:kotlin-stdlib:1.2.71") { + exclude group: 'org.jetbrains', module: 'annotations' + } implementation 'com.android.support:appcompat-v7:28.0.0-rc01' implementation 'com.android.support:design:28.0.0-rc01' implementation 'com.android.support:customtabs:28.0.0-rc01' @@ -146,6 +148,5 @@ dependencies { implementation("com.github.HITGIF:TextFieldBoxes:1.4.4") { exclude group: 'com.android.support' } - - implementation 'net.sf.kxml:kxml2:2.1.8' + implementation 'org.jetbrains:annotations-java5:15.0' } diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_altitude_range.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_altitude_range.png new file mode 100644 index 0000000000..d3c1ff597b Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_altitude_range.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_add.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_add.png new file mode 100644 index 0000000000..f7e9962a63 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_add.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_end.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_end.png new file mode 100644 index 0000000000..0af2e77948 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_end.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_start.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_start.png new file mode 100644 index 0000000000..f27db2f105 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_date_start.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_distance_16dp.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_distance_16dp.png new file mode 100644 index 0000000000..bebd1402c4 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_distance_16dp.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_record.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_record.png new file mode 100644 index 0000000000..203e939f9e Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_record.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_round_shape.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_round_shape.png new file mode 100644 index 0000000000..be05e8b378 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_round_shape.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_share.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_share.png new file mode 100644 index 0000000000..b5219f938a Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_share.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_speed_average.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_speed_average.png new file mode 100644 index 0000000000..d514a54ffb Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_speed_average.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/ic_action_timeline.png b/OsmAnd-telegram/res/drawable-hdpi/ic_action_timeline.png new file mode 100644 index 0000000000..5cc366eec5 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/ic_action_timeline.png differ diff --git a/OsmAnd-telegram/res/drawable-hdpi/img_timeline_no_data.png b/OsmAnd-telegram/res/drawable-hdpi/img_timeline_no_data.png new file mode 100644 index 0000000000..3f1ac0c2c5 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-hdpi/img_timeline_no_data.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_altitude_range.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_altitude_range.png new file mode 100644 index 0000000000..82a122006f Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_altitude_range.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_add.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_add.png new file mode 100644 index 0000000000..ee38eab5bd Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_add.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_end.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_end.png new file mode 100644 index 0000000000..f342cde0cd Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_end.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_start.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_start.png new file mode 100644 index 0000000000..c15212f4c0 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_date_start.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_distance_16dp.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_distance_16dp.png new file mode 100644 index 0000000000..1c91ed322b Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_distance_16dp.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_record.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_record.png new file mode 100644 index 0000000000..8c9ae2965a Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_record.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_round_shape.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_round_shape.png new file mode 100644 index 0000000000..e31a19f04c Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_round_shape.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_share.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_share.png new file mode 100644 index 0000000000..9acdb7b3b6 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_share.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_speed_average.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_speed_average.png new file mode 100644 index 0000000000..55220303a2 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_speed_average.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/ic_action_timeline.png b/OsmAnd-telegram/res/drawable-mdpi/ic_action_timeline.png new file mode 100644 index 0000000000..a5ef5bd4fe Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/ic_action_timeline.png differ diff --git a/OsmAnd-telegram/res/drawable-mdpi/img_timeline_no_data.png b/OsmAnd-telegram/res/drawable-mdpi/img_timeline_no_data.png new file mode 100644 index 0000000000..842fe8571d Binary files /dev/null and b/OsmAnd-telegram/res/drawable-mdpi/img_timeline_no_data.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_altitude_range.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_altitude_range.png new file mode 100644 index 0000000000..b96be7510f Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_altitude_range.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_add.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_add.png new file mode 100644 index 0000000000..61f43922ce Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_add.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_end.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_end.png new file mode 100644 index 0000000000..4de11b71f3 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_end.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_start.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_start.png new file mode 100644 index 0000000000..c2032d5c18 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_date_start.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_distance_16dp.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_distance_16dp.png new file mode 100644 index 0000000000..578af8e5eb Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_distance_16dp.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_record.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_record.png new file mode 100644 index 0000000000..0e400cc264 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_record.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_round_shape.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_round_shape.png new file mode 100644 index 0000000000..bab16b9517 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_round_shape.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_share.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_share.png new file mode 100644 index 0000000000..ddcde8ddfb Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_share.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_speed_average.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_speed_average.png new file mode 100644 index 0000000000..9de62628f5 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_speed_average.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/ic_action_timeline.png b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_timeline.png new file mode 100644 index 0000000000..2eb0d10d2a Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/ic_action_timeline.png differ diff --git a/OsmAnd-telegram/res/drawable-xhdpi/img_timeline_no_data.png b/OsmAnd-telegram/res/drawable-xhdpi/img_timeline_no_data.png new file mode 100644 index 0000000000..b9befd7e37 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xhdpi/img_timeline_no_data.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_altitude_range.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_altitude_range.png new file mode 100644 index 0000000000..073ca37a2d Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_altitude_range.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_add.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_add.png new file mode 100644 index 0000000000..e46425373d Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_add.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_end.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_end.png new file mode 100644 index 0000000000..ae6ea10cf3 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_end.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_start.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_start.png new file mode 100644 index 0000000000..11748b8ace Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_date_start.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_distance_16dp.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_distance_16dp.png new file mode 100644 index 0000000000..817e0f9559 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_distance_16dp.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_record.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_record.png new file mode 100644 index 0000000000..d3257c7b55 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_record.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_round_shape.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_round_shape.png new file mode 100644 index 0000000000..6f83570488 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_round_shape.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_share.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_share.png new file mode 100644 index 0000000000..191e70da0e Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_share.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_speed_average.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_speed_average.png new file mode 100644 index 0000000000..82d5b48cf9 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_speed_average.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_timeline.png b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_timeline.png new file mode 100644 index 0000000000..452d6879bc Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/ic_action_timeline.png differ diff --git a/OsmAnd-telegram/res/drawable-xxhdpi/img_timeline_no_data.png b/OsmAnd-telegram/res/drawable-xxhdpi/img_timeline_no_data.png new file mode 100644 index 0000000000..997e11df36 Binary files /dev/null and b/OsmAnd-telegram/res/drawable-xxhdpi/img_timeline_no_data.png differ diff --git a/OsmAnd-telegram/res/drawable/btn_round_border.xml b/OsmAnd-telegram/res/drawable/btn_round_border.xml new file mode 100644 index 0000000000..ae932320c8 --- /dev/null +++ b/OsmAnd-telegram/res/drawable/btn_round_border.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/activity_main.xml b/OsmAnd-telegram/res/layout/activity_main.xml index 5118492b0c..8b5341d7c2 100644 --- a/OsmAnd-telegram/res/layout/activity_main.xml +++ b/OsmAnd-telegram/res/layout/activity_main.xml @@ -8,8 +8,9 @@ android:orientation="vertical" tools:context=".ui.MainActivity"> - @@ -25,7 +26,7 @@ android:scaleType="fitXY" android:src="?attr/bottom_nav_shadow"/> - + + + + + + + + + + + - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml new file mode 100644 index 0000000000..4d7636dc1b --- /dev/null +++ b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml @@ -0,0 +1,526 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/item_with_desc_and_right_value.xml b/OsmAnd-telegram/res/layout/item_with_desc_and_right_value.xml index 7262dc7b93..0c5bfb1feb 100644 --- a/OsmAnd-telegram/res/layout/item_with_desc_and_right_value.xml +++ b/OsmAnd-telegram/res/layout/item_with_desc_and_right_value.xml @@ -44,7 +44,7 @@ android:layout_height="wrap_content" android:textColor="?android:attr/textColorSecondary" android:textSize="@dimen/list_item_description_text_size" - app:firstBaselineToTopHeight="20sp" + app:firstBaselineToTopHeight="14sp" app:lastBaselineToBottomHeight="16sp" app:typeface="@string/font_roboto_regular" tools:text="Some long description"/> diff --git a/OsmAnd-telegram/res/layout/item_with_descr_and_right_switch.xml b/OsmAnd-telegram/res/layout/item_with_descr_and_right_switch.xml new file mode 100644 index 0000000000..4969b003ed --- /dev/null +++ b/OsmAnd-telegram/res/layout/item_with_descr_and_right_switch.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + diff --git a/OsmAnd-telegram/res/layout/live_now_chat_card.xml b/OsmAnd-telegram/res/layout/live_now_chat_card.xml index 70091f6c0b..c42646c74d 100644 --- a/OsmAnd-telegram/res/layout/live_now_chat_card.xml +++ b/OsmAnd-telegram/res/layout/live_now_chat_card.xml @@ -40,7 +40,8 @@ android:layout_marginEnd="@dimen/content_padding_standard" android:layout_marginRight="@dimen/content_padding_standard" android:layout_weight="1" - android:orientation="vertical"> + android:orientation="vertical" + android:paddingBottom="@dimen/content_padding_half"> + android:paddingBottom="2dp" + android:gravity="center_vertical"> - - - + android:orientation="horizontal" + android:visibility="gone" + tools:visibility="visible"> + + + + + + + + + + + + + + + android:layout_height="wrap_content"> @@ -128,7 +168,7 @@ android:textColor="?attr/android:textColorSecondary" android:textSize="@dimen/list_item_description_text_size" android:visibility="visible" - app:firstBaselineToTopHeight="@dimen/list_item_icon_margin_right" /> + app:firstBaselineToTopHeight="18dp" /> @@ -141,12 +181,34 @@ android:maxLines="1" android:textColor="?attr/android:textColorSecondary" android:textSize="@dimen/list_item_description_text_size" - app:firstBaselineToTopHeight="@dimen/list_item_icon_margin_right" + app:firstBaselineToTopHeight="18dp" app:typeface="@string/font_roboto_regular" tools:text="Live: 1 • All: 36"/> + + + + + + @@ -352,6 +353,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + - + android:layout_gravity="center_vertical" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/hint_text_size" + app:typeface="@string/font_roboto_regular" + app:lastBaselineToBottomHeight="10sp" + tools:text="@string/expire_at" /> - - - - - - - + diff --git a/OsmAnd-telegram/res/layout/user_list_item.xml b/OsmAnd-telegram/res/layout/user_list_item.xml index 3ec45fd1d9..7dd6034ec0 100644 --- a/OsmAnd-telegram/res/layout/user_list_item.xml +++ b/OsmAnd-telegram/res/layout/user_list_item.xml @@ -15,7 +15,8 @@ @@ -46,6 +47,7 @@ android:maxLines="1" android:textColor="?android:textColorPrimary" android:textSize="@dimen/list_item_title_text_size" + android:paddingTop="@dimen/content_padding_half" app:typeface="@string/font_roboto_regular" tools:text="Share location"/> @@ -106,6 +108,29 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-ast/strings.xml b/OsmAnd-telegram/res/values-ast/strings.xml new file mode 100644 index 0000000000..495cf2ac0f --- /dev/null +++ b/OsmAnd-telegram/res/values-ast/strings.xml @@ -0,0 +1,152 @@ + +Altitú media + Velocidá media + Mapa + Amestar + Anubrir + Estáu + Desactivar + Guardar + Nome + Colar + Zarrar + Non + Instalar + Compartir + Atrás + Siguir + Encaboxar + Axustes + yd + ft + mi + km + m + nmi + min/m + min/km + m/s + km/h + mph + Quilómetros per hora + Milles per hora + Metros per segundu + Minutos per quilómetru + Minutos per milla + Milles/pies + Milles/yardes + Quilómetros/metros + Milles náutiques + Milles/metros + La supervisión ta activada + La supervisión ta desactivada + Data de fin + Data d\'aniciu + Unviar l\'allugamientu como + Escueyi cómo van vese los mensaxes col to allugamientu. + Testu + Mapa y testu + L\'últimu anovamientu dende Telegram + Escueyi un nome que nun usares yá + Amestóse %1$s. + Nun pudo amestase\'l preséu nuevu + El nome del preséu nuevu, 200 caráuteres como máximu. + El nome del preséu ye perllargu + El nome del preséu nun pue tar baleru + Nome del preséu + Pues crear y ver la ID del preséu nun veceru pa Telegram usando la charra col robó %1$s .%2$s + Si quies coneutar munchos preseos a una cuenta de Telegram, precises usar un preséu diferente al compartir l\'allugamientu. + Esperando pola rempuesta de Telegram + Aniciando + Posicionando… + Camuda los axustes de la optimización de la batería pa estabilizar la compartición del allugamientu. + Trabayu en segundu planu + Compartición en segundu planu + Dir a Axustes + Más sero + Compartición: Activada + Nun hai conexón al GPS + Nun hai conexón a internet + Amestar un preséu + Contautos y grupos que tán compartiendo l\'allugamientu contigo. + ¿De xuru que quies zarrar sesión n\'OsmAnd Tracker pa que nun pueas compartir l\'allugamientu nin ver el d\'otros\? + ¿Zarrar la sesión d\'OsmAnd Tracker\? + pola distancia + pol nome + pol grupu + Ordenar por + Esbilla la versión d\'OsmAnd onde van amosase los contautos nel mapa. + Desactiva la compartición del allugamientu en toles charres esbillaes ((%1$d). + Desactivar toles comparticiones + Desactivar too + Grupu + Pa revocar l\'accesu a la compartición del allugamientu. Abri Telegram, vete a Axustes → Privacidá y seguranza → Sesiones y zarra la sesión d\'OsmAnd Tracker. + Cómo desactivar la compartición del allugamientu con OsmAnd dende Telegram + Cómo desactivar la compartición del allugamientu con OsmAnd dende Telegram + Cuenta coneutada + Cuenta + en %1$s + Escueyi la versión d\'OsmAnd qu\'OsmAnd Tracker va usar p\'amosar les posiciones. + Conexón a OsmAnd + Anubre los contautos que nun se movieren nel tiempu apurríu. + Historial d\'allugamientos + La última vez qu\'un contautu se movió. + Ausencia de movición + Afita l\'intervalu mínimu pa la compartición del allugamientu. + Unviar el mio allugamientu + Posición + Tiempu de la compartición + Caduca a les + Desactivar la compartición del allugamientu + Abrir OsmAnd + En direuto + Robó + Rexistru en Telegram + Precises una cuenta de Telegram pa usar la compartición d\'allugamientuos. + Instala Telegram y configura una cuenta. + Darréu vas poder usar esta aplicación. + Too + Nun teo una cuenta de Telegram + Introduz el númberu telefónicu + Introduz el códigu d\'autentación + %1$d h %2$d m + %1$d m + %1$d h + Visibilidá xeneral demientres + Afitamientu del tiempu + Esbilla los contautos y grupos colos que quies compartir el to allugamientu. + OsmAnd Online GPS Tracker + Númberu telefónicu + Númberu telefónicu nel formatu internacional + Contraseña + Códigu d\'autenticación + Telegram unvióte un códigu pa OsmAnd pa qu\'anicies sesión na to cuenta. + Introduz la contraseña + Contraseña de Telegram + Zarrar sesión + ¿Activar «Allugamientu»\? + Nun aniciesti sesión + Activa «Allugamientu» nos axustes del sistema + OsmAnd Tracker execútase en segundu planu cola pantalla apagada. + Distancia + Compartiendo l\'allugamientu + Serviciu d\'OsmAnd Tracker + Logu d\'OsmAnd + Primero precises instalar la versión de baldre o de pagu d\'OsmAnd + Instalación d\'OsmAnd + Autorización + Introduz el to númberu telefónicu de Telegram nel formatu internacional + Afáyate + nmi/h + Milles náutiques per hora (nuedos) + h + m + s + La compartición del allugamientu con OsmAnd déxate compartir el to allugamientu y ver el d\'otros n\'OsmAnd.

L\'aplicación usa l\'API de Telegram y vas precisar una cuenta de Telegram.
+ El mio allugamientu + En direuto + Llinia temporal + L\'últimu allugamientu disponible + Estáu de la compartición + Afita\'l tiempu nel que los contautos y grupos esbillaos van ver el to allugamientu en tiempu real. +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-be/strings.xml b/OsmAnd-telegram/res/values-be/strings.xml index f2a4885dd4..230c149fbf 100644 --- a/OsmAnd-telegram/res/values-be/strings.xml +++ b/OsmAnd-telegram/res/values-be/strings.xml @@ -1,4 +1,4 @@ - + Апошняе абнаўленне з Telegram Абярыце імя, якое вы яшчэ не выкарыстоўвалі %1$s дададзена. @@ -10,7 +10,7 @@ Назва прылады Схаваць Вы можаце стварыць і праглядзець ідэнтыфікатар прылады ў кліенце Тэлеграма, выкарыстоўваючы %1$s бота. %2$s - Калі вы хочаце падлучыць некалькі прылад да аднаго рахунка тэлеграм, то вам неабходна выкарыстаць розныя прылады, каб падзяліцца месцазнаходжаннем. + Калі вы хочаце падлучыць некалькі прылад да аднаго акаўнта тэлеграм, то вам неабходна выкарыстаць розныя прылады, каб падзяліцца месцазнаходжаннем. Апошняе абнаўленне месцазнаходжання: Паспяхова адпраўлена і абноўлена Немагчыма адправіць у размову Тэлеграм: @@ -21,7 +21,7 @@ Злучэнне з Інтэрнэтам Змена параметраў аптымізацыі батарэі для стабілізацыі абмену інфармацыяй аб месцазнаходжанні. Праца ў фонавым рэжыме - Выключыць аптымізацыю батарэі для OsmAnd Telegram, каб прадухіліць нечаканае выключэнне фонавага рэжыму. + Выключыць аптымізацыю батарэі для OsmAnd Tracker, каб прадухіліць нечаканае выключэнне фонавага рэжыму. Абмен у фонавым рэжыме Перайсці ў налады Пазней @@ -39,8 +39,8 @@ Дадаць прыладу Падзяліцца месцазнаходжаннем як Кантакты і групы для абмену месцазнаходжаннем. - "Вы не зможаце падзяліцца сваім месцазнаходжаннем і ўбачыць месцазнаходжанне іншых. Сапраўды выйсці з OsmAnd Telegram\? " - Выйсці з OsmAnd Telegram\? + Вы не зможаце падзяліцца сваім месцазнаходжаннем і ўбачыць месцазнаходжанне іншых. Сапраўды выйсці з OsmAnd Tracker\? + Выйсці з OsmAnd Tracker\? Імя Па адлегласці Па імёнах @@ -58,13 +58,13 @@ Група Падлучыцеся да Інтэрнэту, каб карэктна выйсці з Тэлеграм. Закрыць - Для таго, каб скасаваць абмен месцазнаходжаннем, адкрыйце Тэлеграм, перайдзіце ў Налады → Прыватнасць і бяспека → Сеансы і спыніце сеанс OsmAnd Telegram. + Для таго, каб скасаваць абмен месцазнаходжаннем, адкрыйце Тэлеграм, перайдзіце ў Налады → Прыватнасць і бяспека → Сеансы і спыніце сеанс OsmAnd Tracker. Як выключыць абмен месцазнаходжаннем у OsmAnd праз Тэлеграм Як выключыць абмен месцазнаходжаннем у OsmAnd праз Тэлеграм - Падлучаны рахунак + Падлучаны акаўнт Рахунак у %1$s - Абраць версію OsmAnd, якую OsmAnd Telegram будзе выкарыстоўваць для адлюстравання пазіцыі. + Абраць версію OsmAnd, якую OsmAnd Tracker будзе выкарыстоўваць для адлюстравання пазіцыі. Злучэнне з OsmAnd Схаваць кантакты, якія не перамяшчаліся пэўны час. Гісторыя месцазнаходжанняў @@ -81,13 +81,13 @@ Дзейных Бот Рэгістрацыя ў Telegram - Для абмену вам неабходны рахунак Тэлеграм. - Калі ласка, ўсталюйце Тэлеграм і наладзьце рахунак. + Для абмену вам неабходны акаўнт Тэлеграм. + Калі ласка, ўсталюйце Тэлеграм і наладзьце акаўнт. Пасля гэтага вы зможаце выкарыстоўваць дадатак. Усе Выкл - Вам неабходна мець рахунак Тэлеграм і нумар тэлефона - У мяне няма рахунка Тэлеграм + Вам неабходна мець акаўнт Тэлеграм і нумар тэлефона + У мяне няма акаўнта Тэлеграм Увядзіце нумар тэлефона Увядзіце код аўтарызацыі Вызначце бачны час для ўсіх @@ -104,13 +104,13 @@ Пошук: група альбо кантакт Падзяліцца месцазнаходжаннем Паказаць на мапе - ОsmAnd Telegram + OsmAnd Online GPS Tracker Нумар тэлефона Нумар тэлефона ў міжнародным фармаце Пароль Увядзіце код Код аўтэнтыфікацыі - Тэлеграм адправіў вам код для OsmAnd для ўваходу ў рахунак. + Тэлеграм адправіў вам код для OsmAnd для ўваходу ў акаўнт. Увядзіце пароль Пароль Тэлеграм Увайсці @@ -127,11 +127,11 @@ Калі ласка, ўключыце \"Месцазнаходжанне\" ў сістэмных наладах Абярыце аднаго пастаўшчыка месцазнаходжання, каб падзяліцца сваім месцазнаходжаннем. Фонавы рэжым - OsmAnd Telegram працуе ў фонавым рэжыме з выключаным экранам. + OsmAnd Tracker працуе ў фонавым рэжыме з выключаным экранам. Адлегласць Падзяліцца месцазнаходжаннем Абмен данымі аб месцазнаходжанні - Сэрвіс OsmAnd Telegram + Сэрвіс OsmAnd Tracker Лагатып OsmAnd Спачатку вам неабходна ўсталяваць бясплатную ці платную версію OsmAnd Усталяваць OsmAnd @@ -167,7 +167,7 @@ г хвіл сек - Абмен месцазнаходжаннем OsmAnd Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.

Дадатак выкарыстоўвае Telegram API, таму вам неабходны рахунак Тэлеграм.
+ Абмен месцазнаходжаннем OsmAnd Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.

Дадатак выкарыстоўвае Telegram API, таму вам неабходны акаўнт Тэлеграм.
Маё месцазнаходжанне Зараз дзейнічае @@ -177,4 +177,21 @@ Мапа Тэкст Мапа і тэкст -
+ Маніторынг уключаны + Маніторынг адключаны + час руху + Сярэдняя вышыня + Сярэдняя хуткасць + Адкрыць у OsmAnd + Дата закрыцця + Час адкрыцця + Храналогія + Калі ласка, абнавіце OsmAnd каб паглядзець даныя на мапе + Абнавіць + адпраўлена (%1$d у буферы) + %1$d кропак + Дата + Сабраны + GPS-кропкі + Адпраўлены + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-da/strings.xml b/OsmAnd-telegram/res/values-da/strings.xml index 5a78bc2761..af2f4bbea1 100644 --- a/OsmAnd-telegram/res/values-da/strings.xml +++ b/OsmAnd-telegram/res/values-da/strings.xml @@ -1,7 +1,7 @@ - + Skift indstillinger for batterioptimering, for stabil deling af placering. Baggrundstilstand - Sluk for batterioptimering af OsmAnd Telegram, så det ikke pludseligt bliver slukket, når det er i baggrunden. + Sluk for batterioptimering af OsmAnd Tracker, så det ikke pludseligt bliver slukket, når det er i baggrunden. Deling i baggrunden Gå til indstillinger Senere @@ -18,8 +18,8 @@ Tilføj enhed Del placering som Kontakter og grupper som der deles placering med. - Er du sikker på at du vil logge ud af OsmAnd Telegram, så du ikke kan dele din placering eller se placeringen af andre\? - Log ud af OsmAnd Telegram\? + Er du sikker på at du vil logge ud af OsmAnd Tracker, så du ikke kan dele din placering eller se placeringen af andre\? + Log ud af OsmAnd Tracker\? Navn Efter afstand Efter navn @@ -37,13 +37,13 @@ Gruppe Opret forbindelse til Internettet for at logge ud af Telegram. Luk - "Sådan tilbagekaldes adgang til placeringsdeling. Åbn Telegram, gå til Settings - Privacy and Security - Sessions og afslut OsmAnd Telegram session. " + "Sådan tilbagekaldes adgang til placeringsdeling. Åbn Telegram, gå til Settings - Privacy and Security - Sessions og afslut OsmAnd Tracker session. " Hvordan afbrydes OsmAnd placeringsdeling fra Telegram Hvordan afbrydes OsmAnd placeringsdeling fra Telegram Tilsluttet konto Konto i %1$s - Vælg den OsmAnd version, som OsmAnd Telegram bruger til vise placeringer på kortet. + Vælg den OsmAnd version, som OsmAnd Tracker bruger til vise placeringer på kortet. Tilslut OsmAnd Skjul kontakter, der ikke har bevæget sig i en given tid. Placeringshistorik @@ -82,7 +82,7 @@ Søg: gruppe eller kontakt Del placering Vis på kort - OsmAnd Telegram + OsmAnd Online GPS Tracker Telefonnummer Telefonnummer i internationalt format Adgangskode @@ -101,11 +101,11 @@ Tænd for \"Placering\" i systemindstillinger Vælg en af placeringsudbyderne til at dele placering. Baggrundstilstand - OsmAnd Telegram kører i baggrunden med skærmen slukket. + OsmAnd Tracker kører i baggrunden med skærmen slukket. Afstand Del placering Deler placering - OsmAnd Telegram tjeneste + OsmAnd Tracker tjeneste OsmAnd logo Det er nødvendigt at installere en gratis eller betalt version af OsmAnd Installer OsmAnd @@ -179,4 +179,21 @@ Kort Tekst Kort og tekst - + Gennemsnitshøjde + Gennemsnitshastighed + Åbn i OsmAnd + Slutdato + Startdato + Tidslinje + Opdater OsmAnd for at se data på kortet + Opdater + sendt (%1$d i buffer) + %1$s punkter + Dato + Indsamlet + GPS punkter + Sendt + Overvågning er aktiveret + Overvågning er deaktiveret + tid i bevægelse + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-de/strings.xml b/OsmAnd-telegram/res/values-de/strings.xml index 175cc9cfb4..4c43a3a3a0 100644 --- a/OsmAnd-telegram/res/values-de/strings.xml +++ b/OsmAnd-telegram/res/values-de/strings.xml @@ -1,182 +1,199 @@ -Teilen im Hintergrund - Einstellungen öffnen - Noch nicht gesendet - Noch nicht gefunden - Position neu senden - Freigabestatus - Teilen: Aktiviert - Status - Kein GPS-Signal - Keine Internetverbindung - Deaktivieren - Gerät hinzufügen - Später - Letzter verfügbarer Standort - Standort teilen als - Kontakte und Gruppen, die ihren Standort mit Ihnen teilen. - Aus OsmAnd Telegram abmelden\? - "Name " - Nach Entfernung - Nach Namen - Nach Gruppe - Sortieren - Sortieren nach - Wählen Sie die OsmAnd-Version, in der Kontakte auf der Karte angezeigt werden. - Zu verwendende OsmAnd-Version auswählen - Alle ausschalten - Beenden - vor - Letzte Antwort - Gruppe - Verbinden Sie sich mit dem Internet, um sich ordnungsgemäß von Telegram abzumelden. - Schließen - Verbundenes Konto - Konto - "in %1$s " - Wählen Sie die OsmAnd-Version, die OsmAnd Telegram verwendet, um Positionen anzuzeigen. - OsmAnd verbinden - Blendet Kontakte aus, die sich eine bestimmte Zeit lang nicht bewegt haben. - Standortverlauf - Das letzte Mal, dass sich ein Kontakt bewegt hat. - Meinen Standort senden - Position - OsmAnd öffnen - "Live " - Registrierung in Telegram - Sie benötigen ein Telegram-Konto, um die Standortfreigabe nutzen zu können. - Bitte installieren Sie Telegram und richten Sie ein Konto ein. - Dann können Sie diese App nutzen. - Alle - Aus - Sie benötigen ein registriertes Telegram-Konto und eine Telefonnummer - Ich habe kein Telegram-Konto - Telefonnummer eingeben - Authentifizierungscode eingeben - %1$d h %2$d min - %1$d min - %1$d h - Installieren - Teilen - Zurück - Für alle sichtbare Zeit - Zeit einstellen - Suche: Gruppe oder Kontakt - Auf Karte anzeigen - "OsmAnd Telegram " - Telefonnummer - Telefonnummer in internationalem Format - Passwort - Code eingeben - Authentifizierungscode - Telegram hat Ihnen einen Code für OsmAnd zur Anmeldung an Ihrem Konto gesendet. - Passwort eingeben - Anmelden - Abmelden - Sie sind nicht angemeldet - Fortfahren - Abbrechen - Einstellungen - Der App fehlt die Berechtigung, auf Standortdaten zuzugreifen. - Bitte schalten Sie \"Standort\" in den Systemeinstellungen ein - Hintergrundmodus - OsmAnd Telegram läuft im Hintergrund bei ausgeschaltetem Bildschirm. - Distanz - Standort teilen - OsmAnd Telegram-Service - OsmAnd-Logo - Sie müssen zuerst die kostenlose oder kostenpflichtige Version von OsmAnd installieren - OsmAnd installieren - Benutzer auf der Karte anzeigen - Aktive Chats - Autorisation - Bitte geben Sie Ihre Telegram-Telefonnummer im internationalen Format ein - Willkommen - - yd - ft - mi - km - m - min/m - min/km - m/s - km/h - mph - Kilometer pro Stunde - Meilen pro Stunde - Meter pro Sekunde - Minuten pro Kilometer - Minuten pro Meile - Seemeilen pro Stunde (Knoten) - Meilen/Fuß - Meilen/Yards - Kilometer/Meter - Seemeilen - Meilen/Meter - h - min - s - Mein Standort - - -Hintergrundbetrieb - Erfolgreich gesendet und aktualisiert - Warten auf Antwort von Telegram - Sende Standort - Startet - Positioniere..… - Verbindung zum Internet - Akkuoptimierungseinstellungen, um die Standortfreigabe zu stabilisieren. - Telegram-Passwort - kn - kn/h - Zuletzt aktualisierter Ort: - Es ist nicht möglich, an Telegram-Chats zu senden: - Schalten Sie die Batterieoptimierung für OsmAnd Telegram aus, damit es nicht plötzlich im Hintergrund ausgeschaltet wird. - Sind Sie sicher, dass Sie sich von OsmAnd Telegram abmelden möchten, so dass Sie den Standort nicht teilen oder den Standort anderer sehen können\? - Schaltet die Standortfreigabe für alle ausgewählten Chats aus (%1$d). - Alle Freigaben deaktivieren - Um den Zugriff auf die Standortfreigabe zu widerrufen, öffnen Sie Telegram, gehen zu Einstellungen → Datenschutz und Sicherheit → Sitzungen und beenden die OsmAnd Telegram-Sitzung. - So deaktivieren Sie OsmAnd Standortfreigabe von Telegram aus - So deaktivieren Sie OsmAnd Standortfreigabe von Telegram aus - Keine Bewegung - Legen Sie das Mindestintervall für die Standortfreigabe fest. - Freigabezeit - Verfällt - Standortfreigabe ausschalten - Bot - Für alle sichtbare Zeit einstellen - Zeit einstellen, zu der Ihren Kontakten und Gruppen Ihr Standorts in Echtzeit angezeigt wird. - Kontakte und Gruppen wählen, denen Sie Ihren Standort freigeben möchten. - Standort freigeben - Wählen Sie einen der Standortanbieter aus, um Ihren Standort freizugeben. - Standort teilen - OsmAnd Standortfreigabe ermöglicht es Ihnen, Ihren Standort zu teilen und den anderer in OsmAnd zu sehen.

Die App verwendet die API von Telegram und Sie benötigen ein Telegram-Konto.
- Wählen Sie einen Namen, den Sie noch nicht benutzt haben - %1$s hinzugefügt. - Hinzufügen - Neues Gerät konnte nicht hinzugefügt werden - Benennen Sie Ihr neues Gerät mit max. 200 Symbolen. - Gerätename ist zu lang - Der Gerätename darf nicht leer sein - Gerätename - Verbergen - Sie können die Geräte-ID im Telegram-Client mit dem Chat-Bot %1$s erstellen und anzeigen. %2$s - Wenn Sie mehrere Geräte mit einem Telegram-Konto verbinden möchten, müssen Sie ein anderes Gerät verwenden, um Ihren Standort zu teilen. - Speichern - Die Freigabe ist eingeschaltet (ausschalten) - Startet - Abmeldevorgang - Schliesst - \"Standort\" einschalten\? - Jetzt live - - -Letztes Update von Telegram + + Teilen im Hintergrund + Einstellungen öffnen + Noch nicht gesendet + Noch nicht gefunden + Position neu senden + Freigabestatus + Teilen: Aktiviert + Status + Kein GPS-Signal + Keine Internetverbindung + Deaktivieren + Gerät hinzufügen + Später + Letzter verfügbarer Standort + Standort teilen als + Kontakte und Gruppen, die ihren Standort mit Ihnen teilen. + Aus OsmAnd Telegram abmelden\? + "Name " + Nach Entfernung + Nach Namen + Nach Gruppe + Sortieren + Sortieren nach + Wählen Sie die OsmAnd-Version, in der Kontakte auf der Karte angezeigt werden. + Zu verwendende OsmAnd-Version auswählen + Alle ausschalten + Beenden + vor + Letzte Antwort + Gruppe + Verbinden Sie sich mit dem Internet, um sich ordnungsgemäß von Telegram abzumelden. + Schließen + Verbundenes Konto + Konto + "in %1$s " + Wählen Sie die OsmAnd-Version, die OsmAnd Telegram verwendet, um Positionen anzuzeigen. + OsmAnd verbinden + Blendet Kontakte aus, die sich eine bestimmte Zeit lang nicht bewegt haben. + Standortverlauf + Das letzte Mal, dass sich ein Kontakt bewegt hat. + Meinen Standort senden + Position + OsmAnd öffnen + "Live " + Registrierung in Telegram + Sie benötigen ein Telegram-Konto, um die Standortfreigabe nutzen zu können. + Bitte installieren Sie Telegram und richten Sie ein Konto ein. + Dann können Sie diese App nutzen. + Alle + Aus + Sie benötigen ein registriertes Telegram-Konto und eine Telefonnummer + Ich habe kein Telegram-Konto + Telefonnummer eingeben + Authentifizierungscode eingeben + %1$d h %2$d min + %1$d min + %1$d h + Installieren + Teilen + Zurück + Für alle sichtbare Zeit + Zeit einstellen + Suche: Gruppe oder Kontakt + Auf Karte anzeigen + "OsmAnd Telegram " + Telefonnummer + Telefonnummer in internationalem Format + Passwort + Code eingeben + Authentifizierungscode + Telegram hat Ihnen einen Code für OsmAnd zur Anmeldung an Ihrem Konto gesendet. + Passwort eingeben + Anmelden + Abmelden + Sie sind nicht angemeldet + Fortfahren + Abbrechen + Einstellungen + Der App fehlt die Berechtigung, auf Standortdaten zuzugreifen. + Bitte schalten Sie \"Standort\" in den Systemeinstellungen ein + Hintergrundmodus + OsmAnd Telegram läuft im Hintergrund bei ausgeschaltetem Bildschirm. + Distanz + Standort teilen + OsmAnd Telegram-Service + OsmAnd-Logo + Sie müssen zuerst die kostenlose oder kostenpflichtige Version von OsmAnd installieren + OsmAnd installieren + Benutzer auf der Karte anzeigen + Aktive Chats + Autorisation + Bitte geben Sie Ihre Telegram-Telefonnummer im internationalen Format ein + Willkommen + yd + ft + mi + km + m + min/m + min/km + m/s + km/h + mph + Kilometer pro Stunde + Meilen pro Stunde + Meter pro Sekunde + Minuten pro Kilometer + Minuten pro Meile + Seemeilen pro Stunde (Knoten) + Meilen/Fuß + Meilen/Yards + Kilometer/Meter + Seemeilen + Meilen/Meter + h + min + s + Mein Standort + Hintergrundbetrieb + Erfolgreich gesendet und aktualisiert + Warten auf Antwort von Telegram + Sende Standort + Startet + Positioniere..… + Verbindung zum Internet + Akkuoptimierungseinstellungen, um die Standortfreigabe zu stabilisieren. + Telegram-Passwort + kn + kn/h + Zuletzt aktualisierter Ort: + Es ist nicht möglich, an Telegram-Chats zu senden: + Schalten Sie die Batterieoptimierung für OsmAnd Telegram aus, damit es nicht plötzlich im Hintergrund ausgeschaltet wird. + Sind Sie sicher, dass Sie sich von OsmAnd Telegram abmelden möchten, so dass Sie den Standort nicht teilen oder den Standort anderer sehen können\? + Schaltet die Standortfreigabe für alle ausgewählten Chats aus (%1$d). + Alle Freigaben deaktivieren + Um den Zugriff auf die Standortfreigabe zu widerrufen, öffnen Sie Telegram, gehen zu Einstellungen → Datenschutz und Sicherheit → Sitzungen und beenden die OsmAnd Telegram-Sitzung. + So deaktivieren Sie OsmAnd Standortfreigabe von Telegram aus + So deaktivieren Sie OsmAnd Standortfreigabe von Telegram aus + Keine Bewegung + Legen Sie das Mindestintervall für die Standortfreigabe fest. + Freigabezeit + Verfällt + Standortfreigabe ausschalten + Bot + Für alle sichtbare Zeit einstellen + Zeit einstellen, zu der Ihren Kontakten und Gruppen Ihr Standorts in Echtzeit angezeigt wird. + Kontakte und Gruppen wählen, denen Sie Ihren Standort freigeben möchten. + Standort freigeben + Wählen Sie einen der Standortanbieter aus, um Ihren Standort freizugeben. + Standort teilen + + OsmAnd Standortfreigabe ermöglicht es Ihnen, Ihren Standort zu teilen und den anderer in OsmAnd zu sehen.
+
Die App verwendet die API von Telegram und Sie benötigen ein Telegram-Konto.
+ Wählen Sie einen Namen, den Sie noch nicht benutzt haben + %1$s hinzugefügt. + Hinzufügen + Neues Gerät konnte nicht hinzugefügt werden + Benennen Sie Ihr neues Gerät mit max. 200 Symbolen. + Gerätename ist zu lang + Der Gerätename darf nicht leer sein + Gerätename + Verbergen + Sie können die Geräte-ID im Telegram-Client mit dem Chat-Bot %1$s erstellen und anzeigen. %2$s + Wenn Sie mehrere Geräte mit einem Telegram-Konto verbinden möchten, müssen Sie ein anderes Gerät verwenden, um Ihren Standort zu teilen. + Speichern + Die Freigabe ist eingeschaltet (ausschalten) + Startet + Abmeldevorgang + Schliesst + \"Standort\" einschalten\? + Jetzt live + Letztes Update von Telegram Senden Standort als Wählen Sie, wie Nachrichten mit Ihrem Standort aussehen sollen. Karte Text Karte und Text + Überwachung aktiviert + Überwachung deaktiviert + Zeit in Bewegung + Durchschnittliche Höhe + Durchschnittliche Geschwindigkeit + In OsmAnd öffnen + Endzeit + Startzeit + Zeitachse + Bitte aktualisieren Sie OsmAnd, um die Daten auf der Karte zu sehen + Aktualisieren + gesendet (%1$d im Puffer) + %1$d Punkte + Datum + Gesammelt + GPS-Punkte + Gesendet + Telegram + Telegram (die Messaging-App) wird verwendet, um sich mit Menschen zu verbinden und mit ihnen zu kommunizieren.
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-es-rUS/strings.xml b/OsmAnd-telegram/res/values-es-rUS/strings.xml index 775401aea9..3a2766bd4c 100644 --- a/OsmAnd-telegram/res/values-es-rUS/strings.xml +++ b/OsmAnd-telegram/res/values-es-rUS/strings.xml @@ -1,180 +1,208 @@ -Cambiar los ajustes de optimización de la batería para estabilizar la ubicación compartida. - Funcionamiento en segundo plano - Desactivar la optimización de la batería para OsmAnd Telegram de modo que no se desconecte repentinamente cuando esté en segundo plano. - Compartir en segundo plano - Ir a los ajustes - Luego - No enviado aún - Aún no encontrada - Reenviar ubicación - Última ubicación disponible - Estado de compartición - Compartir: Activado - Estado - Sin conexión GPS - Sin conexión a Internet - Desactivar - Guardar - Añadir dispositivo - Compartir ubicación como - Contactos y grupos que comparten la ubicación contigo. - ¿Cerrar sesión en OsmAnd Telegram\? No podrá compartir la ubicación o ver la ubicación de otros - ¿Cerrar sesión de OsmAnd Telegram\? - Nombre - Por distancia - Por nombre - Por grupo - Ordenar - Ordenar por - Seleccione la versión de OsmAnd donde los contactos se mostrarán en el mapa. - Seleccione la versión de OsmAnd para usar - Desactivar la ubicación compartida en todos los chats seleccionados (%1$d). - Desactivar el uso compartido - Apagar todos - Salir - hace - Última respuesta - Grupo - Conéctese a Internet para cerrar sesión en Telegram correctamente. - Cerrar - Para revocar el acceso a la ubicación compartida. Abra Telegram, vaya a «Ajustes → Privacidad y Seguridad → Sesiones» y cierre sesión de OsmAnd Telegram. - Cómo desactivar la «Ubicación Compartida de OsmAnd» desde Telegram - Cómo desactivar la «Ubicación Compartida de OsmAnd» desde Telegram - Cuenta conectada - Cuenta - en %1$s - Elija la versión de OsmAnd el cual «OsmAnd Telegram» utilizará para mostrar las ubicaciones. - Conectar OsmAnd - Ocultar los contactos que no se han movido en un tiempo determinado. - Historial de ubicación - La última vez que un contacto se movió. - Sin movimiento - Fijar el intervalo mínimo para compartir la ubicación. - Enviar mi ubicación - Ubicación - Tiempo compartido - Expira en - Desactivar la ubicación compartida - Abrir OsmAnd - En vivo - Bot - Registro en Telegram - Necesita una cuenta en Telegram para utilizar la ubicación compartida. - Instale Telegram y cree una cuenta. - Después podrá utilizar esta aplicación. - Todos - No - Necesita una cuenta registrada en Telegram y un número de teléfono - No tengo cuenta de Telegram - Ingrese el número de teléfono - Ingrese el código de autenticación - Ajustar la hora visible para todos - %1$d h %2$d m - %1$d m - %1$d h - Instalar - Compartir - Atrás - Tiempo visible para todos - Ajustar la hora en que los contactos y grupos marcados verán la ubicación en tiempo real. - Fijar la hora - Marque los contactos y grupos con los que desea compartir su ubicación. - Buscar: Grupo o contacto - Compartir ubicación - Mostrar en el mapa - OsmAnd Telegram - Número de teléfono - Número de teléfono en formato internacional - Contraseña - Ingresar código - Código de autenticación - Telegram ha enviado un código para que OsmAnd inicie sesión en su cuenta. - Ingresar contraseña - Contraseña de Telegram - Iniciar sesión - Cerrar sesión - No ha iniciado sesión - Continuar - Cancelar - ¿Activar «Ubicación»\? - Ajustes - La aplicación no tiene permiso para acceder a los datos de ubicación. - Activar «Ubicación» en los ajustes del sistema - Elija uno de los proveedores de ubicación para compartir su ubicación. - Modo reposo - OsmAnd Telegram se ejecuta en modo reposo con la pantalla apagada. - Distancia - Compartir ubicación - Compartir ubicación - Servicio de OsmAnd Telegram - Logotipo de OsmAnd - Necesita instalar primero la versión gratuita o de pago de OsmAnd - Instalar OsmAnd - Mostrar usuarios en el mapa - Chats activos - Autorización - Ingrese el número de teléfono de Telegram en el formato internacional - Bienvenido - - yd - ft - mi - km - m - nmi - min/m - min/km - nmi/h - m/s - km/h - mph - Kilómetros por hora - Millas por hora - Metros por segundo - Minutos por kilómetro - Minutos por milla - Millas náuticas por hora (nudos) - Millas/pies - Millas/yardas - Kilómetros/metros - Millas náuticas - Millas/metros - h - min - seg - La Ubicación Compartida de OsmAnd (en inglés, «OsmAnd Location Sharing») permite compartir su ubicación y ver las ubicaciones de otras personas en OsmAnd.

La aplicación funciona sobre la base de Telegram API. Para utilizar esta aplicación debe tener una cuenta de Telegram.
- Mi ubicación - En vivo ahora - - -Elige un nombre que no se haya usado - %1$s añadido(s). - Añadir - No se pudo añadir el dispositivo - El nombre del dispositivo nuevo (máx. 200 símbolos). - Nombre del dispositivo demasiado largo - El nombre del dispositivo no debe estar vacío - Nombre del dispositivo - Ocultar - Puede crear y ver el ID de dispositivo en el cliente de Telegram usando el chat bot %1$s. %2$s - Si desea conectar varios dispositivos a una cuenta de Telegram, debe utilizar un dispositivo diferente para compartir su ubicación. - Última ubicación actualizada: - Enviado y actualizado con éxito - Imposible enviar a los chats de Telegram: - Esperando la respuesta de Telegram - Ubicación de envío - Iniciando - Ubicando… - Conexión a Internet - Última actualización de Telegram - Compartir está activado (desactivar) - Iniciando - Cerrar sesión - Cerrando + + Cambiar los ajustes de optimización de la batería para estabilizar la ubicación compartida. + Funcionamiento en segundo plano + Desactivar la optimización de la batería para OsmAnd Telegram de modo que no se desconecte repentinamente cuando esté en segundo plano. + Compartir en segundo plano + Ir a los ajustes + Luego + No enviado aún + Aún no encontrada + Reenviar ubicación + Última ubicación disponible + Estado de compartición + Compartir: Activado + Estado + Sin conexión GPS + Sin conexión a Internet + Desactivar + Guardar + Añadir dispositivo + Compartir ubicación como + Contactos y grupos que comparten la ubicación contigo. + ¿Cerrar sesión en OsmAnd Telegram\? No podrá compartir la ubicación o ver la ubicación de otros + ¿Cerrar sesión de OsmAnd Telegram\? + Nombre + Por distancia + Por nombre + Por grupo + Ordenar + Ordenar por + Seleccione la versión de OsmAnd donde los contactos se mostrarán en el mapa. + Seleccione la versión de OsmAnd para usar + Desactivar la ubicación compartida en todos los chats seleccionados (%1$d). + Desactivar el uso compartido + Apagar todos + Salir + hace + Última respuesta + Grupo + Conéctese a Internet para cerrar sesión en Telegram correctamente. + Cerrar + Para revocar el acceso a la ubicación compartida. Abra Telegram, vaya a «Ajustes → Privacidad y Seguridad → Sesiones» y cierre sesión de OsmAnd Telegram. + Cómo desactivar la «Ubicación Compartida de OsmAnd» desde Telegram + Cómo desactivar la «Ubicación Compartida de OsmAnd» desde Telegram + Cuenta conectada + Cuenta + en %1$s + Elija la versión de OsmAnd el cual «OsmAnd Telegram» utilizará para mostrar las ubicaciones. + Conectar OsmAnd + Ocultar los contactos que no se han movido en un tiempo determinado. + Historial de ubicación + La última vez que un contacto se movió. + Sin movimiento + Fijar el intervalo mínimo para compartir la ubicación. + Enviar mi ubicación + Ubicación + Tiempo compartido + Expira en + Desactivar la ubicación compartida + Abrir OsmAnd + En vivo + Bot + Registro en Telegram + Necesita una cuenta en Telegram para utilizar la ubicación compartida. + Instale Telegram y cree una cuenta. + Después podrá utilizar esta aplicación. + Todos + No + Necesita una cuenta registrada en Telegram y un número de teléfono + No tengo cuenta de Telegram + Ingrese el número de teléfono + Ingrese el código de autenticación + Ajustar la hora visible para todos + %1$d h %2$d m + %1$d m + %1$d h + Instalar + Compartir + Atrás + Tiempo visible para todos + Ajustar la hora en que los contactos y grupos marcados verán la ubicación en tiempo real. + Fijar la hora + Marque los contactos y grupos con los que desea compartir su ubicación. + Buscar: Grupo o contacto + Compartir ubicación + Mostrar en el mapa + OsmAnd Telegram + Número de teléfono + Número de teléfono en formato internacional + Contraseña + Ingresar código + Código de autenticación + Telegram ha enviado un código para que OsmAnd inicie sesión en su cuenta. + Ingresar contraseña + Contraseña de Telegram + Iniciar sesión + Cerrar sesión + No ha iniciado sesión + Continuar + Cancelar + ¿Activar «Ubicación»\? + Ajustes + La aplicación no tiene permiso para acceder a los datos de ubicación. + Activar «Ubicación» en los ajustes del sistema + Elija uno de los proveedores de ubicación para compartir su ubicación. + Modo reposo + OsmAnd Telegram se ejecuta en modo reposo con la pantalla apagada. + Distancia + Compartir ubicación + Compartir ubicación + Servicio de OsmAnd Telegram + Logotipo de OsmAnd + Necesita instalar primero la versión gratuita o de pago de OsmAnd + Instalar OsmAnd + Mostrar usuarios en el mapa + Chats activos + Autorización + Ingrese el número de teléfono de Telegram en el formato internacional + Bienvenido + yd + ft + mi + km + m + nmi + min/m + min/km + nmi/h + m/s + km/h + mph + Kilómetros por hora + Millas por hora + Metros por segundo + Minutos por kilómetro + Minutos por milla + Millas náuticas por hora (nudos) + Millas/pies + Millas/yardas + Kilómetros/metros + Millas náuticas + Millas/metros + h + min + seg + La Ubicación Compartida de OsmAnd (en inglés, «OsmAnd Location Sharing») permite compartir su ubicación y ver las ubicaciones de otras personas en OsmAnd.
+
La aplicación funciona sobre la base de Telegram API. Para utilizar esta aplicación debe tener una cuenta de Telegram.
+ Mi ubicación + En vivo ahora + Elige un nombre que no se haya usado + %1$s añadido(s). + Añadir + No se pudo añadir el dispositivo + El nombre del dispositivo nuevo (máx. 200 símbolos). + Nombre del dispositivo demasiado largo + El nombre del dispositivo no debe estar vacío + Nombre del dispositivo + Ocultar + Puede crear y ver el ID de dispositivo en el cliente de Telegram usando el chat bot %1$s. %2$s + Si desea conectar varios dispositivos a una cuenta de Telegram, debe utilizar un dispositivo diferente para compartir su ubicación. + Última ubicación actualizada: + Enviado y actualizado con éxito + Imposible enviar a los chats de Telegram: + Esperando la respuesta de Telegram + Ubicación de envío + Iniciando + Ubicando… + Conexión a Internet + Última actualización de Telegram + Compartir está activado (desactivar) + Iniciando + Cerrar sesión + Cerrando Enviar ubicación como Elige cómo se verán los mensajes con tu ubicación. Mapa Texto Mapa y texto + Monitorización activada + Monitorización desactivada + tiempo en movimiento + Altitud media + Velocidad media + Abrir en OsmAnd + Fecha de finalización + Fecha de inicio + Línea de tiempo + enviado (%1$d en búfer) + %1$d puntos + Fecha + Recolectados + Puntos GPS + Enviado + Actualiza OsmAnd para ver datos en el mapa + Actualizar + Telegram + Telegram (la aplicación de mensajería) se utiliza para conectarse y comunicarse con las personas. + La plataforma abierta de Telegram y «OsmAnd Tracker» es uno de los clientes. Los contactos pueden utilizar cualquier otro cliente de Telegram. + Al pulsar en continuar, acepta la Políticas de Privacidad de Telegram y de OsmAnd. + Aceptar + Política de privacidad de Telegram + Política de privacidad de OsmAnd + Cómo funciona + Puntos GPX recibidos: %1$s + Aspecto + Mostrar puntos GPS + Muestra la cantidad de puntos GPS recibidos y enviados.
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-es/strings.xml b/OsmAnd-telegram/res/values-es/strings.xml index d12ff7b16d..3e1b4d31c1 100644 --- a/OsmAnd-telegram/res/values-es/strings.xml +++ b/OsmAnd-telegram/res/values-es/strings.xml @@ -1,7 +1,7 @@ Cambiar los ajustes de optimización de la batería para estabilizar la ubicación compartida. Funcionamiento en segundo plano - Desactivar la optimización de la batería para OsmAnd Telegram de modo que no se desconecte repentinamente cuando esté en segundo plano. + Desactivar la optimización de la batería para OsmAnd Tracker de modo que no se desconecte repentinamente cuando esté en segundo plano. Compartir en segundo plano Ir a los ajustes Luego @@ -18,8 +18,8 @@ Añadir dispositivo Compartir ubicación como Contactos y grupos que comparten la ubicación contigo. - ¿Cerrar sesión en OsmAnd Telegram\? No podrá compartir la ubicación o ver la ubicación de otros - ¿Cerrar sesión de OsmAnd Telegram\? + ¿Cerrar sesión en OsmAnd Tracker\? No podrá compartir la ubicación o ver la ubicación de otros + ¿Cerrar sesión de OsmAnd Tracker\? Nombre Por distancia Por nombre @@ -43,7 +43,7 @@ Cuenta conectada Cuenta en %1$s - Elija la versión de OsmAnd el cual «OsmAnd Telegram» utilizará para mostrar las ubicaciones. + Elija la versión de OsmAnd el cual «OsmAnd Tracker» utilizará para mostrar las ubicaciones. Conectar OsmAnd Ocultar los contactos que no se han movido en un tiempo determinado. Historial de ubicación @@ -82,7 +82,7 @@ Buscar: Grupo o contacto Compartir ubicación Mostrar en el mapa - OsmAnd Telegram + OsmAnd Online GPS Tracker Número de teléfono Número de teléfono en formato internacional Contraseña @@ -101,11 +101,11 @@ Activar «Ubicación» en los ajustes del sistema Elija uno de los proveedores de ubicación para compartir su ubicación. Modo en segundo plano - OsmAnd Telegram se ejecuta en modo reposo con la pantalla apagada. + OsmAnd Tracker se ejecuta en modo reposo con la pantalla apagada. Distancia Compartir ubicación Compartir ubicación - Servicio de OsmAnd Telegram + Servicio de OsmAnd Tracker Logotipo de OsmAnd Necesita instalar primero la versión gratuita o de pago de OsmAnd Instalar OsmAnd @@ -177,4 +177,21 @@ Mapa Texto Mapa y texto + Monitorización activada + Monitorización desactivada + tiempo en movimiento + Altitud media + Velocidad media + Abrir en OsmAnd + Fecha de fin + Fecha de inicio + Línea de tiempo + enviado (%1$d en búfer) + %1$d puntos + Fecha + Recolectados + Puntos de GPS + Enviado + Actualice OsmAnd para ver datos en el mapa + Actualizar \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-fa/strings.xml b/OsmAnd-telegram/res/values-fa/strings.xml index 302ffb89d8..6ec7716a90 100644 --- a/OsmAnd-telegram/res/values-fa/strings.xml +++ b/OsmAnd-telegram/res/values-fa/strings.xml @@ -11,7 +11,7 @@ با موفقیت ارسال و به‌روز شد در انتظار پاسخ از تلگرام در حال موقعیت‌یابی… - بهینه‌سازی باتری را برای OsmAnd Telegram غیرفعال کنید تا هنگامی که در پس‌زمینه کار می‌کند ناگهان بسته نشود. + بهینه‌سازی باتری را برای OsmAnd Tracker غیرفعال کنید تا هنگامی که در پس‌زمینه کار می‌کند ناگهان بسته نشود. برو به تنظیمات بعداً هنوز ارسال نشده diff --git a/OsmAnd-telegram/res/values-gl/strings.xml b/OsmAnd-telegram/res/values-gl/strings.xml index 7cc9d9e139..abac18d60e 100644 --- a/OsmAnd-telegram/res/values-gl/strings.xml +++ b/OsmAnd-telegram/res/values-gl/strings.xml @@ -18,8 +18,8 @@ Engadir dispositivo Compartillar ubicación coma Contactos e grupos que están a compartilla-la súa ubicación para vostede. - Está na certeza de que desexa desconectarse do Telegram OsmAnd para non poder compartilla-la ubicación ou olla-la ubicación doutros\? - Pecha-la sesión do Telegram OsmAnd\? + Está na certeza de que desexa desconectarse do Tracker OsmAnd para non poder compartilla-la ubicación ou olla-la ubicación doutros\? + Pecha-la sesión do Tracker OsmAnd\? Nome Pola distancia Polo nome @@ -88,12 +88,12 @@ Estase a iniciar Estase a posicionar… Estase a conectar á Internet - Desactiva-la optimización da batería para o OsmAnd Telegram de xeito que non se desconecte de xeito súbito cando esté no segundo plano. + Desactiva-la optimización da batería para o OsmAnd Tracker de xeito que non se desconecte de xeito súbito cando esté no segundo plano. Conéctese á Internet para pecha-la sesión no Telegram de xeito correcto. Para revoga-lo acceso á ubicación compartillada. Abra o Telegram, vaia cara ós «Axustes → Privacidade e Seguranza → Sesións» e peche a sesión do OsmAnd Telegram. De que xeito desactiva-la «Ubicación compartillada do OsmAnd» dende o Telegram De que xeito desactiva-la «Ubicación compartillada do OsmAnd» dende o Telegram - Escolla a versión do OsmAnd na cal o «OsmAnd Telegram» empregará para amosa-las ubicacións. + Escolla a versión do OsmAnd na cal o «OsmAnd Tracker» empregará para amosa-las ubicacións. Conecta-lo OsmAnd Agocha-los contactos que non se moveron nun tempo determinado. A última vez que un contacto se moveu. @@ -116,7 +116,7 @@ Procurar: Grupo ou contacto Compartillar ubicación Amosar no mapa - OsmAnd Telegram + OsmAnd Online GPS Tracker Número do teléfono móbil Número do teléfono móbil no formato internacional Contrasinal @@ -138,11 +138,11 @@ Activar «Ubicación» nos axustes do sistema Escolla un dos fornecedores da ubicación para compartilla-la súa ubicación. Modo no segundo plano - O OsmAnd Telegram execútase no modo en segundo plano ca pantalla apagada. + O OsmAnd Tracker execútase no modo en segundo plano ca pantalla apagada. Distancia Compartillar ubicación Estase a compartilla-la ubicación - Servizo do OsmAnd Telegram + Servizo do OsmAnd Tracker Logotipo do OsmAnd Precísase instalar primeiro a versión de balde ou de pagamento do OsmAnd Instala-lo OsmAnd @@ -177,4 +177,13 @@ Mapa Texto Mapa e texto -
\ No newline at end of file + O monitoramento está activado + O monitoramento está desactivado + tempo en movemento + Altitude media + Velocidade media + Abrir no OsmAnd + Data de finalización + Data de inicio + Liña do tempo +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-he/strings.xml b/OsmAnd-telegram/res/values-he/strings.xml index 10f7e532ac..175dda977a 100644 --- a/OsmAnd-telegram/res/values-he/strings.xml +++ b/OsmAnd-telegram/res/values-he/strings.xml @@ -1,48 +1,49 @@ -עדכון אחרון מטלגרם - נא לבחור שם שלא השתמשת בו עדיין - %1$s נוסף. - הוספה - לא ניתן להוסיף מכשיר חדש - נא לתת שם באורך של עד 200 תווים למכשיר החדש שלך. - שם המכשיר ארוך מדי - שם המכשיר לא יכול להישאר ריק - שם המכשיר - הסתרה - ניתן ליצור ולצפות במזהה ההתקן בלקוח הטלגרם הזה באמצעות רובוט ההתכתבות %1$s.‏ %2$s - אם ברצונך לחבר מגוון מכשירים לחשבון טלגרם אחד, עליך להשתמש במכשיר אחר כדי לשתף את המיקום שלך. - המיקום האחרון שעודכן: - נשלח ועודכן בהצלחה - אין אפשרות לשלוח להתכתבויות בטלגרם: - שליחת מיקום בתור - נא לבחור כיצד הודעות עם המיקום שלך תיראנה. - מפה - טקסט - מפה וטקסט - בהמתנה לתגובה מטלגרם - המיקום נשלח - מופעל - מתבצע איתור המיקום… - מתבצעת התחברות לאינטרנט - ניתן לשנות הגדרות מיטוב סוללה כדי לייצב את שיתוף המיקום. - עבודת רקע - מתבצע שיתוף ברקע - מעבר להגדרות - אחר כך - לא נשלח עדיין - לא נמצא עדיין - שליחת המיקום מחדש - המיקום האחרון הזמין - מצב שיתוף - שיתוף: פעיל - מצב - אין חיבור ל־GPS - אין חיבור לאינטרנט - השבתה - שמירה - הוספת מכשיר - שיתוף מיקום בתור - אנשי קשר וקבוצות שמשתפים אתך מיקום. + + עדכון אחרון מטלגרם + נא לבחור שם שלא השתמשת בו עדיין + %1$s נוסף. + הוספה + לא ניתן להוסיף מכשיר חדש + נא לתת שם באורך של עד 200 תווים למכשיר החדש שלך. + שם המכשיר ארוך מדי + שם המכשיר לא יכול להישאר ריק + שם המכשיר + הסתרה + ניתן ליצור ולצפות במזהה ההתקן בלקוח הטלגרם הזה באמצעות רובוט ההתכתבות %1$s.‏ %2$s + אם ברצונך לחבר מגוון מכשירים לחשבון טלגרם אחד, עליך להשתמש במכשיר אחר כדי לשתף את המיקום שלך. + המיקום האחרון שעודכן: + נשלח ועודכן בהצלחה + אין אפשרות לשלוח להתכתבויות בטלגרם: + שליחת מיקום בתור + נא לבחור כיצד הודעות עם המיקום שלך תיראנה. + מפה + טקסט + מפה וטקסט + בהמתנה לתגובה מטלגרם + המיקום נשלח + מופעל + מתבצע איתור המיקום… + מתבצעת התחברות לאינטרנט + ניתן לשנות הגדרות מיטוב סוללה כדי לייצב את שיתוף המיקום. + עבודת רקע + מתבצע שיתוף ברקע + מעבר להגדרות + אחר כך + לא נשלח עדיין + לא נמצא עדיין + שליחת המיקום מחדש + המיקום האחרון הזמין + מצב שיתוף + שיתוף: פעיל + מצב + אין חיבור ל־GPS + אין חיבור לאינטרנט + השבתה + שמירה + הוספת מכשיר + שיתוף מיקום בתור + אנשי קשר וקבוצות שמשתפים אתך מיקום. כיבוי מיטוב סוללה עבור טלגרם OsmAnd כדי שהשירות לא יכבה ברקע. לצאת מטלגרם OsmAnd כדי לכבות את האפשרות לשתף מיקום או לצפות במיקום של אחרים\? לצאת מטלגרם OsmAnd\? @@ -63,4 +64,145 @@ השבתת כל השיתופים לכבות הכול חשבון + הניטור פעיל + הניטור מושבת + זמן בתנועה + גובה ממוצע + מהירות ממוצעת + פתיחה ב־OsmAnd + מועד סיום + מועד התחלה + כדי לשלול גישה לשיתוף המיקום. יש לפתוח את טלגרם, לגשת להגדרות ← פרטיות ← הפעלות ולסגור את הפעלת הטלגרם של OsmAnd. + איך לכבות את שיתוף המיקום של OsmAnd מטלגרם + איך לכבות את שיתוף המיקום של OsmAnd מטלגרם + חשבון מחובר + בעוד %1$s + נא לבחור את גרסת ה־OsmAnd בה יעשה שימוש OsmAnd טלגרם לטובת הצגת מיקומים. + להסתיר אנשי קשר שלא זזו במשך זה מסוים. + היסטוריית מיקום + הפעם האחרונה שאיש קשר זז. + אין תזוזה + שליחת המיקום שלי + מיקום + זמן השיתוף + תפוגה + השיתוף פעיל (כיבוי) + כיבוי שיתוף מיקום + פתיחת OsmAnd + חי + רובוט + הרשמה בטלגרם + כדי להשתמש בשיתוף מיקום צריך חשבון בטלגרם. + נא להתקין טלגרם ולהגדיר חשבון. + לאחר מכן תהיה לך אפשרות להשתמש ביישומון הזה. + הכול + כבוי + צריך חשבון רשום בטלגרם ומספר טלפון + אין לי חשבון בטלגרם + נא להקליד מספר טלפון + נא להקליד קוד אימות + הגדרת זמן חשיפה לכולם + %1$d שע׳ %2$d דק׳ + %1$d דק׳ + %1$d שע׳ + התקנה + שיתוף + חזרה + זמן חשיפה לכולם + נא להגדיר את משך הזמן שבו הקבוצות ואנשי הקשר הנבחרים שלך יראו את המיקום שלך בזמן אמת. + הגדרת זמן + נא לבחור את אנשי הקשר והקבוצות עמם ברצונך לשתף את מיקומך. + חיפוש: קבוצה או איש קשר + שיתוף מיקום + הצגה במפה + OsmAnd Online GPS Tracker + מספר טלפון + מספר טלפון בתצורה בינלאומית + ססמה + נא להכניס קוד + קוד אימות + נשלח אליך קוד מטלגרם עבור OsmAnd לטובת כניסה לחשבון שלך. + נא להקליד ססמה + ססמת טלגרם + כניסה + יציאה + התחלה + מתבצעת יציאה + מתבצעת סגירה + להפעיל „מיקום”\? + לא נכנסת + המשך + ביטול + הגדרות + ליישומון חסרה הרשאה לגישה לנתוני מיקום. + נא להפעיל את „מיקום” בהגדרות המערכת + נא לבחור את אחד מספקי המיקום כדי לשתף את המיקום שלך. + מצב רקע + OsmAnd טלגרם פועל ברקע בזמן שהמסך כבוי. + מרחק + שיתוף מיקום + המיקום משותף + שירות OsmAnd טלגרם + הלוגו של OsmAnd + עליך להתקין את הגרסה החופשית או את הגרסה בתשלום של OsmAnd תחילה + התקנת OsmAnd + הצגת משתמשים במפה + התכתבויות פעילות + אימות + נא להקליד את מספר הטלפון שלך בטלגרם בתצורה בינלאומית + ברוך בואך + יארד + רגל + מייל + ק״מ + מ׳ + מייל ימי + דק׳/מ׳ + דק׳/ק״מ + קשר + מ/שנ׳ + קמ״ש + מייל לשעה + קילומטרים לשעה + מיילים לשעה + מטרים לשנייה + דקות לקילומטר + דקות למייל + מייל ימי לשעה (קשר) + מיילים/רגל + מיילים/יארדים + קילומטרים/מטרים + מיילים ימיים + מיילים/מטרים + שע + דק׳ + שנ׳ + המיקום שלי + ציר זמן + הגדרת טווח הזמן המזערי לשיתוף מיקום. + + שיתוף המקום דרךOsmAnd מאפשר לך לשתף את המיקום ולראות את מיקומם של אחרים ב־OsmAnd.
+
היישומון משתמש ב־API ודורש חשבון טלגרם.
+ חי כעת + נשלחו (%1$d ממתינות) + %1$d נקודות + תאריך + נאספו + נקודות GPS + נשלחו + OsmAnd connect + נא לעדכן את OsmAnd כדי לצפות בנתונים במפה + עדכון + טלגרם + אנו משתמשים בטלגרם (יישומון התכתבות) כדי להתחבר לאנשים ולהקל עליך לתקשר אתם. + הפלטפורמה הפתוחה של טלגרם והעוקב של OsmAnd נמנים מבין לקוחותינו. אנשי הקשר שלך יכולים להשתמש בכל לקוח טלגרם שיבחרו. + לחיצה על המשך מהווה את הסכמתך למדיניות הפרטיות של טלגרם ולמדיניות הפרטיות של OsmAnd. + קבלה + מדיניות הפרטיות של טלגרם + מדיניות הפרטיות של OsmAnd + כיצד זה עובד + "התקבלו נקודות GPX:‏ %1$s " + מראה + הצגת נקודות GPS + הצגת כמות נקודות ה־GPS שנאספו ונשלחו.
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-hu/strings.xml b/OsmAnd-telegram/res/values-hu/strings.xml new file mode 100644 index 0000000000..a6d4ec0edc --- /dev/null +++ b/OsmAnd-telegram/res/values-hu/strings.xml @@ -0,0 +1,47 @@ + + + Telegram + A Telegram (üzenetküldő alkalmazás) a másokkal való összekapcsolódásra és kommunikációra használható. + A Telegram egy nyílt platform, amelynek egyik ügyfele az OsmAnd Tracker. Partnerei bármelyik másik Telegram kliensprogramot is használhatják. + A továbbra kattintva elfogadja a Telegram és az OsmAnd adatvédelmi irányelveit. + Elfogadás + Telegram adatvédelmi irányelvei + OsmAnd adatvédelmi irányelvei + Hogyan működik\? + Kapott GPX-pontok: %1$s + Megjelenés + GPS-pontok megjelenítése + Gyűjtött és küldött GPS-pontok mennyiségének megjelenítése. + Az adatok térképen való megtekintéséhez kérjük, frissítse az OsmAndot. + Frissítés + elküldve (%1$d a pufferben) + %1$d pont + Dátum + Gyűjtve + GPS-pont + Elküldve + Figyelemmel kísérés engedélyezve + Figyelemmel kísérés letiltva + Mozgással töltött idő + Átlagos magasság + Átlagsebesség + Megnyitás az OsmAndban + Záró dátum + Kezdő dátum + Helyzet elküldése mint + Válassza ki, hogyan nézzenek ki a helyzetét tartalmazó üzenetek. + Térkép + Szöveg + Térkép és szöveg + Utolsó frissítés a Telegramból + Válasszon egy olyan nevet, amelyet még nem használt + %1$s hozzáadva. + Hozzáadás + Új eszköz hozzáadása sikertelen + Az új eszköz neve legfeljebb 200 karakter hosszú lehet. + Túl hosszú az eszköz neve + Az eszköz nevének helye nem lehet üres + Eszköz neve + Elrejtés + Az eszköz azonosítóját létrehozhatja és megtekintheti telegram ügyfélszoftverrel a(z) %1$s chatbotot használva. %2$s + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-it/strings.xml b/OsmAnd-telegram/res/values-it/strings.xml index 5918fb311a..76ce6037c8 100644 --- a/OsmAnd-telegram/res/values-it/strings.xml +++ b/OsmAnd-telegram/res/values-it/strings.xml @@ -1,4 +1,4 @@ - + Ultimo aggiornamento da Telegram Scegli un nome che non hai ancora usato %1$s aggiunto. @@ -21,7 +21,7 @@ Collegamento a Internet Cambia le impostazioni di ottimizzazione della batteria per stabilizzare la condivisione della posizione. Funzionamento in secondo piano - Disattiva l\'ottimizzazione della batteria per OsmAnd Telegram in modo che non venga chiuso improvvisamente quando è in secondo piano. + Disattiva l\'ottimizzazione della batteria per OsmAnd Tracker in modo che non venga chiuso improvvisamente quando è in secondo piano. Condivisione in secondo piano Vai alle impostazioni Più tardi @@ -39,7 +39,7 @@ Aggiungi dispositivo Condividi posizione come Contatti e gruppi che condividono la posizione con te. - Disconnettersi da OsmAnd Telegram\? + Disconnettersi da OsmAnd Tracker\? Nome Per distanza Per nome @@ -63,7 +63,7 @@ Account connesso Account in %1$s - Scegli la versione di OsmAnd che OsmAnd Telegram userà per mostrare le posizioni. + Scegli la versione di OsmAnd che OsmAnd Tracker userà per mostrare le posizioni. Nascondi i contatti che non si sono spostati per un certo periodo di tempo. Cronologia delle posizioni L\'ultima volta in cui un contatto si è spostato. @@ -100,7 +100,7 @@ Cerca: gruppo o contatto Condividi la posizione Mostra sulla mappa - OsmAnd Telegram + OsmAnd Online GPS Tracker Numero di telefono Numero di telefono nel formato internazionale Password @@ -123,11 +123,11 @@ Si prega di attivare la \"posizione\" nelle impostazioni di sistema Seleziona uno dei provider della posizione per condividerla. In secondo piano - OsmAnd Telegram funziona in secondo piano quando lo schermo è spento. + OsmAnd Tracker funziona in secondo piano quando lo schermo è spento. Distanza Condividi la posizione Condivisione della posizione - Servizio di OsmAnd Telegram + Servizio di OsmAnd Tracker Logo di OsmAnd Devi installare prima la versione gratis o a pagamento di OsmAnd Installa OsmAnd @@ -163,8 +163,8 @@ Ora in diretta -Sei sicuro di volerti disconnettere da OsmAnd Telegram, cosicché non potrai condividere la posizione o vedere quella di altri\? - OsmAnd connect +Sei sicuro di volerti disconnettere da OsmAnd Tracker, cosicché non potrai condividere la posizione o vedere quella di altri\? + Connessione OsmAnd Imposta un orario visibile a tutti Orario visibile a tutti min/km @@ -172,4 +172,18 @@ m/s km/h mi/h - + Monitoraggio abilitato + Monitoraggio disabilitato + tempo in movimento + Altitudine media + Velocità media + Apri in OsmAnd + Data di fine + Data d\'inizio + Invia la localizzazione come + Scegli l\'aspetto dei messaggi con la tua localizzazione. + Mappa + Testo + Mappa e testo + Cronologia +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-nb/strings.xml b/OsmAnd-telegram/res/values-nb/strings.xml index 3c4192de7d..07873f5620 100644 --- a/OsmAnd-telegram/res/values-nb/strings.xml +++ b/OsmAnd-telegram/res/values-nb/strings.xml @@ -1,182 +1,206 @@ - -Endre batterioptimiseringsinnstillinger for mer stabil posisjonsdeling. - Bakgrunnsarbeid - Skru av batterioptimisering for OsmAnd Telegram slik at det ikke plutselig skrur seg av når det er i bakgrunnen. - Deling i bakgrunnen - Gå til innstillinger - Senere - Ikke sendt enda - Ikke funnet enda - Send posisjon på nytt - Sist tilgjengelige posisjon - Delingsstatus - Deling: Påslått - Status - Ingen GPS-tilkobling - Ingen internettilknytning - Skru av - Legg til enhet - Del posisjon som - Kontakter og grupper som deler sin posisjon med deg. - Er du sikker på at du vil logge ut av OsmAnd Telegram slik at du ikke kan sende din posisjon, eller se andres\? - Logg ut av OsmAnd Telegram\? - Navn - Etter distanse - Etter navn - Etter gruppe - Sorter - Sorter etter - Velg OsmAnd-versjon der kontaktene skal vises på kartet. - Velg OsmAnd versjon å bruke - Det vil skru av posisjonsdeling til alle valgte sludringer (%1$d). - Skru av all deling - Skru av alle - Avslutt - siden - Siste svar - Gruppe - Koble til Internett for å logge ut av Telegram ordentlig. - Lukk - For å tilbakekalle posisjonsdelingstilgang. Åpne Telegram, gå til Innstillinger - Personvern og sikkerhet - Økter, og sluttfør OsmAnd Telegram-økta. - Hvordan koble fra OsmAnd posisjonsdeling fra Telegram - Hvordan koble fra OsmAnd posisjonsdeling fra Telegram - Tilkoblet konto - Konto - i %1$s - Velg OsmAnd-versjonen OsmAnd Telegram bruker for å vise posisjoner på kartet. - OsmAnd connect - Skjul kontaktene som ikke har oppdatert sin plassering etter et gitt tidsintervall. - Posisjonshistorikk - Siste tidspunkt en kontakt sendte en plasseringsoppdatering. - Urørlig plassering - Sett minimumsintervall for posisjonsdeling. - Send min posisjon - GPS og plassering - Delingstid - Utløper - Skru av posisjonsdeling - Åpne OsmAnd - Sanntid - Bot - Registrering i Telegram - Du trenger en Telegram-konto for å bruke funksjonene i OsmAnd posisjonsdeling. - Hvis du ønsker å fortsette, installer Telegram fra Google Play og registrer din konto. - Etter oppretting av konto, kan du bruke dette programmet. - Alle - Av - Du trenger registrert konto og telefonnummer i Telegram - Jeg har ikke en Telegram-konto - Skriv inn telefonnummer - Skriv inn identitetsbekreftelseskode - Sett synlig tid for alle - %1$d t %2$d m - %1$d m - %1$d t - Installer - Del - Tilbake - Synlig tid for alle - Sett tiden dine valgte kontakter og grupper vil se din posisjon i sanntid. - Sett tid - Velg kontaktene og gruppene du ønsker å dele din posisjon med. - Søk: Gruppe eller kontakt - Del posisjon - Vis på kartet - OsmAnd Telegram - Telefonnummer - Telefonnummer i internasjonalt format - Passord - Skriv inn kode - Identitetsbekreftelseskode - Telegram har sendt deg en kode slik at OsmAnd kan logge inn på kontoen din. - Skriv inn passord - Skriv inn passordet for din Telegram-konto - Logg inn - Logg ut - Du er ikke innlogget - Fortsett - Avbryt - Innstillinger - Programmet mangler tilgang til posisjonsdata. - Skru på «Posisjon» i systeminnstillingene - Velg en av posisjonstilbyderne for å dele din posisjon. - Bakgrunnsmodus - OsmAnd Telegram kjører som nisse med skjermen av. - Distanse - Del posisjon - Deler posisjon - OsmAnd Telegram-tjeneste - OsmAnd-logo - Du må installere gratis- eller betalt versjon av OsmAnd først - Installer OsmAnd - Vis brukere på kartet - Aktive sludringer - Identitetsbekreftelse - Skriv inn ditt Telegram-telefonnummer i internasjonalt format - Velkommen - - km - m - Kilometer i timen - Meter i sekunder - Minutter per kilometer - Nautiske mil per time (knop) - t - min - sek - OsmAnd posisjonsdeling lar deg dele din posisjon og se andres posisjon i OsmAnd.

Programmet belager seg på Telegram-API-et. For å bruke dette programmet må du ha en Telegram-konto.
- Min posisjon - - -Sist oppdaterte posisjon: - Sendt og oppdatert - Kan ikke sende til Telegram-sludringer: - Venter på svar fra Telegram - Sender posisjon - Starter - Posisjonerer… - Kobler til Internett - yd - fot - mil - nmi - min/m - min/km - nmi/t - m/s - km/t - mph - Engelske mil i timen - Minutter per engelske mil - Engelske mil/fot - Engelske mil/yard - Kilometer/meter - Nautiske mil - Engelske mil/meter - Du har allerede en enhet med samme navn, velg noe annet - %1$s lagt til. - Legg til - Kunne ikke legge til ny enhet - Skriv inn et nytt navn for din nye enhet. Maksimal lengde 200 tegn. - For lang enhetsnavn - Enhetsnavn må velges - Enhetsnavn - Skjul - Du kan opprette og vise enhets-ID i Telegram-klienten ved bruk av %1$s-sludrebot-en. %2$s - Hvis du vil koble flere enheter til én Telegram-konto, må du bruke en annen enhet til å dele din posisjon. - Lagre - Deling er på (skru av) - Starter - Logger ut - Lukker - Skru på \"Posisjon\"\? - Nå direkte - - -Siste oppdatering fra Telegram - Send plassering som - Velg hvordan meldinger med din plassering skal se ut. - Kart - Tekst - Kart og tekst -
+ + + Endre batterioptimiseringsinnstillinger for mer stabil posisjonsdeling. + Bakgrunnsarbeid + Skru av batterioptimisering for OsmAnd Telegram slik at det ikke plutselig skrur seg av når det er i bakgrunnen. + Deling i bakgrunnen + Gå til innstillinger + Senere + Ikke sendt enda + Ikke funnet enda + Send posisjon på nytt + Sist tilgjengelige posisjon + Delingsstatus + Deling: Påslått + Status + Ingen GPS-tilkobling + Ingen internettilknytning + Skru av + Legg til enhet + Del posisjon som + Kontakter og grupper som deler sin posisjon med deg. + Er du sikker på at du vil logge ut av OsmAnd Telegram slik at du ikke kan sende din posisjon, eller se andres\? + Logg ut av OsmAnd Telegram\? + Navn + Etter distanse + Etter navn + Etter gruppe + Sorter + Sorter etter + Velg OsmAnd-versjon der kontaktene skal vises på kartet. + Velg OsmAnd versjon å bruke + Det vil skru av posisjonsdeling til alle valgte sludringer (%1$d). + Skru av all deling + Skru av alle + Avslutt + siden + Siste svar + Gruppe + Koble til Internett for å logge ut av Telegram ordentlig. + Lukk + For å tilbakekalle posisjonsdelingstilgang. Åpne Telegram, gå til Innstillinger - Personvern og sikkerhet - Økter, og sluttfør OsmAnd Telegram-økta. + Hvordan koble fra OsmAnd posisjonsdeling fra Telegram + Hvordan koble fra OsmAnd posisjonsdeling fra Telegram + Tilkoblet konto + Konto + i %1$s + Velg OsmAnd-versjonen OsmAnd Telegram bruker for å vise posisjoner på kartet. + OsmAnd connect + Skjul kontaktene som ikke har oppdatert sin plassering etter et gitt tidsintervall. + Posisjonshistorikk + Siste tidspunkt en kontakt sendte en plasseringsoppdatering. + Urørlig plassering + Sett minimumsintervall for posisjonsdeling. + Send min posisjon + GPS og plassering + Delingstid + Utløper + Skru av posisjonsdeling + Åpne OsmAnd + Sanntid + Bot + Registrering i Telegram + Du trenger en Telegram-konto for å bruke funksjonene i OsmAnd posisjonsdeling. + Hvis du ønsker å fortsette, installer Telegram fra Google Play og registrer din konto. + Etter oppretting av konto, kan du bruke dette programmet. + Alle + Av + Du trenger registrert konto og telefonnummer i Telegram + Jeg har ikke en Telegram-konto + Skriv inn telefonnummer + Skriv inn identitetsbekreftelseskode + Sett synlig tid for alle + %1$d t %2$d m + %1$d m + %1$d t + Installer + Del + Tilbake + Synlig tid for alle + Sett tiden dine valgte kontakter og grupper vil se din posisjon i sanntid. + Sett tid + Velg kontaktene og gruppene du ønsker å dele din posisjon med. + Søk: Gruppe eller kontakt + Del posisjon + Vis på kartet + OsmAnd Telegram + Telefonnummer + Telefonnummer i internasjonalt format + Passord + Skriv inn kode + Identitetsbekreftelseskode + Telegram har sendt deg en kode slik at OsmAnd kan logge inn på kontoen din. + Skriv inn passord + Skriv inn passordet for din Telegram-konto + Logg inn + Logg ut + Du er ikke innlogget + Fortsett + Avbryt + Innstillinger + Programmet mangler tilgang til posisjonsdata. + Skru på «Posisjon» i systeminnstillingene + Velg en av posisjonstilbyderne for å dele din posisjon. + Bakgrunnsmodus + OsmAnd Telegram kjører som nisse med skjermen av. + Distanse + Del posisjon + Deler posisjon + OsmAnd Telegram-tjeneste + OsmAnd-logo + Du må installere gratis- eller betalt versjon av OsmAnd først + Installer OsmAnd + Vis brukere på kartet + Aktive sludringer + Identitetsbekreftelse + Skriv inn ditt Telegram-telefonnummer i internasjonalt format + Velkommen + km + m + Kilometer i timen + Meter i sekunder + Minutter per kilometer + Nautiske mil per time (knop) + t + min + sek + + OsmAnd posisjonsdeling lar deg dele din posisjon og se andres posisjon i OsmAnd.
+
Programmet belager seg på Telegram-API-et. For å bruke dette programmet må du ha en Telegram-konto.
+ Min posisjon + Sist oppdaterte posisjon: + Sendt og oppdatert + Kan ikke sende til Telegram-sludringer: + Venter på svar fra Telegram + Sender posisjon + Starter + Posisjonerer… + Kobler til Internett + yd + fot + mil + nmi + min/m + min/km + nmi/t + m/s + km/t + mph + Engelske mil i timen + Minutter per engelske mil + Engelske mil/fot + Engelske mil/yard + Kilometer/meter + Nautiske mil + Engelske mil/meter + Du har allerede en enhet med samme navn, velg noe annet + %1$s lagt til. + Legg til + Kunne ikke legge til ny enhet + Skriv inn et nytt navn for din nye enhet. Maksimal lengde 200 tegn. + For lang enhetsnavn + Enhetsnavn må velges + Enhetsnavn + Skjul + Du kan opprette og vise enhets-ID i Telegram-klienten ved bruk av %1$s-sludrebot-en. %2$s + Hvis du vil koble flere enheter til én Telegram-konto, må du bruke en annen enhet til å dele din posisjon. + Lagre + Deling er på (skru av) + Starter + Logger ut + Lukker + Skru på \"Posisjon\"\? + Nå direkte + Siste oppdatering fra Telegram + Send plassering som + Velg hvordan meldinger med din plassering skal se ut. + Kart + Tekst + Kart og tekst + Oppsyn er påskrudd + Oppsyn er avskrudd + tid i bevegelse + Gjennomsnittlig høyde + Gjennomsnittsfart + Åpne i OsmAnd + Sluttdato + Startdato + Tidslinje + Telegram + Telegram åpen plattform, og OsmAnd-sporeren er én av kundene. Dine kontakter kan bruke enhver annen Telegram-klient. + Ved å klikke \"Fortsett\" samtykker du til Telegram-personvernspraksisen, og OsmAnd-personvernspraksisen. + Godta + Telegram-personvernspraksis + Hvordan det virker + Mottatte GPX-punkter: %1$s + Utseende + Vis GPS-punkter + Vis mengden innsamlede og sendte GPS-punkter. + Oppdater + sendt (%1$d i hurtiglager) + %1$d punkter + Dato + Innsamlet + GPS-punkter + Sendt +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-pl/strings.xml b/OsmAnd-telegram/res/values-pl/strings.xml index 57f2a238ed..3773b6bfd8 100644 --- a/OsmAnd-telegram/res/values-pl/strings.xml +++ b/OsmAnd-telegram/res/values-pl/strings.xml @@ -1,7 +1,7 @@ - + Zmień ustawienia optymalizacji baterii, aby zapewnić stabilną lokalizację. Praca w tle - Aby stabilnie udostępniać swoją pozycję w tle, zaleca się wyłączenie optymalizacji baterii dla Telegram OsmAnd. + Aby stabilnie udostępniać swoją pozycję w tle, zaleca się wyłączenie optymalizacji baterii dla Tracker OsmAnd. \n \nJeśli włączona jest optymalizacja, system może automatycznie wyłączyć aplikację działającą w tle (gdy ekran jest zablokowany i/lub aplikacja jest zminimalizowana). Dzieje się tak bez powiadomienia i powoduje zatrzymanie położenia geograficznego. Udostępnianie w tle @@ -21,8 +21,8 @@ Dodaj urządzenie Udostępnij lokalizację jako Kontakty i grupy, które udostępniają Ci swoją lokalizację. - Czy na pewno chcesz się wylogować z OsmAnd Telegram, aby nie udostępniać lokalizacji ani nie widzieć lokalizacji innych\? - Wylogować się z OsmAnd Telegram\? + Czy na pewno chcesz się wylogować z OsmAnd Tracker, aby nie udostępniać lokalizacji ani nie widzieć lokalizacji innych\? + Wylogować się z OsmAnd Tracker\? Nazwa Według odległości Według nazwy @@ -45,7 +45,7 @@ Połączone konta Konto w %1$s - Wybierz wersję OsmAnd, którą Telegram OsmAnd będzie używał do wyświetlaniu pozycji. + Wybierz wersję OsmAnd, którą Tracker OsmAnd będzie używał do wyświetlaniu pozycji. Połącz OsmAnd Ukryj kontakty, które nie zostały przeniesione w określonym czasie. Historia lokalizacji @@ -83,7 +83,7 @@ Szukaj: Grupa lub kontakt Udostępnij lokalizację Pokazuj na mapie - OsmAnd Telegram + OsmAnd Online GPS Tracker Numer telefonu Numer telefonu w formacie międzynarodowym Hasło @@ -103,11 +103,11 @@ Włącz \"Lokalizacja\" w ustawieniach systemu Wybierz jednego z dostawców lokalizacji, aby udostępnić swoją lokalizację. Tryb tła - Telegram OsmAnd będzie działał w tle przy wygaszonym ekranie. + Tracker OsmAnd będzie działał w tle przy wygaszonym ekranie. Odległość Udostępnij lokalizację Udostępnianie lokalizacji - Usługa Telegramu OsmAnd + Usługa Tracker OsmAnd Logo OsmAnd Najpierw musisz zainstalować darmową lub płatną wersję OsmAnd Zainstaluj OsmAnd @@ -179,4 +179,20 @@ Mapa Tekst Mapa i tekst - + Monitorowanie jest włączone + Monitorowanie jest wyłączone + czas w ruchu + Średnia wysokość + Średnia prędkość + Otwarte w OsmAnd + Data końcowa + Data rozpoczęcia + Linia czasu + Proszę zaktualizować OsmAnd, by zobaczyć dane na mapie + Aktualizuj + %1$d punktów + Data + Zgromadzone + Punkty GPS + Wyślij + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-pt-rBR/strings.xml b/OsmAnd-telegram/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..28f262a100 --- /dev/null +++ b/OsmAnd-telegram/res/values-pt-rBR/strings.xml @@ -0,0 +1,209 @@ + + + Enviar localização como + Escolha como as mensagens com sua localização serão exibidas. + Mapa + Texto + Mapa e texto + Última atualização do Telegram + Escolha um nome que você ainda não usou + %1$s adicionado. + Adicionar + Não foi possível adicionar novo dispositivo + Nomeie seu novo dispositivo no máximo 200 símbolos. + Nome do dispositivo muito longo + O nome do dispositivo não pode estar vazio + Nome do dispositivo + Ocultar + Você pode criar e visualizar o ID do dispositivo no cliente de telegrama usando o bot de bate-papo %1$s. %2$s + Se você deseja conectar vários dispositivos a uma conta de telegrama, é necessário usar um dispositivo diferente para compartilhar sua localização. + Última localização atualizada: + Enviado e atualizado com sucesso + Não é possível enviar para bate-papo do Telegram: + Aguardando resposta do Telegram + enviando localização + Iniciando + Posicionando… + Conectando-se à Internet + Altere as configurações de otimização da bateria para estabilizar o compartilhamento de local. + "Funcionamento em segundo plano " + Desative a otimização da bateria do OsmAnd Tracker para que não seja desligado repentinamente quando estiver em segundo plano. + Compartilhando em segundo plano + Vá para as configurações + Mais tarde + Ainda não enviado + Ainda não encontrado + Reenvie o local + Última localização disponível + Status de compartilhamento + Compartilhamento: Ativado + Status + Sem conexão GPS + Sem conexão com a internet + Desabilitar + Salvar + Adicionar dispositivo + Compartilhar localização como + Contatos e grupos compartilhando o local para você. + Tem certeza de que deseja sair do OsmAnd Tracker para que você não possa compartilhar a localização ou ver a localização de outras pessoas\? + Sair do OsmAnd Tracker\? + Nome + Pela distância + Por nome + Por grupo + Ordenar + Classificar por + Selecione a versão OsmAnd, onde os contatos serão exibidos no mapa. + Selecione a versão do OsmAnd para usar + Desativa o compartilhamento de local para todos os bate-papos selecionados (%1$d). + Desativar todo o compartilhamento + Desligue todos + Sair + atrás + Última resposta + Grupo + Conecte-se à Internet para efetuar a saída do Telegram corretamente. + Fechar + Para revogar o acesso ao compartilhamento de local. Abra o Telegram, vá para Configurações → Privacidade e Segurança → Sessões e termine a sessão do OsmAnd Telegram. + "Como desativar o compartilhamento de local do OsmAnd para Telegram" + "Como desativar o compartilhamento de local do OsmAnd para Telegram" + Conta conectada + Conta + no %1$s + Escolha a versão OsmAnd que OsmAnd Tracker usa para exibir posições. + "Conectar OsmAnd " + Ocultar contatos que não foram movidos em um determinado momento. + Histórico de localização + A última vez que um contato foi movido. + Parado + Defina o intervalo mínimo para o compartilhamento de local. + Enviar minha localização + Posição + Tempo de compartilhamento + Expira + O compartilhamento está ativado (desativado) + Desativar o compartilhamento de local + Abrir OsmAnd + Vivo + Robô + Registro no Telegram + Você precisa de uma conta do Telegram para usar o compartilhamento de local. + Por favor, instale o Telegram e configure uma conta. + Então você pode usar este aplicativo. + Todos + Desativado + Você precisa de uma conta e número de telefone registrados no Telegram + Eu não tenho conta Telegram + Digite o número de telefone + Digite o código de autenticação + Definir tempo visível para todos + %1$d h %2$d m + %1$d m + %1$d h + Instalar + Compartilhar + Voltar + Hora visível para todos + Defina a hora em que seus contatos e grupos selecionados verão sua localização em tempo real. + Definir tempo + Selecione os contatos e grupos com os quais você deseja compartilhar sua localização. + Pesquisa: Grupo ou contato + Compartilhar localização + Mostrar no mapa + OsmAnd Online GPS Tracker + Número de telefone + Número de telefone no formato internacional + Senha + Coloque o código + Código de Autenticação + Uma faixa GPX é salva automaticamente durante a navegação. + Digite a senha + Senha do telegrama + Entrar + Sair + Iniciando + Saindo + Fechando + Ativar \"Localização\"\? + você não está logado no + Continuar + Cancelar + Configurações + O aplicativo não tem permissão para acessar os dados de localização. + Por favor, ligue \"Localização\" nas configurações do sistema + Selecione um dos provedores de localização para compartilhar sua localização. + Modo em segundo plano + OsmAnd Tracker é executado em segundo plano com a tela desligada. + Distância + Compartilhar localização + Compartilhando localização + Serviço OsmAnd Tracker + Logotipo do OsmAnd + Você precisa instalar a versão gratuita ou paga do OsmAnd primeiro + Instalar OsmAnd + Mostrar usuários no mapa + Bate-papos ativos + Autorização + Por favor, insira o número de telefone do seu Telegram em formato internacional + Boas vindas + yd + ft + mi + km + m + mn + min/m + min/km + mn/h + m/s + km/h + mph + Quilômetros por hora + Milhas por hora + Metros por segundo + Minutos por quilômetro + Minutos por milha + Milhas náuticas por hora (nó) + Milhas/pés + Milhas/jardas + Quilômetros/metros + Milhas náuticas + Milhas/metros + h + min + seg + + Compartilhamento de local OsmAnd permite que você compartilhe sua localização e veja a dos outros no OsmAnd.
+
O aplicativo usa a API Telegram e você precisa de uma conta Telegram.
+ Minha localização + Ao vivo agora + Monitoramento está ativado + Monitoramento está desativado + tempo em movimento + Altitude média + Velocidade média + Aberto no OsmAnd + Data final + Data de início + "Linha do tempo " + enviado (%1$d em buffer) + "%1$d pontos " + Data + Coletado + Pontos de GPS + "Enviar " + Por favor, atualize o OsmAnd para ver os dados no mapa + "Atualizar " + Telegram + Telegrama (o aplicativo de mensagens) é usado para se conectar e se comunicar com as pessoas. + Telegram é uma plataforma aberta e OsmAnd Tracker é um dos clientes. Seus contatos podem usar qualquer outro cliente Telegram. + Ao clicar em continuar, você concorda com a Política de Privacidade da Telegram e a Política de Privacidade da OsmAnd. + Aceitar + Política de privacidade do Telegram + "Política de privacidade do OsmAnd " + Como funciona + Pontos GPX recebidos: %1$s + Aparência + Mostrar pontos de GPS + Mostrar quantidade de pontos de GPS coletados e enviados. +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-ru/strings.xml b/OsmAnd-telegram/res/values-ru/strings.xml index ce07a813e2..c8ba7cc3aa 100644 --- a/OsmAnd-telegram/res/values-ru/strings.xml +++ b/OsmAnd-telegram/res/values-ru/strings.xml @@ -1,5 +1,11 @@ + "Попробуйте функцию Хронология бесплатно. + Запись местоположений включена + Мы используем Telegram (messaging app), чтобы соединять людей и чтобы вам было проще общаться с ними. + Telegram открытая платформа и OsmAnd Tracker является одним из клиентов. Ваши контакты могут использовать любой другой Telegram client. + Нажимая продолжить, вы соглашаетесь с Telegram Privacy Policy и OsmAnd Privacy Policy. + Включите мониторинг, чтобы сохранять все местоположения в истории. Последнее обновление в Telegram Имя устройства Спрятать @@ -20,9 +26,9 @@ Пожалуйста, включите «Местоположение» в системных настройках Выберите один из провайдеров определения местоположения, чтобы поделиться своим местоположением. Работа в фоне - Отключите оптимизацию батареи для OsmAnd Telegram, чтобы оно не было внезапно отключено в фоновом режиме. + Отключите оптимизацию батареи для OsmAnd Tracker, чтобы оно не было внезапно отключено в фоновом режиме. Фоновый режим - OsmAnd Telegram работает в фоновом режиме с выключенным экраном. + OsmAnd Tracker работает в фоновом режиме с выключенным экраном. Выберите версию OsmAnd, в которой контакты будут отображаться на карте. Выберите версию OsmAnd для использования Остановить трансляцию своей позиции всем ранее выбранным чатам (%1$d). @@ -113,7 +119,7 @@ Скройте контакты, которые не обновили свое местоположение за определенный промежуток времени. Поделиться местоположением как Если вы хотите подключить несколько устройств к одной учетной записи Telegram, вам необходимо использовать другое устройство для трансляции местоположения. - Выберите версию OsmAnd которую OsmAnd Telegram использует для отображения позиций на карте. + Выберите версию OsmAnd которую OsmAnd Tracker использует для отображения позиций на карте. OsmAnd подключение Связанная учетная запись Учетная запись @@ -121,11 +127,11 @@ Вам необходимо сначала установить бесплатную или платную версию OsmAnd Как отключить передачу данных OsmAnd от Telegram Как отключить передачу данных OsmAnd от Telegram - Отмена доступа к отправке локаций. Откройте Telegram, перейдите в Настройки - Конфиденциальность и безопасность - Сессии и завершите сеанс OsmAnd Telegram. - Выйти из OsmAnd Telegram? + Отмена доступа к отправке локаций. Откройте Telegram, перейдите в Настройки - Конфиденциальность и безопасность - Сессии и завершите сеанс OsmAnd Tracker. + Выйти из OsmAnd Tracker? Войти Выйти - Вы уверены, что хотите выйти из OsmAnd Telegram, так вы не сможете делиться местоположением или не видеть местоположения других? + Вы уверены, что хотите выйти из OsmAnd Tracker, так вы не сможете делиться местоположением или не видеть местоположения других? Не найдено Отправить местоположение diff --git a/OsmAnd-telegram/res/values-sc/strings.xml b/OsmAnd-telegram/res/values-sc/strings.xml new file mode 100644 index 0000000000..092d35ab23 --- /dev/null +++ b/OsmAnd-telegram/res/values-sc/strings.xml @@ -0,0 +1,208 @@ + + + Artària mèdia + Lestresa mèdia + Aberi in OsmAnd + Data de fine + Data de incumintzu + Mapa + Testu + Mapa e testu + Annanghe + Nùmene de su dispositivu + Istichi + Prus a tardu + Istadu + Disabìlita + Sarva + Nùmene + Òrdina + Essi + Serra + Totus + Istudadu + Installa + Cumpartzi + In dae segus + Sighi + Annulla + Impostatziones + S’aplicatzione no est autorizada a atzèdere a sos datos de positzione. + Allughe sa \"positzione\" in sas impostatziones + Modalidade de isfundu + OsmAnd Tracker sighit a funtzionare in s’isfundu, cun s’ischermu mortu. + Distàntzia + Cumpartzi sa positzione + Installa OsmAnd + Tzarradas ativas + Su monitoràgiu est abilitadu + Su monitoràgiu est disabilitadu + tempus in movimentu + Imbia sa positzione comente + Issèbera comente sos messàgios cun sa positzione tua ant a aparire. + Ùrtimu agiornamentu de Telegram + Issèbera unu nùmene chi no as giai impreadu + %1$s annantu. + Impossìbile annànghere su dispositivu nou + Dae unu nùmene de non prus de 200 caràteres a su dispositivu nou tuo. + Nùmene de su dispositivu tropu longu + Su nùmene de su dispositivu non podet èssere bòidu + Podes creare e abbaidare s\'ID de su dispositivu in su cliente de Telegram impreende su bot pro sas tzarradas %1$s. %2$s + Si cheres cunnètere prus dispositivos a unu contu de Telegram, depes impreare unu dispositivu diferente pro cumpartzire sa positzione tua. + Ùrtima positzione agiornada: + Imbiadu e agiornadu cun sutzessu + Impossìbile imbiare a sas tzarradas de Telegram: + Isetende una risposta dae Telegram + Imbiende sa positzione + Incumintzende + Positzionende… + Collegamentu a ìnternet + Muda sas impostatziones de otimizatzione de sa bateria pro istabilizare sa cumpartzidura de sa positzione. + Modalidade de isfundu + Istuda s\'otimizatzione de sa bateria pro OsmAnd Tracker pro fàghere in modu chi non bèngiat serradu de repente cando est in s\'isfundu. + Cumpartzidura in s\'isfundu + Bae a sas impostatziones + Galu non imbiadu + Galu no agatadu + Torra a imbiare sa positzione + Ùrtima positzione disponìbile + Istadu de cumpartzidura + Cumpartzidura: Abilitada + Peruna connessione GPS + Peruna connessione a ìnternet + Annanghe unu dispositivu + Cumpartzi sa positzione comente + Cuntatos e grupos chi cumpartzent sa positzione cun tie. + Ses seguru de chèrrere essire dae OsmAnd Tracker pro non cumpartzire sa positzione tua o bìdere sa de àtere\? + Serrare sa sessione de OsmAnd Tracker\? + Pro distàntzia + Pro nùmene + Pro grupu + Òrdina pro + Ischerta sa versione de OsmAnd in ue sos cuntatos ant a èssere ammustrados in sa mapa. + Ischerta sa versione de OsmAnd de impreare + Istuda sa cumpartzidura de sa positzione pro totu sas tzarradas ischertadas (%1$d). + Istuda totu sas cumpartziduras + Istuda totu + a como + Ùrtima risposta + Grupu + Collega·ti a ìnternet pro essire in manera curreta dae Telegram. + Pro bogare s\'atzessu a sa cumpartzidura de sa positzione. Aberi Telegram, bae a Cunfiguratziones → Riservadesa e Seguresa → Sessiones ativas, e acaba sa sessione de OsmAnd Telegram. + Comente istudare sa cumpartzidura de sa positzione de OsmAnd dae Telegram + Comente istudare sa cumpartzidura de sa positzione de OsmAnd dae Telegram + Contu connessu + Contu + in %1$s + Issèbera sa versione de OsmAnd chi OsmAnd Tracker at a impreare pro ammustrare sas positziones. + Istichi sos cuntatos chi non si sunt mòvidos in unu perìodu de tempus determinadu. + Cronologia de sas positziones + S\'ùrtima borta chi unu cuntatu s\'est mòvidu. + Non in movimentu + Imposta s\'intervallu mìnimu pro cumpartzire sa positzione. + Imbia sa positzione mea + Positzione + Tempus de cumpartzidura + Iscadit a sas + Sa cumpartzidura est alluta (istuda) + Istuda sa cumpartzidura de sa positzione + Aberi OsmAnd + In direta + Bot + Registratzione in Telegram + Tenes bisòngiu de unu contu de Telegram pro impreare sa cumpartzidura de sa positzione. + Pro praghere installa Telegram e imposta unu contu. + A pustis as a pòdere impreare s\'aplicatzione. + Tenes bisòngiu de unu contu registradu de Telegram e de unu nùmeru de telèfono + Non tèngio unu contu de Telegram + Inserta unu nùmeru de telèfono + Inserta su còdighe de autenticatzione + Imposta un\'oràriu visìbile pro totus + %1$d h %2$d m + %1$d m + %1$d h + Oràriu visìbile pro totus + yd + ft + mi + km + m + nmi + min/m + min/km + nmi/h + m/s + km/h + mph + Chilòmetros a s\'ora + Mìglios a s\'ora + Metros a su segundu + Minutos a su chilòmetru + Minutos a su mìgliu + Mìglios nàuticos a s\'ora (knot) + Mìglios/pedes + Mìglios/iardas + Chilòmetros/metros + Mìglios nàuticos + Mìglios/metros + h + min + seg + Positzione mea + Lìnia temporale + Connessione OsmAnd + Imposta su tempus in su cale sos cuntatos e sos grupos chi as ischertadu ant a bìdere sa positzione tua in direta. + Imposta su tempus + Ischerta sos cuntatos e sos grupos cun sos cales cheres cumpartzire sa positzione. + Chirca: grupu o cuntatu + Cumpartzi sa positzione + Ammustra in sa mapa + OsmAnd Online GPS Tracker + Nùmeru de telèfono + Nùmeru de telèfono in formadu internatzionale + Crae de intrada + Inserta su còdighe + Còdighe de autenticatzione + Telegram t\'at imbiadu unu còdighe pro permìtere a OsmAnd de intrare in su contu tuo. + Inserta sa crae + Crae de Telegram + Intra + Essi + Incumintzende + Essende + Serrende + Allùghere sa \"Positzione\"\? + No as fatu s\'atzessu + Ischerta unu de sos frunidores de sa positzione pro la cumpartzire. + Cumpartzende sa positzione + Servìtziu de OsmAnd Tracker + Logo de OsmAnd + In antis depes installare sa versione de badas o a pagamentu de OsmAnd + Ammustra sos impreadores in sa mapa + Autorizatzione + Pro praghere inserta su nùmeru de telèfono tuo in formadu internatzionale + Bene bènnidu + Sa Cumpartzidura de sa Positzione de OsmAnd ti permitit de cumpartzire sa positzione tua e de bìdere cussa de sos àteros in OsmAnd.
+
S\'aplicatzione impreat s\'API de Telegram e tenes bisòngiu de unu contu de Telegram.
+ In direta como + imbiadu (%1$d in sa memòria tampone) + %1$d puntos + Data + Collidos + Puntos GPS + Imbiadu + Pro praghere agiorna OsmAnd pro bìdere sos datos in sa mapa + Agiorna + Telegram + Impreamus Telegram (s\'aplicatzione pro sos messàgios) pro cunnètere sa gente e fàghere in modu chi comunicare cun issos ti siat prus fàtzile. + Telegram est una prataforma aberta e OsmAnd Tracker est unu de sos comporadores. Sos cuntatos tuos podent impreare cale si siat aplicatzione cliente Telegram. + Incarchende \"sighi\" atzetas sa Polìtica de Riservadesa de Telegram e cussa de OsmAnd. + Atzeta + Polìtica de riservadesa de Telegram + Polìtica de riservadesa de OsmAnd + Comente funtzionat + Puntos GPX retzidos: %1$s + Aparèntzia + Ammustra sos puntos GPS + Ammustra sa cantidade de puntos GPS collidos e imbiados. +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values-sl/strings.xml b/OsmAnd-telegram/res/values-sl/strings.xml index 980ec22611..04b19258f8 100644 --- a/OsmAnd-telegram/res/values-sl/strings.xml +++ b/OsmAnd-telegram/res/values-sl/strings.xml @@ -32,7 +32,7 @@ Shrani Dodaj napravo Objavi trenutno mesto kot - Ali se želite odjaviti iz storitve OsmAnd Telegram\? + Ali se želite odjaviti iz storitve OsmAnd Tracker\? Ime Po razdalji Po imenu @@ -51,7 +51,7 @@ Račun v %1$s Pokaži na zemljevidu - OsmAnd Telegram + OsmAnd Online GPS Tracker Telefonska številka Telefonska številka v mednarodnem zapisu Geslo diff --git a/OsmAnd-telegram/res/values-uk/strings.xml b/OsmAnd-telegram/res/values-uk/strings.xml index 91c5164ce2..d531f8c29a 100644 --- a/OsmAnd-telegram/res/values-uk/strings.xml +++ b/OsmAnd-telegram/res/values-uk/strings.xml @@ -20,7 +20,7 @@ Зʼєднання із інтернетом Змініть налаштування оптимізації батареї, для стабільної трансляції позиціювання. Робота у фоні - Вимкніть оптимізацію батареї для OsmAnd Telegram, аби додаток не вимикався у фоновому режимі. + Вимкніть оптимізацію батареї для OsmAnd Tracker, аби додаток не вимикався у фоновому режимі. Трансляція у фоновому режимі Перейти до налаштувань Пізніше @@ -38,8 +38,8 @@ Додати пристрій Поділитися позицією як Контакти та групи, які транслюють свою позицію Вам. - Ви впевнені, що бажаєте вийти з OsmAnd Telegram\? Оскільки не зможете транслювати свою позицію чи бачити позицію інших. - Вийти з OsmAnd Telegram\? + Ви впевнені, що бажаєте вийти з OsmAnd Tracker\? Оскільки не зможете транслювати свою позицію чи бачити позицію інших. + Вийти з OsmAnd Tracker\? Імʼя За відстанню За імʼям @@ -63,7 +63,7 @@ Повʼязаний обліковий запис Обліковий запис через %1$s - Оберіть версію OsmAnd, яку OsmAnd Telegram буде використовувати для показу позицій на карті. + Оберіть версію OsmAnd, яку OsmAnd Tracker буде використовувати для показу позицій на карті. Підключення OsmAnd Приховайте контакти, які не рухалися на протязі визначеного проміжку часу. Історія позицій @@ -95,7 +95,7 @@ Пошук: Група або контакт Транслювати позицію Показати на карті - OsmAnd Telegram + OsmAnd Online GPS Tracker Номер телефону Номер телефону у міжнародному форматі Пароль @@ -117,7 +117,7 @@ Додаток не має дозволу до отримання даних позиціювання. Будь ласка, увімкніть «Позиціювання» у системних налаштуваннях Фоновий режим - OsmAnd працює у фоновому режимі з вимкненим екраном. + OsmAnd Tracker працює у фоновому режимі з вимкненим екраном. Відстань Поділитися позицією Трансляція позиції diff --git a/OsmAnd-telegram/res/values-zh-rTW/strings.xml b/OsmAnd-telegram/res/values-zh-rTW/strings.xml index a6b3daec93..1208daf5e4 100644 --- a/OsmAnd-telegram/res/values-zh-rTW/strings.xml +++ b/OsmAnd-telegram/res/values-zh-rTW/strings.xml @@ -1,2 +1,210 @@ - \ No newline at end of file + + 傳送位置為 + 選擇您所在位置怎樣的訊息外觀。 + 地圖 + 文字 + 地圖與文字 + %1$s 已新增。 + 新增 + 無法增加新的裝置 + 命名您的新裝置,最多 200 個字符。 + 裝置名稱太長 + 裝置名稱不能為空白 + 裝置名稱 + 隱藏 + 上次更新的位置: + 正在傳送位置 + 正在啟動 + 正在定位… + 正在連線至網路 + 背景工作 + 在背景分享 + 轉至設定 + 隨後 + 尚未傳送 + 尚未找到 + 重新傳送位置 + 最後可用的位置 + 分享狀態 + 分享:已啟用 + 狀態 + 沒有 GPS 連接 + 沒有網際網路連線 + 停用 + 儲存 + 新增裝置 + 分享位置為 + 名稱 + 按距離 + 按名稱 + 按群組 + 分類 + 排序方式 + 停用所有分享 + 關閉全部 + 離開 + Telegram 的最後一次更新 + 選擇一個尚未使用的名稱 + 您可以使用 %1$s 聊天機器人在電報用戶端中創建和查看裝置 ID。 %2$s + 如果要將多個設備連接到一個 Telegram 帳戶,則需要使用不同的設備來共用您的位置。 + 已成功發送和更新 + 無法發送到 Telegram 聊天: + 等待 Telegram 回復 + 更改電池最佳化設置以穩定位置共用。 + 關閉 OsmAnd Tracker 的電池最佳化,這樣在後臺時就不會突然關閉。 + 連絡人和群組共用位置給您。 + 你確定要登出 OsmAnd Tracker,這樣你就不能共用位置或看到其他人的位置嗎? + 要登出 OsmAnd Tracker 嗎\? + 選擇將在地圖上顯示連絡人的 OsmAnd 版本。 + 選擇要使用的 OsmAnd 版本 + 關閉所有選定聊天的位置共用(%1$d)。 + + 上次回應 + 群組 + 連接到網路以正確登出 Telegram。 + 關閉 + 撤銷位置共用訪問。打開 Telegram,進入設定→隱私和安全→會話,並終 OsmAnd Tracker 會話。 + 如何關閉 Telegram 中的 OsmAnd 交給位置共用 + 如何關閉 Telegram 中的 OsmAnd 交給位置共用 + 已連接帳戶 + 帳戶 + 在 %1$s + 選擇 OsmAnd 和版本的 OsmAnd Tracker 用於顯示位置。 + OsMand 連接 + 隱藏在給定時間內未移動的連絡人。 + 位置歷史記錄 + 上次移動連絡人的時間。 + 沒有移動 +\n + 設置位置共用的最小間隔。 + 發送我的位置 + 位置 + 共用時間 + 到期 + 共用已打開 (關閉) + 關閉位置共用 + 打開 OsmAnd + 即時 + 提供 + 註冊 Telegram + 您需要一個 Telegram 帳戶才能使用位置共用。 + 請安裝 Telegram 並建立帳戶。 + 然後您可以使用這個應用程式。 + 所有 + 關閉 + 你需要一個 Telegram 帳號和電話號碼 + 我沒有 Telegram 帳戶 + 添加電話號碼 + 輸入身份驗證程式碼 + 為所有人設定可見時間 + %1$d 時 %2$d 分 + %1$d 分 + %1$d 時 + 安裝 + 分享 + 返回 + 所有人的可見時間 + 設置所選連絡人和組即時查看您的位置的時間。 + 設定時間 + 選擇要共用位置的連絡人和群組。 + 搜尋:群組或連絡人 + 共用位置 + 在地圖上顯示 + OsmAnd Online GPS Tracker + 電話號碼 + 國際格式的電話號碼 + 密碼 + 輸入代碼 + 身份驗證代碼 + Telegram 給你發了一個代碼,要求 OsmAnd 登入你的帳戶。 + 輸入密碼 + Telegram 密碼 + 登入 + 登出 + 正在啟動 + 已登出 + 關閉 + 打開“位置”? + 您沒有登入 + 繼續 + 取消 + 設定 + 應用程式沒有存取位置數據的許可權。 + 請在系統設置中打開“位置” + 選擇一個位置提供商來共享您的位置。 + 背景模式 + 當螢幕關閉後,讓 OsmAnd 在背景運行。 + 距離 + 共用位置 + 共用位置 + OsmAnd Tracker 伺服器 + OsmAnd 圖示 + 您需要先安裝免費或付費版本的 OsmAnd + 安裝 OsMand + 在地圖上顯示用戶 + 主動聊天 + 授權 + 請以國際格式輸入 Telegram 電話號碼 + 歡迎 + + + + 公里 + 公尺 + + 分/公尺 + 分/公里 + 浬/時 + 公尺/秒 + 公里/小時 + 哩/小時 + 公里每小時 + 英里每小時 + 公尺每秒 + 分鐘每公里 + 分鐘每英里 + 浬每小時 (節) + 英哩/英呎 + 英哩/碼 + 公里/公尺 + 海浬 + 英里/米 + + + + + OsmAnd 位置共用可以讓您共用您的位置,並在 OsmAnd 中看到其他人的位置。
+
該應用程式使用電報 API,你需要一個 Telegram 帳戶。
+ 我的位置 + 即時狀況 + 監視已啟用 + 監視已停用 + 移動時間 + 平均海拔 + 平均速率 + 在 OsmAnd 中開啟 + 結束日期 + 開始日期 + 時間軸 + Telegram + Telegram(即時通訊應用程式)是用於與人們連線與溝通。 + Telegram 開放平臺,而 OsmAnd 是其中一個客戶。您的聯絡人可以使用其他任何 Telegram 客戶端。 + 點選繼續就代表您同意 Telegram 隱私權政策與 OsmAnd 隱私權政策。 + 接受 + Telegram 隱私權政策 + OsmAnd 隱私權政策 + 如何運作 + "已收到 GPX 點:%1$s " + 外觀 + 顯示 GPS 點 + 顯示蒐集到與發送的 GPS 點數量。 + 請更新 OsmAnd 以在地圖上檢視資料 + 更新 + 傳送(%1$d 在緩衝區中) + %1$d 點 + 日期 + 已蒐集 + GPS 點 + 傳送 +
\ No newline at end of file diff --git a/OsmAnd-telegram/res/values/colors.xml b/OsmAnd-telegram/res/values/colors.xml index a7ecf7ec06..b4752ded07 100644 --- a/OsmAnd-telegram/res/values/colors.xml +++ b/OsmAnd-telegram/res/values/colors.xml @@ -41,4 +41,6 @@ #ee5622 #78cc5c + #5959FF +
diff --git a/OsmAnd-telegram/res/values/dimens.xml b/OsmAnd-telegram/res/values/dimens.xml index 6b2139f3a5..f878e08672 100644 --- a/OsmAnd-telegram/res/values/dimens.xml +++ b/OsmAnd-telegram/res/values/dimens.xml @@ -24,6 +24,7 @@ 89dp 48dp + 42dp 56dp 48dp @@ -52,6 +53,9 @@ 32dp 60dp + 56dp + 50dp + 132dp 30dp @@ -79,7 +83,7 @@ 14sp 16sp - 12sp + 13sp 15sp diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index 3e134ade9b..8a86fe6f76 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,181 +1,215 @@ + - Send location as - Choose how messages with your location will look like. - Map - Text - Map and text - Last update from Telegram - Pick a name you haven\'t already used - %1$s added. - Add - Could not add new device - Name your new device in max. 200 symbols. - Device name too long - Device name cannot be empty - Device name - Hide - You can create and view the Device ID in the telegram client using the %1$s chat bot. %2$s - If you want to connect multiple devices to one telegram account, you need to use different Device to share your location. - Last updated location: - Successfully sent and updated - Not possible to send to Telegram chats: - Waiting for response from Telegram - Sending location - Starting - Positioning… - Connecting to the Internet - Change battery optimization settings to stabilize location sharing. - Background work - Turn off battery optimization for OsmAnd Telegram so that it isn\'t suddenly turned off when in the background. - Sharing in the background - Go to settings - Later - Not sent yet - Not found yet - Re-send location - Last available location - Sharing status - Sharing: Enabled - Status - No GPS connection - No internet connection - Disable - Save - Add device - Share location as - Contacts and groups sharing location to you. - Are you sure you want to log out of OsmAnd Telegram so you can\'t share location or see the location of others? - Log out of OsmAnd Telegram? - Name - By distance - By name - By group - Sort - Sort by - Select OsmAnd version where contacts will be displayed on the map. - Select OsmAnd version to use - Turns off location sharing to all selected chats (%1$d). - Disable all sharing - Turn off all - Exit - ago - Last response - Group - Connect to the Internet to properly log out of Telegram. - Close - To revoke location sharing access. Open Telegram, go to Settings → Privacy and Security → Sessions, and terminate the OsmAnd Telegram session. - How to turn off OsmAnd Location Sharing from Telegram - How to turn off OsmAnd Location Sharing from Telegram - Connected account - Account - in %1$s - Choose the OsmAnd version OsmAnd Telegram uses to display positions. - OsmAnd connect - Hide contacts that have not moved in a given time. - Location history - The last time a contact moved. - Not moving - Set the minimum interval for location sharing. - Send my location - Position - Sharing time - - Expires - Sharing is on (turn off) - Turn off location sharing - Open OsmAnd - Live - Bot - Registration in Telegram - You need a Telegram account to use location sharing. - Please install Telegram and set up an account. - Then you can use this app. - All - Off - You need a registered Telegram account and phone number - I don\'t have Telegram account - Enter phone number - Enter authentication code - Set visible time for all - %1$d h %2$d m - %1$d m - %1$d h - Install - Share - Back - Visible time for all - Set the time that your selected contacts and groups will see your location in real time. - Set time - Select the contacts and groups you want to share your location with. - Search: Group or contact - Share location - Show on map - OsmAnd Telegram - Phone number - Phone number in international format - Password - Enter code - Authentication code - Telegram has sent you a code for OsmAnd to log in on your account. - Enter password - Telegram password - Log in - Log out - Starting - Logging out - Closing - Turn on \"Location\"? - You are not logged in - Continue - Cancel - Settings - App lacks permission for location data access. - Please turn on \"Location\" in the system settings - Select one of the location providers to share your location. - Background mode - OsmAnd Telegram runs in the background with the screen off. - Distance - Share location - Sharing location - OsmAnd Telegram service - OsmAnd logo - You need to install the free or paid version of OsmAnd first - Install OsmAnd - Show users on the map - Active chats - Authorization - Please enter your Telegram phone number in international format - Welcome - - yd - ft - mi - km - m - nmi - min/m - min/km - nmi/h - m/s - km/h - mph - Kilometers per hour - Miles per hour - Meters per second - Minutes per kilometer - Minutes per mile - Nautical miles per hour (knot) - Miles/feet - Miles/yards - Kilometers/meters - Nautical miles - Miles/meters - h - min - sec - OsmAnd Location Sharing lets you share your location and see that of others in the OsmAnd.

The app uses Telegram API and you need a Telegram account.]]>
- My location - Live now - - -
+ OK + Timeline is a feature available now for free. + Disable monitoring + Location recording enabled + Enable monitoring to save all locations in history. + Online Tracker + OsmAnd Tracker + Telegram + Telegram (the messaging app) is used to to connect to and communicate with people. + Telegram open platform and OsmAnd Tracker is one of the customers. Your contacts can use any other Telegram client. + By clicking continue you agree to the Telegram Privacy Policy and the OsmAnd Privacy Policy. + Accept + Telegram Privacy Policy + OsmAnd Privacy Policy + How it works + Received GPX points: %1$s + Appearance + Show GPS points + Show quantity of collected and sent GPS points. + Please update OsmAnd to view data on the map + Update + sent (%1$d in buffer) + %1$d points + Date + Collected + GPS points + Sent + Monitoring is enabled + Monitoring is disabled + time on the move + Average altitude + Average speed + Open in OsmAnd + End date + Start date + Send location as + Choose how messages with your location will look like. + Map + Text + Map and text + Last update from Telegram + Pick a name you haven\'t already used + %1$s added. + Add + Could not add new device + Name your new device in max. 200 symbols. + Device name too long + Device name cannot be empty + Device name + Hide + You can create and view the Device ID in the telegram client using the %1$s chat bot. %2$s + If you want to connect multiple devices to one telegram account, you need to use different Device to share your location. + Last updated location: + Successfully sent and updated + Not possible to send to Telegram chats: + Waiting for response from Telegram + Sending location + Starting + Positioning… + Connecting to the Internet + Change battery optimization settings to stabilize location sharing. + Background work + Turn off battery optimization for OsmAnd Telegram so that it isn\'t suddenly turned off when in the background. + Sharing in the background + Go to settings + Later + Not sent yet + Not found yet + Re-send location + Last available location + Sharing status + Sharing: Enabled + Status + No GPS connection + No internet connection + Disable + Save + Add device + Share location as + Contacts and groups sharing location to you. + Are you sure you want to log out of OsmAnd Telegram so you can\'t share location or see the location of others? + Log out of OsmAnd Telegram? + Name + By distance + By name + By group + Sort + Sort by + Select OsmAnd version where contacts will be displayed on the map. + Select OsmAnd version to use + Turns off location sharing to all selected chats (%1$d). + Disable all sharing + Turn off all + Exit + ago + Last response + Group + Connect to the Internet to properly log out of Telegram. + Close + To revoke location sharing access. Open Telegram, go to Settings → Privacy and Security → Sessions, and terminate the OsmAnd Telegram session. + How to turn off OsmAnd Location Sharing from Telegram + How to turn off OsmAnd Location Sharing from Telegram + Connected account + Account + in %1$s + Choose the OsmAnd version OsmAnd Telegram uses to display positions. + OsmAnd connect + Hide contacts that have not moved in a given time. + Location history + The last time a contact moved. + Not moving + Set the minimum interval for location sharing. + Send my location + Position + Sharing time + + Expires + Sharing is on (turn off) + Turn off location sharing + Open OsmAnd + Live + Bot + Registration in Telegram + You need a Telegram account to use location sharing. + Please install Telegram and set up an account. + Then you can use this app. + All + Off + You need a registered Telegram account and phone number + I don\'t have Telegram account + Enter phone number + Enter authentication code + Set visible time for all + %1$d h %2$d m + %1$d m + %1$d h + Install + Share + Back + Visible time for all + Set the time that your selected contacts and groups will see your location in real time. + Set time + Select the contacts and groups you want to share your location with. + Search: Group or contact + Share location + Show on map + OsmAnd Telegram + Phone number + Phone number in international format + Password + Enter code + Authentication code + Telegram has sent you a code for OsmAnd to log in on your account. + Enter password + Telegram password + Log in + Log out + Starting + Logging out + Closing + Turn on \"Location\"? + You are not logged in + Continue + Cancel + Settings + App lacks permission for location data access. + Please turn on \"Location\" in the system settings + Select one of the location providers to share your location. + Background mode + OsmAnd Telegram runs in the background with the screen off. + Distance + Share location + Sharing location + OsmAnd Telegram service + OsmAnd logo + You need to install the free or paid version of OsmAnd first + Install OsmAnd + Show users on the map + Active chats + Authorization + Please enter your Telegram phone number in international format + Welcome + yd + ft + mi + km + m + nmi + min/m + min/km + nmi/h + m/s + km/h + mph + Kilometers per hour + Miles per hour + Meters per second + Minutes per kilometer + Minutes per mile + Nautical miles per hour (knot) + Miles/feet + Miles/yards + Kilometers/meters + Nautical miles + Miles/meters + h + min + sec + OsmAnd Location Sharing lets you share your location and see that of others in the OsmAnd.

The app uses Telegram API and you need a Telegram account.]]>
+ My location + Live now + Timeline + \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java index 26a8b13c67..f1d8143948 100644 --- a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java +++ b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java @@ -1,5 +1,7 @@ package net.osmand; +import android.util.Xml; + import org.apache.commons.logging.Log; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -147,10 +149,10 @@ public class PlatformUtil { } public static XmlPullParser newXMLPullParser() throws XmlPullParserException { - return new org.kxml2.io.KXmlParser(); + return Xml.newPullParser(); } public static XmlSerializer newSerializer() { - return new org.kxml2.io.KXmlSerializer(); + return Xml.newSerializer(); } } diff --git a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl index be010bb062..6f0cb8f65b 100644 --- a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl +++ b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl @@ -1,9 +1,14 @@ package net.osmand.aidl; import net.osmand.aidl.search.SearchResult; +import net.osmand.aidl.gpx.AGpxBitmap; interface IOsmAndAidlCallback { void onSearchComplete(in List resultSet); void onUpdate(); + + void onAppInitialized(); + + void onGpxBitmapCreated(in AGpxBitmap bitmap); } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl index d7ebb4492d..59fd213ab6 100644 --- a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl +++ b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl @@ -55,6 +55,9 @@ import net.osmand.aidl.maplayer.point.ShowMapPointParams; import net.osmand.aidl.navdrawer.SetNavDrawerItemsParams; +import net.osmand.aidl.navdrawer.NavDrawerFooterParams; +import net.osmand.aidl.navdrawer.NavDrawerHeaderParams; + import net.osmand.aidl.navigation.PauseNavigationParams; import net.osmand.aidl.navigation.ResumeNavigationParams; import net.osmand.aidl.navigation.StopNavigationParams; @@ -67,6 +70,16 @@ import net.osmand.aidl.search.SearchResult; import net.osmand.aidl.search.SearchParams; import net.osmand.aidl.navigation.NavigateSearchParams; +import net.osmand.aidl.customization.SetWidgetsParams; +import net.osmand.aidl.customization.OsmandSettingsParams; + +import net.osmand.aidl.gpx.AGpxFile; +import net.osmand.aidl.gpx.AGpxFileDetails; +import net.osmand.aidl.gpx.CreateGpxBitmapParams; +import net.osmand.aidl.tiles.ASqliteDbFile; + +import net.osmand.aidl.plugins.PluginParams; + // NOTE: Add new methods at the end of file!!! interface IOsmAndAidlInterface { @@ -133,4 +146,34 @@ interface IOsmAndAidlInterface { long registerForUpdates(in long updateTimeMS, IOsmAndAidlCallback callback); boolean unregisterFromUpdates(in long callbackId); + + boolean setNavDrawerLogo(in String imageUri); + + boolean setEnabledIds(in List ids); + boolean setDisabledIds(in List ids); + boolean setEnabledPatterns(in List patterns); + boolean setDisabledPatterns(in List patterns); + + boolean regWidgetVisibility(in SetWidgetsParams params); + boolean regWidgetAvailability(in SetWidgetsParams params); + + boolean customizeOsmandSettings(in OsmandSettingsParams params); + + boolean getImportedGpx(out List files); + + boolean getSqliteDbFiles(out List files); + boolean getActiveSqliteDbFiles(out List files); + boolean showSqliteDbFile(String fileName); + boolean hideSqliteDbFile(String fileName); + + boolean setNavDrawerLogoWithParams(in NavDrawerHeaderParams params); + boolean setNavDrawerFooterWithParams(in NavDrawerFooterParams params); + + boolean restoreOsmand(); + + boolean changePluginState(in PluginParams params); + + boolean registerForOsmandInitListener(in IOsmAndAidlCallback callback); + + boolean getBitmapForGpx(in CreateGpxBitmapParams file, IOsmAndAidlCallback callback); } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl new file mode 100644 index 0000000000..770070bb46 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.customization; + +parcelable OsmandSettingsParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java new file mode 100644 index 0000000000..bff8c68018 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java @@ -0,0 +1,60 @@ +package net.osmand.aidl.customization; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class OsmandSettingsParams implements Parcelable { + + private String sharedPreferencesName; + private Bundle bundle; + + public OsmandSettingsParams(@NonNull String sharedPreferencesName, @Nullable Bundle bundle) { + this.sharedPreferencesName = sharedPreferencesName; + this.bundle = bundle; + } + + public OsmandSettingsParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public OsmandSettingsParams createFromParcel(Parcel in) { + return new OsmandSettingsParams(in); + } + + @Override + public OsmandSettingsParams[] newArray(int size) { + return new OsmandSettingsParams[size]; + } + }; + + public String getSharedPreferencesName() { + return sharedPreferencesName; + } + + public Bundle getBundle() { + return bundle; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(sharedPreferencesName); + out.writeBundle(bundle); + } + + @SuppressLint("ParcelClassLoader") + private void readFromParcel(Parcel in) { + sharedPreferencesName = in.readString(); + bundle = in.readBundle(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl new file mode 100644 index 0000000000..235a4abe51 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.customization; + +parcelable SetWidgetsParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java new file mode 100644 index 0000000000..d9343b920e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java @@ -0,0 +1,93 @@ +package net.osmand.aidl.customization; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class SetWidgetsParams implements Parcelable { + + private String widgetKey; + private List appModesKeys; + + public SetWidgetsParams(String widgetKey, @Nullable List appModesKeys) { + this.widgetKey = widgetKey; + this.appModesKeys = appModesKeys; + } + + public SetWidgetsParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SetWidgetsParams createFromParcel(Parcel in) { + return new SetWidgetsParams(in); + } + + @Override + public SetWidgetsParams[] newArray(int size) { + return new SetWidgetsParams[size]; + } + }; + + public String getWidgetKey() { + return widgetKey; + } + + public List getAppModesKeys() { + return appModesKeys; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(widgetKey); + writeStringList(out, appModesKeys); + } + + private void readFromParcel(Parcel in) { + widgetKey = in.readString(); + appModesKeys = readStringList(in); + } + + @Override + public int describeContents() { + return 0; + } + + private void writeStringList(Parcel out, List val) { + if (val == null) { + out.writeInt(-1); + return; + } + int N = val.size(); + int i = 0; + out.writeInt(N); + while (i < N) { + out.writeString(val.get(i)); + i++; + } + } + + private List readStringList(Parcel in) { + List list = new ArrayList<>(); + int M = list.size(); + int N = in.readInt(); + if (N == -1) { + return null; + } + int i = 0; + for (; i < M && i < N; i++) { + list.set(i, in.readString()); + } + for (; i < N; i++) { + list.add(in.readString()); + } + for (; i < M; i++) { + list.remove(N); + } + return list; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl new file mode 100644 index 0000000000..128f5e6b94 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl @@ -0,0 +1,4 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxBitmap; + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java new file mode 100644 index 0000000000..2f423e126e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java @@ -0,0 +1,50 @@ +package net.osmand.aidl.gpx; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public class AGpxBitmap implements Parcelable { + + private Bitmap bitmap; + + public AGpxBitmap(@NonNull Bitmap bitmap) { + this.bitmap = bitmap; + } + + public AGpxBitmap(Parcel in) { + readFromParcel(in); + } + + public Bitmap getBitmap() { + return bitmap; + } + + public void setBitmap(Bitmap bitmap) { + this.bitmap = bitmap; + } + + public static final Creator CREATOR = new + Creator() { + public AGpxBitmap createFromParcel(Parcel in) { + return new AGpxBitmap(in); + } + + public AGpxBitmap[] newArray(int size) { + return new AGpxBitmap[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(bitmap, flags); + } + + private void readFromParcel(Parcel in) { + bitmap = in.readParcelable(Bitmap.class.getClassLoader()); + } + + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl new file mode 100644 index 0000000000..413d34a571 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxFile; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java new file mode 100644 index 0000000000..9572057fdc --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java @@ -0,0 +1,89 @@ +package net.osmand.aidl.gpx; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class AGpxFile implements Parcelable { + + private String fileName; + private long modifiedTime; + private long fileSize; + private boolean active; + private AGpxFileDetails details; + + public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, @Nullable AGpxFileDetails details) { + this.fileName = fileName; + this.modifiedTime = modifiedTime; + this.fileSize = fileSize; + this.active = active; + this.details = details; + } + + public AGpxFile(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public AGpxFile createFromParcel(Parcel in) { + return new AGpxFile(in); + } + + public AGpxFile[] newArray(int size) { + return new AGpxFile[size]; + } + }; + + public String getFileName() { + return fileName; + } + + public long getModifiedTime() { + return modifiedTime; + } + + public long getFileSize() { + return fileSize; + } + + public boolean isActive() { + return active; + } + + public AGpxFileDetails getDetails() { + return details; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(fileName); + out.writeLong(modifiedTime); + out.writeLong(fileSize); + out.writeByte((byte) (active ? 1 : 0)); + + out.writeByte((byte) (details != null ? 1 : 0)); + if (details != null) { + out.writeParcelable(details, flags); + } + } + + private void readFromParcel(Parcel in) { + fileName = in.readString(); + modifiedTime = in.readLong(); + fileSize = in.readLong(); + active = in.readByte() != 0; + + boolean hasDetails= in.readByte() != 0; + if (hasDetails) { + details = in.readParcelable(AGpxFileDetails.class.getClassLoader()); + } else { + details = null; + } + } + + public int describeContents() { + return 0; + } +} + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl new file mode 100644 index 0000000000..1e2cdec2b1 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxFileDetails; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java new file mode 100644 index 0000000000..3fe755135e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java @@ -0,0 +1,196 @@ +package net.osmand.aidl.gpx; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class AGpxFileDetails implements Parcelable { + + private float totalDistance = 0; + private int totalTracks = 0; + private long startTime = Long.MAX_VALUE; + private long endTime = Long.MIN_VALUE; + private long timeSpan = 0; + private long timeMoving = 0; + private float totalDistanceMoving = 0; + + private double diffElevationUp = 0; + private double diffElevationDown = 0; + private double avgElevation = 0; + private double minElevation = 99999; + private double maxElevation = -100; + + private float minSpeed = Float.MAX_VALUE; + private float maxSpeed = 0; + private float avgSpeed; + + private int points; + private int wptPoints = 0; + + private List wptCategoryNames = new ArrayList<>(); + + public AGpxFileDetails(float totalDistance, int totalTracks, + long startTime, long endTime, + long timeSpan, long timeMoving, float totalDistanceMoving, + double diffElevationUp, double diffElevationDown, + double avgElevation, double minElevation, double maxElevation, + float minSpeed, float maxSpeed, float avgSpeed, + int points, int wptPoints, Set wptCategoryNames) { + this.totalDistance = totalDistance; + this.totalTracks = totalTracks; + this.startTime = startTime; + this.endTime = endTime; + this.timeSpan = timeSpan; + this.timeMoving = timeMoving; + this.totalDistanceMoving = totalDistanceMoving; + this.diffElevationUp = diffElevationUp; + this.diffElevationDown = diffElevationDown; + this.avgElevation = avgElevation; + this.minElevation = minElevation; + this.maxElevation = maxElevation; + this.minSpeed = minSpeed; + this.maxSpeed = maxSpeed; + this.avgSpeed = avgSpeed; + this.points = points; + this.wptPoints = wptPoints; + if (wptCategoryNames != null) { + this.wptCategoryNames = new ArrayList<>(wptCategoryNames); + } + } + + public AGpxFileDetails(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public AGpxFileDetails createFromParcel(Parcel in) { + return new AGpxFileDetails(in); + } + + public AGpxFileDetails[] newArray(int size) { + return new AGpxFileDetails[size]; + } + }; + + public float getTotalDistance() { + return totalDistance; + } + + public int getTotalTracks() { + return totalTracks; + } + + public long getStartTime() { + return startTime; + } + + public long getEndTime() { + return endTime; + } + + public long getTimeSpan() { + return timeSpan; + } + + public long getTimeMoving() { + return timeMoving; + } + + public float getTotalDistanceMoving() { + return totalDistanceMoving; + } + + public double getDiffElevationUp() { + return diffElevationUp; + } + + public double getDiffElevationDown() { + return diffElevationDown; + } + + public double getAvgElevation() { + return avgElevation; + } + + public double getMinElevation() { + return minElevation; + } + + public double getMaxElevation() { + return maxElevation; + } + + public float getMinSpeed() { + return minSpeed; + } + + public float getMaxSpeed() { + return maxSpeed; + } + + public float getAvgSpeed() { + return avgSpeed; + } + + public int getPoints() { + return points; + } + + public int getWptPoints() { + return wptPoints; + } + + public List getWptCategoryNames() { + return wptCategoryNames; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeFloat(totalDistance); + out.writeInt(totalTracks); + out.writeLong(startTime); + out.writeLong(endTime); + out.writeLong(timeSpan); + out.writeLong(timeMoving); + out.writeFloat(totalDistanceMoving); + out.writeDouble(diffElevationUp); + out.writeDouble(diffElevationDown); + out.writeDouble(avgElevation); + out.writeDouble(minElevation); + out.writeDouble(maxElevation); + out.writeFloat(minSpeed); + out.writeFloat(maxSpeed); + out.writeFloat(avgSpeed); + out.writeInt(points); + out.writeInt(wptPoints); + out.writeStringList(wptCategoryNames); + } + + private void readFromParcel(Parcel in) { + totalDistance = in.readFloat(); + totalTracks = in.readInt(); + startTime = in.readLong(); + endTime = in.readLong(); + timeSpan = in.readLong(); + timeMoving = in.readLong(); + totalDistanceMoving = in.readFloat(); + diffElevationUp = in.readDouble(); + diffElevationDown = in.readDouble(); + avgElevation = in.readDouble(); + minElevation = in.readDouble(); + maxElevation = in.readDouble(); + minSpeed = in.readFloat(); + maxSpeed = in.readFloat(); + avgSpeed = in.readFloat(); + points = in.readInt(); + wptPoints = in.readInt(); + in.readStringList(wptCategoryNames); + } + + public int describeContents() { + return 0; + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl new file mode 100644 index 0000000000..b02d5c6e70 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable CreateGpxBitmapParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java new file mode 100644 index 0000000000..b6af1d354c --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java @@ -0,0 +1,101 @@ +package net.osmand.aidl.gpx; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; + +public class CreateGpxBitmapParams implements Parcelable { + + private File gpxFile; + private Uri gpxUri; + private float density; + private int widthPixels; + private int heightPixels; + private int color; //ARGB color int + + public CreateGpxBitmapParams(File gpxFile, float density, int widthPixels, int heightPixels, int color) { + this.gpxFile = gpxFile; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Uri gpxUri, float density, int widthPixels, int heightPixels, int color) { + this.gpxUri = gpxUri; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public CreateGpxBitmapParams createFromParcel(Parcel in) { + return new CreateGpxBitmapParams(in); + } + + public CreateGpxBitmapParams[] newArray(int size) { + return new CreateGpxBitmapParams[size]; + } + }; + + public File getGpxFile() { + return gpxFile; + } + + public Uri getGpxUri() { + return gpxUri; + } + + public int getWidthPixels() { + return widthPixels; + } + + public int getHeightPixels() { + return heightPixels; + } + + public float getDensity() { + return density; + } + + public int getColor() { + return color; + } + + public void writeToParcel(Parcel out, int flags) { + if (gpxFile != null) { + out.writeString(gpxFile.getAbsolutePath()); + } else { + out.writeString(null); + } + out.writeParcelable(gpxUri, flags); + out.writeFloat(density); + out.writeInt(widthPixels); + out.writeInt(heightPixels); + out.writeInt(color); + } + + private void readFromParcel(Parcel in) { + String gpxAbsolutePath = in.readString(); + if (gpxAbsolutePath != null) { + gpxFile = new File(gpxAbsolutePath); + } + gpxUri = in.readParcelable(Uri.class.getClassLoader()); + density = in.readFloat(); + widthPixels = in.readInt(); + heightPixels = in.readInt(); + color = in.readInt(); + } + + public int describeContents() { + return 0; + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl new file mode 100644 index 0000000000..fc1271a8ca --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.navdrawer; + +parcelable NavDrawerFooterParams; diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java new file mode 100644 index 0000000000..9bfd70193f --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java @@ -0,0 +1,68 @@ +package net.osmand.aidl.navdrawer; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class NavDrawerFooterParams implements Parcelable { + + @NonNull + private String packageName; + @Nullable + private String intent; + @Nullable + private String appName; + + @NonNull + public String getPackageName() { + return packageName; + } + + @Nullable + public String getIntent() { + return intent; + } + + @Nullable + public String getAppName() { + return appName; + } + + public NavDrawerFooterParams(@NonNull String packageName, @Nullable String intent, + @Nullable String appName) { + this.packageName = packageName; + this.intent = intent; + this.appName = appName; + } + + protected NavDrawerFooterParams(Parcel in) { + packageName = in.readString(); + intent = in.readString(); + appName = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeString(intent); + dest.writeString(appName); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NavDrawerFooterParams createFromParcel(Parcel in) { + return new NavDrawerFooterParams(in); + } + + @Override + public NavDrawerFooterParams[] newArray(int size) { + return new NavDrawerFooterParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl new file mode 100644 index 0000000000..c230824841 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.navdrawer; + +parcelable NavDrawerHeaderParams; diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java new file mode 100644 index 0000000000..c88950fd10 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java @@ -0,0 +1,68 @@ +package net.osmand.aidl.navdrawer; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class NavDrawerHeaderParams implements Parcelable { + + @NonNull + private String imageUri; + @NonNull + private String packageName; + @Nullable + private String intent; + + @NonNull + public String getImageUri() { + return imageUri; + } + + @NonNull + public String getPackageName() { + return packageName; + } + + @Nullable + public String getIntent() { + return intent; + } + + public NavDrawerHeaderParams(@NonNull String imageUri, @NonNull String packageName, + @Nullable String intent) { + this.imageUri = imageUri; + this.packageName = packageName; + this.intent = intent; + } + + public NavDrawerHeaderParams(Parcel in) { + imageUri = in.readString(); + packageName = in.readString(); + intent = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(imageUri); + dest.writeString(packageName); + dest.writeString(intent); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NavDrawerHeaderParams createFromParcel(Parcel in) { + return new NavDrawerHeaderParams(in); + } + + @Override + public NavDrawerHeaderParams[] newArray(int size) { + return new NavDrawerHeaderParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl new file mode 100644 index 0000000000..beff693f5a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl @@ -0,0 +1,5 @@ +// PluginParams.aidl +package net.osmand.aidl.plugins; + +parcelable PluginParams; + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java new file mode 100644 index 0000000000..028bd8676a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java @@ -0,0 +1,51 @@ +package net.osmand.aidl.plugins; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PluginParams implements Parcelable { + + private String pluginId; + private int newState; //0- off, 1 - on + + public PluginParams(String pluginId, int newState) { + this.pluginId = pluginId; + this.newState = newState; + } + + public String getPluginId() { + return pluginId; + } + + public int getNewState() { + return newState; + } + + protected PluginParams(Parcel in) { + pluginId = in.readString(); + newState = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(pluginId); + dest.writeInt(newState); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public PluginParams createFromParcel(Parcel in) { + return new PluginParams(in); + } + + @Override + public PluginParams[] newArray(int size) { + return new PluginParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl new file mode 100644 index 0000000000..319cfd5035 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.tiles; + +parcelable ASqliteDbFile; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java new file mode 100644 index 0000000000..ac6f5e55d2 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java @@ -0,0 +1,69 @@ +package net.osmand.aidl.tiles; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public class ASqliteDbFile implements Parcelable { + + private String fileName; + private long modifiedTime; + private long fileSize; + private boolean active; + + public ASqliteDbFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active) { + this.fileName = fileName; + this.modifiedTime = modifiedTime; + this.fileSize = fileSize; + this.active = active; + } + + public ASqliteDbFile(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public ASqliteDbFile createFromParcel(Parcel in) { + return new ASqliteDbFile(in); + } + + public ASqliteDbFile[] newArray(int size) { + return new ASqliteDbFile[size]; + } + }; + + public String getFileName() { + return fileName; + } + + public long getModifiedTime() { + return modifiedTime; + } + + public long getFileSize() { + return fileSize; + } + + public boolean isActive() { + return active; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(fileName); + out.writeLong(modifiedTime); + out.writeLong(fileSize); + out.writeByte((byte) (active ? 1 : 0)); + } + + private void readFromParcel(Parcel in) { + fileName = in.readString(); + modifiedTime = in.readLong(); + fileSize = in.readLong(); + active = in.readByte() != 0; + } + + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt index 2addc53573..eaa9c5bf88 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt @@ -24,8 +24,7 @@ class TelegramApplication : Application(), OsmandHelperListener { lateinit var notificationHelper: NotificationHelper private set lateinit var osmandAidlHelper: OsmandAidlHelper private set lateinit var locationProvider: TelegramLocationProvider private set - lateinit var messagesDbHelper: MessagesDbHelper private set - lateinit var savingTracksDbHelper: SavingTracksDbHelper private set + lateinit var locationMessages: LocationMessages private set var telegramService: TelegramService? = null @@ -48,7 +47,7 @@ class TelegramApplication : Application(), OsmandHelperListener { if (connected) { osmandAidlHelper.setNavDrawerItems( applicationContext.packageName, - listOf(getString(R.string.app_name)), + listOf(getString(R.string.app_name_short_online)), listOf("osmand_telegram://main_activity"), listOf("ic_action_location_sharing_app"), listOf(-1) @@ -68,12 +67,14 @@ class TelegramApplication : Application(), OsmandHelperListener { showLocationHelper = ShowLocationHelper(this) notificationHelper = NotificationHelper(this) locationProvider = TelegramLocationProvider(this) - messagesDbHelper = MessagesDbHelper(this) - savingTracksDbHelper = SavingTracksDbHelper(this) + locationMessages = LocationMessages(this) if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) { shareLocationHelper.startSharingLocation() } + if (settings.monitoringEnabled) { + showLocationHelper.startShowingLocation() + } } fun cleanupResources() { @@ -87,6 +88,15 @@ class TelegramApplication : Application(), OsmandHelperListener { telegramHelper.stopSendingLiveLocationMessages(settings.getChatsShareInfo()) } + fun stopMonitoring() { + settings.monitoringEnabled = false + stopUserLocationService() + } + + fun isAnyOsmAndInstalled() = TelegramSettings.AppConnect.getInstalledApps(this).isNotEmpty() + + fun isOsmAndChosen() = settings.appToConnectPackage.isNotEmpty() + fun isOsmAndInstalled() = AndroidUtils.isAppInstalled(this, settings.appToConnectPackage) val isWifiConnected: Boolean @@ -96,6 +106,13 @@ class TelegramApplication : Application(), OsmandHelperListener { return ni != null && ni.type == ConnectivityManager.TYPE_WIFI } + val isMobileConnected: Boolean + get() { + val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val ni = mgr.activeNetworkInfo + return ni != null && ni.type == ConnectivityManager.TYPE_MOBILE + } + private val isInternetConnected: Boolean get() { val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt index 18bf39723a..d0f283df01 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramLocationProvider.kt @@ -187,7 +187,11 @@ class TelegramLocationProvider(private val app: TelegramApplication) : SensorEve @SuppressLint("MissingPermission") override fun onGpsStatusChanged(event: Int) { - gpsStatus = service.getGpsStatus(gpsStatus) + try { + gpsStatus = service.getGpsStatus(gpsStatus) + } catch (e: Exception) { + e.printStackTrace() + } updateGPSInfo(gpsStatus) updateLocation(lastKnownLocation) } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt index 4b90dd5a93..904aa83b7e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt @@ -13,8 +13,8 @@ import android.os.* import android.util.Log import android.widget.Toast import net.osmand.PlatformUtil -import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener +import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener import net.osmand.telegram.notifications.TelegramNotification.NotificationType import net.osmand.telegram.utils.AndroidUtils import org.drinkless.td.libcore.telegram.TdApi @@ -123,6 +123,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis app.telegramHelper.stopLiveMessagesUpdates() app.telegramHelper.removeIncomingMessagesListener(this) app.telegramHelper.removeOutgoingMessagesListener(this) + app.settings.save() app.telegramService = null mHandlerThread.quit() @@ -202,13 +203,14 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis providers.add(0, providers.removeAt(passiveFirst)) } // find location + var location: net.osmand.Location? = null for (provider in providers) { - val location = convertLocation(service.getLastKnownLocation(provider)) - if (location != null) { - return location + val loc = convertLocation(service.getLastKnownLocation(provider)) + if (loc != null && (location == null || loc.hasAccuracy() && loc.accuracy < location.accuracy)) { + location = loc } } - return null + return location } private fun setupAlarm() { @@ -274,6 +276,11 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) { app().showLocationHelper.startShowMessagesTask(chatId, *messages) + messages.forEach { + if (!it.isOutgoing) { + app().locationMessages.addNewLocationMessage(it) + } + } } override fun onDeleteChatLocationMessages(chatId: Long, messages: List) { @@ -287,6 +294,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis override fun onUpdateMessages(messages: List) { messages.forEach { app().settings.updateShareInfo(it) + app().shareLocationHelper.checkAndSendBufferMessagesToChat(it.chatId) + if (it.sendingState == null && !it.isOutgoing && (it.content is TdApi.MessageLocation || it.content is TdApi.MessageText)) { + app().locationMessages.addNewLocationMessage(it) + } } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index a08e5763ee..bbe431bbf8 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -7,6 +7,7 @@ import android.support.annotation.DrawableRes import android.support.annotation.StringRes import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan +import net.osmand.PlatformUtil import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.utils.AndroidUtils @@ -14,6 +15,7 @@ import net.osmand.telegram.utils.OsmandApiUtils import net.osmand.telegram.utils.OsmandFormatter import net.osmand.telegram.utils.OsmandFormatter.MetricsConstants import net.osmand.telegram.utils.OsmandFormatter.SpeedConstants +import net.osmand.telegram.utils.OsmandLocationUtils import org.drinkless.td.libcore.telegram.TdApi import org.json.JSONArray import org.json.JSONException @@ -79,12 +81,19 @@ private const val SHARE_CHATS_INFO_KEY = "share_chats_info" private const val BATTERY_OPTIMISATION_ASKED = "battery_optimisation_asked" +private const val MONITORING_ENABLED = "monitoring_enabled" + +private const val SHOW_GPS_POINTS = "show_gps_points" + private const val SHARING_INITIALIZATION_TIME = 60 * 2L // 2 minutes +private const val WAITING_TDLIB_TIME = 30 // 2 seconds private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes class TelegramSettings(private val app: TelegramApplication) { + private val log = PlatformUtil.getLog(TelegramSettings::class.java) + private var shareChatsInfo = ConcurrentHashMap() private var hiddenOnMapChats: Set = emptySet() private var shareDevices: Set = emptySet() @@ -111,6 +120,10 @@ class TelegramSettings(private val app: TelegramApplication) { var batteryOptimisationAsked = false + var monitoringEnabled = false + + var showGpsPoints = false + init { updatePrefs() read() @@ -181,12 +194,37 @@ class TelegramSettings(private val app: TelegramApplication) { fun updateCurrentSharingMode(sharingMode: String) { if (currentSharingMode != sharingMode) { shareChatsInfo.forEach { (_, shareInfo) -> - shareInfo.shouldSendViaBotMessage = true + shareInfo.shouldSendViaBotTextMessage = true + shareInfo.shouldSendViaBotMapMessage = true } + prepareForSharingNewMessages() } currentSharingMode = sharingMode } + fun prepareForSharingNewMessages() { + shareChatsInfo.forEach { (_, shareInfo) -> + prepareForSharingNewMessages(shareInfo) + } + } + + fun prepareForSharingNewMessages(chatsIds: List) { + chatsIds.forEach { + shareChatsInfo[it]?.also { shareInfo -> + prepareForSharingNewMessages(shareInfo) + } + } + } + + fun prepareForSharingNewMessages(shareInfo: ShareChatInfo) { + shareInfo.pendingTdLibText = 0 + shareInfo.pendingTdLibMap = 0 + shareInfo.currentTextMessageId = -1L + shareInfo.currentMapMessageId = -1L + shareInfo.pendingTextMessage = false + shareInfo.pendingMapMessage = false + } + fun getChatLivePeriod(chatId: Long) = shareChatsInfo[chatId]?.livePeriod fun getChatsShareInfo() = shareChatsInfo @@ -204,7 +242,11 @@ class TelegramSettings(private val app: TelegramApplication) { fun getCurrentSharingDevice() = shareDevices.singleOrNull { it.externalId == currentSharingMode } - fun getLastSuccessfulSendTime() = shareChatsInfo.values.maxBy { it.lastSuccessfulSendTimeMs }?.lastSuccessfulSendTimeMs ?: -1 + fun getLastSuccessfulSendTime(): Long { + val lastSuccessTextSend = shareChatsInfo.values.maxBy { it.lastTextSuccessfulSendTime }?.lastTextSuccessfulSendTime ?: -1 + val lastSuccessMapSend = shareChatsInfo.values.maxBy { it.lastMapSuccessfulSendTime }?.lastMapSuccessfulSendTime ?: -1 + return Math.max(lastSuccessTextSend, lastSuccessMapSend) + } fun stopSharingLocationToChats() { shareChatsInfo.clear() @@ -238,21 +280,71 @@ class TelegramSettings(private val app: TelegramApplication) { } fun updateShareInfo(message: TdApi.Message) { - val shareChatInfo = shareChatsInfo[message.chatId] + val shareInfo = shareChatsInfo[message.chatId] val content = message.content - if (shareChatInfo != null) { + val isOsmAndBot = app.telegramHelper.isOsmAndBot(OsmandLocationUtils.getSenderMessageId(message)) || app.telegramHelper.isOsmAndBot(message.viaBotUserId) + if (shareInfo != null) { when (content) { is TdApi.MessageLocation -> { - shareChatInfo.currentMapMessageId = message.id - shareChatInfo.pendingMapMessage = false + val state = message.sendingState + if (state != null) { + if (state.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR) { + shareInfo.pendingMapMessage = true + log.debug("updateShareInfo MAP ${message.id} MessageSendingStatePending") + shareInfo.oldMapMessageId = message.id + if (isOsmAndBot) { + shareInfo.shouldSendViaBotMapMessage = false + } + } else if (state.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) { + shareInfo.hasSharingError = true + shareInfo.pendingMapMessage = false + log.debug("updateShareInfo MAP ${message.id} MessageSendingStateFailed") + } + } else { + shareInfo.currentMapMessageId = message.id + shareInfo.pendingMapMessage = false + shareInfo.lastMapSuccessfulSendTime = System.currentTimeMillis() / 1000 + if (!isOsmAndBot) { + shareInfo.pendingTdLibMap-- + if (shareTypeValue == SHARE_TYPE_MAP) { + shareInfo.sentMessages++ + } + } else { + shareInfo.shouldSendViaBotMapMessage = false + } + log.debug("updateShareInfo MAP ${message.id} SUCCESS pendingTdLibMap: ${shareInfo.pendingTdLibMap}") + } } is TdApi.MessageText -> { - shareChatInfo.currentTextMessageId = message.id - shareChatInfo.updateTextMessageId++ - shareChatInfo.pendingTextMessage = false + val state = message.sendingState + if (state != null) { + if (state.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR) { + log.debug("updateShareInfo TEXT ${message.id} MessageSendingStatePending") + shareInfo.pendingTextMessage = true + shareInfo.oldTextMessageId = message.id + if (isOsmAndBot) { + shareInfo.shouldSendViaBotTextMessage = false + } + } else if (state.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) { + log.debug("updateShareInfo TEXT ${message.id} MessageSendingStateFailed") + shareInfo.hasSharingError = true + shareInfo.pendingTextMessage = false + } + } else { + shareInfo.currentTextMessageId = message.id + shareInfo.updateTextMessageId++ + shareInfo.pendingTextMessage = false + shareInfo.lastTextSuccessfulSendTime = System.currentTimeMillis() / 1000 + if (!isOsmAndBot) { + shareInfo.pendingTdLibText-- + shareInfo.sentMessages++ + } else { + shareInfo.shouldSendViaBotTextMessage = false + } + log.debug("updateShareInfo TEXT ${message.id} SUCCESS pendingTdLibMap: ${shareInfo.pendingTdLibText}") + } } } - shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L } } @@ -267,7 +359,7 @@ class TelegramSettings(private val app: TelegramApplication) { lastSharingStatus.apply { statusChangeTime = newSharingStatus.statusChangeTime locationTime = newSharingStatus.locationTime - chatsTitles = newSharingStatus.chatsTitles + chatsIds = newSharingStatus.chatsIds title = newSharingStatus.title if (statusType == SharingStatusType.INITIALIZING @@ -297,7 +389,8 @@ class TelegramSettings(private val app: TelegramApplication) { val currentTime = System.currentTimeMillis() / 1000 val user = app.telegramHelper.getCurrentUser() if (user != null && currentSharingMode != user.id.toString() && shareChatInfo.start == -1L) { - shareChatInfo.shouldSendViaBotMessage = true + shareChatInfo.shouldSendViaBotTextMessage = true + shareChatInfo.shouldSendViaBotMapMessage = true } shareChatInfo.start = currentTime @@ -318,7 +411,7 @@ class TelegramSettings(private val app: TelegramApplication) { if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) { val loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER) val gpsActive = loc != null && ((statusChangeTime - loc.time) / 1000) < GPS_UPDATE_EXPIRED_TIME - val lastSentLocationExpired = ((statusChangeTime - app.shareLocationHelper.lastLocationMessageSentTime) / 1000) > GPS_UPDATE_EXPIRED_TIME + val lastSentLocationExpired = ((statusChangeTime - app.shareLocationHelper.lastLocationUpdateTime) / 1000) > GPS_UPDATE_EXPIRED_TIME (gpsActive || !lastSentLocationExpired) } else { false @@ -331,16 +424,16 @@ class TelegramSettings(private val app: TelegramApplication) { var sendChatsErrors = false shareChatsInfo.forEach { (_, shareInfo) -> - if (shareInfo.lastSuccessfulSendTimeMs == -1L && ((statusChangeTime / 1000 - shareInfo.start) < SHARING_INITIALIZATION_TIME)) { + if (shareInfo.lastTextSuccessfulSendTime == -1L && shareInfo.lastMapSuccessfulSendTime == -1L + && ((statusChangeTime / 1000 - shareInfo.start) < SHARING_INITIALIZATION_TIME)) { initializing = true - } - if (shareInfo.hasSharingError) { + } else if (shareInfo.hasSharingError + || (shareInfo.lastSendTextMessageTime - shareInfo.lastTextSuccessfulSendTime > WAITING_TDLIB_TIME) + || (shareInfo.lastSendMapMessageTime - shareInfo.lastMapSuccessfulSendTime > WAITING_TDLIB_TIME) + ) { sendChatsErrors = true - locationTime = shareInfo.lastSuccessfulSendTimeMs - val title = app.telegramHelper.getChat(shareInfo.chatId)?.title - if (title != null) { - chatsTitles.add(title) - } + locationTime = Math.max(shareInfo.lastTextSuccessfulSendTime, shareInfo.lastMapSuccessfulSendTime) + chatsIds.add(shareInfo.chatId) } } @@ -351,7 +444,7 @@ class TelegramSettings(private val app: TelegramApplication) { } else if (!initializing) { when { !gpsEnabled -> { - locationTime = app.shareLocationHelper.lastLocationMessageSentTime + locationTime = app.shareLocationHelper.lastLocationUpdateTime if (locationTime <= 0) { locationTime = getLastSuccessfulSendTime() } @@ -399,11 +492,15 @@ class TelegramSettings(private val app: TelegramApplication) { val currentMapMessageId = shareChatsInfo[chatId]?.currentMapMessageId if (messages.contains(currentMapMessageId)) { shareChatsInfo[chatId]?.currentMapMessageId = -1 + shareChatsInfo[chatId]?.shouldSendViaBotMapMessage = true + shareChatsInfo[chatId]?.shouldSendViaBotTextMessage = true } val currentTextMessageId = shareChatsInfo[chatId]?.currentTextMessageId if (messages.contains(currentTextMessageId)) { shareChatsInfo[chatId]?.currentTextMessageId = -1 shareChatsInfo[chatId]?.updateTextMessageId = 1 + shareChatsInfo[chatId]?.shouldSendViaBotMapMessage = true + shareChatsInfo[chatId]?.shouldSendViaBotTextMessage = true } } @@ -435,6 +532,10 @@ class TelegramSettings(private val app: TelegramApplication) { edit.putBoolean(BATTERY_OPTIMISATION_ASKED, batteryOptimisationAsked) + edit.putBoolean(MONITORING_ENABLED, monitoringEnabled) + + edit.putBoolean(SHOW_GPS_POINTS, showGpsPoints) + val jArray = convertShareChatsInfoToJson() if (jArray != null) { edit.putString(SHARE_CHATS_INFO_KEY, jArray.toString()) @@ -482,15 +583,21 @@ class TelegramSettings(private val app: TelegramApplication) { val shareTypeDef = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX] shareTypeValue = prefs.getString(SHARE_TYPE_KEY, shareTypeDef) - currentSharingMode = prefs.getString(SHARING_MODE_KEY, "") + val currentUserId = app.telegramHelper.getCurrentUserId() + currentSharingMode = prefs.getString(SHARING_MODE_KEY, if (currentUserId != -1) currentUserId.toString() else "") - appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "") + val defPackage = if (AppConnect.getInstalledApps(app).size == 1) AppConnect.getInstalledApps(app).first().appPackage else "" + appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, defPackage) liveNowSortType = LiveNowSortType.valueOf( prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_DISTANCE.name) ) batteryOptimisationAsked = prefs.getBoolean(BATTERY_OPTIMISATION_ASKED,false) + + monitoringEnabled = prefs.getBoolean(MONITORING_ENABLED,false) + + showGpsPoints = prefs.getBoolean(SHOW_GPS_POINTS,false) } private fun convertShareDevicesToJson():JSONObject?{ @@ -529,9 +636,13 @@ class TelegramSettings(private val app: TelegramApplication) { obj.put(ShareChatInfo.CURRENT_TEXT_MESSAGE_ID_KEY, chatInfo.currentTextMessageId) obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY, chatInfo.userSetLivePeriod) obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY, chatInfo.userSetLivePeriodStart) - obj.put(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastSuccessfulSendTimeMs) + obj.put(ShareChatInfo.LAST_TEXT_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastTextSuccessfulSendTime) + obj.put(ShareChatInfo.LAST_MAP_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastMapSuccessfulSendTime) obj.put(ShareChatInfo.LAST_SEND_MAP_TIME_KEY, chatInfo.lastSendMapMessageTime) obj.put(ShareChatInfo.LAST_SEND_TEXT_TIME_KEY, chatInfo.lastSendTextMessageTime) + obj.put(ShareChatInfo.PENDING_TEXT_MESSAGE_KEY, chatInfo.pendingTextMessage) + obj.put(ShareChatInfo.PENDING_MAP_MESSAGE_KEY, chatInfo.pendingMapMessage) + obj.put(ShareChatInfo.SENT_MESSAGES_KEY, chatInfo.sentMessages) jArray.put(obj) } jArray @@ -555,9 +666,13 @@ class TelegramSettings(private val app: TelegramApplication) { currentTextMessageId = obj.optLong(ShareChatInfo.CURRENT_TEXT_MESSAGE_ID_KEY) userSetLivePeriod = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY) userSetLivePeriodStart = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY) - lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY) + lastTextSuccessfulSendTime = obj.optLong(ShareChatInfo.LAST_TEXT_SUCCESSFUL_SEND_TIME_KEY) + lastMapSuccessfulSendTime = obj.optLong(ShareChatInfo.LAST_MAP_SUCCESSFUL_SEND_TIME_KEY) lastSendMapMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_MAP_TIME_KEY) lastSendTextMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_TEXT_TIME_KEY) + pendingTextMessage = obj.optBoolean(ShareChatInfo.PENDING_TEXT_MESSAGE_KEY) + pendingMapMessage = obj.optBoolean(ShareChatInfo.PENDING_MAP_MESSAGE_KEY) + sentMessages = obj.optInt(ShareChatInfo.SENT_MESSAGES_KEY) } shareChatsInfo[shareInfo.chatId] = shareInfo } @@ -645,7 +760,8 @@ class TelegramSettings(private val app: TelegramApplication) { val newSharingType = SHARE_TYPE_VALUES[index] if (shareTypeValue != newSharingType && app.telegramHelper.getCurrentUser()?.id.toString() != currentSharingMode) { shareChatsInfo.forEach { (_, shareInfo) -> - shareInfo.shouldSendViaBotMessage = true + shareInfo.shouldSendViaBotTextMessage = true + shareInfo.shouldSendViaBotMapMessage = true } } shareTypeValue = newSharingType @@ -720,6 +836,16 @@ class TelegramSettings(private val app: TelegramApplication) { return 0 } + @DrawableRes + fun getIconId(appPackage: String): Int { + for (item in values()) { + if (item.appPackage == appPackage) { + return item.iconId + } + } + return 0 + } + fun getInstalledApps(context: Context) = values().filter { AndroidUtils.isAppInstalled(context, it.appPackage) } } @@ -806,22 +932,25 @@ class TelegramSettings(private val app: TelegramApplication) { var description: String = "" var locationTime: Long = -1 var statusChangeTime: Long = -1 - var chatsTitles: MutableList = mutableListOf() + var chatsIds: MutableList = mutableListOf() lateinit var statusType: SharingStatusType fun getTitle(app: TelegramApplication): CharSequence { - return if (statusType != SharingStatusType.NOT_POSSIBLE_TO_SENT_TO_CHATS || chatsTitles.isEmpty()) { + return if (statusType != SharingStatusType.NOT_POSSIBLE_TO_SENT_TO_CHATS || chatsIds.isEmpty()) { title } else { val spannableString = SpannableStringBuilder(title) - val iterator = chatsTitles.iterator() + val iterator = chatsIds.iterator() while (iterator.hasNext()) { - val chatTitle = iterator.next() - val start = spannableString.length - val newSpannable = if (iterator.hasNext()) " @$chatTitle," else " @$chatTitle." - spannableString.append(newSpannable) - spannableString.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), start, spannableString.length - 1, 0) + val chatId = iterator.next() + val chatTitle = app.telegramHelper.getChat(chatId)?.title + if (chatTitle != null) { + val start = spannableString.length + val newSpannable = if (iterator.hasNext()) " @$chatTitle," else " @$chatTitle." + spannableString.append(newSpannable) + spannableString.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), start, spannableString.length - 1, 0) + } } spannableString } @@ -837,18 +966,23 @@ class TelegramSettings(private val app: TelegramApplication) { var updateTextMessageId = 1 var currentMessageLimit = -1L var currentMapMessageId = -1L + var oldMapMessageId = -1L var currentTextMessageId = -1L + var oldTextMessageId = -1L var userSetLivePeriod = -1L var userSetLivePeriodStart = -1L - var lastSuccessfulSendTimeMs = -1L + var lastTextSuccessfulSendTime = -1L + var lastMapSuccessfulSendTime = -1L var lastSendTextMessageTime = -1 var lastSendMapMessageTime = -1 + var sentMessages = 0 + var pendingTdLibText = 0 + var pendingTdLibMap = 0 var pendingTextMessage = false var pendingMapMessage = false - var shouldSendViaBotMessage = false + var shouldSendViaBotTextMessage = false + var shouldSendViaBotMapMessage = false var hasSharingError = false - var shouldDeletePreviousMapMessage = false - var shouldDeletePreviousTextMessage = false var additionalActiveTime = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0] fun getNextAdditionalActiveTime(): Long { @@ -876,9 +1010,13 @@ class TelegramSettings(private val app: TelegramApplication) { internal const val CURRENT_TEXT_MESSAGE_ID_KEY = "currentTextMessageId" internal const val USER_SET_LIVE_PERIOD_KEY = "userSetLivePeriod" internal const val USER_SET_LIVE_PERIOD_START_KEY = "userSetLivePeriodStart" - internal const val LAST_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime" + internal const val LAST_MAP_SUCCESSFUL_SEND_TIME_KEY = "lastMapSuccessfulSendTime" + internal const val LAST_TEXT_SUCCESSFUL_SEND_TIME_KEY = "lastTextSuccessfulSendTime" internal const val LAST_SEND_MAP_TIME_KEY = "lastSendMapMessageTime" internal const val LAST_SEND_TEXT_TIME_KEY = "lastSendTextMessageTime" + internal const val PENDING_TEXT_MESSAGE_KEY = "pendingTextMessage" + internal const val PENDING_MAP_MESSAGE_KEY = "pendingMapMessage" + internal const val SENT_MESSAGES_KEY = "sentMessages" } } } \ 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 new file mode 100644 index 0000000000..ff46e36e6a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/LocationMessages.kt @@ -0,0 +1,516 @@ +package net.osmand.telegram.helpers + +import android.content.Context +import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import net.osmand.Location +import net.osmand.PlatformUtil +import net.osmand.data.LatLon +import net.osmand.telegram.TelegramApplication +import net.osmand.telegram.utils.OsmandLocationUtils +import net.osmand.util.MapUtils +import org.drinkless.td.libcore.telegram.TdApi + +class LocationMessages(val app: TelegramApplication) { + + private val log = PlatformUtil.getLog(LocationMessages::class.java) + + private var bufferedMessages = emptyList() + + private var lastLocationPoints = mutableListOf() + + private val dbHelper: SQLiteHelper + + init { + dbHelper = SQLiteHelper(app) + readBufferedMessages() + readLastMessages() + } + + fun getBufferedMessages(): List { + return bufferedMessages.sortedBy { it.time } + } + + fun getBufferedMessagesCount(): Int { + return bufferedMessages.size + } + + fun getBufferedMessagesCountForChat(chatId: Long, type: Int): Int { + return bufferedMessages.count { it.chatId == chatId && it.type == type } + } + + fun getBufferedMessagesCountForChat(chatId: Long): Int { + return bufferedMessages.count { it.chatId == chatId} + } + + fun getBufferedMessagesForChat(chatId: Long): List { + return bufferedMessages.filter { it.chatId == chatId }.sortedBy { it.time } + } + + fun getBufferedTextMessagesForChat(chatId: Long): List { + return bufferedMessages.filter { it.chatId == chatId && it.type == TYPE_TEXT }.sortedBy { it.time } + } + + fun getBufferedMapMessagesForChat(chatId: Long): List { + return bufferedMessages.filter { it.chatId == chatId && it.type == TYPE_MAP }.sortedBy { it.time } + } + + fun getIngoingMessages(currentUserId: Int, start: Long, end: Long): List { + return dbHelper.getIngoingMessages(currentUserId, start, end) + } + + fun getIngoingUserLocations(start: Long, end: Long): List { + return dbHelper.getIngoingUserLocations(start, end) + } + + fun getIngoingUserLocationsInChat(userId: Int, chatId: Long, deviceName: String, start: Long, end: Long): UserLocations? { + return dbHelper.getIngoingUserLocationsInChat(userId, chatId, deviceName, start, end) + } + + fun getMessagesForUserInChat(userId: Int, chatId: Long, deviceName: String, start: Long, end: Long): List { + return dbHelper.getMessagesForUserInChat(userId, chatId,deviceName, start, end) + } + + fun getMessagesForUser(userId: Int, start: Long, end: Long): List { + return dbHelper.getMessagesForUser(userId, start, end) + } + + fun addBufferedMessage(message: BufferMessage) { + log.debug("addBufferedMessage $message") + val messages = mutableListOf(*this.bufferedMessages.toTypedArray()) + messages.add(message) + this.bufferedMessages = messages + dbHelper.addBufferedMessage(message) + } + + fun addNewLocationMessage(message: TdApi.Message) { + log.debug("try addNewLocationMessage ${message.id}") + val type = OsmandLocationUtils.getMessageType(message) + val senderId = OsmandLocationUtils.getSenderMessageId(message) + 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 { + it.userId == senderId && it.chatId == message.chatId && it.deviceName == deviceName && it.type == type + } + if (previousLocationMessage == null || content.lastUpdated * 1000L > previousLocationMessage.time) { + log.debug("addNewLocationMessage passed ${message.id}") + val previousMessageLatLon = if (previousLocationMessage != null) LatLon(previousLocationMessage.lat, previousLocationMessage.lon) else null + val locationMessage = OsmandLocationUtils.createLocationMessage(message, content, previousMessageLatLon) + if (locationMessage != null) { + dbHelper.addLocationMessage(locationMessage) + lastLocationPoints.remove(previousLocationMessage) + lastLocationPoints.add(locationMessage) + } + } + } + } + + fun addMyLocationMessage(loc: Location) { + log.debug("addMyLocationMessage") + val currentUserId = app.telegramHelper.getCurrentUserId() + val previousLocationMessage = lastLocationPoints.sortedBy { it.time }.firstOrNull { it.userId == currentUserId && it.type == TYPE_MY_LOCATION } + val distance = if (previousLocationMessage != null) MapUtils.getDistance(previousLocationMessage.lat, previousLocationMessage.lon, loc.latitude, loc.longitude) else 0.0 + val message = LocationMessages.LocationMessage(currentUserId, 0, loc.latitude, loc.longitude, loc.altitude, + loc.speed.toDouble(), loc.accuracy.toDouble(), loc.bearing.toDouble(), loc.time, TYPE_MY_LOCATION, 0, distance, "") + + dbHelper.addLocationMessage(message) + lastLocationPoints.remove(previousLocationMessage) + lastLocationPoints.add(message) + } + + fun clearBufferedMessages() { + log.debug("clearBufferedMessages") + dbHelper.clearBufferedMessages() + bufferedMessages = emptyList() + } + + fun removeBufferedMessage(message: BufferMessage) { + log.debug("removeBufferedMessage $message") + val messages = mutableListOf(*this.bufferedMessages.toTypedArray()) + messages.remove(message) + this.bufferedMessages = messages + dbHelper.removeBufferedMessage(message) + } + + private fun readBufferedMessages() { + this.bufferedMessages = dbHelper.getBufferedMessages() + } + + private fun readLastMessages() { + this.lastLocationPoints = dbHelper.getLastMessages() + } + + private class SQLiteHelper(context: Context) : + SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { + + private val log = PlatformUtil.getLog(SQLiteHelper::class.java) + + override fun onCreate(db: SQLiteDatabase) { + db.execSQL(TIMELINE_TABLE_CREATE) + db.execSQL("CREATE INDEX IF NOT EXISTS $DATE_INDEX ON $TIMELINE_TABLE_NAME (\"$COL_TIME\" DESC);") + db.execSQL(BUFFER_TABLE_CREATE) + } + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL(TIMELINE_TABLE_DELETE) + db.execSQL(BUFFER_TABLE_DELETE) + onCreate(db) + } + + internal fun addBufferedMessage(message: BufferMessage) { + writableDatabase?.execSQL(BUFFER_TABLE_INSERT, + arrayOf(message.chatId, message.lat, message.lon, message.altitude, message.speed, + message.hdop, message.bearing, message.time, message.type, message.deviceName)) + } + + internal fun addLocationMessage(message: LocationMessage) { + writableDatabase?.execSQL(TIMELINE_TABLE_INSERT, + arrayOf(message.userId, message.chatId, message.lat, message.lon, message.altitude, message.speed, + message.hdop, message.bearing, message.time, message.type, message.messageId, message.distanceFromPrev, message.deviceName)) + } + + internal fun getMessagesForUser(userId: Int, start: Long, end: Long): List { + val res = arrayListOf() + readableDatabase?.rawQuery( + "$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_CHAT_ID ASC, $COL_TYPE DESC, $COL_TIME ASC ", + arrayOf(userId.toString()))?.apply { + if (moveToFirst()) { + do { + res.add(readLocationMessage(this@apply)) + } while (moveToNext()) + } + close() + } + return res + } + + internal fun getIngoingMessages(currentUserId: Int, start: Long, end: Long): List { + val res = arrayListOf() + readableDatabase?.rawQuery( + "$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID != ? AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_USER_ID, $COL_CHAT_ID, $COL_TYPE DESC, $COL_TIME ", + arrayOf(currentUserId.toString()))?.apply { + if (moveToFirst()) { + do { + res.add(readLocationMessage(this@apply)) + } while (moveToNext()) + } + close() + } + return res + } + + internal fun getIngoingUserLocations(start: Long, end: Long): List { + val res = arrayListOf() + readableDatabase?.rawQuery("$TIMELINE_TABLE_SELECT WHERE $COL_TIME BETWEEN $start AND $end ORDER BY $COL_USER_ID, $COL_CHAT_ID, $COL_DEVICE_NAME, $COL_TYPE DESC, $COL_TIME ", null)?.apply { + if (moveToFirst()) { + var userId: Int + var chatId: Long + var deviceName: String + var userLocations: UserLocations? = null + var userLocationsMap: MutableMap>? = null + var segment: UserTrkSegment? = null + do { + val locationMessage = readLocationMessage(this@apply) + userId = locationMessage.userId + chatId = locationMessage.chatId + deviceName = locationMessage.deviceName + if (userLocations == null || userLocations.userId != userId || + userLocations.chatId != chatId || userLocations.deviceName != deviceName) { + userLocationsMap = mutableMapOf() + userLocations = UserLocations(userId, chatId, deviceName, userLocationsMap) + res.add(userLocations) + segment = null + } + if (segment == null || segment.type != locationMessage.type || locationMessage.time - segment.maxTime > 30 * 1000 * 60) { + segment = UserTrkSegment(mutableListOf(), 0.0, locationMessage.type, locationMessage.time, locationMessage.time) + if (userLocationsMap!![segment.type] == null) { + userLocationsMap[segment.type] = mutableListOf() + } + userLocationsMap[segment.type]!!.add(segment) + } + if (segment.points.size > 0) { + segment.distance += MapUtils.getDistance(locationMessage.lat, locationMessage.lon, segment.points.last().lat, segment.points.last().lon) + } + segment.maxTime = locationMessage.time + segment.points.add(locationMessage) + } while (moveToNext()) + } + close() + } + return res + } + + internal fun getIngoingUserLocationsInChat(userId: Int, chatId: Long, deviceName: String,start: Long, end: Long): UserLocations? { + val userLocationsMap: MutableMap> = mutableMapOf() + val userLocations = UserLocations(userId,chatId,deviceName,userLocationsMap) + val whereDeviceQuery = if (deviceName.isNotEmpty()) "AND $COL_DEVICE_NAME = ?" else "" + val args = if (deviceName.isNotEmpty()) arrayOf(userId.toString(), chatId.toString(), deviceName) else arrayOf(userId.toString(), chatId.toString()) + readableDatabase?.rawQuery("$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_CHAT_ID = ? $whereDeviceQuery AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_TYPE DESC, $COL_TIME ", args)?.apply { + if (moveToFirst()) { + var segment: UserTrkSegment? = null + do { + val locationMessage = readLocationMessage(this@apply) + if (segment == null || segment.type != locationMessage.type || locationMessage.time - segment.maxTime > 30 * 1000 * 60) { + segment = UserTrkSegment(mutableListOf(), 0.0, locationMessage.type, locationMessage.time, locationMessage.time) + if (userLocationsMap[segment.type] == null) { + userLocationsMap[segment.type] = mutableListOf() + } + userLocationsMap[segment.type]?.add(segment) + } + if (segment.points.size > 0) { + segment.distance += MapUtils.getDistance(locationMessage.lat, locationMessage.lon, segment.points.last().lat, segment.points.last().lon) + } + segment.maxTime = locationMessage.time + segment.points.add(locationMessage) + } while (moveToNext()) + } + close() + } + return userLocations + } + + internal fun getMessagesForUserInChat(userId: Int, chatId: Long, deviceName: String, start: Long, end: Long): List { + val res = arrayListOf() + val whereDeviceQuery = if (deviceName.isNotEmpty()) "AND $COL_DEVICE_NAME = ?" else "" + val args = if (deviceName.isNotEmpty()) arrayOf(userId.toString(), chatId.toString(), deviceName) else arrayOf(userId.toString(), chatId.toString()) + readableDatabase?.rawQuery( + "$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_CHAT_ID = ? $whereDeviceQuery AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_TYPE DESC, $COL_TIME ", args)?.apply { + if (moveToFirst()) { + do { + res.add(readLocationMessage(this@apply)) + } while (moveToNext()) + } + close() + } + return res + } + + internal fun getBufferedMessages(): List { + val res = arrayListOf() + readableDatabase?.rawQuery(BUFFER_TABLE_SELECT, null)?.apply { + if (moveToFirst()) { + do { + res.add(readBufferMessage(this@apply)) + } while (moveToNext()) + } + close() + } + return res + } + + internal fun getLastMessages(): MutableList { + val res = arrayListOf() + readableDatabase?.rawQuery("$TIMELINE_TABLE_SELECT_LAST_LOCATIONS GROUP BY $COL_USER_ID, $COL_CHAT_ID, $COL_DEVICE_NAME, $COL_TYPE", null)?.apply { + if (moveToFirst()) { + do { + val locationMessage = readLocationMessage(this@apply) + res.add(locationMessage) + log.debug("add last location message - $locationMessage") + } while (moveToNext()) + } + close() + } + return res + } + + internal fun readLocationMessage(cursor: Cursor): LocationMessage { + val userId = cursor.getInt(0) + val chatId = cursor.getLong(1) + val lat = cursor.getDouble(2) + val lon = cursor.getDouble(3) + val altitude = cursor.getDouble(4) + val speed = cursor.getDouble(5) + val hdop = cursor.getDouble(6) + val bearing = cursor.getDouble(7) + val date = cursor.getLong(8) + val type = cursor.getInt(9) + val messageId = cursor.getLong(10) + val distanceFromPrev = cursor.getDouble(11) + val botName = cursor.getString(12) + + return LocationMessage(userId, chatId, lat, lon, altitude, speed, hdop, bearing, date, type, messageId, distanceFromPrev, botName) + } + + internal fun readBufferMessage(cursor: Cursor): BufferMessage { + val chatId = cursor.getLong(0) + val lat = cursor.getDouble(1) + val lon = cursor.getDouble(2) + val altitude = cursor.getDouble(3) + val speed = cursor.getDouble(4) + val hdop = cursor.getDouble(5) + val bearing = cursor.getDouble(6) + val date = cursor.getLong(7) + val type = cursor.getInt(8) + val botName = cursor.getString(9) + + return BufferMessage(chatId, lat, lon, altitude, speed, hdop, bearing, date, type, botName) + } + + internal fun clearBufferedMessages() { + writableDatabase?.execSQL(BUFFER_TABLE_CLEAR) + } + + internal fun removeBufferedMessage(message: BufferMessage) { + + writableDatabase?.execSQL( + BUFFER_TABLE_REMOVE, + arrayOf( + message.chatId, + message.lat, + message.lon, + message.altitude, + message.speed, + message.hdop, + message.bearing, + message.time, + message.type, + message.deviceName + ) + ) + } + + companion object { + + private const val DATABASE_NAME = "location_messages" + private const val DATABASE_VERSION = 6 + + private const val TIMELINE_TABLE_NAME = "timeline" + private const val BUFFER_TABLE_NAME = "buffer" + + private const val COL_USER_ID = "user_id" + private const val COL_CHAT_ID = "chat_id" + private const val COL_TIME = "time" + private const val COL_LAT = "lat" + private const val COL_LON = "lon" + private const val COL_ALTITUDE = "altitude" + private const val COL_SPEED = "speed" + private const val COL_HDOP = "hdop" + private const val COL_BEARING = "bearing" + private const val COL_TYPE = "type" // 0 = user map message, 1 = user text message, 2 = bot map message, 3 = bot text message + private const val COL_MESSAGE_ID = "message_id" + private const val COL_DISTANCE_FROM_PREV = "distance_from_prev" + private const val COL_DEVICE_NAME = "device_name" + + private const val DATE_INDEX = "date_index" + + // Timeline messages table + private const val TIMELINE_TABLE_INSERT = + ("INSERT INTO $TIMELINE_TABLE_NAME ($COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_ID, $COL_DISTANCE_FROM_PREV, $COL_DEVICE_NAME) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + + private const val TIMELINE_TABLE_CREATE = + ("CREATE TABLE IF NOT EXISTS $TIMELINE_TABLE_NAME ($COL_USER_ID long, $COL_CHAT_ID long,$COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_TIME long, $COL_TYPE int, $COL_MESSAGE_ID long, $COL_DISTANCE_FROM_PREV double, $COL_DEVICE_NAME TEXT NOT NULL DEFAULT '')") + + private const val TIMELINE_TABLE_SELECT = + "SELECT $COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_ID, $COL_DISTANCE_FROM_PREV, $COL_DEVICE_NAME FROM $TIMELINE_TABLE_NAME" + + private const val TIMELINE_TABLE_SELECT_LAST_LOCATIONS = + "SELECT $COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_ID, $COL_DISTANCE_FROM_PREV, $COL_DEVICE_NAME, MAX($COL_TIME) FROM $TIMELINE_TABLE_NAME" + + private const val TIMELINE_TABLE_CLEAR = "DELETE FROM $TIMELINE_TABLE_NAME" + + private const val TIMELINE_TABLE_DELETE = "DROP TABLE IF EXISTS $TIMELINE_TABLE_NAME" + + // Buffer messages table + private const val BUFFER_TABLE_INSERT = + ("INSERT INTO $BUFFER_TABLE_NAME ($COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_DEVICE_NAME) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + + private const val BUFFER_TABLE_CREATE = + ("CREATE TABLE IF NOT EXISTS $BUFFER_TABLE_NAME ($COL_CHAT_ID long, $COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_TIME long, $COL_TYPE int, $COL_DEVICE_NAME TEXT NOT NULL DEFAULT '')") + + private const val BUFFER_TABLE_SELECT = + "SELECT $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_DEVICE_NAME FROM $BUFFER_TABLE_NAME" + + private const val BUFFER_TABLE_CLEAR = "DELETE FROM $BUFFER_TABLE_NAME" + + private const val BUFFER_TABLE_REMOVE = "DELETE FROM $BUFFER_TABLE_NAME WHERE $COL_CHAT_ID = ? AND $COL_LAT = ? AND $COL_LON = ? AND $COL_ALTITUDE = ? AND $COL_SPEED = ? AND $COL_HDOP = ? AND $COL_BEARING = ? AND $COL_TIME = ? AND $COL_TYPE = ? AND $COL_DEVICE_NAME = ?" + + private const val BUFFER_TABLE_DELETE = "DROP TABLE IF EXISTS $BUFFER_TABLE_NAME" + } + } + + data class LocationMessage( + val userId: Int, + val chatId: Long, + val lat: Double, + val lon: Double, + val altitude: Double, + val speed: Double, + val hdop: Double, + val bearing: Double, + val time: Long, + val type: Int, + val messageId: Long, + val distanceFromPrev: Double, + val deviceName: String) + + data class BufferMessage ( + val chatId: Long, + val lat: Double, + val lon: Double, + val altitude: Double, + val speed: Double, + val hdop: Double, + val bearing: Double, + val time: Long, + val type: Int, + val deviceName: String) + + data class UserLocations( + val userId: Int, + val chatId: Long, + val deviceName: String, + val locationsByType: Map> + ) { + fun getUniqueSegments(): List { + val list = mutableListOf() + if (locationsByType.containsKey(TYPE_MY_LOCATION)) { + return locationsByType[TYPE_MY_LOCATION] ?: list + } + list.addAll(locationsByType[TYPE_TEXT] ?: emptyList()) + val mapList = locationsByType[TYPE_MAP] ?: emptyList() + mapList.forEach { + var ti = 0 + while (ti < list.size && list[ti].maxTime < it.minTime) { + ti++ + } + if (ti < list.size && list[ti].minTime > it.maxTime) { + list.add(ti, it) + } else if (ti == list.size) { + list.add(it) + } + } + + + return list + } + } + + data class UserTrkSegment( + val points: MutableList, + var distance: Double, + var type: Int, + var minTime: Long, + var maxTime: Long + ) { + fun newer(other: UserTrkSegment): Boolean { + return other.maxTime < maxTime + } + + fun overlap(other: UserTrkSegment): Boolean { + return if (other.maxTime < maxTime) { + other.maxTime > minTime + } else { + other.minTime < maxTime + } + } + } + + companion object { + + const val TYPE_MAP = 0 + const val TYPE_TEXT = 1 + const val TYPE_MY_LOCATION = 3 + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/MessagesDbHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/MessagesDbHelper.kt deleted file mode 100644 index dcd4d67c46..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/MessagesDbHelper.kt +++ /dev/null @@ -1,122 +0,0 @@ -package net.osmand.telegram.helpers - -import android.content.Context -import android.database.sqlite.SQLiteDatabase -import android.database.sqlite.SQLiteOpenHelper -import net.osmand.telegram.TelegramApplication -import org.drinkless.td.libcore.telegram.TdApi - -class MessagesDbHelper(val app: TelegramApplication) { - - private val messages = HashSet() - - private val sqliteHelper: SQLiteHelper - - init { - sqliteHelper = SQLiteHelper(app) - sqliteHelper.getMessages().forEach { - app.telegramHelper.loadMessage(it.chatId, it.messageId) - } - app.telegramHelper.addIncomingMessagesListener(object : - TelegramHelper.TelegramIncomingMessagesListener { - - override fun onReceiveChatLocationMessages( - chatId: Long, vararg messages: TdApi.Message - ) { - messages.forEach { addMessage(chatId, it.id) } - } - - override fun onDeleteChatLocationMessages(chatId: Long, messages: List) { - messages.forEach { removeMessage(chatId, it.id) } - } - - override fun updateLocationMessages() {} - }) - } - - fun saveMessages() { - clearMessages() - synchronized(messages) { - sqliteHelper.addMessages(messages) - } - } - - fun clearMessages() { - sqliteHelper.clearMessages() - } - - private fun addMessage(chatId: Long, messageId: Long) { - synchronized(messages) { - messages.add(Message(chatId, messageId)) - } - } - - private fun removeMessage(chatId: Long, messageId: Long) { - synchronized(messages) { - messages.remove(Message(chatId, messageId)) - } - } - - private class SQLiteHelper(context: Context) : - SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { - - override fun onCreate(db: SQLiteDatabase) { - db.execSQL(MESSAGES_TABLE_CREATE) - } - - override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { - db.execSQL(MESSAGES_TABLE_DELETE) - onCreate(db) - } - - internal fun addMessages(messages: Set) { - messages.forEach { - writableDatabase?.execSQL(MESSAGES_TABLE_INSERT, arrayOf(it.chatId, it.messageId)) - } - } - - internal fun getMessages(): Set { - val res = HashSet() - readableDatabase?.rawQuery(MESSAGES_TABLE_SELECT, null)?.apply { - if (moveToFirst()) { - do { - res.add(Message(getLong(0), getLong(1))) - } while (moveToNext()) - } - close() - } - return res - } - - internal fun clearMessages() { - writableDatabase?.execSQL(MESSAGES_TABLE_CLEAR) - } - - companion object { - - private const val DB_NAME = "messages.db" - private const val DB_VERSION = 1 - - private const val MESSAGES_TABLE_NAME = "messages" - private const val MESSAGES_COL_CHAT_ID = "chat_id" - private const val MESSAGES_COL_MESSAGE_ID = "message_id" - - private const val MESSAGES_TABLE_CREATE = - "CREATE TABLE IF NOT EXISTS $MESSAGES_TABLE_NAME (" + - "$MESSAGES_COL_CHAT_ID LONG, " + - "$MESSAGES_COL_MESSAGE_ID LONG)" - - private const val MESSAGES_TABLE_DELETE = "DROP TABLE IF EXISTS $MESSAGES_TABLE_NAME" - - private const val MESSAGES_TABLE_SELECT = - "SELECT $MESSAGES_COL_CHAT_ID, $MESSAGES_COL_MESSAGE_ID FROM $MESSAGES_TABLE_NAME" - - private const val MESSAGES_TABLE_CLEAR = "DELETE FROM $MESSAGES_TABLE_NAME" - - private const val MESSAGES_TABLE_INSERT = "INSERT INTO $MESSAGES_TABLE_NAME (" + - "$MESSAGES_COL_CHAT_ID, $MESSAGES_COL_MESSAGE_ID) VALUES (?, ?)" - } - } - - private data class Message(val chatId: Long, val messageId: Long) -} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt index edac62b427..9608affa9e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt @@ -75,6 +75,12 @@ class OsmandAidlHelper(private val app: TelegramApplication) { fun onSearchComplete(resultSet: List) } + private var gpxBitmapCreatedListener: GpxBitmapCreatedListener? = null + + interface GpxBitmapCreatedListener { + fun onGpxBitmapCreated(bitmap: AGpxBitmap) + } + private val mIOsmAndAidlCallback = object : IOsmAndAidlCallback.Stub() { @Throws(RemoteException::class) @@ -90,12 +96,26 @@ class OsmandAidlHelper(private val app: TelegramApplication) { mUpdatesListener!!.update() } } + + override fun onAppInitialized() { + + } + + override fun onGpxBitmapCreated(bitmap: AGpxBitmap) { + if (gpxBitmapCreatedListener != null) { + gpxBitmapCreatedListener!!.onGpxBitmapCreated(bitmap) + } + } } fun setSearchCompleteListener(mSearchCompleteListener: SearchCompleteListener) { this.mSearchCompleteListener = mSearchCompleteListener } + fun setGpxBitmapCreatedListener(gpxBitmapCreatedListener: GpxBitmapCreatedListener) { + this.gpxBitmapCreatedListener = gpxBitmapCreatedListener + } + private var mUpdatesListener: UpdatesListener? = null interface UpdatesListener { @@ -106,6 +126,7 @@ class OsmandAidlHelper(private val app: TelegramApplication) { this.mUpdatesListener = mUpdatesListener } + fun updatesCallbackRegistered() = osmandCallbackId > 0 /** * Class for interacting with the main interface of the service. */ @@ -183,6 +204,15 @@ class OsmandAidlHelper(private val app: TelegramApplication) { } } + fun execOsmandApi(action: (() -> Unit)) { + if (!isOsmandConnected() && isOsmandBound()) { + connectOsmand() + } + if (isOsmandConnected()) { + action.invoke() + } + } + private fun bindService(packageName: String): Boolean { return if (mIOsmAndAidlInterface == null) { val intent = Intent("net.osmand.aidl.OsmandAidlService") @@ -1052,7 +1082,23 @@ class OsmandAidlHelper(private val app: TelegramApplication) { fun unregisterFromUpdates(): Boolean { if (mIOsmAndAidlInterface != null) { try { - return mIOsmAndAidlInterface!!.unregisterFromUpdates(osmandCallbackId) + val unregistered = mIOsmAndAidlInterface!!.unregisterFromUpdates(osmandCallbackId) + if (unregistered) { + osmandCallbackId = 0 + } + return unregistered + } catch (e: RemoteException) { + e.printStackTrace() + } + } + return false + } + + fun getBitmapForGpx(gpxUri: Uri, density: Float, widthPixels: Int, heightPixels: Int, color: Int): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + app.grantUriPermission(app.settings.appToConnectPackage, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + return mIOsmAndAidlInterface!!.getBitmapForGpx(CreateGpxBitmapParams(gpxUri, density, widthPixels, heightPixels, color), mIOsmAndAidlCallback) } catch (e: RemoteException) { e.printStackTrace() } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java b/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java deleted file mode 100644 index 6664f8ee69..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java +++ /dev/null @@ -1,311 +0,0 @@ -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 messages) { - - } - - @Override - public void updateLocationMessages() { - - } - }); - app.getTelegramHelper().addOutgoingMessagesListener(new TelegramHelper.TelegramOutgoingMessagesListener() { - - @Override - public void onUpdateMessages(@NotNull List messages) { - for (TdApi.Message message : messages) { - updateLocationMessage(message); - } - } - - @Override - public void onDeleteMessages(long chatId, @NotNull List 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> { - - private TelegramApplication app; - private WeakReference 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 doInBackground(Void... params) { - List warnings = new ArrayList(); - 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 warnings) { - if (warnings != null && warnings.isEmpty()) { - LiveNowTabFragment fragment = fragmentRef.get(); - if (fragment != null && fragment.isResumed()) { - fragment.shareGpx(gpxFile.path); - } - } - } - } -} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt index e8040846ff..449d9a9fe1 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt @@ -3,6 +3,7 @@ package net.osmand.telegram.helpers import net.osmand.Location import net.osmand.PlatformUtil import net.osmand.telegram.* +import net.osmand.telegram.helpers.LocationMessages.BufferMessage import net.osmand.telegram.notifications.TelegramNotification.NotificationType import net.osmand.telegram.utils.AndroidNetworkUtils import net.osmand.telegram.utils.BASE_URL @@ -12,6 +13,8 @@ import org.json.JSONObject private const val USER_SET_LIVE_PERIOD_DELAY_MS = 5000 // 5 sec +private const val UPDATE_LOCATION_ACCURACY = 150 // 150 meters + class ShareLocationHelper(private val app: TelegramApplication) { private val log = PlatformUtil.getLog(ShareLocationHelper::class.java) @@ -25,7 +28,7 @@ class ShareLocationHelper(private val app: TelegramApplication) { var distance: Int = 0 private set - var lastLocationMessageSentTime: Long = 0 + var lastLocationUpdateTime: Long = 0 var lastLocation: Location? = null set(value) { @@ -47,39 +50,11 @@ class ShareLocationHelper(private val app: TelegramApplication) { fun updateLocation(location: Location?) { lastLocation = location - if (location != null) { - val chatsShareInfo = app.settings.getChatsShareInfo() - if (chatsShareInfo.isNotEmpty()) { - val latitude = location.latitude - val longitude = location.longitude - val user = app.telegramHelper.getCurrentUser() - val sharingMode = app.settings.currentSharingMode - - if (user != null && sharingMode == user.id.toString()) { - when (app.settings.shareTypeValue) { - SHARE_TYPE_MAP -> app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, latitude, longitude) - SHARE_TYPE_TEXT -> app.telegramHelper.sendLiveLocationText(chatsShareInfo, location) - SHARE_TYPE_MAP_AND_TEXT -> { - app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, latitude, longitude) - app.telegramHelper.sendLiveLocationText(chatsShareInfo, location) - } - } - } else if (sharingMode.isNotEmpty()) { - val url = getDeviceSharingUrl(location,sharingMode) - AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false, - object : AndroidNetworkUtils.OnRequestResultListener { - override fun onResult(result: String?) { - updateShareInfoSuccessfulSendTime(result, chatsShareInfo) - - val osmandBot = app.telegramHelper.getOsmandBot() - if (osmandBot != null) { - checkAndSendViaBotMessages(chatsShareInfo, TdApi.Location(latitude, longitude), osmandBot) - } - } - }) - } + if (location != null && location.accuracy < UPDATE_LOCATION_ACCURACY) { + lastLocationUpdateTime = System.currentTimeMillis() + if (app.settings.getChatsShareInfo().isNotEmpty()) { + shareLocationMessages(location, app.telegramHelper.getCurrentUserId()) } - lastLocationMessageSentTime = System.currentTimeMillis() } app.settings.updateSharingStatusHistory() refreshNotification() @@ -101,16 +76,12 @@ class ShareLocationHelper(private val app: TelegramApplication) { livePeriod } livePeriod = newLivePeriod - shouldDeletePreviousMapMessage = true - shouldDeletePreviousTextMessage = true currentMessageLimit = currentTime + Math.min(newLivePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()) } } shareInfo.userSetLivePeriod != shareInfo.livePeriod && (shareInfo.userSetLivePeriodStart + USER_SET_LIVE_PERIOD_DELAY_MS) > currentTime -> { shareInfo.apply { - shouldDeletePreviousMapMessage = true - shouldDeletePreviousTextMessage = true livePeriod = shareInfo.userSetLivePeriod currentMessageLimit = currentTime + Math.min(livePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()) } @@ -122,6 +93,50 @@ class ShareLocationHelper(private val app: TelegramApplication) { } } + fun checkAndSendBufferMessages() { + log.debug("checkAndSendBufferMessages") + var bufferedMessagesFull = false + app.settings.getChatsShareInfo().forEach { (chatId, shareInfo) -> + checkAndSendBufferMessagesToChat(chatId) + if (shareInfo.pendingTdLibText >= MAX_MESSAGES_IN_TDLIB_PER_CHAT || shareInfo.pendingTdLibMap >= MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + bufferedMessagesFull = true + } + } + if (bufferedMessagesFull) { + checkNetworkType() + } + } + + fun checkAndSendBufferMessagesToChat(chatId: Long) { + val shareInfo = app.settings.getChatsShareInfo()[chatId] + if (shareInfo != null) { + app.locationMessages.getBufferedTextMessagesForChat(chatId).take(MAX_MESSAGES_IN_TDLIB_PER_CHAT).forEach { + if (shareInfo.pendingTdLibText < MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + if (it.deviceName.isEmpty()) { + if (!shareInfo.pendingTextMessage && shareInfo.currentTextMessageId != -1L) { + app.telegramHelper.editTextLocation(shareInfo, it) + app.locationMessages.removeBufferedMessage(it) + } + } else { + sendLocationToBot(it, shareInfo, SHARE_TYPE_TEXT) + } + } + } + app.locationMessages.getBufferedMapMessagesForChat(chatId).take(MAX_MESSAGES_IN_TDLIB_PER_CHAT).forEach { + if (shareInfo.pendingTdLibMap < MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + if (it.deviceName.isEmpty()) { + if (!shareInfo.pendingMapMessage && shareInfo.currentMapMessageId != -1L) { + app.telegramHelper.editMapLocation(shareInfo, it) + app.locationMessages.removeBufferedMessage(it) + } + } else { + sendLocationToBot(it, shareInfo, SHARE_TYPE_MAP) + } + } + } + } + } + fun startSharingLocation() { if (!sharingLocation) { sharingLocation = true @@ -129,6 +144,8 @@ class ShareLocationHelper(private val app: TelegramApplication) { app.startMyLocationService() refreshNotification() + + checkAndSendBufferMessages() } else { app.forceUpdateMyLocation() } @@ -158,50 +175,182 @@ class ShareLocationHelper(private val app: TelegramApplication) { refreshNotification() } - private fun getDeviceSharingUrl(loc: Location, sharingMode: String): String { - val url = "$BASE_URL/device/$sharingMode/send?lat=${loc.latitude}&lon=${loc.longitude}" + + private fun shareLocationMessages(location: Location, userId: Int) { + val chatsShareInfo = app.settings.getChatsShareInfo() + val latitude = location.latitude + val longitude = location.longitude + val altitude = location.altitude + val speed = location.speed.toDouble() + val accuracy = location.accuracy.toDouble() + val bearing = location.bearing.toDouble() + val time = location.time + val isBot = app.settings.currentSharingMode != userId.toString() + val deviceName = if (isBot) app.settings.currentSharingMode else "" + var bufferedMessagesFull = false + + chatsShareInfo.values.forEach { shareInfo -> + if (shareInfo.pendingTdLibText >= MAX_MESSAGES_IN_TDLIB_PER_CHAT || shareInfo.pendingTdLibMap >= MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + bufferedMessagesFull = true + } + when (app.settings.shareTypeValue) { + SHARE_TYPE_MAP -> { + val message = BufferMessage(shareInfo.chatId, latitude, longitude, altitude, speed, accuracy, bearing, time, LocationMessages.TYPE_MAP, deviceName) + prepareMapMessage(shareInfo, message, isBot) + } + SHARE_TYPE_TEXT -> { + val message = BufferMessage(shareInfo.chatId, latitude, longitude, altitude, speed, accuracy, bearing, time, LocationMessages.TYPE_TEXT, deviceName) + prepareTextMessage(shareInfo, message, isBot) + } + SHARE_TYPE_MAP_AND_TEXT -> { + val messageMap = BufferMessage(shareInfo.chatId, latitude, longitude, altitude, speed, accuracy, bearing, time, LocationMessages.TYPE_MAP, deviceName) + val messageText = BufferMessage(shareInfo.chatId, latitude, longitude, altitude, speed, accuracy, bearing, time, LocationMessages.TYPE_TEXT, deviceName) + prepareMapMessage(shareInfo, messageMap, isBot) + prepareTextMessage(shareInfo, messageText, isBot) + } + } + } + app.locationMessages.addMyLocationMessage(location) + if (bufferedMessagesFull) { + checkNetworkType() + } + } + + private fun prepareTextMessage(shareInfo: TelegramSettings.ShareChatInfo, message: BufferMessage, isBot: Boolean) { + log.debug("prepareTextMessage $message") + if (shareInfo.currentTextMessageId == -1L) { + if (shareInfo.pendingTextMessage) { + app.locationMessages.addBufferedMessage(message) + } else { + if (isBot) { + sendLocationToBot(message, shareInfo, SHARE_TYPE_TEXT) + } else { + app.telegramHelper.sendNewTextLocation(shareInfo, message) + } + } + } else { + if (isBot) { + if (app.isInternetConnectionAvailable) { + sendLocationToBot(message, shareInfo, SHARE_TYPE_TEXT) + } else { + app.locationMessages.addBufferedMessage(message) + } + } else { + if (shareInfo.pendingTdLibText < MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + app.telegramHelper.editTextLocation(shareInfo, message) + } else { + app.locationMessages.addBufferedMessage(message) + } + } + } + } + + private fun prepareMapMessage(shareInfo: TelegramSettings.ShareChatInfo, message: BufferMessage, isBot: Boolean) { + log.debug("prepareMapMessage $message") + if (shareInfo.currentMapMessageId == -1L) { + if (shareInfo.pendingMapMessage) { + app.locationMessages.addBufferedMessage(message) + } else { + if (isBot) { + if (app.isInternetConnectionAvailable) { + sendLocationToBot(message, shareInfo, SHARE_TYPE_MAP) + } else { + app.locationMessages.addBufferedMessage(message) + } + } else { + app.telegramHelper.sendNewMapLocation(shareInfo, message) + } + } + } else { + if (isBot) { + if (app.isInternetConnectionAvailable) { + sendLocationToBot(message, shareInfo, SHARE_TYPE_MAP) + } else { + app.locationMessages.addBufferedMessage(message) + } + } else { + if (shareInfo.pendingTdLibMap < MAX_MESSAGES_IN_TDLIB_PER_CHAT) { + app.telegramHelper.editMapLocation(shareInfo, message) + } else { + app.locationMessages.addBufferedMessage(message) + } + } + } + } + + fun checkNetworkType(){ + if (app.isInternetConnectionAvailable) { + val networkType = when { + app.isWifiConnected -> TdApi.NetworkTypeWiFi() + app.isMobileConnected -> TdApi.NetworkTypeMobile() + else -> TdApi.NetworkTypeOther() + } + app.telegramHelper.networkChange(networkType) + } + } + + private fun sendLocationToBot(locationMessage: BufferMessage, shareInfo: TelegramSettings.ShareChatInfo, shareType: String) { + if (app.isInternetConnectionAvailable) { + log.debug("sendLocationToBot ${locationMessage.deviceName}") + val url = getDeviceSharingUrl(locationMessage, locationMessage.deviceName) + if (shareType == SHARE_TYPE_TEXT) { + shareInfo.lastSendTextMessageTime = (System.currentTimeMillis() / 1000).toInt() + } else if (shareType == SHARE_TYPE_MAP) { + shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt() + } + AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false, + object : AndroidNetworkUtils.OnRequestResultListener { + override fun onResult(result: String?) { + val success = checkResult(result) + val osmandBotId = app.telegramHelper.getOsmandBot()?.id ?: -1 + val device = app.settings.getCurrentSharingDevice() + if (success) { + shareInfo.sentMessages++ + if (shareType == SHARE_TYPE_TEXT) { + shareInfo.lastTextSuccessfulSendTime = System.currentTimeMillis() / 1000 + } else if (shareType == SHARE_TYPE_MAP) { + shareInfo.lastMapSuccessfulSendTime = System.currentTimeMillis() / 1000 + } + app.locationMessages.removeBufferedMessage(locationMessage) + if ((shareInfo.shouldSendViaBotTextMessage || shareInfo.shouldSendViaBotMapMessage) && osmandBotId != -1 && device != null) { + app.telegramHelper.sendViaBotLocationMessage(osmandBotId, shareInfo, TdApi.Location(locationMessage.lat, locationMessage.lon), device, shareType) + shareInfo.shouldSendViaBotTextMessage = false + shareInfo.shouldSendViaBotMapMessage = false + } + } + } + }) + } + } + + private fun getDeviceSharingUrl(loc: BufferMessage, sharingMode: String): String { + val url = "$BASE_URL/device/$sharingMode/send?lat=${loc.lat}&lon=${loc.lon}" val builder = StringBuilder(url) - if (loc.hasBearing() && loc.bearing != 0.0f) { + if (loc.bearing != 0.0) { builder.append("&azi=${loc.bearing}") } - if (loc.hasSpeed() && loc.speed != 0.0f) { + if (loc.speed != 0.0) { builder.append("&spd=${loc.speed}") } - if (loc.hasAltitude() && loc.altitude != 0.0) { + if (loc.altitude != 0.0) { builder.append("&alt=${loc.altitude}") } - if (loc.hasAccuracy() && loc.accuracy != 0.0f) { - builder.append("&hdop=${loc.accuracy}") + if (loc.hdop != 0.0) { + builder.append("&hdop=${loc.hdop}") } return builder.toString() } - private fun updateShareInfoSuccessfulSendTime(result: String?, chatsShareInfo: Map) { + private fun checkResult(result: String?): Boolean { if (result != null) { try { val jsonResult = JSONObject(result) val status = jsonResult.getString("status") - val currentTime = System.currentTimeMillis() - if (status == "OK") { - chatsShareInfo.forEach { (_, shareInfo) -> - shareInfo.lastSuccessfulSendTimeMs = currentTime - } - } + return status == "OK" } catch (e: JSONException) { } } - } - - private fun checkAndSendViaBotMessages(chatsShareInfo: Map, location: TdApi.Location, osmandBot: TdApi.User) { - val device = app.settings.getCurrentSharingDevice() - if (device != null) { - chatsShareInfo.forEach { (_, shareInfo) -> - if (shareInfo.shouldSendViaBotMessage) { - app.telegramHelper.sendViaBotLocationMessage(osmandBot.id, shareInfo, location, device,app.settings.shareTypeValue) - shareInfo.shouldSendViaBotMessage = false - } - } - } + return false } private fun refreshNotification() { @@ -212,8 +361,10 @@ class ShareLocationHelper(private val app: TelegramApplication) { companion object { + const val MAX_MESSAGES_IN_TDLIB_PER_CHAT = 10 + // min and max values for the UI const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = TelegramHelper.MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC - 1 const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC + 1 } -} +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index 9d52abdb37..baa27e4bd0 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -8,10 +8,11 @@ import net.osmand.aidl.map.ALatLon import net.osmand.aidl.maplayer.point.AMapPoint import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication -import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation -import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation import net.osmand.telegram.helpers.TelegramUiHelper.ListItem import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.OsmandLocationUtils +import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation +import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation import org.drinkless.td.libcore.telegram.TdApi import java.io.File import java.util.concurrent.Executors @@ -34,7 +35,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { private var forcedStop: Boolean = false fun setupMapLayer() { - execOsmandApi { + osmandAidlHelper.execOsmandApi { osmandAidlHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) } } @@ -43,7 +44,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { if (item.latLon == null) { return } - execOsmandApi { + osmandAidlHelper.execOsmandApi { osmandAidlHelper.showMapPoint( MAP_LAYER_ID, item.getMapPointId(), @@ -59,10 +60,10 @@ class ShowLocationHelper(private val app: TelegramApplication) { } fun updateLocationsOnMap() { - execOsmandApi { + osmandAidlHelper.execOsmandApi { val messages = telegramHelper.getMessages() for (message in messages) { - val date = telegramHelper.getLastUpdatedTime(message) + val date = OsmandLocationUtils.getLastUpdatedTime(message) val messageShowingTime = System.currentTimeMillis() / 1000 - date if (messageShowingTime > app.settings.locHistoryTime) { removeMapPoint(message.chatId, message) @@ -74,16 +75,16 @@ class ShowLocationHelper(private val app: TelegramApplication) { } fun addOrUpdateLocationOnMap(message: TdApi.Message, update: Boolean = false) { - execOsmandApi { + osmandAidlHelper.execOsmandApi { val chatId = message.chatId val chatTitle = telegramHelper.getChat(message.chatId)?.title val content = message.content - val date = telegramHelper.getLastUpdatedTime(message) + val date = OsmandLocationUtils.getLastUpdatedTime(message) val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime - if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserTextLocation && content.isValid()))) { + if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserLocation && content.isValid()))) { var userName = "" var photoPath: String? = null - val user = telegramHelper.getUser(message.senderUserId) + val user = telegramHelper.getUser(OsmandLocationUtils.getSenderMessageId(message)) if (user != null) { userName = "${user.firstName} ${user.lastName}".trim() if (userName.isEmpty()) { @@ -99,26 +100,26 @@ class ShowLocationHelper(private val app: TelegramApplication) { } } if (userName.isEmpty()) { - userName = message.senderUserId.toString() + userName = OsmandLocationUtils.getSenderMessageId(message).toString() } setupMapLayer() val params = generatePointParams(photoPath, stale) val aLatLon = when (content) { is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude) - is MessageUserTextLocation -> ALatLon(content.lat, content.lon) + is MessageUserLocation -> ALatLon(content.lat, content.lon) else -> null } if (aLatLon != null) { if (update) { - osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, + osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${OsmandLocationUtils.getSenderMessageId(message)}", userName, userName, chatTitle, Color.WHITE, aLatLon, null, params) } else { - osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, + osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${OsmandLocationUtils.getSenderMessageId(message)}", userName, userName, chatTitle, Color.WHITE, aLatLon, null, params) } } } else if (chatTitle != null && content is MessageOsmAndBotLocation && content.isValid()) { - val name = content.name + val name = content.deviceName setupMapLayer() if (update) { osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_$name", name, name, @@ -132,7 +133,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { } fun showChatMessages(chatId: Long) { - execOsmandApi { + osmandAidlHelper.execOsmandApi { val messages = telegramHelper.getChatMessages(chatId) for (message in messages) { addOrUpdateLocationOnMap(message) @@ -145,9 +146,9 @@ class ShowLocationHelper(private val app: TelegramApplication) { } fun hideMessages(messages: List) { - execOsmandApi { + osmandAidlHelper.execOsmandApi { for (message in messages) { - val user = telegramHelper.getUser(message.senderUserId) + val user = telegramHelper.getUser(OsmandLocationUtils.getSenderMessageId(message)) if (user != null) { removeMapPoint(message.chatId, message) } @@ -157,7 +158,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { fun startShowingLocation() { if (!showingLocation && !forcedStop) { - showingLocation = if (isUseOsmandCallback()) { + showingLocation = if (isUseOsmandCallback() && !app.settings.monitoringEnabled) { osmandAidlHelper.registerForUpdates() } else { app.startUserLocationService() @@ -170,14 +171,39 @@ class ShowLocationHelper(private val app: TelegramApplication) { forcedStop = force if (showingLocation) { showingLocation = false - if (isUseOsmandCallback()) { + if (isUseOsmandCallback() && app.osmandAidlHelper.updatesCallbackRegistered()) { osmandAidlHelper.unregisterFromUpdates() - } else { + } else if (!app.settings.monitoringEnabled) { app.stopUserLocationService() } } } + fun changeUpdatesType() { + if (!forcedStop) { + if (app.settings.monitoringEnabled) { + if (app.osmandAidlHelper.updatesCallbackRegistered()) { + osmandAidlHelper.unregisterFromUpdates() + } + app.startUserLocationService() + showingLocation = true + } else { + showingLocation = if (isUseOsmandCallback()) { + app.stopUserLocationService() + osmandAidlHelper.registerForUpdates() + } else { + if (app.settings.hasAnyChatToShowOnMap()) { + app.startUserLocationService() + true + } else { + app.stopUserLocationService() + false + } + } + } + } + } + fun isUseOsmandCallback(): Boolean { val version = AndroidUtils.getAppVersionCode(app, app.settings.appToConnectPackage) return version >= MIN_OSMAND_CALLBACK_VERSION_CODE @@ -250,19 +276,10 @@ class ShowLocationHelper(private val app: TelegramApplication) { private fun removeMapPoint(chatId: Long, message: TdApi.Message) { val content = message.content - if (content is TdApi.MessageLocation || content is MessageUserTextLocation) { - osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}") + if (content is TdApi.MessageLocation || content is MessageUserLocation) { + osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${OsmandLocationUtils.getSenderMessageId(message)}") } else if (content is MessageOsmAndBotLocation) { - osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.name}") - } - } - - private fun execOsmandApi(action: (() -> Unit)) { - if (!osmandAidlHelper.isOsmandConnected() && osmandAidlHelper.isOsmandBound()) { - osmandAidlHelper.connectOsmand() - } - if (osmandAidlHelper.isOsmandConnected()) { - action.invoke() + osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.deviceName}") } } } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index 251447062e..17bc313815 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -1,23 +1,22 @@ package net.osmand.telegram.helpers import android.text.TextUtils -import net.osmand.Location import net.osmand.PlatformUtil import net.osmand.telegram.SHARE_TYPE_MAP import net.osmand.telegram.SHARE_TYPE_MAP_AND_TEXT import net.osmand.telegram.SHARE_TYPE_TEXT import net.osmand.telegram.TelegramSettings import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.* -import net.osmand.telegram.utils.BASE_SHARING_URL import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_DIR import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_EXT -import net.osmand.util.GeoPointParserUtil +import net.osmand.telegram.utils.OsmandLocationUtils +import net.osmand.telegram.utils.OsmandLocationUtils.DEVICE_PREFIX +import net.osmand.telegram.utils.OsmandLocationUtils.USER_TEXT_LOCATION_TITLE import org.drinkless.td.libcore.telegram.Client import org.drinkless.td.libcore.telegram.Client.ResultHandler import org.drinkless.td.libcore.telegram.TdApi import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState import java.io.File -import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors @@ -36,37 +35,11 @@ class TelegramHelper private constructor() { private const val IGNORED_ERROR_CODE = 406 private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5 - private const val DEVICE_PREFIX = "Device: " - private const val LOCATION_PREFIX = "Location: " - private const val LAST_LOCATION_PREFIX = "Last location: " - private const val UPDATED_PREFIX = "Updated: " - private const val USER_TEXT_LOCATION_TITLE = "\uD83D\uDDFA OsmAnd sharing:" - - private const val SHARING_LINK = "https://play.google.com/store/apps/details?id=net.osmand.telegram" - - private const val ALTITUDE_PREFIX = "Altitude: " - private const val SPEED_PREFIX = "Speed: " - private const val HDOP_PREFIX = "Horizontal precision: " - - private const val NOW = "now" - private const val FEW_SECONDS_AGO = "few seconds ago" - private const val SECONDS_AGO_SUFFIX = " seconds ago" - private const val MINUTES_AGO_SUFFIX = " minutes ago" - private const val HOURS_AGO_SUFFIX = " hours ago" - private const val UTC_FORMAT_SUFFIX = " UTC" - - private val UTC_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US).apply { - timeZone = TimeZone.getTimeZone("UTC") - } - - private val UTC_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.US).apply { - timeZone = TimeZone.getTimeZone("UTC") - } // min and max values for the Telegram API const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61 const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day - const val SEND_NEW_MESSAGE_INTERVAL_SEC = 10 * 60 // 10 minutes + const val MAX_LOCATION_MESSAGE_HISTORY_SCAN_SEC = 60 * 60 * 24 // one day private var helper: TelegramHelper? = null @@ -164,14 +137,16 @@ class TelegramHelper private constructor() { fun getChat(id: Long) = chats[id] - fun getUser(id: Int) = users[id] + fun getUser(id: Int) = if (id == getCurrentUserId()) currentUser else users[id] fun getOsmandBot() = osmandBot fun getCurrentUser() = currentUser + fun getCurrentUserId() = currentUser?.id ?: -1 + fun getUserMessage(user: TdApi.User) = - usersLocationMessages.values.firstOrNull { it.senderUserId == user.id } + usersLocationMessages.values.firstOrNull { OsmandLocationUtils.getSenderMessageId(it) == user.id } fun getChatMessages(chatId: Long) = usersLocationMessages.values.filter { it.chatId == chatId } @@ -181,7 +156,7 @@ class TelegramHelper private constructor() { fun getMessagesByChatIds(messageExpTime: Long): Map> { val res = mutableMapOf>() for (message in usersLocationMessages.values) { - if (System.currentTimeMillis() / 1000 - getLastUpdatedTime(message) < messageExpTime) { + if (System.currentTimeMillis() / 1000 - OsmandLocationUtils.getLastUpdatedTime(message) < messageExpTime) { var messages = res[message.chatId] if (messages != null) { messages.add(message) @@ -214,15 +189,6 @@ class TelegramHelper private constructor() { return chat.type is TdApi.ChatTypeSupergroup || chat.type is TdApi.ChatTypeBasicGroup } - fun getLastUpdatedTime(message: TdApi.Message): Int { - val content = message.content - return when (content) { - is MessageOsmAndBotLocation -> content.lastUpdated - 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 @@ -322,7 +288,7 @@ class TelegramHelper private constructor() { try { log.debug("Loading native tdlib...") System.loadLibrary("tdjni") - Client.setLogVerbosityLevel(0) + Client.setLogVerbosityLevel(1) libraryLoaded = true } catch (e: Throwable) { log.error("Failed to load tdlib", e) @@ -347,6 +313,12 @@ class TelegramHelper private constructor() { } } + fun networkChange(networkType: TdApi.NetworkType) { + client?.send(TdApi.SetNetworkType(networkType)) { obj -> + log.debug(obj) + } + } + fun isInit() = client != null && haveAuthorization fun getUserPhotoPath(user: TdApi.User?) = when { @@ -366,19 +338,6 @@ class TelegramHelper private constructor() { else -> null } - fun getOsmAndBotDeviceName(message: TdApi.Message): String { - var deviceName = "" - if (message.replyMarkup is TdApi.ReplyMarkupInlineKeyboard) { - val replyMarkup = message.replyMarkup as TdApi.ReplyMarkupInlineKeyboard - try { - deviceName = replyMarkup.rows[0][1].text.split("\\s".toRegex())[1] - } catch (e: Exception) { - - } - } - return deviceName - } - fun getUserIdFromChatType(type: TdApi.ChatType) = when (type) { is TdApi.ChatTypePrivate -> type.userId is TdApi.ChatTypeSecret -> type.userId @@ -408,11 +367,6 @@ class TelegramHelper private constructor() { return File("$appDir/$GRAYSCALE_PHOTOS_DIR$userId$GRAYSCALE_PHOTOS_EXT").exists() } - private fun isUserLocationMessage(message: TdApi.Message): Boolean { - val cont = message.content - return (cont is MessageUserTextLocation || cont is TdApi.MessageLocation) - } - private fun hasLocalUserPhoto(user: TdApi.User): Boolean { val localPhoto = user.profilePhoto?.small?.local return if (localPhoto != null) { @@ -450,7 +404,7 @@ class TelegramHelper private constructor() { } } - private fun requestChats(reload: Boolean = false) { + private fun requestChats(reload: Boolean = false, onComplete: (() -> Unit)?) { synchronized(chatList) { if (reload) { chatList.clear() @@ -481,7 +435,8 @@ class TelegramHelper private constructor() { } } // chats had already been received through updates, let's retry request - requestChats() + requestChats(false, this@TelegramHelper::scanChatsHistory) + onComplete?.invoke() } else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj") } @@ -519,7 +474,8 @@ class TelegramHelper private constructor() { if (error.code != IGNORED_ERROR_CODE) { listener?.onTelegramError(error.code, error.message) } else { - shareInfo.shouldSendViaBotMessage = true + shareInfo.shouldSendViaBotTextMessage = true + shareInfo.shouldSendViaBotMapMessage = true } } TdApi.InlineQueryResults.CONSTRUCTOR -> { @@ -615,6 +571,63 @@ class TelegramHelper private constructor() { } } + fun scanChatsHistory() { + log.debug("scanChatsHistory: chatList: ${chatList.size}") + chatList.forEach { + scanChatHistory(it.chatId, 0, 0, 100, mutableListOf()) + } + } + + private fun scanChatHistory( + chatId: Long, + fromMessageId: Long, + offset: Int, + limit: Int, + locations: MutableList + ) { + client?.send(TdApi.GetChatHistory(chatId, fromMessageId, offset, limit, false)) { obj -> + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + if (error.code != IGNORED_ERROR_CODE) { + listener?.onTelegramError(error.code, error.message) + } + } + TdApi.Messages.CONSTRUCTOR -> { + val messages = (obj as TdApi.Messages).messages + log.debug("scanChatHistory: chatId: $chatId fromMessageId: $fromMessageId size: ${messages.size}") + if (messages.isNotEmpty()) { + locations.addAll(messages.filter { it.isAppropriate() && !it.isOutgoing }) + val lastMessage = messages.last() + val currentTime = System.currentTimeMillis() / 1000 + if (currentTime - Math.max(lastMessage.date, lastMessage.editDate) < MAX_LOCATION_MESSAGE_HISTORY_SCAN_SEC) { + scanChatHistory(chatId, lastMessage.id, 0, 100, locations) + log.debug("scanChatHistory searchMessageId: ${lastMessage.id}") + } else { + log.debug("scanChatHistory finishForChat: $chatId") + processScannedLocationsForChat(chatId, locations) + } + } else { + log.debug("scanChatHistory finishForChat: $chatId") + processScannedLocationsForChat(chatId, locations) + } + } + } + } + } + + private fun processScannedLocationsForChat(chatId: Long, locations: MutableList) { + if (locations.isNotEmpty()) { + locations.sortBy { message -> OsmandLocationUtils.getLastUpdatedTime(message) } + locations.forEach { + updateLastMessage(it) + } + incomingMessagesListeners.forEach { + it.onReceiveChatLocationMessages(chatId, *locations.toTypedArray()) + } + } + } + private fun requestUser(id: Int) { client?.send(TdApi.GetUser(id)) { obj -> when (obj.constructor) { @@ -658,10 +671,6 @@ class TelegramHelper private constructor() { } } - fun loadMessage(chatId: Long, messageId: Long) { - requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage) - } - private fun requestMessage(chatId: Long, messageId: Long, onComplete: (TdApi.Message) -> Unit) { client?.send(TdApi.GetMessage(chatId, messageId)) { obj -> if (obj is TdApi.Message) { @@ -671,27 +680,22 @@ class TelegramHelper private constructor() { } private fun addNewMessage(message: TdApi.Message) { - lastTelegramUpdateTime = Math.max(message.date, message.editDate) + lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, Math.max(message.date, message.editDate)) if (message.isAppropriate()) { - val fromBot = isOsmAndBot(message.senderUserId) + log.debug("addNewMessage: ${message.id}") + val fromBot = isOsmAndBot(OsmandLocationUtils.getSenderMessageId(message)) val viaBot = isOsmAndBot(message.viaBotUserId) - val oldContent = message.content - if (oldContent is TdApi.MessageText) { - if (oldContent.text.text.startsWith(DEVICE_PREFIX)) { - message.content = parseTextLocation(oldContent.text) - } else if (oldContent.text.text.startsWith(USER_TEXT_LOCATION_TITLE)) { - message.content = parseTextLocation(oldContent.text, false) - } - } else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) { - message.content = parseOsmAndBotLocation(message) + if (message.isOutgoing && !fromBot && !viaBot) { + return } + updateLastMessage(message) if (message.isOutgoing) { - outgoingMessagesListeners.forEach { - it.onUpdateMessages(listOf(message)) + if (fromBot||viaBot) { + outgoingMessagesListeners.forEach { + it.onUpdateMessages(listOf(message)) + } } } else { - removeOldMessages(message, fromBot, viaBot) - usersLocationMessages[message.id] = message incomingMessagesListeners.forEach { it.onReceiveChatLocationMessages(message.chatId, message) } @@ -699,53 +703,24 @@ class TelegramHelper private constructor() { } } - private fun removeOldMessages(newMessage: TdApi.Message, fromBot: Boolean, viaBot: Boolean) { - val iterator = usersLocationMessages.entries.iterator() - while (iterator.hasNext()) { - val message = iterator.next().value - if (newMessage.chatId == message.chatId) { - val sameSender = newMessage.senderUserId == message.senderUserId - val viaSameBot = newMessage.viaBotUserId == message.viaBotUserId - if (fromBot || viaBot) { - if ((fromBot && sameSender) || (viaBot && viaSameBot)) { - val newCont = newMessage.content - val cont = message.content - if (newCont is MessageOsmAndBotLocation && cont is MessageOsmAndBotLocation) { - if (newCont.name == cont.name) { - iterator.remove() - } - } - } - } else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)) { - iterator.remove() - } - } + private fun updateLastMessage(message: TdApi.Message) { + val oldMessage = usersLocationMessages.values.firstOrNull { + OsmandLocationUtils.getSenderMessageId(it) == OsmandLocationUtils.getSenderMessageId(message) + && it.chatId == message.chatId && message.viaBotUserId == message.viaBotUserId + && OsmandLocationUtils.getOsmAndBotDeviceName(it) == OsmandLocationUtils.getOsmAndBotDeviceName(message) } - } - - /** - * @chatId Id of the chat - * @livePeriod Period for which the location can be updated, in seconds; should be between 60 and 86400 for a live location and 0 otherwise. - * @latitude Latitude of the location - * @longitude Longitude of the location - */ - fun sendLiveLocationMessage(chatsShareInfo:Map, latitude: Double, longitude: Double): Boolean { - if (!requestingActiveLiveLocationMessages && haveAuthorization) { - if (needRefreshActiveLiveLocationMessages) { - getActiveLiveLocationMessages { - sendLiveLocationImpl(chatsShareInfo, latitude, longitude) - } - needRefreshActiveLiveLocationMessages = false - } else { - sendLiveLocationImpl(chatsShareInfo, latitude, longitude) + if (oldMessage == null || (Math.max(message.editDate, message.date) > Math.max(oldMessage.editDate, oldMessage.date))) { + message.content = OsmandLocationUtils.parseMessageContent(message, this) + oldMessage?.let { + usersLocationMessages.remove(it.id) } - return true + usersLocationMessages[message.id] = message } - return false } fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) { if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) { + shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt() client?.send( TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, null)) { obj -> handleMapLocationMessageUpdate(obj, shareInfo) @@ -791,10 +766,7 @@ class TelegramHelper private constructor() { } } - private fun recreateLiveLocationMessage( - shareInfo: TelegramSettings.ShareChatInfo, - content: TdApi.InputMessageContent - ) { + private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) { if (shareInfo.chatId != -1L) { val array = LongArray(1) if (content is TdApi.InputMessageLocation) { @@ -826,71 +798,76 @@ class TelegramHelper private constructor() { private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) { needRefreshActiveLiveLocationMessages = true log.debug("sendNewLiveLocationMessage") - client?.send( - TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj -> - handleMapLocationMessageUpdate(obj, shareInfo) + if (content is TdApi.InputMessageText) { + shareInfo.lastSendTextMessageTime = (System.currentTimeMillis() / 1000).toInt() + } else if (content is TdApi.InputMessageLocation) { + shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt() } - } - - private fun sendLiveLocationImpl(chatsShareInfo: Map, latitude: Double, longitude: Double) { - val location = TdApi.Location(latitude, longitude) - chatsShareInfo.forEach { (chatId, shareInfo) -> - if (shareInfo.getChatLiveMessageExpireTime() <= 0) { - return@forEach - } - val livePeriod = - if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) { - MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC - } else { - shareInfo.livePeriod.toInt() - } - val content = TdApi.InputMessageLocation(location, livePeriod) - val msgId = shareInfo.currentMapMessageId - val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendMapMessageTime) - log.debug("sendLiveLocationImpl - $msgId pendingMapMessage ${shareInfo.pendingMapMessage}") - if (msgId != -1L) { - if (shareInfo.shouldDeletePreviousMapMessage) { - recreateLiveLocationMessage(shareInfo, content) - shareInfo.shouldDeletePreviousMapMessage = false - shareInfo.currentMapMessageId = -1 - } else { - log.debug("EditMessageLiveLocation - $msgId") - client?.send( - TdApi.EditMessageLiveLocation(chatId, msgId, null, location)) { obj -> - handleMapLocationMessageUpdate(obj, shareInfo) - } - } - } else if (!shareInfo.pendingMapMessage || shareInfo.pendingMapMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) { - sendNewLiveLocationMessage(shareInfo, content) + client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj -> + if (content is TdApi.InputMessageText) { + handleTextLocationMessageUpdate(obj, shareInfo) + } else if (content is TdApi.InputMessageLocation) { + handleMapLocationMessageUpdate(obj, shareInfo) } } } - fun sendLiveLocationText(chatsShareInfo: Map, location: Location) { - chatsShareInfo.forEach { (chatId, shareInfo) -> - if (shareInfo.getChatLiveMessageExpireTime() <= 0) { - return@forEach + fun sendNewTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) { + shareInfo.updateTextMessageId = 1 + val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location) + if (!shareInfo.pendingTextMessage) { + shareInfo.pendingTextMessage = true + shareInfo.pendingTdLibText++ + shareInfo.lastSendTextMessageTime = (System.currentTimeMillis() / 1000).toInt() + log.error("sendNewTextLocation ${shareInfo.pendingTdLibText}") + client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj -> + handleTextLocationMessageUpdate(obj, shareInfo) } - val msgId = shareInfo.currentTextMessageId - if (msgId == -1L) { - shareInfo.updateTextMessageId = 1 + } + } + + fun editTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) { + val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location) + if (shareInfo.currentTextMessageId!=-1L) { + shareInfo.pendingTdLibText++ + shareInfo.lastSendTextMessageTime = (System.currentTimeMillis() / 1000).toInt() + log.info("editTextLocation ${shareInfo.currentTextMessageId} pendingTdLibText: ${shareInfo.pendingTdLibText}") + client?.send(TdApi.EditMessageText(shareInfo.chatId, shareInfo.currentTextMessageId, null, content)) { obj -> + handleTextLocationMessageUpdate(obj, shareInfo) } - val content = getTextMessageContent(shareInfo.updateTextMessageId, location) - val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendTextMessageTime) - log.debug("sendLiveLocationText - $msgId pendingMapMessage ${shareInfo.pendingTextMessage}") - if (msgId != -1L) { - if (shareInfo.shouldDeletePreviousTextMessage) { - recreateLiveLocationMessage(shareInfo, content) - shareInfo.shouldDeletePreviousTextMessage = false - } else { - client?.send(TdApi.EditMessageText(chatId, msgId, null, content)) { obj -> - handleTextLocationMessageUpdate(obj, shareInfo) - } - } - } else if (!shareInfo.pendingTextMessage || shareInfo.pendingTextMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) { - client?.send(TdApi.SendMessage(chatId, 0, false, false, null, content)) { obj -> - handleTextLocationMessageUpdate(obj, shareInfo) - } + } + } + + fun sendNewMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) { + needRefreshActiveLiveLocationMessages = true + val location = TdApi.Location(locationMessage.lat, locationMessage.lon) + val livePeriod = + if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) { + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC + } else { + shareInfo.livePeriod.toInt() + } + val content = TdApi.InputMessageLocation(location, livePeriod) + if (!shareInfo.pendingMapMessage) { + shareInfo.pendingMapMessage = true + shareInfo.pendingTdLibMap++ + shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt() + log.error("sendNewMapLocation ${shareInfo.pendingTdLibMap}") + client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj -> + handleMapLocationMessageUpdate(obj, shareInfo) + } + } + } + + fun editMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) { + needRefreshActiveLiveLocationMessages = true + val location = TdApi.Location(locationMessage.lat, locationMessage.lon) + if (shareInfo.currentMapMessageId!=-1L) { + shareInfo.pendingTdLibMap++ + shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt() + log.info("editMapLocation ${shareInfo.currentMapMessageId} pendingTdLibMap: ${shareInfo.pendingTdLibMap}") + client?.send(TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, location)) { obj -> + handleMapLocationMessageUpdate(obj, shareInfo) } } } @@ -898,11 +875,11 @@ class TelegramHelper private constructor() { private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) { when (obj.constructor) { TdApi.Error.CONSTRUCTOR -> { + log.debug("handleMapLocationMessageUpdate - ERROR $obj") val error = obj as TdApi.Error needRefreshActiveLiveLocationMessages = true - if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) { - shareInfo.shouldDeletePreviousMapMessage = true - } else if (error.code != IGNORED_ERROR_CODE) { + shareInfo.pendingMapMessage = false + if (error.code != IGNORED_ERROR_CODE) { shareInfo.hasSharingError = true outgoingMessagesListeners.forEach { it.onSendLiveLocationError(error.code, error.message) @@ -915,17 +892,23 @@ class TelegramHelper private constructor() { obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> { shareInfo.hasSharingError = true needRefreshActiveLiveLocationMessages = true + shareInfo.pendingMapMessage = false + log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed") outgoingMessagesListeners.forEach { it.onSendLiveLocationError(-1, "Map location message ${obj.id} failed to send") } } obj.sendingState?.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR -> { shareInfo.pendingMapMessage = true - shareInfo.lastSendMapMessageTime = obj.date log.debug("handleMapLocationMessageUpdate - MessageSendingStatePending") + outgoingMessagesListeners.forEach { + it.onUpdateMessages(listOf(obj)) + } } else -> { shareInfo.hasSharingError = false + shareInfo.pendingMapMessage = false + log.debug("handleMapLocationMessageUpdate - MessageSendingStateSuccess") outgoingMessagesListeners.forEach { it.onUpdateMessages(listOf(obj)) } @@ -939,10 +922,10 @@ class TelegramHelper private constructor() { private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) { when (obj.constructor) { TdApi.Error.CONSTRUCTOR -> { + log.debug("handleTextLocationMessageUpdate - ERROR") val error = obj as TdApi.Error - if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) { - shareInfo.shouldDeletePreviousTextMessage = true - } else if (error.code != IGNORED_ERROR_CODE) { + shareInfo.pendingMapMessage = false + if (error.code != IGNORED_ERROR_CODE) { shareInfo.hasSharingError = true outgoingMessagesListeners.forEach { it.onSendLiveLocationError(error.code, error.message) @@ -954,18 +937,24 @@ class TelegramHelper private constructor() { when { obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> { shareInfo.hasSharingError = true + shareInfo.pendingTextMessage = false needRefreshActiveLiveLocationMessages = true + log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed") outgoingMessagesListeners.forEach { it.onSendLiveLocationError(-1, "Text location message ${obj.id} failed to send") } } obj.sendingState?.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR -> { shareInfo.pendingTextMessage = true - shareInfo.lastSendTextMessageTime = obj.date log.debug("handleTextLocationMessageUpdate - MessageSendingStatePending") + outgoingMessagesListeners.forEach { + it.onUpdateMessages(listOf(obj)) + } } else -> { shareInfo.hasSharingError = false + shareInfo.pendingTextMessage = false + log.debug("handleTextLocationMessageUpdate - MessageSendingStateSuccess") outgoingMessagesListeners.forEach { it.onUpdateMessages(listOf(obj)) } @@ -976,71 +965,6 @@ class TelegramHelper private constructor() { } } - private fun formatLocation(sig: Location): String { - return String.format(Locale.US, "%.5f, %.5f", sig.latitude, sig.longitude) - } - - private fun formatFullTime(ti: Long): String { - val dt = Date(ti) - return UTC_DATE_FORMAT.format(dt) + " " + UTC_TIME_FORMAT.format(dt) + " UTC" - } - - private fun getTextMessageContent(updateId: Int, location: Location): TdApi.InputMessageText { - val entities = mutableListOf() - val builder = StringBuilder() - val locationMessage = formatLocation(location) - - val firstSpace = USER_TEXT_LOCATION_TITLE.indexOf(' ') - val secondSpace = USER_TEXT_LOCATION_TITLE.indexOf(' ', firstSpace + 1) - entities.add(TdApi.TextEntity(builder.length + firstSpace + 1, secondSpace - firstSpace, TdApi.TextEntityTypeTextUrl(SHARING_LINK))) - builder.append("$USER_TEXT_LOCATION_TITLE\n") - - entities.add(TdApi.TextEntity(builder.lastIndex, LOCATION_PREFIX.length, TdApi.TextEntityTypeBold())) - builder.append(LOCATION_PREFIX) - - entities.add(TdApi.TextEntity(builder.length, locationMessage.length, - TdApi.TextEntityTypeTextUrl("$BASE_SHARING_URL?lat=${location.latitude}&lon=${location.longitude}"))) - builder.append("$locationMessage\n") - - if (location.hasAltitude() && location.altitude != 0.0) { - entities.add(TdApi.TextEntity(builder.lastIndex, ALTITUDE_PREFIX.length, TdApi.TextEntityTypeBold())) - builder.append(String.format(Locale.US, "$ALTITUDE_PREFIX%.1f m\n", location.altitude)) - } - if (location.hasSpeed() && location.speed > 0) { - entities.add(TdApi.TextEntity(builder.lastIndex, SPEED_PREFIX.length, TdApi.TextEntityTypeBold())) - builder.append(String.format(Locale.US, "$SPEED_PREFIX%.1f m/s\n", location.speed)) - } - if (location.hasAccuracy() && location.accuracy != 0.0f && location.speed == 0.0f) { - entities.add(TdApi.TextEntity(builder.lastIndex, HDOP_PREFIX.length, TdApi.TextEntityTypeBold())) - builder.append(String.format(Locale.US, "$HDOP_PREFIX%d m\n", location.accuracy.toInt())) - } - if (updateId == 0) { - builder.append(String.format("$UPDATED_PREFIX%s\n", formatFullTime(location.time))) - } else { - builder.append(String.format("$UPDATED_PREFIX%s (%d)\n", formatFullTime(location.time), updateId)) - } - val textMessage = builder.toString().trim() - - return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true) - } - - /** - * @chatId Id of the chat - * @message Text of the message - */ - fun sendTextMessage(chatId: Long, message: String): Boolean { - // initialize reply markup just for testing - //val row = arrayOf(TdApi.InlineKeyboardButton("https://telegram.org?1", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?2", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?3", TdApi.InlineKeyboardButtonTypeUrl())) - //val replyMarkup = TdApi.ReplyMarkupInlineKeyboard(arrayOf(row, row, row)) - - if (haveAuthorization) { - val content = TdApi.InputMessageText(TdApi.FormattedText(message, null), false, true) - client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), defaultHandler) - return true - } - return false - } - fun logout(): Boolean { return if (libraryLoaded) { haveAuthorization = false @@ -1138,7 +1062,7 @@ class TelegramHelper private constructor() { if (wasAuthorized != haveAuthorization) { needRefreshActiveLiveLocationMessages = true if (haveAuthorization) { - requestChats(true) + requestChats(true, null) requestCurrentUser() requestContacts() } @@ -1153,7 +1077,7 @@ class TelegramHelper private constructor() { } val content = content val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE) - val isOsmAndBot = isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId) + val isOsmAndBot = isOsmAndBot(OsmandLocationUtils.getSenderMessageId(this)) || isOsmAndBot(viaBotUserId) if (!(isUserTextLocation || content is TdApi.MessageLocation || isOsmAndBot)) { return false } @@ -1169,188 +1093,6 @@ class TelegramHelper private constructor() { } } - private fun parseOsmAndBotLocation(message: TdApi.Message): MessageOsmAndBotLocation { - val messageLocation = message.content as TdApi.MessageLocation - return MessageOsmAndBotLocation().apply { - name = getOsmAndBotDeviceName(message) - lat = messageLocation.location.latitude - lon = messageLocation.location.longitude - lastUpdated = getLastUpdatedTime(message) - } - } - - private fun parseOsmAndBotLocationContent(oldContent:MessageOsmAndBotLocation, content: TdApi.MessageContent): MessageOsmAndBotLocation { - val messageLocation = content as TdApi.MessageLocation - return MessageOsmAndBotLocation().apply { - name = oldContent.name - lat = messageLocation.location.latitude - lon = messageLocation.location.longitude - lastUpdated = (System.currentTimeMillis() / 1000).toInt() - } - } - - private fun parseTextLocation(text: TdApi.FormattedText, botLocation: Boolean = true): MessageLocation { - val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserTextLocation() - - var locationNA = false - for (s in text.text.lines()) { - when { - s.startsWith(DEVICE_PREFIX) -> { - if (res is MessageOsmAndBotLocation) { - res.name = s.removePrefix(DEVICE_PREFIX) - } - } - s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> { - var locStr: String - var parse = true - if (s.startsWith(LAST_LOCATION_PREFIX)) { - locStr = s.removePrefix(LAST_LOCATION_PREFIX) - if (!locationNA) { - parse = false - } - } else { - locStr = s.removePrefix(LOCATION_PREFIX) - if (locStr.trim() == "n/a") { - locationNA = true - parse = false - } - } - if (parse) { - try { - val urlTextEntity = text.entities.firstOrNull { it.type is TdApi.TextEntityTypeTextUrl } - if (urlTextEntity != null && urlTextEntity.offset == text.text.indexOf(locStr)) { - val url = (urlTextEntity.type as TdApi.TextEntityTypeTextUrl).url - val point: GeoPointParserUtil.GeoParsedPoint? = GeoPointParserUtil.parse(url) - if (point != null) { - res.lat = point.latitude - res.lon = point.longitude - } - } else { - val (latS, lonS) = locStr.split(" ") - res.lat = latS.dropLast(1).toDouble() - res.lon = lonS.toDouble() - - val timeIndex = locStr.indexOf("(") - if (timeIndex != -1) { - val updatedS = locStr.substring(timeIndex, locStr.length) - res.lastUpdated = (parseTime(updatedS.removePrefix("(").removeSuffix(")")) / 1000).toInt() - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } - } - 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) - val endIndex = updatedStr.indexOf("(") - val updatedS = updatedStr.substring(0, if (endIndex != -1) endIndex else updatedStr.length) - val parsedTime = (parseTime(updatedS.trim()) / 1000).toInt() - val currentTime = (System.currentTimeMillis() / 1000) - 1 - res.lastUpdated = if (parsedTime < currentTime) parsedTime else currentTime.toInt() - } - } - } - } - return res - } - - private fun parseTime(timeS: String): Long { - try { - when { - timeS.endsWith(FEW_SECONDS_AGO) -> return System.currentTimeMillis() - 5000 - - timeS.endsWith(SECONDS_AGO_SUFFIX) -> { - val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX) - return System.currentTimeMillis() - locStr.toLong() * 1000 - } - timeS.endsWith(MINUTES_AGO_SUFFIX) -> { - val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX) - val minutes = locStr.toLong() - return System.currentTimeMillis() - minutes * 60 * 1000 - } - timeS.endsWith(HOURS_AGO_SUFFIX) -> { - val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX) - val hours = locStr.toLong() - return (System.currentTimeMillis() - hours * 60 * 60 * 1000) - } - timeS.endsWith(UTC_FORMAT_SUFFIX) -> { - val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX) - val (latS, lonS) = locStr.split(" ") - val date = UTC_DATE_FORMAT.parse(latS) - val time = UTC_TIME_FORMAT.parse(lonS) - val res = date.time + time.time - return res - } - } - } catch (e: Exception) { - e.printStackTrace() - } - return 0 - } - - abstract class MessageLocation : TdApi.MessageContent() { - - var lat: Double = Double.NaN - internal set - var lon: Double = Double.NaN - 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 - - abstract fun isValid(): Boolean - } - - class MessageOsmAndBotLocation : MessageLocation() { - - var name: String = "" - internal set - - override fun isValid() = name != "" && lat != Double.NaN && lon != Double.NaN - } - - class MessageUserTextLocation : MessageLocation() { - - override fun isValid() = lat != Double.NaN && lon != Double.NaN - - } - class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long, internal val isChannel: Boolean) : Comparable { override fun compareTo(other: OrderedChat): Int { @@ -1539,44 +1281,28 @@ class TelegramHelper private constructor() { } TdApi.UpdateMessageEdited.CONSTRUCTOR -> { val updateMessageEdited = obj as TdApi.UpdateMessageEdited + lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, updateMessageEdited.editDate) val message = usersLocationMessages[updateMessageEdited.messageId] - if (message == null) { - updateMessageEdited.apply { - requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage) - } - } else { + if (message != null) { synchronized(message) { message.editDate = updateMessageEdited.editDate - lastTelegramUpdateTime = Math.max(message.date, message.editDate) - } - incomingMessagesListeners.forEach { - it.updateLocationMessages() } } } TdApi.UpdateMessageContent.CONSTRUCTOR -> { val updateMessageContent = obj as TdApi.UpdateMessageContent val message = usersLocationMessages[updateMessageContent.messageId] + log.debug("UpdateMessageContent " + updateMessageContent.messageId) if (message == null) { updateMessageContent.apply { requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage) } } else { synchronized(message) { - lastTelegramUpdateTime = Math.max(message.date, message.editDate) - val newContent = updateMessageContent.newContent - val fromBot = isOsmAndBot(message.senderUserId) - val viaBot = isOsmAndBot(message.viaBotUserId) - message.content = if (newContent is TdApi.MessageText) { - parseTextLocation(newContent.text, (fromBot || viaBot)) - } else if (newContent is TdApi.MessageLocation && - (isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) { - parseOsmAndBotLocationContent(message.content as MessageOsmAndBotLocation, newContent) - } else { - newContent - } + lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, Math.max(message.date, message.editDate)) + message.content = updateMessageContent.newContent + message.content = OsmandLocationUtils.parseMessageContent(message, this@TelegramHelper) } - log.debug("UpdateMessageContent " + message.senderUserId) incomingMessagesListeners.forEach { it.onReceiveChatLocationMessages(message.chatId, message) } @@ -1584,6 +1310,7 @@ class TelegramHelper private constructor() { } TdApi.UpdateNewMessage.CONSTRUCTOR -> { addNewMessage((obj as TdApi.UpdateNewMessage).message) + log.debug("UpdateNewMessage " + obj.message.id) } TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> { val updateChat = obj as TdApi.UpdateMessageMentionRead @@ -1690,9 +1417,9 @@ class TelegramHelper private constructor() { } } TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> { - val udateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded - val message = udateMessageSendSucceeded.message - log.debug("UpdateMessageSendSucceeded: $message") + val updateSucceeded = obj as TdApi.UpdateMessageSendSucceeded + val message = updateSucceeded.message + log.debug("UpdateMessageSendSucceeded: ${message.id} oldId: ${updateSucceeded.oldMessageId}") outgoingMessagesListeners.forEach { it.onUpdateMessages(listOf(message)) } @@ -1718,4 +1445,4 @@ class TelegramHelper private constructor() { }// result is already received through UpdateAuthorizationState, nothing to do } } -} +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt index a1670f1912..380fc6f2be 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt @@ -6,8 +6,9 @@ import android.widget.ImageView import net.osmand.data.LatLon import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication -import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation -import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation +import net.osmand.telegram.utils.OsmandLocationUtils +import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation +import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation import org.drinkless.td.libcore.telegram.TdApi object TelegramUiHelper { @@ -64,11 +65,11 @@ object TelegramUiHelper { val user = helper.getUser(userId) val message = messages.firstOrNull { it.viaBotUserId == 0 } if (message != null) { - res.lastUpdated = helper.getLastUpdatedTime(message) - val content = message.content + res.lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message) + val content = OsmandLocationUtils.parseMessageContent(message, helper) if (content is TdApi.MessageLocation) { res.latLon = LatLon(content.location.latitude, content.location.longitude) - } else if (content is MessageUserTextLocation) { + } else if (content is MessageUserLocation) { res.latLon = LatLon(content.lat, content.lon) } } @@ -106,10 +107,10 @@ object TelegramUiHelper { chat: TdApi.Chat, message: TdApi.Message ): LocationItem? { - val content = message.content + val content = OsmandLocationUtils.parseMessageContent(message, helper) return when (content) { is MessageOsmAndBotLocation -> botMessageToLocationItem(chat, content) - is MessageUserTextLocation -> locationMessageToLocationItem(helper, chat, message) + is MessageUserLocation -> locationMessageToLocationItem(helper, chat, message) is TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message) else -> null } @@ -120,11 +121,11 @@ object TelegramUiHelper { chat: TdApi.Chat, message: TdApi.Message ): ChatItem? { - val content = message.content + val content = OsmandLocationUtils.parseMessageContent(message, helper) return when (content) { - is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content) + is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content, message) is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message) - is MessageUserTextLocation -> locationMessageToChatItem(helper, chat, message) + is MessageUserLocation -> locationMessageToChatItem(helper, chat, message) else -> null } } @@ -137,7 +138,7 @@ object TelegramUiHelper { LocationItem().apply { chatId = chat.id chatTitle = chat.title - name = content.name + name = content.deviceName latLon = LatLon(content.lat, content.lon) placeholderId = R.drawable.img_user_picture lastUpdated = content.lastUpdated @@ -152,35 +153,36 @@ object TelegramUiHelper { chat: TdApi.Chat, message: TdApi.Message ): LocationItem? { - val user = helper.getUser(message.senderUserId) ?: return null - val content = message.content + val user = helper.getUser(OsmandLocationUtils.getSenderMessageId(message)) ?: return null + val content = OsmandLocationUtils.parseMessageContent(message, helper) return LocationItem().apply { chatId = chat.id chatTitle = chat.title name = TelegramUiHelper.getUserName(user) latLon = when (content) { is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude) - is MessageUserTextLocation -> LatLon(content.lat, content.lon) + is MessageUserLocation -> LatLon(content.lat, content.lon) else -> null } photoPath = helper.getUserPhotoPath(user) grayscalePhotoPath = helper.getUserGreyPhotoPath(user) placeholderId = R.drawable.img_user_picture - userId = message.senderUserId - lastUpdated = helper.getLastUpdatedTime(message) + userId = OsmandLocationUtils.getSenderMessageId(message) + lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message) } } private fun botMessageToChatItem( helper: TelegramHelper, chat: TdApi.Chat, - content: MessageOsmAndBotLocation - ): ChatItem? { + content: MessageOsmAndBotLocation, + message: TdApi.Message): ChatItem? { return if (content.isValid()) { ChatItem().apply { + userId = OsmandLocationUtils.getSenderMessageId(message) chatId = chat.id chatTitle = chat.title - name = content.name + name = content.deviceName latLon = LatLon(content.lat, content.lon) photoPath = chat.photo?.small?.local?.path placeholderId = R.drawable.img_user_picture @@ -198,15 +200,15 @@ object TelegramUiHelper { chat: TdApi.Chat, message: TdApi.Message ): ChatItem? { - val user = helper.getUser(message.senderUserId) ?: return null - val content = message.content + val user = helper.getUser(OsmandLocationUtils.getSenderMessageId(message)) ?: return null + val content = OsmandLocationUtils.parseMessageContent(message, helper) return ChatItem().apply { chatId = chat.id chatTitle = chat.title name = TelegramUiHelper.getUserName(user) latLon = when (content) { is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude) - is MessageUserTextLocation -> LatLon(content.lat, content.lon) + is MessageUserLocation -> LatLon(content.lat, content.lon) else -> null } if (helper.isGroup(chat)) { @@ -217,10 +219,38 @@ object TelegramUiHelper { } grayscalePhotoPath = helper.getUserGreyPhotoPath(user) placeholderId = R.drawable.img_user_picture - userId = message.senderUserId + userId = OsmandLocationUtils.getSenderMessageId(message) privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat) chatWithBot = helper.isBot(userId) - lastUpdated = helper.getLastUpdatedTime(message) + lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message) + } + } + + fun userLocationsToChatItem(helper: TelegramHelper, userLocation: LocationMessages.UserLocations): LocationMessagesChatItem? { + val user = helper.getUser(userLocation.userId) + val chat = helper.getChat(userLocation.chatId) + return LocationMessagesChatItem().apply { + if (chat != null) { + chatId = chat.id + chatTitle = chat.title + if (helper.isGroup(chat)) { + photoPath = helper.getUserPhotoPath(user) + groupPhotoPath = chat.photo?.small?.local?.path + } else { + photoPath = user?.profilePhoto?.small?.local?.path + privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat) + } + } else { + photoPath = user?.profilePhoto?.small?.local?.path + } + if (user != null) { + name = TelegramUiHelper.getUserName(user) + userId = user.id + } + userLocations = userLocation + grayscalePhotoPath = helper.getUserGreyPhotoPath(user) + placeholderId = R.drawable.img_user_picture + chatWithBot = userLocation.deviceName.isNotEmpty() } } @@ -272,6 +302,24 @@ object TelegramUiHelper { override fun getVisibleName() = chatTitle } + class LocationMessagesChatItem : ListItem() { + + var userLocations: LocationMessages.UserLocations? = null + internal set + var groupPhotoPath: String? = null + internal set + var privateChat: Boolean = false + internal set + var chatWithBot: Boolean = false + internal set + + override fun canBeOpenedOnMap() = latLon != null + + override fun getMapPointId() = "${chatId}_$userId" + + override fun getVisibleName() = chatTitle + } + class LocationItem : ListItem() { override fun canBeOpenedOnMap() = latLon != null @@ -283,4 +331,4 @@ object TelegramUiHelper { override fun getVisibleName() = name } -} +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt index 5e55476462..3acc330828 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt @@ -14,6 +14,7 @@ import net.osmand.util.Algorithms private const val GROUP_NAME = "share_location" private const val DISABLE_SHARING_ACTION = "disable_sharing_action" +private const val DISABLE_MONITORING_ACTION = "disable_monitoring_action" class LocationNotification(app: TelegramApplication) : TelegramNotification(app, GROUP_NAME) { @@ -23,6 +24,11 @@ class LocationNotification(app: TelegramApplication) : TelegramNotification(app, app.stopSharingLocation() } }, IntentFilter(DISABLE_SHARING_ACTION)) + app.registerReceiver(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + app.stopMonitoring() + } + }, IntentFilter(DISABLE_MONITORING_ACTION)) } override val type: TelegramNotification.NotificationType @@ -66,10 +72,17 @@ class LocationNotification(app: TelegramApplication) : TelegramNotification(app, PendingIntent.FLAG_UPDATE_CURRENT ) } else { - notificationTitle = app.getString(R.string.show_users_on_map) + notificationTitle = app.getString(R.string.location_recording_enabled) notificationText = app.getString(R.string.active_chats) + ": " + app.settings.getShowOnMapChatsCount() color = 0 - icon = R.drawable.ic_action_view + icon = R.drawable.ic_action_timeline + actionTextId = R.string.disable_monitoring + actionIntent = PendingIntent.getBroadcast( + app, + 0, + Intent(DISABLE_MONITORING_ACTION), + PendingIntent.FLAG_UPDATE_CURRENT + ) } return createBuilder(wearable) @@ -77,6 +90,6 @@ class LocationNotification(app: TelegramApplication) : TelegramNotification(app, .setStyle(NotificationCompat.BigTextStyle().bigText(notificationText)) } - private fun isShowingChatsNotificationEnabled() = !app.showLocationHelper.isUseOsmandCallback() - && app.isOsmAndInstalled() && app.settings.hasAnyChatToShowOnMap() + private fun isShowingChatsNotificationEnabled() = (!app.showLocationHelper.isUseOsmandCallback() || app.settings.monitoringEnabled) + && app.isAnyOsmAndInstalled() && app.settings.hasAnyChatToShowOnMap() } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt index bd85fa36c0..b1589b96c8 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt @@ -1,8 +1,10 @@ package net.osmand.telegram.ui import android.content.Intent +import android.graphics.Color import android.os.Bundle import android.support.v4.app.Fragment +import android.support.v4.widget.SwipeRefreshLayout import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.ListPopupWindow import android.support.v7.widget.RecyclerView @@ -33,7 +35,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 +import java.util.* private const val CHAT_VIEW_TYPE = 0 private const val LOCATION_ITEM_VIEW_TYPE = 1 @@ -45,7 +47,6 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage get() = activity?.application as TelegramApplication private val telegramHelper get() = app.telegramHelper - private val osmandAidlHelper get() = app.osmandAidlHelper private val settings get() = app.settings private lateinit var adapter: LiveNowListAdapter @@ -100,11 +101,20 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage } } + mainView.findViewById(R.id.swipe_refresh).apply { + setOnRefreshListener { + app.shareLocationHelper.checkNetworkType() + app.telegramHelper.scanChatsHistory() + updateList() + isRefreshing = false + } + setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE, Color.CYAN) + } + openOsmAndBtn = mainView.findViewById(R.id.open_osmand_btn).apply { setOnClickListener { - val pack = settings.appToConnectPackage - if (AndroidUtils.isAppInstalled(context, pack)) { - activity?.packageManager?.getLaunchIntentForPackage(pack)?.also { intent -> + if (app.isOsmAndInstalled()) { + activity?.packageManager?.getLaunchIntentForPackage(settings.appToConnectPackage)?.also { intent -> startActivity(intent) } } else { @@ -233,15 +243,6 @@ 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) @@ -450,21 +451,14 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage openOnMapView?.isEnabled = canBeOpenedOnMap if (canBeOpenedOnMap) { openOnMapView?.setOnClickListener { - if (!app.isOsmAndInstalled()) { + if (!app.isAnyOsmAndInstalled()) { showOsmAndMissingDialog() - } else { + } else if (!app.isOsmAndChosen() || (app.isOsmAndChosen() && !app.isOsmAndInstalled())) { + fragmentManager?.also { ChooseOsmAndBottomSheet.showInstance(it, this@LiveNowTabFragment) } + } else if(app.isOsmAndInstalled()){ 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) } @@ -485,21 +479,14 @@ 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 } + val points = getChatItemGpxPointsSize(item) + holder.receivedGpxPointsContainer?.visibility = if (settings.showGpsPoints && points > 0) View.VISIBLE else View.GONE + holder.receivedGpxPointsDescr?.text = getString(R.string.received_gps_points, points) + if (item is ChatItem && holder is ChatViewHolder) { val nextIsLocation = !lastItem && (items[position + 1] is LocationItem || !sortByGroup) val chatId = item.chatId @@ -551,6 +538,16 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage } } + private fun getChatItemGpxPointsSize(item: ListItem): Int { + val deviceName = if (item is ChatItem && item.chatWithBot) item.name else "" + val start = System.currentTimeMillis() - settings.locHistoryTime * 1000 + val end = System.currentTimeMillis() + val userLocations = app.locationMessages.getIngoingUserLocationsInChat(item.userId, item.chatId, deviceName, start, end) + var points = 0 + userLocations?.getUniqueSegments()?.forEach { points += it.points.size } + return points + } + private fun showPopupMenu(holder: ChatViewHolder, chatId: Long) { val ctx = holder.itemView.context @@ -565,17 +562,21 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage settings.showChatOnMap(chatId, allSelected) if (settings.hasAnyChatToShowOnMap()) { - if (!app.isOsmAndInstalled()) { + if (!app.isAnyOsmAndInstalled()) { if (allSelected) { showOsmAndMissingDialog() } + } else if (!app.isOsmAndChosen() || (app.isOsmAndChosen() && !app.isOsmAndInstalled())) { + fragmentManager?.also { ChooseOsmAndBottomSheet.showInstance(it, this@LiveNowTabFragment) } } else { - if (allSelected) { - app.showLocationHelper.showChatMessages(chatId) - } else { - app.showLocationHelper.hideChatMessages(chatId) + if (app.isOsmAndInstalled()) { + if (allSelected) { + app.showLocationHelper.showChatMessages(chatId) + } else { + app.showLocationHelper.hideChatMessages(chatId) + } + app.showLocationHelper.startShowingLocation() } - app.showLocationHelper.startShowingLocation() } } else { app.showLocationHelper.stopShowingLocation() @@ -598,6 +599,8 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage val directionIcon: ImageView? = view.findViewById(R.id.direction_icon) val distanceText: TextView? = view.findViewById(R.id.distance_text) val description: TextView? = view.findViewById(R.id.description) + val receivedGpxPointsContainer: View? = view.findViewById(R.id.received_gps_points_container) + val receivedGpxPointsDescr: TextView? = view.findViewById(R.id.received_gps_points_description) val bottomShadow: View? = view.findViewById(R.id.bottom_shadow) val lastTelegramUpdateTime: TextView? = view.findViewById(R.id.last_telegram_update_time) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/LoginDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/LoginDialogFragment.kt index d60b0a1cfa..d0a28d1d48 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/LoginDialogFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/LoginDialogFragment.kt @@ -1,31 +1,36 @@ package net.osmand.telegram.ui +import android.annotation.SuppressLint import android.app.Dialog import android.content.DialogInterface +import android.content.Intent import android.graphics.Rect +import android.net.Uri import android.os.Build import android.os.Bundle import android.support.annotation.StringRes import android.support.v4.app.FragmentManager import android.support.v4.content.ContextCompat import android.support.v7.widget.AppCompatImageView -import android.text.Editable -import android.text.Html -import android.text.TextUtils -import android.text.TextWatcher +import android.text.* +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.text.style.ForegroundColorSpan +import android.text.style.StyleSpan import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.EditorInfo -import android.widget.Button -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView +import android.widget.* import net.osmand.PlatformUtil import net.osmand.telegram.R +import net.osmand.telegram.utils.AndroidNetworkUtils import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.DataConstants +import net.osmand.telegram.utils.OsmandApiUtils +import org.json.JSONObject import studio.carbonylgroup.textfieldboxes.ExtendedEditText @@ -34,12 +39,15 @@ class LoginDialogFragment : BaseDialogFragment() { companion object { private const val TAG = "LoginDialogFragment" - private val LOG = PlatformUtil.getLog(LoginDialogFragment::class.java) + private val log = PlatformUtil.getLog(LoginDialogFragment::class.java) private const val LOGIN_DIALOG_TYPE_PARAM_KEY = "login_dialog_type_param" private const val SHOW_PROGRESS_PARAM_KEY = "show_progress_param" private const val SHOW_WELCOME_DIALOG_PARAM_KEY = "show_welcome_dialog_param" + private const val PRIVACY_POLICY_AGREED_PARAM_KEY = "privacy_policy_agreed_param" private const val TELEGRAM_PACKAGE = "org.telegram.messenger" + private const val TELEGRAM_PRIVACY_POLICY = "https://telegram.org/privacy" + private const val OSMAND_PRIVACY_POLICY = "https://osmand.net/help-online/privacy-policy" private const val SOFT_KEYBOARD_MIN_DETECTION_SIZE = 0.15 var welcomeDialogShown = false @@ -47,12 +55,14 @@ class LoginDialogFragment : BaseDialogFragment() { private var softKeyboardShown: Boolean = false + private var countryPhoneCode: String = "+" + fun showWelcomeDialog(fragmentManager: FragmentManager) { welcomeDialogShown = true showDialog(fragmentManager, welcomeDialog = true) } - fun showDialog(fragmentManager: FragmentManager, loginDialogType: LoginDialogType? = null, welcomeDialog: Boolean = false) { + fun showDialog(fragmentManager: FragmentManager, loginDialogType: LoginDialogType? = null, welcomeDialog: Boolean = false, privacyPolicyAgreed: Boolean = false) { try { /* @@ -70,6 +80,7 @@ class LoginDialogFragment : BaseDialogFragment() { args.putString(LOGIN_DIALOG_TYPE_PARAM_KEY, loginDialogType.name) } args.putBoolean(SHOW_WELCOME_DIALOG_PARAM_KEY, welcomeDialog) + args.putBoolean(PRIVACY_POLICY_AGREED_PARAM_KEY, privacyPolicyAgreed) fragment.arguments = args fragment.show(fragmentManager, TAG) } else { @@ -80,7 +91,7 @@ class LoginDialogFragment : BaseDialogFragment() { fragment.updateDialog(loginDialogType, showWelcomeDialog) } } catch (e: RuntimeException) { - LOG.error(e) + log.error(e) } } @@ -98,9 +109,11 @@ class LoginDialogFragment : BaseDialogFragment() { private var loginDialogActiveType: LoginDialogType? = null private var showWelcomeDialog = false + private var privacyPolicyAgreed = false private var showProgress = false private var dismissedManually = false private lateinit var continueButton: Button + private lateinit var scrollView: ScrollView enum class LoginDialogType(val viewId: Int, val editorId: Int, @StringRes val titleId: Int, @StringRes val descriptionId: Int, @@ -112,7 +125,8 @@ class LoginDialogFragment : BaseDialogFragment() { ENTER_PASSWORD(R.id.enter_password_layout, R.id.password_edit_text, R.string.enter_password, R.string.password_descr, R.string.enter_password), GET_TELEGRAM(R.id.get_telegram_layout, 0, - R.string.get_telegram_title, R.string.get_telegram_account_first, 0); + R.string.get_telegram_title, R.string.get_telegram_account_first, 0), + PRIVACY_POLICY(R.id.privacy_policy_layout, 0, R.string.how_it_works, R.string.privacy_policy_use_telegram, 0); } override fun onCreate(savedInstanceState: Bundle?) { @@ -129,9 +143,11 @@ class LoginDialogFragment : BaseDialogFragment() { } showProgress = args.getBoolean(SHOW_PROGRESS_PARAM_KEY, false) showWelcomeDialog = args.getBoolean(SHOW_WELCOME_DIALOG_PARAM_KEY, false) + privacyPolicyAgreed = args.getBoolean(PRIVACY_POLICY_AGREED_PARAM_KEY, false) } val view = inflater.inflate(R.layout.login_dialog, container) continueButton = view.findViewById(R.id.continue_button) + scrollView = view.findViewById(R.id.scroll_view) buildDialog(view) view.viewTreeObserver.addOnGlobalLayoutListener { @@ -143,6 +159,7 @@ class LoginDialogFragment : BaseDialogFragment() { if (!softKeyboardShown && softKeyboardVisible) { softKeyboardShown = softKeyboardVisible transformContinueButton(true) + scrollToBottom() } else if (softKeyboardShown && !softKeyboardVisible) { transformContinueButton(false) } @@ -151,17 +168,22 @@ class LoginDialogFragment : BaseDialogFragment() { return view } - private fun transformContinueButton(expanded: Boolean) { + private fun transformContinueButton(expanded: Boolean, acceptMode: Boolean = false) { val params = continueButton.layoutParams as ViewGroup.MarginLayoutParams - val margin = if (expanded) 16f else 40f + val bottomMargin = if (expanded || acceptMode) 16f else 40f val width = if (expanded) ViewGroup.LayoutParams.MATCH_PARENT else ViewGroup.LayoutParams.WRAP_CONTENT params.apply { - setMargins(leftMargin, topMargin, rightMargin, AndroidUtils.dpToPx(context!!, margin)) + val horizontalMargin = app.resources.getDimensionPixelSize(if (acceptMode) R.dimen.dialog_welcome_padding_horizontal else R.dimen.content_padding_half) + setMargins(horizontalMargin, topMargin, horizontalMargin, AndroidUtils.dpToPx(app, bottomMargin)) this.width = width } continueButton.requestLayout() } + private fun scrollToBottom() { + scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN); } + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return object : Dialog(requireActivity(), theme) { override fun onBackPressed() { @@ -179,6 +201,7 @@ class LoginDialogFragment : BaseDialogFragment() { } } + @SuppressLint("SetTextI18n") @Suppress("DEPRECATION") private fun buildDialog(view: View?) { if (showWelcomeDialog) { @@ -197,7 +220,10 @@ class LoginDialogFragment : BaseDialogFragment() { val continueButton = view?.findViewById