Merge branch 'master' into RoutePreparationMenu

This commit is contained in:
crimean 2019-01-22 11:08:53 +03:00
commit 48b6692b44
183 changed files with 8689 additions and 887 deletions

17
AUTHORS
View file

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

7
AUTHORS.md Normal file
View file

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

View file

@ -554,13 +554,15 @@ public class BinaryMapIndexReader {
}
return false;
}
public List<TransportStop> searchTransportIndex(SearchRequest<TransportStop> req) throws IOException {
for (TransportIndex index : transportIndexes) {
if (index.stopsFileLength == 0 || index.right < req.left || index.left > req.right || index.top > req.bottom
|| index.bottom < req.top) {
continue;
}
if (req.stringTable != null) {
req.stringTable.clear();
}
codedIS.seek(index.stopsFileOffset);
int oldLimit = codedIS.pushLimit(index.stopsFileLength);
int offset = req.searchResults.size();
@ -875,6 +877,8 @@ public class BinaryMapIndexReader {
continue;
}
// lazy initializing trees
if (index.trees == null) {
index.trees = new ArrayList<MapTree>();
@ -2074,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) {
@ -2090,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");
@ -2280,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(" ");
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

@ -435,7 +435,6 @@ public class TileSourceManager {
public static java.util.List<TileSourceTemplate> getKnownSourceTemplates() {
java.util.List<TileSourceTemplate> list = new ArrayList<TileSourceTemplate>();
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;

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/ctrl_active_light" />
<corners android:radius="@dimen/dialog_button_radius" />
</shape>
</item>
<item android:state_enabled="false">
<shape android:shape="rectangle">
<solid android:color="@color/ctrl_light" />
<corners android:radius="@dimen/dialog_button_radius" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="@null" />
<corners android:radius="@dimen/dialog_button_radius" />
<stroke android:width="1dp" android:color="@color/app_bar_divider" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/card_bg_color">
<LinearLayout
android:id="@+id/title_row"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height"
android:gravity="center_vertical">
<net.osmand.telegram.ui.views.TextViewEx
android:layout_width="0dp"
android:layout_height="@dimen/action_bar_height"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:letterSpacing="@dimen/title_letter_spacing"
android:maxLines="1"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/timeline"
android:textColor="@color/app_bar_title_light"
android:textSize="@dimen/title_text_size"
app:typeface="@string/font_roboto_mono_bold" />
<ImageView
android:id="@+id/options"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingLeft="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half"
tools:src="@drawable/ic_action_other_menu"
tools:tint="@color/icon_light"
tools:visibility="visible" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/action_bar_descr_bottom_margin"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/timeline_description"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/descr_text_size" />
<FrameLayout
android:id="@+id/monitoring_container"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/monitoring_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="@string/monitoring_is_enabled"
android:textColor="@color/ctrl_active_light"
android:textSize="@dimen/descr_text_size"
app:typeface="@string/font_roboto_medium" />
<Switch
android:id="@+id/monitoring_switcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:background="@null"
android:clickable="false"
android:focusable="false" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/app_bar_divider" />
<LinearLayout
android:id="@+id/date_row"
android:layout_width="match_parent"
android:layout_height="@dimen/list_header_height"
android:background="@color/screen_bg_light"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/date_start_btn"
android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_weight="1"
android:background="@drawable/btn_round_border"
android:drawablePadding="@dimen/content_padding_half"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:text="@string/start_date"
android:textColor="?attr/ctrl_active_color"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium" />
<View
android:layout_width="16dp"
android:layout_height="1dp"
android:layout_gravity="center_vertical"
android:background="@color/app_bar_divider" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/date_end_btn"
android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginStart="@dimen/content_padding_half"
android:layout_weight="1"
android:background="@drawable/btn_round_border"
android:drawablePadding="@dimen/content_padding_half"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:text="@string/end_date"
android:textColor="?attr/ctrl_active_color"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/last_telegram_update_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:gravity="center"
android:paddingTop="@dimen/content_padding_half"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
android:visibility="gone"
app:typeface="@string/font_roboto_regular" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/list_item_content_margin"
android:scrollbars="vertical" />
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,509 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/card_bg_color">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/image_container"
android:layout_width="match_parent"
android:layout_height="120dp"
app:layout_scrollFlags="scroll">
<ImageView
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="@dimen/content_padding_standard"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/shared_string_back"
android:padding="@dimen/content_padding_standard"
tools:src="@drawable/ic_arrow_back"
tools:tint="@color/icon_light" />
<ImageView
android:id="@+id/user_icon"
android:layout_width="@dimen/my_location_user_icon_size"
android:layout_height="@dimen/my_location_user_icon_size"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/content_padding_standard"
tools:src="@drawable/img_user_picture" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:text="@string/start_location_sharing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/title_text_size"
app:typeface="@string/font_roboto_mono_bold" />
</FrameLayout>
<LinearLayout
android:id="@+id/interval_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/start_descr"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/start_date"
android:textColor="?attr/android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height"
app:typeface="@string/font_roboto_regular" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/end_descr"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginStart="@dimen/content_padding_standard"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/end_date"
android:textColor="?attr/android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height"
app:typeface="@string/font_roboto_regular" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/list_header_height"
android:orientation="horizontal">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/date_start_btn"
android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_weight="1"
android:background="@drawable/btn_round"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctrl_active_color"
tools:visibility="visible" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/time_start_btn"
android:layout_width="wrap_content"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:background="@drawable/btn_round"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctrl_active_color"
tools:visibility="visible" />
<View
android:layout_width="16dp"
android:layout_height="1dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:background="@color/card_divider_light" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/date_end_btn"
android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_weight="1"
android:background="@drawable/btn_round"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctrl_active_color"
tools:visibility="visible" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/time_end_btn"
android:layout_width="wrap_content"
android:layout_height="@dimen/dialog_button_height"
android:layout_gravity="center_vertical"
android:background="@drawable/btn_round"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingLeft="@dimen/image_button_padding"
android:paddingRight="@dimen/image_button_padding"
android:textSize="@dimen/text_button_text_size"
app:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctrl_active_color"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="152dp"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard">
<ImageView
android:id="@+id/gpx_map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/shared_string_map" />
</FrameLayout>
<LinearLayout
android:id="@+id/gpx_statistic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/distance_time_span"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="11dp"
android:paddingTop="13dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="16dp">
<ImageView
android:id="@+id/distance_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:src="@drawable/ic_action_polygom_dark" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:orientation="vertical">
<TextView
android:id="@+id/distance_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:background="@null"
android:text="@string/shared_string_distance"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/distance_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:text="40 km"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/hint_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="16dp">
<ImageView
android:id="@+id/duration_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:src="@drawable/ic_action_share_location" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:orientation="vertical">
<TextView
android:id="@+id/duration_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:background="@null"
android:text="@string/time_on_the_move"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/duration_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:text="3:32:44"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/hint_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/list_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="8dp">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/card_divider_light" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="8dp"
android:paddingRight="16dp">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/card_divider_light" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/start_end_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="11dp"
android:paddingTop="13dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="16dp">
<ImageView
android:id="@+id/average_speed_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:src="@drawable/ic_action_speed_average" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:orientation="vertical">
<TextView
android:id="@+id/average_speed_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@null"
android:text="@string/average_speed"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/average_speed_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:text="15:04:58"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/hint_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingLeft="16dp">
<ImageView
android:id="@+id/average_altitude_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:src="@drawable/ic_action_share_location" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:orientation="vertical">
<TextView
android:id="@+id/average_altitude_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@null"
android:text="@string/average_altitude"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/average_altitude_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:text="20:58:00"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/hint_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/open_in_osmand_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height_min"
android:layout_marginBottom="@dimen/content_padding_standard"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:background="@drawable/btn_round"
android:gravity="center_vertical"
android:paddingLeft="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half">
<ImageView
android:id="@+id/open_in_osmand_icon"
android:layout_width="@dimen/list_item_icon_size"
android:layout_height="@dimen/list_item_icon_size"
android:src="@drawable/ic_logo_osmand_free" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/open_in_osmand_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding_big"
android:layout_marginRight="@dimen/content_padding_big"
android:text="@string/open_in_osmand"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
<LinearLayout
android:id="@+id/share_gpx_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height_min"
android:layout_marginBottom="@dimen/content_padding_standard"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:background="@drawable/btn_round"
android:gravity="center_vertical"
android:paddingLeft="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half">
<ImageView
android:id="@+id/share_gpx_icon"
android:layout_width="@dimen/list_item_icon_size"
android:layout_height="@dimen/list_item_icon_size"
android:src="@drawable/ic_action_share"
android:tint="@color/ctrl_active_light" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/share_gpx_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding_big"
android:layout_marginRight="@dimen/content_padding_big"
android:text="@string/shared_string_share"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size"
app:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -9,4 +9,9 @@
android:id="@+id/action_live_now"
android:icon="@drawable/ic_action_live_now"
android:title="@string/live_now"/>
<item
android:id="@+id/action_timeline"
android:icon="@drawable/ic_action_timeline"
android:title="@string/timeline"/>
</menu>

View file

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="last_update_from_telegram">Апошняе абнаўленне з Telegram</string>
<string name="enter_another_device_name">Абярыце імя, якое вы яшчэ не выкарыстоўвалі</string>
<string name="device_added_successfully">%1$s дададзена.</string>
@ -10,7 +10,7 @@
<string name="device_name">Назва прылады</string>
<string name="shared_string_hide">Схаваць</string>
<string name="share_location_as_description_second_line">Вы можаце стварыць і праглядзець ідэнтыфікатар прылады ў кліенце Тэлеграма, выкарыстоўваючы %1$s бота. %2$s</string>
<string name="share_location_as_description">Калі вы хочаце падлучыць некалькі прылад да аднаго рахунка тэлеграм, то вам неабходна выкарыстаць розныя прылады, каб падзяліцца месцазнаходжаннем.</string>
<string name="share_location_as_description">Калі вы хочаце падлучыць некалькі прылад да аднаго акаўнта тэлеграм, то вам неабходна выкарыстаць розныя прылады, каб падзяліцца месцазнаходжаннем.</string>
<string name="last_updated_location">Апошняе абнаўленне месцазнаходжання:</string>
<string name="successfully_sent_and_updated">Паспяхова адпраўлена і абноўлена</string>
<string name="not_possible_to_send_to_telegram_chats">Немагчыма адправіць у размову Тэлеграм:</string>
@ -61,7 +61,7 @@
<string name="disconnect_from_telegram_desc">Для таго, каб скасаваць абмен месцазнаходжаннем, адкрыйце Тэлеграм, перайдзіце ў Налады → Прыватнасць і бяспека → Сеансы і спыніце сеанс OsmAnd Telegram.</string>
<string name="disconnect_from_telegram">Як выключыць абмен месцазнаходжаннем у OsmAnd праз Тэлеграм</string>
<string name="logout_help_desc">Як выключыць абмен месцазнаходжаннем у OsmAnd праз Тэлеграм</string>
<string name="connected_account">Падлучаны рахунак</string>
<string name="connected_account">Падлучаны акаўнт</string>
<string name="shared_string_account">Рахунак</string>
<string name="in_time">у %1$s</string>
<string name="osmand_connect_desc">Абраць версію OsmAnd, якую OsmAnd Telegram будзе выкарыстоўваць для адлюстравання пазіцыі.</string>
@ -81,13 +81,13 @@
<string name="shared_string_live">Дзейных</string>
<string name="shared_string_bot">Бот</string>
<string name="get_telegram_title">Рэгістрацыя ў Telegram</string>
<string name="get_telegram_account_first">Для абмену вам неабходны рахунак Тэлеграм.</string>
<string name="get_telegram_description_continue">Калі ласка, ўсталюйце Тэлеграм і наладзьце рахунак.</string>
<string name="get_telegram_account_first">Для абмену вам неабходны акаўнт Тэлеграм.</string>
<string name="get_telegram_description_continue">Калі ласка, ўсталюйце Тэлеграм і наладзьце акаўнт.</string>
<string name="get_telegram_after_creating_account">Пасля гэтага вы зможаце выкарыстоўваць дадатак.</string>
<string name="shared_string_all">Усе</string>
<string name="shared_string_off">Выкл</string>
<string name="already_registered_in_telegram">Вам неабходна мець рахунак Тэлеграм і нумар тэлефона</string>
<string name="do_not_have_telegram">У мяне няма рахунка Тэлеграм</string>
<string name="already_registered_in_telegram">Вам неабходна мець акаўнт Тэлеграм і нумар тэлефона</string>
<string name="do_not_have_telegram">У мяне няма акаўнта Тэлеграм</string>
<string name="enter_phone_number">Увядзіце нумар тэлефона</string>
<string name="enter_authentication_code">Увядзіце код аўтарызацыі</string>
<string name="set_visible_time_for_all">Вызначце бачны час для ўсіх</string>
@ -110,7 +110,7 @@
<string name="shared_string_password">Пароль</string>
<string name="enter_code">Увядзіце код</string>
<string name="authentication_code">Код аўтэнтыфікацыі</string>
<string name="authentication_code_descr">Тэлеграм адправіў вам код для OsmAnd для ўваходу ў рахунак.</string>
<string name="authentication_code_descr">Тэлеграм адправіў вам код для OsmAnd для ўваходу ў акаўнт.</string>
<string name="enter_password">Увядзіце пароль</string>
<string name="password_descr">Пароль Тэлеграм</string>
<string name="shared_string_login">Увайсці</string>
@ -167,7 +167,7 @@
<string name="shared_string_hour_short">г</string>
<string name="shared_string_minute_short">хвіл</string>
<string name="shared_string_second_short">сек</string>
<string name="welcome_descr"><b>Абмен месцазнаходжаннем OsmAnd</b> Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.<br/><br/> Дадатак выкарыстоўвае Telegram API, таму вам неабходны рахунак Тэлеграм.</string>
<string name="welcome_descr"><b>Абмен месцазнаходжаннем OsmAnd</b> Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.<br/><br/> Дадатак выкарыстоўвае Telegram API, таму вам неабходны акаўнт Тэлеграм.</string>
<string name="my_location">Маё месцазнаходжанне</string>
<string name="live_now">Зараз дзейнічае</string>
@ -177,4 +177,4 @@
<string name="shared_string_map">Мапа</string>
<string name="shared_string_text">Тэкст</string>
<string name="map_and_text">Мапа і тэкст</string>
</resources>
</resources>

View file

@ -177,4 +177,14 @@
<string name="shared_string_map">Mapa</string>
<string name="shared_string_text">Texto</string>
<string name="map_and_text">Mapa y texto</string>
<string name="monitoring_is_enabled">Monitorización activada</string>
<string name="monitoring_is_disabled">Monitorización desactivada</string>
<string name="time_on_the_move">tiempo en movimiento</string>
<string name="average_altitude">Altitud media</string>
<string name="average_speed">Velocidad media</string>
<string name="open_in_osmand">Abrir en OsmAnd</string>
<string name="end_date">Fecha de finalización</string>
<string name="start_date">Fecha de inicio</string>
<string name="timeline_description">Activar la monitorización para recopilar datos de movimiento en segundo plano.</string>
<string name="timeline">Línea de tiempo</string>
</resources>

View file

@ -177,4 +177,14 @@
<string name="shared_string_map">Mapa</string>
<string name="shared_string_text">Texto</string>
<string name="map_and_text">Mapa y texto</string>
<string name="monitoring_is_enabled">Monitorización activada</string>
<string name="monitoring_is_disabled">Monitorización desactivada</string>
<string name="time_on_the_move">tiempo en movimiento</string>
<string name="average_altitude">Altitud media</string>
<string name="average_speed">Velocidad media</string>
<string name="open_in_osmand">Abrir en OsmAnd</string>
<string name="end_date">Fecha de fin</string>
<string name="start_date">Fecha de inicio</string>
<string name="timeline_description">Activar la monitorización para recopilar datos de movimiento en segundo plano.</string>
<string name="timeline">Línea de tiempo</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="background_work_description">Endre batterioptimiseringsinnstillinger for mer stabil posisjonsdeling.</string>
<string name="background_work">Bakgrunnsarbeid</string>
<string name="battery_optimization_description">Skru av batterioptimisering for OsmAnd Telegram slik at det ikke plutselig skrur seg av når det er i bakgrunnen.</string>
@ -179,4 +179,14 @@
<string name="shared_string_map">Kart</string>
<string name="shared_string_text">Tekst</string>
<string name="map_and_text">Kart og tekst</string>
</resources>
<string name="monitoring_is_enabled">Oppsyn er påskrudd</string>
<string name="monitoring_is_disabled">Oppsyn er avskrudd</string>
<string name="time_on_the_move">tid i bevegelse</string>
<string name="average_altitude">Gjennomsnittlig høyde</string>
<string name="average_speed">Gjennomsnittsfart</string>
<string name="open_in_osmand">Åpne i OsmAnd</string>
<string name="end_date">Sluttdato</string>
<string name="start_date">Startdato</string>
<string name="timeline_description">Skru på oppsyn for å samle bevegelsesdata i bakgrunnen.</string>
<string name="timeline">Tidslinje</string>
</resources>

View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="send_location_as">Enviar localização como</string>
<string name="send_location_as_descr">Escolha como as mensagens com sua localização serão exibidas.</string>
<string name="shared_string_map">Mapa</string>
<string name="shared_string_text">Texto</string>
<string name="map_and_text">Mapa e texto</string>
<string name="last_update_from_telegram">Última atualização do Telegram</string>
<string name="enter_another_device_name">Escolha um nome que você ainda não usou</string>
<string name="device_added_successfully">%1$s adicionado.</string>
<string name="shared_string_add">Adicionar</string>
<string name="error_adding_new_device">Não foi possível adicionar novo dispositivo</string>
<string name="enter_device_name_description">Nomeie seu novo dispositivo no máximo 200 símbolos.</string>
<string name="device_name_is_too_long">Nome do dispositivo muito longo</string>
<string name="device_name_cannot_be_empty">O nome do dispositivo não pode estar vazio</string>
<string name="device_name">Nome do dispositivo</string>
<string name="shared_string_hide">Ocultar</string>
<string name="share_location_as_description_second_line">Você pode criar e visualizar o ID do dispositivo no cliente de telegrama usando o bot de bate-papo %1$s. %2$s</string>
<string name="share_location_as_description">Se você deseja conectar vários dispositivos a uma conta de telegrama, é necessário usar um dispositivo diferente para compartilhar sua localização.</string>
<string name="last_updated_location">Última localização atualizada:</string>
<string name="successfully_sent_and_updated">Enviado e atualizado com sucesso</string>
<string name="not_possible_to_send_to_telegram_chats">Não é possível enviar para bate-papo do Telegram:</string>
<string name="waiting_for_response_from_telegram">Aguardando resposta do Telegram</string>
<string name="sending_location_messages">enviando localização</string>
<string name="initializing">Iniciando</string>
<string name="searching_for_gps">Posicionando…</string>
<string name="connecting_to_the_internet">Conectando-se à Internet</string>
<string name="background_work_description">Altere as configurações de otimização da bateria para estabilizar o compartilhamento de local.</string>
<string name="background_work">Trabalho de fundo</string>
<string name="battery_optimization_description">Desative a otimização da bateria do OsmAnd Telegram para que não seja desligado repentinamente quando estiver em segundo plano.</string>
<string name="sharing_in_background">Compartilhando em segundo plano</string>
<string name="go_to_settings">Vá para as configurações</string>
<string name="shared_string_later">Mais tarde</string>
<string name="not_sent_yet">Ainda não enviado</string>
<string name="not_found_yet">Ainda não encontrado</string>
<string name="re_send_location">Reenvie o local</string>
<string name="last_available_location">Última localização disponível</string>
<string name="sharing_status">Status de compartilhamento</string>
<string name="sharing_enabled">Compartilhamento: Ativado</string>
<string name="shared_string_status">Status</string>
<string name="no_gps_connection">Sem conexão GPS</string>
<string name="no_internet_connection">Sem conexão com a internet</string>
<string name="shared_string_disable">Desabilitar</string>
<string name="shared_string_save">Salvar</string>
<string name="add_device">Adicionar dispositivo</string>
<string name="share_location_as">Compartilhar localização como</string>
<string name="live_now_description">Contatos e grupos compartilhando o local para você.</string>
<string name="logout_from_osmand_telegram_descr">Tem certeza de que deseja sair do OsmAnd Telegram para que você não possa compartilhar a localização ou ver a localização de outras pessoas\?</string>
<string name="logout_from_osmand_telegram">Sair do OsmAnd Telegram\?</string>
<string name="shared_string_name">Nome</string>
<string name="by_distance">Pela distância</string>
<string name="by_name">Por nome</string>
<string name="by_group">Por grupo</string>
<string name="shared_string_sort">Ordenar</string>
<string name="shared_string_sort_by">Classificar por</string>
<string name="choose_osmand_desc">Selecione a versão OsmAnd, onde os contatos serão exibidos no mapa.</string>
<string name="choose_osmand">Selecione a versão do OsmAnd para usar</string>
<string name="disable_all_sharing_desc">Desativa o compartilhamento de local para todos os bate-papos selecionados (%1$d).</string>
<string name="disable_all_sharing">Desativar todo o compartilhamento</string>
<string name="turn_off_all">Desligue todos</string>
<string name="shared_string_exit">Sair</string>
<string name="time_ago">atrás</string>
<string name="last_response">Última resposta</string>
<string name="shared_string_group">Grupo</string>
<string name="logout_no_internet_msg">Conecte-se à Internet para efetuar a saída do Telegram corretamente.</string>
<string name="shared_string_close">Fechar</string>
<string name="disconnect_from_telegram_desc">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.</string>
<string name="disconnect_from_telegram">"Como desativar o compartilhamento de local do OsmAnd para Telegram"</string>
<string name="logout_help_desc">"Como desativar o compartilhamento de local do OsmAnd para Telegram"</string>
<string name="connected_account">Conta conectada</string>
<string name="shared_string_account">Conta</string>
<string name="in_time">no %1$s</string>
<string name="osmand_connect_desc">Escolha a versão OsmAnd que OsmAnd Telegram usa para exibir posições.</string>
<string name="osmand_connect">"conectar ao OsmAnd "</string>
<string name="location_history_desc">Ocultar contatos que não foram movidos em um determinado momento.</string>
<string name="location_history">Histórico de localização</string>
<string name="stale_location_desc">A última vez que um contato foi movido.</string>
<string name="stale_location">Parado</string>
<string name="send_my_location_desc">Defina o intervalo mínimo para o compartilhamento de local.</string>
<string name="send_my_location">Enviar minha localização</string>
<string name="gps_and_location">Posição</string>
<string name="sharing_time">Tempo de compartilhamento</string>
<string name="expire_at">Expira</string>
<string name="stop_sharing_all">O compartilhamento está ativado (desativado)</string>
<string name="turn_off_location_sharing">Desativar o compartilhamento de local</string>
<string name="open_osmand">Abrir OsmAnd</string>
<string name="shared_string_live">Vivo</string>
<string name="shared_string_bot">Robô</string>
<string name="get_telegram_title">Registro no Telegram</string>
<string name="get_telegram_account_first">Você precisa de uma conta do Telegram para usar o compartilhamento de local.</string>
<string name="get_telegram_description_continue">Por favor, instale o Telegram e configure uma conta.</string>
<string name="get_telegram_after_creating_account">Então você pode usar este aplicativo.</string>
<string name="shared_string_all">Todos</string>
<string name="shared_string_off">Desativado</string>
<string name="already_registered_in_telegram">Você precisa de uma conta e número de telefone registrados no Telegram</string>
<string name="do_not_have_telegram">Eu não tenho conta Telegram</string>
<string name="enter_phone_number">Digite o número de telefone</string>
<string name="enter_authentication_code">Digite o código de autenticação</string>
<string name="set_visible_time_for_all">Definir tempo visível para todos</string>
<string name="hours_and_minutes_format">%1$d h %2$d m</string>
<string name="minutes_format">%1$d m</string>
<string name="hours_format">%1$d h</string>
<string name="shared_string_install">Instalar</string>
<string name="shared_string_share">Compartilhar</string>
<string name="shared_string_back">Voltar</string>
<string name="visible_time_for_all">Hora visível para todos</string>
<string name="set_time_description">Defina a hora em que seus contatos e grupos selecionados verão sua localização em tempo real.</string>
<string name="set_time">Definir tempo</string>
<string name="location_sharing_description">Selecione os contatos e grupos com os quais você deseja compartilhar sua localização.</string>
<string name="my_location_search_hint">Pesquisa: Grupo ou contato</string>
<string name="start_location_sharing">Compartilhar localização</string>
<string name="show_on_map">Mostrar no mapa</string>
<string name="app_name">OsmAnd Telegram</string>
<string name="phone_number_title">Número de telefone</string>
<string name="phone_number_descr">Número de telefone no formato internacional</string>
<string name="shared_string_password">Senha</string>
<string name="enter_code">Coloque o código</string>
<string name="authentication_code">Código de Autenticação</string>
<string name="authentication_code_descr">Uma faixa GPX é salva automaticamente durante a navegação.</string>
<string name="enter_password">Digite a senha</string>
<string name="password_descr">Senha do telegrama</string>
<string name="shared_string_login">Entrar</string>
<string name="shared_string_logout">Sair</string>
<string name="initialization">Iniciando</string>
<string name="logging_out">Saindo</string>
<string name="closing">Fechando</string>
<string name="gps_network_not_enabled">Ativar \"Localização\"\?</string>
<string name="not_logged_in">você não está logado no</string>
<string name="shared_string_continue">Continuar</string>
<string name="shared_string_cancel">Cancelar</string>
<string name="shared_string_settings">Configurações</string>
<string name="no_location_permission">O aplicativo não tem permissão para acessar os dados de localização.</string>
<string name="gps_not_available">Por favor, ligue \"Localização\" nas configurações do sistema</string>
<string name="location_service_no_gps_available">Selecione um dos provedores de localização para compartilhar sua localização.</string>
<string name="osmand_service">Modo em segundo plano</string>
<string name="osmand_service_descr">OsmAnd Telegram é executado em segundo plano com a tela desligada.</string>
<string name="shared_string_distance">Distância</string>
<string name="share_location">Compartilhar localização</string>
<string name="sharing_location">Compartilhando localização</string>
<string name="process_service">Serviço OsmAnd Telegram</string>
<string name="osmand_logo">Logotipo do OsmAnd</string>
<string name="install_osmand_dialog_message">Você precisa instalar a versão gratuita ou paga do OsmAnd primeiro</string>
<string name="install_osmand">Instalar OsmAnd</string>
<string name="show_users_on_map">Mostrar usuários no mapa</string>
<string name="active_chats">Bate-papos ativos</string>
<string name="shared_string_authorization">Autorização</string>
<string name="shared_string_authorization_descr">Por favor, insira o número de telefone do seu Telegram em formato internacional</string>
<string name="shared_string_welcome">Boas vindas</string>
<string name="yard">yd</string>
<string name="foot">ft</string>
<string name="mile">mi</string>
<string name="km">km</string>
<string name="m">m</string>
<string name="nm">mn</string>
<string name="min_mile">min/m</string>
<string name="min_km">min/km</string>
<string name="nm_h">mn/h</string>
<string name="m_s">m/s</string>
<string name="km_h">km/h</string>
<string name="mile_per_hour">mph</string>
<string name="si_kmh">Quilômetros por hora</string>
<string name="si_mph">Milhas por hora</string>
<string name="si_m_s">Metros por segundo</string>
<string name="si_min_km">Minutos por quilômetro</string>
<string name="si_min_m">Minutos por milha</string>
<string name="si_nm_h">Milhas náuticas por hora (nó)</string>
<string name="si_mi_feet">Milhas/pés</string>
<string name="si_mi_yard">Milhas/jardas</string>
<string name="si_km_m">Quilômetros/metros</string>
<string name="si_nm">Milhas náuticas</string>
<string name="si_mi_meters">Milhas/metros</string>
<string name="shared_string_hour_short">h</string>
<string name="shared_string_minute_short">min</string>
<string name="shared_string_second_short">seg</string>
<string name="welcome_descr"><b>Compartilhamento de local OsmAnd</b> permite que você compartilhe sua localização e veja a dos outros no OsmAnd.<br/><br/>O aplicativo usa a API Telegram e você precisa de uma conta Telegram.</string>
<string name="my_location">Minha localização</string>
<string name="live_now">Ao vivo agora</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="timeline_description">Включите мониторинг, для сбора данных о перемещении в фоновом режиме.</string>
<string name="last_update_from_telegram">Последнее обновление в Telegram</string>
<string name="device_name">Имя устройства</string>
<string name="shared_string_hide">Спрятать</string>

View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="send_location_as">傳送位置為</string>
<string name="send_location_as_descr">選擇您所在位置怎樣的訊息外觀。</string>
<string name="shared_string_map">地圖</string>
<string name="shared_string_text">文字</string>
<string name="map_and_text">地圖與文字</string>
<string name="device_added_successfully">%1$s 已新增。</string>
<string name="shared_string_add">新增</string>
<string name="error_adding_new_device">無法增加新的裝置</string>
<string name="enter_device_name_description">命名您的新裝置,最多 200 個字符。</string>
<string name="device_name_is_too_long">裝置名稱太長</string>
<string name="device_name_cannot_be_empty">裝置名稱不能為空白</string>
<string name="device_name">裝置名稱</string>
<string name="shared_string_hide">隱藏</string>
<string name="last_updated_location">上次更新的位置:</string>
<string name="sending_location_messages">正在傳送位置</string>
<string name="initializing">正在啟動</string>
<string name="searching_for_gps">正在定位…</string>
<string name="connecting_to_the_internet">正在連線至網路</string>
<string name="background_work">背景工作</string>
<string name="sharing_in_background">在背景分享</string>
<string name="go_to_settings">轉至設定</string>
<string name="shared_string_later">隨後</string>
<string name="not_sent_yet">尚未傳送</string>
<string name="not_found_yet">尚未找到</string>
<string name="re_send_location">重新傳送位置</string>
<string name="last_available_location">最後可用的位置</string>
<string name="sharing_status">分享狀態</string>
<string name="sharing_enabled">分享:已啟用</string>
<string name="shared_string_status">狀態</string>
<string name="no_gps_connection">沒有 GPS 連接</string>
<string name="no_internet_connection">沒有網際網路連線</string>
<string name="shared_string_disable">停用</string>
<string name="shared_string_save">儲存</string>
<string name="add_device">新增裝置</string>
<string name="share_location_as">分享位置為</string>
<string name="shared_string_name">名稱</string>
<string name="by_distance">按距離</string>
<string name="by_name">按名稱</string>
<string name="by_group">按群組</string>
<string name="shared_string_sort">分類</string>
<string name="shared_string_sort_by">排序方式</string>
<string name="disable_all_sharing">停用所有分享</string>
<string name="turn_off_all">關閉全部</string>
<string name="shared_string_exit">離開</string>
<string name="last_update_from_telegram">Telegram 的最後一次更新</string>
<string name="enter_another_device_name">選擇一個尚未使用的名稱</string>
<string name="share_location_as_description_second_line">您可以使用 %1$s 聊天機器人在電報用戶端中創建和查看裝置 ID。 %2$s</string>
<string name="share_location_as_description">如果要將多個設備連接到一個 Telegram 帳戶,則需要使用不同的設備來共用您的位置。</string>
<string name="successfully_sent_and_updated">已成功發送和更新</string>
<string name="not_possible_to_send_to_telegram_chats">無法發送到 Telegram 聊天:</string>
<string name="waiting_for_response_from_telegram">等待 Telegram 回復</string>
<string name="background_work_description">更改電池最佳化設置以穩定位置共用。</string>
<string name="battery_optimization_description">關閉 OsmAnd Telegram 的電池最佳化,這樣在後臺時就不會突然關閉。</string>
<string name="live_now_description">連絡人和群組共用位置給您。</string>
<string name="logout_from_osmand_telegram_descr">你確定要登出 OsmAnd Telegram這樣你就不能共用位置或看到其他人的位置嗎</string>
<string name="logout_from_osmand_telegram">要登出 OsmAnd Telegram 嗎\?</string>
<string name="choose_osmand_desc">選擇將在地圖上顯示連絡人的 OsmAnd 版本。</string>
<string name="choose_osmand">選擇要使用的 OsmAnd 版本</string>
<string name="disable_all_sharing_desc">關閉所有選定聊天的位置共用(%1$d)。</string>
<string name="time_ago"></string>
<string name="last_response">上次回應</string>
<string name="shared_string_group">群組</string>
<string name="logout_no_internet_msg">連接到網路以正確登出 Telegram。</string>
<string name="shared_string_close">關閉</string>
<string name="disconnect_from_telegram_desc">撤銷位置共用訪問。打開 Telegram進入設定→隱私和安全→會話並終 OsmAnd Telegram 會話。</string>
<string name="disconnect_from_telegram">如何關閉 Telegram 中的 OsmAnd 交給位置共用</string>
<string name="logout_help_desc">如何關閉 Telegram 中的 OsmAnd 交給位置共用</string>
<string name="connected_account">已連接帳戶</string>
<string name="shared_string_account">帳戶</string>
<string name="in_time">在 %1$s</string>
<string name="osmand_connect_desc">選擇 OsmAnd 和版本的 OsmAnd Telegram 用於顯示位置。</string>
<string name="osmand_connect">OsMand 連接</string>
<string name="location_history_desc">隱藏在給定時間內未移動的連絡人。</string>
<string name="location_history">位置歷史記錄</string>
<string name="stale_location_desc">上次移動連絡人的時間。</string>
<string name="stale_location">沒有移動
\n</string>
<string name="send_my_location_desc">設置位置共用的最小間隔。</string>
<string name="send_my_location">發送我的位置</string>
<string name="gps_and_location">位置</string>
<string name="sharing_time">共用時間</string>
<string name="expire_at">到期</string>
<string name="stop_sharing_all">共用已打開 (關閉)</string>
<string name="turn_off_location_sharing">關閉位置共用</string>
<string name="open_osmand">打開 OsmAnd</string>
<string name="shared_string_live">即時</string>
<string name="shared_string_bot">提供</string>
<string name="get_telegram_title">註冊 Telegram</string>
<string name="get_telegram_account_first">您需要一個 Telegram 帳戶才能使用位置共用。</string>
<string name="get_telegram_description_continue">請安裝 Telegram 並建立帳戶。</string>
<string name="get_telegram_after_creating_account">然後您可以使用這個應用程式。</string>
<string name="shared_string_all">所有</string>
<string name="shared_string_off">關閉</string>
<string name="already_registered_in_telegram">你需要一個 Telegram 帳號和電話號碼</string>
<string name="do_not_have_telegram">我沒有 Telegram 帳戶</string>
<string name="enter_phone_number">添加電話號碼</string>
<string name="enter_authentication_code">輸入身份驗證程式碼</string>
<string name="set_visible_time_for_all">為所有人設定可見時間</string>
<string name="hours_and_minutes_format">%1$d 時 %2$d 分</string>
<string name="minutes_format">%1$d 分</string>
<string name="hours_format">%1$d 時</string>
<string name="shared_string_install">安裝</string>
<string name="shared_string_share">分享</string>
<string name="shared_string_back">返回</string>
<string name="visible_time_for_all">所有人的可見時間</string>
<string name="set_time_description">設置所選連絡人和組即時查看您的位置的時間。</string>
<string name="set_time">設定時間</string>
<string name="location_sharing_description">選擇要共用位置的連絡人和群組。</string>
<string name="my_location_search_hint">搜尋:群組或連絡人</string>
<string name="start_location_sharing">共用位置</string>
<string name="show_on_map">在地圖上顯示</string>
<string name="app_name">OsmAnd Telegram</string>
<string name="phone_number_title">電話號碼</string>
<string name="phone_number_descr">國際格式的電話號碼</string>
<string name="shared_string_password">密碼</string>
<string name="enter_code">輸入代碼</string>
<string name="authentication_code">身份驗證代碼</string>
<string name="authentication_code_descr">Telegram 給你發了一個代碼,要求 OsmAnd 登入你的帳戶。</string>
<string name="enter_password">輸入密碼</string>
<string name="password_descr">Telegram 密碼</string>
<string name="shared_string_login">登入</string>
<string name="shared_string_logout">登出</string>
<string name="initialization">正在啟動</string>
<string name="logging_out">已登出</string>
<string name="closing">關閉</string>
<string name="gps_network_not_enabled">打開“位置”?</string>
<string name="not_logged_in">您沒有登入</string>
<string name="shared_string_continue">繼續</string>
<string name="shared_string_cancel">取消</string>
<string name="shared_string_settings">設定</string>
<string name="no_location_permission">應用程式沒有存取位置數據的許可權。</string>
<string name="gps_not_available">請在系統設置中打開“位置”</string>
<string name="location_service_no_gps_available">選擇一個位置提供商來共享您的位置。</string>
<string name="osmand_service">背景模式</string>
<string name="osmand_service_descr">當螢幕關閉後,讓 OsmAnd 在背景運行。</string>
<string name="shared_string_distance">距離</string>
<string name="share_location">共用位置</string>
<string name="sharing_location">共用位置</string>
<string name="process_service">OsmAnd Telegram 伺服器</string>
<string name="osmand_logo">OsmAnd 圖示</string>
<string name="install_osmand_dialog_message">您需要先安裝免費或付費版本的 OsmAnd</string>
<string name="install_osmand">安裝 OsMand</string>
<string name="show_users_on_map">在地圖上顯示用戶</string>
<string name="active_chats">主動聊天</string>
<string name="shared_string_authorization">授權</string>
<string name="shared_string_authorization_descr">請以國際格式輸入 Telegram 電話號碼</string>
<string name="shared_string_welcome">歡迎</string>
<string name="yard"></string>
<string name="foot"></string>
<string name="mile"></string>
<string name="km">公里</string>
<string name="m">公尺</string>
<string name="nm"></string>
<string name="min_mile">分/公尺</string>
<string name="min_km">分/公里</string>
<string name="nm_h">浬/時</string>
<string name="m_s">公尺/秒</string>
<string name="km_h">公里/小時</string>
<string name="mile_per_hour">哩/小時</string>
<string name="si_kmh">公里每小時</string>
<string name="si_mph">英里每小時</string>
<string name="si_m_s">公尺每秒</string>
<string name="si_min_km">分鐘每公里</string>
<string name="si_min_m">分鐘每英里</string>
<string name="si_nm_h">浬每小時 (節)</string>
<string name="si_mi_feet">英哩/英呎</string>
<string name="si_mi_yard">英哩/碼</string>
<string name="si_km_m">公里/公尺</string>
<string name="si_nm">海浬</string>
<string name="si_mi_meters">英里/米</string>
<string name="shared_string_hour_short"></string>
<string name="shared_string_minute_short"></string>
<string name="shared_string_second_short"></string>
<string name="welcome_descr"><b>OsmAnd 位置共用</b>可以讓您共用您的位置,並在 OsmAnd 中看到其他人的位置。<br/><br/>該應用程式使用電報 API你需要一個 Telegram 帳戶。</string>
<string name="my_location">我的位置</string>
<string name="live_now">即時狀況</string>
</resources>

View file

@ -1,4 +1,13 @@
<resources>
<string name="monitoring_is_enabled">Monitoring is enabled</string>
<string name="monitoring_is_disabled">Monitoring is disabled</string>
<string name="time_on_the_move">time on the move</string>
<string name="average_altitude">Average altitude</string>
<string name="average_speed">Average speed</string>
<string name="open_in_osmand">Open in OsmAnd</string>
<string name="end_date">End date</string>
<string name="start_date">Start date</string>
<string name="timeline_description">Enable monitoring to collect movement data in the background.</string>
<string name="send_location_as">Send location as</string>
<string name="send_location_as_descr">Choose how messages with your location will look like.</string>
<string name="shared_string_map">Map</string>
@ -176,6 +185,7 @@
<string name="welcome_descr"><![CDATA[<b>OsmAnd Location Sharing</b> lets you share your location and see that of others in the OsmAnd.<br/><br/>The app uses Telegram API and you need a Telegram account.]]></string>
<string name="my_location">My location</string>
<string name="live_now">Live now</string>
<string name="timeline">Timeline</string>
</resources>

View file

@ -1,6 +1,11 @@
package net.osmand;
import android.util.Xml;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
public class PlatformUtil {
@ -143,4 +148,11 @@ public class PlatformUtil {
return getLog(cl.getName());
}
public static XmlPullParser newXMLPullParser() throws XmlPullParserException {
return Xml.newPullParser();
}
public static XmlSerializer newSerializer() {
return Xml.newSerializer();
}
}

View file

@ -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<SearchResult> resultSet);
void onUpdate();
void onAppInitialized();
void onGpxBitmapCreated(in AGpxBitmap bitmap);
}

View file

@ -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<String> ids);
boolean setDisabledIds(in List<String> ids);
boolean setEnabledPatterns(in List<String> patterns);
boolean setDisabledPatterns(in List<String> patterns);
boolean regWidgetVisibility(in SetWidgetsParams params);
boolean regWidgetAvailability(in SetWidgetsParams params);
boolean customizeOsmandSettings(in OsmandSettingsParams params);
boolean getImportedGpx(out List<AGpxFile> files);
boolean getSqliteDbFiles(out List<ASqliteDbFile> files);
boolean getActiveSqliteDbFiles(out List<ASqliteDbFile> 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);
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.customization;
parcelable OsmandSettingsParams;

View file

@ -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<OsmandSettingsParams> CREATOR = new Creator<OsmandSettingsParams>() {
@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;
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.customization;
parcelable SetWidgetsParams;

View file

@ -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<String> appModesKeys;
public SetWidgetsParams(String widgetKey, @Nullable List<String> appModesKeys) {
this.widgetKey = widgetKey;
this.appModesKeys = appModesKeys;
}
public SetWidgetsParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<SetWidgetsParams> CREATOR = new Creator<SetWidgetsParams>() {
@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<String> 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<String> 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<String> readStringList(Parcel in) {
List<String> 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;
}
}

View file

@ -0,0 +1,4 @@
package net.osmand.aidl.gpx;
parcelable AGpxBitmap;

View file

@ -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<AGpxBitmap> CREATOR = new
Creator<AGpxBitmap>() {
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;
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.gpx;
parcelable AGpxFile;

View file

@ -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<AGpxFile> CREATOR = new
Creator<AGpxFile>() {
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;
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.gpx;
parcelable AGpxFileDetails;

View file

@ -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<String> 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<String> 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<AGpxFileDetails> CREATOR = new
Creator<AGpxFileDetails>() {
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<String> 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;
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.gpx;
parcelable CreateGpxBitmapParams;

View file

@ -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<CreateGpxBitmapParams> CREATOR = new
Creator<CreateGpxBitmapParams>() {
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;
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.navdrawer;
parcelable NavDrawerFooterParams;

View file

@ -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<NavDrawerFooterParams> CREATOR = new Creator<NavDrawerFooterParams>() {
@Override
public NavDrawerFooterParams createFromParcel(Parcel in) {
return new NavDrawerFooterParams(in);
}
@Override
public NavDrawerFooterParams[] newArray(int size) {
return new NavDrawerFooterParams[size];
}
};
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.navdrawer;
parcelable NavDrawerHeaderParams;

View file

@ -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<NavDrawerHeaderParams> CREATOR = new Creator<NavDrawerHeaderParams>() {
@Override
public NavDrawerHeaderParams createFromParcel(Parcel in) {
return new NavDrawerHeaderParams(in);
}
@Override
public NavDrawerHeaderParams[] newArray(int size) {
return new NavDrawerHeaderParams[size];
}
};
}

View file

@ -0,0 +1,5 @@
// PluginParams.aidl
package net.osmand.aidl.plugins;
parcelable PluginParams;

View file

@ -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<PluginParams> CREATOR = new Creator<PluginParams>() {
@Override
public PluginParams createFromParcel(Parcel in) {
return new PluginParams(in);
}
@Override
public PluginParams[] newArray(int size) {
return new PluginParams[size];
}
};
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidl.tiles;
parcelable ASqliteDbFile;

View file

@ -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<ASqliteDbFile> CREATOR = new
Creator<ASqliteDbFile>() {
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;
}
}

View file

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

View file

@ -79,6 +79,8 @@ 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 SHARING_INITIALIZATION_TIME = 60 * 2L // 2 minutes
private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes
@ -105,12 +107,14 @@ class TelegramSettings(private val app: TelegramApplication) {
var appToConnectPackage = ""
private set
var liveNowSortType = LiveNowSortType.SORT_BY_GROUP
var liveNowSortType = LiveNowSortType.SORT_BY_DISTANCE
val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref())
var batteryOptimisationAsked = false
var monitoringEnabled = false
init {
updatePrefs()
read()
@ -435,6 +439,8 @@ class TelegramSettings(private val app: TelegramApplication) {
edit.putBoolean(BATTERY_OPTIMISATION_ASKED, batteryOptimisationAsked)
edit.putBoolean(MONITORING_ENABLED, monitoringEnabled)
val jArray = convertShareChatsInfoToJson()
if (jArray != null) {
edit.putString(SHARE_CHATS_INFO_KEY, jArray.toString())
@ -487,10 +493,12 @@ class TelegramSettings(private val app: TelegramApplication) {
appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "")
liveNowSortType = LiveNowSortType.valueOf(
prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_GROUP.name)
prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_DISTANCE.name)
)
batteryOptimisationAsked = prefs.getBoolean(BATTERY_OPTIMISATION_ASKED,false)
monitoringEnabled = prefs.getBoolean(MONITORING_ENABLED,false)
}
private fun convertShareDevicesToJson():JSONObject?{

View file

@ -75,6 +75,12 @@ class OsmandAidlHelper(private val app: TelegramApplication) {
fun onSearchComplete(resultSet: List<SearchResult>)
}
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 {
@ -1059,4 +1079,16 @@ class OsmandAidlHelper(private val app: TelegramApplication) {
}
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()
}
}
return false
}
}

View file

@ -0,0 +1,476 @@
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.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.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 )";
private final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class);
private final TelegramApplication app;
public SavingTracksDbHelper(TelegramApplication app) {
super(app, DATABASE_NAME, null, DATABASE_VERSION);
this.app = app;
app.getTelegramHelper().addIncomingMessagesListener(new TelegramHelper.TelegramIncomingMessagesListener() {
@Override
public void onReceiveChatLocationMessages(long chatId, @NotNull TdApi.Message... messages) {
for (TdApi.Message message : messages) {
updateLocationMessage(message);
}
}
@Override
public void onDeleteChatLocationMessages(long chatId, @NotNull List<? extends TdApi.Message> messages) {
}
@Override
public void updateLocationMessages() {
}
});
app.getTelegramHelper().addOutgoingMessagesListener(new TelegramHelper.TelegramOutgoingMessagesListener() {
@Override
public void onUpdateMessages(@NotNull List<? extends TdApi.Message> messages) {
for (TdApi.Message message : messages) {
updateLocationMessage(message);
}
}
@Override
public void onDeleteMessages(long chatId, @NotNull List<Long> messages) {
}
@Override
public void onSendLiveLocationError(int code, @NotNull String message) {
}
});
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_SCRIPT);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 3) {
db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_TEXT_INFO + " int");
}
}
public void saveUserDataToGpx(SaveGpxListener listener, File dir, int userId, long chatId, long start, long end) {
GPXFile gpxFile = collectRecordedDataForUserAndChat(userId, chatId, start, end);
if (gpxFile != null && !gpxFile.isEmpty()) {
SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, userId);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
public void saveGpx(SaveGpxListener listener, File dir, GPXFile gpxFile) {
if (gpxFile != null && !gpxFile.isEmpty()) {
SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void updateLocationMessage(TdApi.Message message) {
log.debug(message);
TdApi.MessageContent content = message.content;
int senderId = app.getTelegramHelper().getSenderMessageId(message);
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(senderId, 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(senderId, 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;
}
public GPXFile collectRecordedDataForUserAndChat(int userId, long chatId, long start, long end) {
GPXFile gpxFile = null;
SQLiteDatabase db = getReadableDatabase();
if (db != null && db.isOpen()) {
try {
gpxFile = collectDBTracksForUser(db, userId, chatId, start, end);
} finally {
db.close();
}
}
return gpxFile;
}
public GPXFile collectRecordedDataForUser(int userId, long chatId, long start, long end) {
GPXFile gpxFile = null;
SQLiteDatabase db = getReadableDatabase();
if (db != null && db.isOpen()) {
try {
if (chatId == 0) {
gpxFile = collectDBTracksForUser(db, userId, start, end);
} else {
gpxFile = collectDBTracksForUser(db, userId, chatId, start, end);
}
} finally {
db.close();
}
}
return gpxFile;
}
public ArrayList<GPXFile> collectRecordedDataForUsers(long start, long end, ArrayList<Integer> ignoredUsersIds) {
ArrayList<GPXFile> data = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
if (db != null && db.isOpen()) {
try {
collectDBTracksForUsers(db, data, start, end, ignoredUsersIds);
} finally {
db.close();
}
}
return data;
}
private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long chatId, long start, long end) {
Cursor query = db.rawQuery("SELECT " + 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 + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?"
+ " AND " + TRACK_COL_CHAT_ID + " = ?" + " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end
+ " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(chatId)});
GPXFile gpxFile = null;
long previousTime = 0;
TrkSegment segment = null;
Track track = null;
if (query.moveToFirst()) {
gpxFile = new GPXFile();
gpxFile.chatId = chatId;
gpxFile.userId = userId;
do {
long time = query.getLong(7);
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 GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long start, long end) {
Cursor query = db.rawQuery("SELECT " + 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 + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?"
+ " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end
+ " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)});
GPXFile gpxFile = null;
long previousTime = 0;
TrkSegment segment = null;
Track track = null;
if (query.moveToFirst()) {
gpxFile = new GPXFile();
gpxFile.userId = userId;
do {
long time = query.getLong(7);
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 void collectDBTracksForUsers(SQLiteDatabase db, ArrayList<GPXFile> dataTracks, long start, long end, ArrayList<Integer> ignoredUsersIds) {
Cursor query = db.rawQuery("SELECT " + 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 + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_DATE
+ " BETWEEN " + start + " AND " + end + " ORDER BY " + TRACK_COL_USER_ID + " ASC, "
+ TRACK_COL_CHAT_ID + " ASC, " + TRACK_COL_DATE + " ASC ", null);
long previousTime = 0;
long previousChatId = 0;
int previousUserId = 0;
TrkSegment segment = null;
Track track = null;
GPXFile gpx = new GPXFile();
if (query.moveToFirst()) {
do {
int userId = query.getInt(0);
if (ignoredUsersIds.contains(userId)) {
continue;
}
int chatId = query.getInt(1);
long time = query.getLong(7);
if (previousUserId != userId || previousChatId != chatId) {
gpx = new GPXFile();
gpx.chatId = chatId;
gpx.userId = userId;
previousTime = 0;
track = null;
segment = null;
dataTracks.add(gpx);
}
WptPt pt = new WptPt();
pt.userId = userId;
pt.chatId = chatId;
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);
gpx.tracks.add(track);
}
previousTime = time;
previousUserId = userId;
previousChatId = chatId;
} while (query.moveToNext());
}
query.close();
}
private static class SaveGPXTrackToFileTask extends AsyncTask<Void, Void, List<String>> {
private TelegramApplication app;
private SaveGpxListener listener;
private final GPXFile gpxFile;
private File dir;
private int userId;
SaveGPXTrackToFileTask(TelegramApplication app, SaveGpxListener listener, GPXFile gpxFile, File dir, int userId) {
this.gpxFile = gpxFile;
this.listener = listener;
this.app = app;
this.dir = dir;
this.userId = userId;
}
@Override
protected List<String> doInBackground(Void... params) {
List<String> warnings = new ArrayList<String>();
dir.mkdirs();
if (dir.getParentFile().canWrite()) {
if (dir.exists()) {
// save file
File fout = new File(dir, userId + ".gpx"); //$NON-NLS-1$
if (!gpxFile.isEmpty()) {
WptPt pt = gpxFile.findPointToShow();
TdApi.User user = app.getTelegramHelper().getUser(pt.userId);
String fileName;
if (user != null) {
fileName = TelegramUiHelper.INSTANCE.getUserName(user)
+ "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
} else {
fileName = userId + "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
}
fout = new File(dir, fileName + ".gpx"); //$NON-NLS-1$
int ind = 1;
while (fout.exists()) {
fout = new File(dir, fileName + "_" + (++ind) + ".gpx"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String warn = GPXUtilities.writeGpxFile(fout, gpxFile, app);
if (warn != null) {
warnings.add(warn);
return warnings;
}
}
}
return warnings;
}
@Override
protected void onPostExecute(List<String> warnings) {
if (listener != null) {
if (warnings != null && warnings.isEmpty()) {
listener.onSavingGpxFinish(gpxFile.path);
} else {
listener.onSavingGpxError(warnings);
}
}
}
}
public interface SaveGpxListener {
void onSavingGpxFinish(String path);
void onSavingGpxError(List<String> warnings);
}
}

View file

@ -66,6 +66,8 @@ class TelegramHelper private constructor() {
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 MAX_LOCATION_MESSAGE_HISTORY_SCAN_SEC = 60 * 60 * 24 // one day
const val SEND_NEW_MESSAGE_INTERVAL_SEC = 10 * 60 // 10 minutes
private var helper: TelegramHelper? = null
@ -221,10 +223,10 @@ class TelegramHelper private constructor() {
is MessageUserTextLocation -> content.lastUpdated
else -> Math.max(message.editDate, message.date)
}
}
}
fun isPrivateChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypePrivate
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
private fun isChannel(chat: TdApi.Chat): Boolean {
@ -384,11 +386,20 @@ class TelegramHelper private constructor() {
is TdApi.ChatTypeSecret -> type.userId
else -> 0
}
fun isOsmAndBot(userId: Int) = users[userId]?.username == OSMAND_BOT_USERNAME
fun isBot(userId: Int) = users[userId]?.type is TdApi.UserTypeBot
fun getSenderMessageId(message: TdApi.Message): Int {
val forwardInfo = message.forwardInfo
return if (forwardInfo != null && forwardInfo is TdApi.MessageForwardedFromUser) {
forwardInfo.senderUserId
} else {
message.senderUserId
}
}
fun startLiveMessagesUpdates(interval: Long) {
stopLiveMessagesUpdates()
@ -450,7 +461,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 +492,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")
}
@ -615,6 +627,49 @@ class TelegramHelper private constructor() {
}
}
fun scanChatsHistory() {
log.debug("scanChatsHistory: chatList: ${chatList.size}")
chatList.forEach {
scanChatHistory(it.chatId, 0, 0, 100)
}
}
private fun scanChatHistory(
chatId: Long,
fromMessageId: Long,
offset: Int,
limit: Int,
onlyLocal: Boolean = false
) {
client?.send(TdApi.GetChatHistory(chatId, fromMessageId, offset, limit, onlyLocal)) { 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()) {
messages.forEach {
addNewMessage(it)
}
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)
log.debug("scanChatHistory searchMessageId: ${lastMessage.id}")
} else {
log.debug("scanChatHistory finishForChat: $chatId")
}
}
}
}
}
}
private fun requestUser(id: Int) {
client?.send(TdApi.GetUser(id)) { obj ->
when (obj.constructor) {
@ -671,8 +726,9 @@ 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()) {
log.debug("addNewMessage: $message")
val fromBot = isOsmAndBot(message.senderUserId)
val viaBot = isOsmAndBot(message.viaBotUserId)
val oldContent = message.content
@ -685,10 +741,22 @@ class TelegramHelper private constructor() {
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
message.content = parseOsmAndBotLocation(message)
}
removeOldMessages(message, fromBot, viaBot)
usersLocationMessages[message.id] = message
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
if (message.isOutgoing) {
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(message))
}
} else {
removeOldMessages(message, fromBot, viaBot)
val oldMessage = usersLocationMessages.values.firstOrNull { getSenderMessageId(it) == getSenderMessageId(message) && !fromBot && !viaBot }
val hasNewerMessage = oldMessage != null && (Math.max(message.editDate, message.date) < Math.max(oldMessage.editDate, oldMessage.date))
if (!hasNewerMessage) {
usersLocationMessages[message.id] = message
}
incomingMessagesListeners.forEach {
if (!hasNewerMessage || it is SavingTracksDbHelper) {
it.onReceiveChatLocationMessages(message.chatId, message)
}
}
}
}
}
@ -698,7 +766,7 @@ class TelegramHelper private constructor() {
while (iterator.hasNext()) {
val message = iterator.next().value
if (newMessage.chatId == message.chatId) {
val sameSender = newMessage.senderUserId == message.senderUserId
val sameSender = getSenderMessageId(newMessage) == getSenderMessageId(message)
val viaSameBot = newMessage.viaBotUserId == message.viaBotUserId
if (fromBot || viaBot) {
if ((fromBot && sameSender) || (viaBot && viaSameBot)) {
@ -710,7 +778,8 @@ class TelegramHelper private constructor() {
}
}
}
} else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)) {
} else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)
&& Math.max(newMessage.editDate, newMessage.date) > Math.max(message.editDate, message.date)) {
iterator.remove()
}
}
@ -753,7 +822,7 @@ class TelegramHelper private constructor() {
stopSendingLiveLocationToChat(chatInfo)
}
}
fun getActiveLiveLocationMessages(onComplete: (() -> Unit)?) {
requestingActiveLiveLocationMessages = true
client?.send(TdApi.GetActiveLiveLocationMessages()) { obj ->
@ -1018,23 +1087,6 @@ class TelegramHelper private constructor() {
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
@ -1132,7 +1184,7 @@ class TelegramHelper private constructor() {
if (wasAuthorized != haveAuthorization) {
needRefreshActiveLiveLocationMessages = true
if (haveAuthorization) {
requestChats(true)
requestChats(true, null)
requestCurrentUser()
requestContacts()
}
@ -1145,19 +1197,20 @@ class TelegramHelper private constructor() {
if (isChannelPost) {
return false
}
val content = content
val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE)
val isOsmAndBot = isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId)
if (isOutgoing && !isOsmAndBot) {
if (!(isUserTextLocation || content is TdApi.MessageLocation || isOsmAndBot)) {
return false
}
val lastEdited = Math.max(date, editDate)
if (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - lastEdited > messageActiveTimeSec) {
return false
}
val content = content
val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE)
return when (content) {
is TdApi.MessageLocation -> true
is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || (isUserTextLocation && senderUserId != currentUser?.id)
is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || isUserTextLocation
else -> false
}
}
@ -1234,6 +1287,33 @@ class TelegramHelper private constructor() {
}
}
}
s.startsWith(ALTITUDE_PREFIX) -> {
val altStr = s.removePrefix(ALTITUDE_PREFIX)
try {
val alt = altStr.split(" ").first()
res.altitude = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(SPEED_PREFIX) -> {
val altStr = s.removePrefix(SPEED_PREFIX)
try {
val alt = altStr.split(" ").first()
res.speed = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(HDOP_PREFIX) -> {
val altStr = s.removePrefix(HDOP_PREFIX)
try {
val alt = altStr.split(" ").first()
res.hdop = alt.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(UPDATED_PREFIX) -> {
if (res.lastUpdated == 0) {
val updatedStr = s.removePrefix(UPDATED_PREFIX)
@ -1291,6 +1371,12 @@ class TelegramHelper private constructor() {
internal set
var lastUpdated: Int = 0
internal set
var speed: Double = 0.0
internal set
var altitude: Double = 0.0
internal set
var hdop: Double = 0.0
internal set
override fun getConstructor() = -1
@ -1510,7 +1596,7 @@ class TelegramHelper private constructor() {
lastTelegramUpdateTime = Math.max(message.date, message.editDate)
}
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
it.updateLocationMessages()
}
}
}
@ -1536,6 +1622,7 @@ class TelegramHelper private constructor() {
newContent
}
}
log.debug("UpdateMessageContent " + message.senderUserId)
incomingMessagesListeners.forEach {
it.onReceiveChatLocationMessages(message.chatId, message)
}
@ -1677,4 +1764,4 @@ class TelegramHelper private constructor() {
}// result is already received through UpdateAuthorizationState, nothing to do
}
}
}
}

View file

@ -8,6 +8,7 @@ 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.GPXUtilities
import org.drinkless.td.libcore.telegram.TdApi
object TelegramUiHelper {
@ -129,6 +130,10 @@ object TelegramUiHelper {
}
}
fun gpxToChatItem(helper: TelegramHelper, gpx: GPXUtilities.GPXFile, simpleUserItem: Boolean): GpxChatItem? {
return if (simpleUserItem) gpxToUserGpxChatItem(helper, gpx) else gpxToGpxChatItem(helper, gpx)
}
private fun botMessageToLocationItem(
chat: TdApi.Chat,
content: MessageOsmAndBotLocation
@ -224,6 +229,49 @@ object TelegramUiHelper {
}
}
private fun gpxToGpxChatItem(
helper: TelegramHelper,
gpx: GPXUtilities.GPXFile
): GpxChatItem? {
val user = helper.getUser(gpx.userId) ?: return null
val chat = helper.getChat(gpx.chatId) ?: return null
return GpxChatItem().apply {
chatId = chat.id
chatTitle = chat.title
gpxFile = gpx
name = TelegramUiHelper.getUserName(user)
if (helper.isGroup(chat)) {
photoPath = helper.getUserPhotoPath(user)
groupPhotoPath = chat.photo?.small?.local?.path
} else {
photoPath = user.profilePhoto?.small?.local?.path
}
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
placeholderId = R.drawable.img_user_picture
userId = user.id
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
chatWithBot = helper.isBot(userId)
lastUpdated = (gpx.modifiedTime / 1000).toInt()
}
}
private fun gpxToUserGpxChatItem(
helper: TelegramHelper,
gpx: GPXUtilities.GPXFile
): GpxChatItem? {
val user = helper.getUser(gpx.userId) ?: return null
return GpxChatItem().apply {
gpxFile = gpx
name = TelegramUiHelper.getUserName(user)
photoPath = user.profilePhoto?.small?.local?.path
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
placeholderId = R.drawable.img_user_picture
userId = user.id
chatWithBot = helper.isBot(userId)
lastUpdated = (gpx.modifiedTime / 1000).toInt()
}
}
abstract class ListItem {
var chatId: Long = 0
@ -272,6 +320,24 @@ object TelegramUiHelper {
override fun getVisibleName() = chatTitle
}
class GpxChatItem : ListItem() {
var gpxFile: GPXUtilities.GPXFile? = 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

View file

@ -11,10 +11,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.*
import net.osmand.Location
import net.osmand.data.LatLon
import net.osmand.telegram.R
@ -22,6 +19,7 @@ import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
import net.osmand.telegram.TelegramSettings
import net.osmand.telegram.helpers.SavingTracksDbHelper
import net.osmand.telegram.helpers.TelegramHelper.*
import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem
@ -33,6 +31,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.util.*
private const val CHAT_VIEW_TYPE = 0
private const val LOCATION_ITEM_VIEW_TYPE = 1

View file

@ -27,6 +27,7 @@ import net.osmand.telegram.ui.MyLocationTabFragment.ActionButtonsListener
import net.osmand.telegram.ui.views.LockableViewPager
import net.osmand.telegram.utils.*
import org.drinkless.td.libcore.telegram.TdApi
import java.io.File
import java.lang.ref.WeakReference
const val OPEN_MY_LOCATION_TAB_KEY = "open_my_location_tab"
@ -35,6 +36,7 @@ private const val PERMISSION_REQUEST_LOCATION = 1
private const val MY_LOCATION_TAB_POS = 0
private const val LIVE_NOW_TAB_POS = 1
private const val TIMELINE_TAB_POS = 2
class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListener, TelegramIncomingMessagesListener {
@ -54,6 +56,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
private var myLocationTabFragment: MyLocationTabFragment? = null
private var liveNowTabFragment: LiveNowTabFragment? = null
private var timelineTabFragment: TimelineTabFragment? = null
private lateinit var buttonsBar: LinearLayout
private lateinit var bottomNav: BottomNavigationView
@ -72,7 +75,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
val viewPager = findViewById<LockableViewPager>(R.id.view_pager).apply {
swipeLocked = true
offscreenPageLimit = 2
offscreenPageLimit = 3
adapter = ViewPagerAdapter(supportFragmentManager)
}
@ -82,11 +85,13 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
when (it.itemId) {
R.id.action_my_location -> pos = MY_LOCATION_TAB_POS
R.id.action_live_now -> pos = LIVE_NOW_TAB_POS
R.id.action_timeline -> pos = TIMELINE_TAB_POS
}
if (pos != -1 && pos != viewPager.currentItem) {
when (pos) {
MY_LOCATION_TAB_POS -> liveNowTabFragment?.tabClosed()
LIVE_NOW_TAB_POS -> liveNowTabFragment?.tabOpened()
TIMELINE_TAB_POS -> liveNowTabFragment?.tabClosed()
}
viewPager.currentItem = pos
return@setOnNavigationItemSelectedListener true
@ -139,10 +144,10 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
if (fragment is TelegramListener) {
listeners.add(WeakReference(fragment))
}
if (fragment is MyLocationTabFragment) {
myLocationTabFragment = fragment
} else if (fragment is LiveNowTabFragment) {
liveNowTabFragment = fragment
when (fragment) {
is MyLocationTabFragment -> myLocationTabFragment = fragment
is LiveNowTabFragment -> liveNowTabFragment = fragment
is TimelineTabFragment -> timelineTabFragment = fragment
}
}
@ -317,6 +322,15 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
android.os.Process.killProcess(android.os.Process.myPid())
}
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)
}
fun loginTelegram() {
if (telegramHelper.getTelegramAuthorizationState() != TelegramAuthorizationState.CLOSED) {
telegramHelper.logout()
@ -463,7 +477,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
class ViewPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
private val fragments = listOf<Fragment>(MyLocationTabFragment(), LiveNowTabFragment())
private val fragments = listOf<Fragment>(MyLocationTabFragment(), LiveNowTabFragment(), TimelineTabFragment())
override fun getItem(position: Int) = fragments[position]

View file

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

View file

@ -0,0 +1,272 @@
package net.osmand.telegram.ui
import android.app.DatePickerDialog
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.support.annotation.DrawableRes
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
import net.osmand.PlatformUtil
import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.telegram.helpers.TelegramUiHelper.ListItem
import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.GPXUtilities
import net.osmand.telegram.utils.OsmandFormatter
import java.util.*
class TimelineTabFragment : Fragment() {
private val log = PlatformUtil.getLog(TimelineTabFragment::class.java)
private val app: TelegramApplication
get() = activity?.application as TelegramApplication
private val telegramHelper get() = app.telegramHelper
private val settings get() = app.settings
private lateinit var adapter: LiveNowListAdapter
private lateinit var dateStartBtn: TextView
private lateinit var dateEndBtn: TextView
private lateinit var mainView: View
private var start = 0L
private var end = 0L
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mainView = inflater.inflate(R.layout.fragment_timeline_tab, container, false)
val appBarLayout = mainView.findViewById<View>(R.id.app_bar_layout)
start = System.currentTimeMillis()
end = System.currentTimeMillis()
AndroidUtils.addStatusBarPadding19v(context!!, appBarLayout)
adapter = LiveNowListAdapter()
mainView.findViewById<RecyclerView>(R.id.recycler_view).apply {
layoutManager = LinearLayoutManager(context)
adapter = this@TimelineTabFragment.adapter
}
val switcher = mainView.findViewById<Switch>(R.id.monitoring_switcher)
val monitoringTv = mainView.findViewById<TextView>(R.id.monitoring_title)
monitoringTv.setText(if (settings.monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled)
mainView.findViewById<View>(R.id.monitoring_container).setOnClickListener {
val monitoringEnabled = !settings.monitoringEnabled
settings.monitoringEnabled = monitoringEnabled
switcher.isChecked = monitoringEnabled
monitoringTv.setText(if (monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled)
}
dateStartBtn = mainView.findViewById<TextView>(R.id.date_start_btn).apply {
setOnClickListener {
selectStartDate()
}
setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_start), null, null, null)
}
dateEndBtn = mainView.findViewById<TextView>(R.id.date_end_btn).apply {
setOnClickListener {
selectEndDate()
}
setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_add), null, null, null)
}
setupBtnTextColor(dateStartBtn)
setupBtnTextColor(dateEndBtn)
return mainView
}
private fun setupBtnTextColor(textView: TextView) {
textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light))
}
private fun selectStartDate() {
val dateFromDialog =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
val from = Calendar.getInstance()
from.set(Calendar.YEAR, year)
from.set(Calendar.MONTH, monthOfYear)
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
from.set(Calendar.HOUR_OF_DAY, 0)
from.clear(Calendar.MINUTE)
from.clear(Calendar.SECOND)
from.clear(Calendar.MILLISECOND)
start = from.timeInMillis
updateList()
updateDateButtons()
}
val startCalendar = Calendar.getInstance()
startCalendar.timeInMillis = start
DatePickerDialog(context, dateFromDialog,
startCalendar.get(Calendar.YEAR),
startCalendar.get(Calendar.MONTH),
startCalendar.get(Calendar.DAY_OF_MONTH)
).show()
}
private fun selectEndDate() {
val dateFromDialog =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
val from = Calendar.getInstance()
from.set(Calendar.YEAR, year)
from.set(Calendar.MONTH, monthOfYear)
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
from.set(Calendar.HOUR_OF_DAY, 23)
from.set(Calendar.MINUTE, 59)
from.set(Calendar.SECOND, 59)
from.set(Calendar.MILLISECOND, 999)
end = from.timeInMillis
updateList()
updateDateButtons()
}
val endCalendar = Calendar.getInstance()
endCalendar.timeInMillis = end
DatePickerDialog(context, dateFromDialog,
endCalendar.get(Calendar.YEAR),
endCalendar.get(Calendar.MONTH),
endCalendar.get(Calendar.DAY_OF_MONTH)
).show()
}
private fun updateDateButtons() {
dateStartBtn.text = OsmandFormatter.getFormattedDate(start / 1000)
dateEndBtn.text = OsmandFormatter.getFormattedDate(end / 1000)
dateEndBtn.setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_end), null, null, null)
}
private fun getPressedStateIcon(@DrawableRes iconId: Int): Drawable? {
val normal = app.uiUtils.getActiveIcon(iconId)
if (Build.VERSION.SDK_INT >= 21) {
val active = app.uiUtils.getIcon(iconId, R.color.ctrl_light)
if (normal != null && active != null) {
return AndroidUtils.createPressedStateListDrawable(normal, active)
}
}
return normal
}
private fun updateList() {
val res = mutableListOf<ListItem>()
val s = System.currentTimeMillis()
log.debug("updateList $s")
val ignoredUsersIds = ArrayList<Int>()
val currentUserId = telegramHelper.getCurrentUser()?.id
if (currentUserId != null) {
val currentUserGpx:GPXUtilities.GPXFile? = app.savingTracksDbHelper.collectRecordedDataForUser(currentUserId, 0, start, end)
if (currentUserGpx != null) {
TelegramUiHelper.gpxToChatItem(telegramHelper, currentUserGpx, true)?.also {
res.add(it)
}
}
ignoredUsersIds.add(currentUserId)
}
val gpxFiles = app.savingTracksDbHelper.collectRecordedDataForUsers(start, end, ignoredUsersIds)
val e = System.currentTimeMillis()
gpxFiles.forEach {
TelegramUiHelper.gpxToChatItem(telegramHelper, it,false)?.also { chatItem ->
res.add(chatItem)
}
}
adapter.items = sortAdapterItems(res)
log.debug("updateList $s dif: ${e - s}")
}
private fun sortAdapterItems(list: MutableList<ListItem>): MutableList<ListItem> {
val currentUserId = telegramHelper.getCurrentUser()?.id ?: 0
list.sortWith(java.util.Comparator { lhs, rhs ->
when (currentUserId) {
lhs.userId -> return@Comparator 1
rhs.userId -> return@Comparator 1
else -> return@Comparator lhs.name.compareTo(rhs.name)
}
})
return list
}
inner class LiveNowListAdapter : RecyclerView.Adapter<BaseViewHolder>() {
var items: List<ListItem> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val inflater = LayoutInflater.from(parent.context)
return BaseViewHolder(inflater.inflate(R.layout.live_now_chat_card, parent, false))
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
val lastItem = position == itemCount - 1
val item = items[position]
val currentUserId = telegramHelper.getCurrentUser()?.id ?: 0
TelegramUiHelper.setupPhoto(app, holder.icon, item.photoPath, R.drawable.img_user_picture_active, false)
holder.title?.text = item.name
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
holder.lastTelegramUpdateTime?.visibility = View.GONE
if (item is TelegramUiHelper.GpxChatItem) {
val gpx = item.gpxFile
val groupDescrRowVisible = (!item.privateChat || item.chatWithBot) && item.userId != currentUserId
if (groupDescrRowVisible) {
holder.groupDescrContainer?.visibility = View.VISIBLE
holder.groupTitle?.text = item.getVisibleName()
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
} else {
holder.groupDescrContainer?.visibility = View.GONE
}
holder.userRow?.setOnClickListener {
if (gpx != null) {
childFragmentManager.also {
UserGpxInfoFragment.showInstance(it, gpx, start, end)
}
}
}
holder.imageButton?.visibility = View.GONE
holder.showOnMapRow?.visibility = View.GONE
holder.bottomDivider?.visibility = if (lastItem) View.GONE else View.VISIBLE
holder.topDivider?.visibility = if (position != 0) View.GONE else View.VISIBLE
}
}
override fun getItemCount() = items.size
inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val icon: ImageView? = view.findViewById(R.id.icon)
val title: TextView? = view.findViewById(R.id.title)
val description: TextView? = view.findViewById(R.id.description)
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
val lastTelegramUpdateTime: TextView? = view.findViewById(R.id.last_telegram_update_time)
val userRow: View? = view.findViewById(R.id.user_row)
val groupDescrContainer: View? = view.findViewById(R.id.group_container)
val groupImage: ImageView? = view.findViewById(R.id.group_icon)
val groupTitle: TextView? = view.findViewById(R.id.group_title)
val imageButton: ImageView? = view.findViewById(R.id.image_button)
val showOnMapRow: View? = view.findViewById(R.id.show_on_map_row)
val topDivider: View? = view.findViewById(R.id.top_divider)
val bottomDivider: View? = view.findViewById(R.id.bottom_divider)
}
}
}

View file

@ -0,0 +1,321 @@
package net.osmand.telegram.ui
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.support.v4.app.FragmentManager
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import net.osmand.PlatformUtil
import net.osmand.aidl.gpx.AGpxBitmap
import net.osmand.telegram.R
import net.osmand.telegram.helpers.OsmandAidlHelper
import net.osmand.telegram.helpers.SavingTracksDbHelper
import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.GPXUtilities
import net.osmand.telegram.utils.OsmandFormatter
import net.osmand.util.Algorithms
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class UserGpxInfoFragment : BaseDialogFragment() {
private val log = PlatformUtil.getLog(UserGpxInfoFragment::class.java)
private val uiUtils get() = app.uiUtils
private lateinit var gpxFile: GPXUtilities.GPXFile
private lateinit var dateStartBtn: TextView
private lateinit var timeStartBtn: TextView
private lateinit var dateEndBtn: TextView
private lateinit var timeEndBtn: TextView
private lateinit var avgElevationTv: TextView
private lateinit var avgSpeedTv: TextView
private lateinit var totalDistanceTv: TextView
private lateinit var timeSpanTv: TextView
private var startCalendar = Calendar.getInstance()
private var endCalendar = Calendar.getInstance()
override fun onCreateView(
inflater: LayoutInflater,
parent: ViewGroup?,
savedInstanceState: Bundle?
): View {
val mainView = inflater.inflate(R.layout.fragment_user_gpx_info, parent)
AndroidUtils.addStatusBarPadding19v(context!!, mainView)
readFromBundle(savedInstanceState ?: arguments)
val userId = gpxFile.userId
val chatId = gpxFile.chatId
val user = app.telegramHelper.getUser(userId)
if (user != null) {
mainView.findViewById<TextView>(R.id.title).text = TelegramUiHelper.getUserName(user)
TelegramUiHelper.setupPhoto(app, mainView.findViewById<ImageView>(R.id.user_icon),
telegramHelper.getUserPhotoPath(user), R.drawable.img_user_placeholder, false)
}
app.osmandAidlHelper.setGpxBitmapCreatedListener(
object : OsmandAidlHelper.GpxBitmapCreatedListener {
override fun onGpxBitmapCreated(bitmap: AGpxBitmap) {
activity?.runOnUiThread {
mainView.findViewById<ImageView>(R.id.gpx_map).setImageDrawable(BitmapDrawable(app.resources, bitmap.bitmap))
}
}
})
updateGPXMap()
val backBtn = mainView.findViewById<ImageView>(R.id.back_button)
backBtn.setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_arrow_back))
backBtn.setOnClickListener {
dismiss()
}
dateStartBtn = mainView.findViewById<TextView>(R.id.date_start_btn)
timeStartBtn = mainView.findViewById<TextView>(R.id.time_start_btn)
dateEndBtn = mainView.findViewById<TextView>(R.id.date_end_btn)
timeEndBtn = mainView.findViewById<TextView>(R.id.time_end_btn)
dateStartBtn.setOnClickListener { selectStartDate() }
timeStartBtn.setOnClickListener { selectStartTime() }
dateEndBtn.setOnClickListener { selectEndDate() }
timeEndBtn.setOnClickListener { selectEndTime() }
setupBtnTextColor(dateStartBtn)
setupBtnTextColor(timeStartBtn)
setupBtnTextColor(dateEndBtn)
setupBtnTextColor(timeEndBtn)
updateDateAndTimeButtons()
avgElevationTv = mainView.findViewById<TextView>(R.id.average_altitude_text)
avgSpeedTv = mainView.findViewById<TextView>(R.id.average_speed_text)
totalDistanceTv = mainView.findViewById<TextView>(R.id.distance_text)
timeSpanTv = mainView.findViewById<TextView>(R.id.duration_text)
mainView.findViewById<ImageView>(R.id.average_altitude_icon).apply {
setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range))
}
mainView.findViewById<ImageView>(R.id.average_speed_icon).apply {
setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_speed_average))
}
mainView.findViewById<ImageView>(R.id.distance_icon).apply {
setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range))
}
mainView.findViewById<ImageView>(R.id.duration_icon).apply {
setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range))
}
updateGPXStatisticRow()
mainView.findViewById<ImageView>(R.id.open_in_osmand_icon).setImageResource(R.drawable.ic_logo_osmand_free)
mainView.findViewById<LinearLayout>(R.id.open_in_osmand_btn).apply {
setOnClickListener {
val gpx = gpxFile
if (gpx.path.isNotEmpty()) {
openGpx(gpx.path)
} else {
saveCurrentGpxToFile(object :
SavingTracksDbHelper.SaveGpxListener {
override fun onSavingGpxFinish(path: String) {
openGpx(path)
}
override fun onSavingGpxError(warnings: MutableList<String>?) {
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
}
})
}
}
}
mainView.findViewById<ImageView>(R.id.share_gpx_icon).setImageDrawable(uiUtils.getActiveIcon(R.drawable.ic_action_share))
mainView.findViewById<LinearLayout>(R.id.share_gpx_btn).apply {
setOnClickListener {
val gpx = gpxFile
if (gpx.path.isNotEmpty()) {
(activity as MainActivity).shareGpx(gpx.path)
} else {
saveCurrentGpxToFile(object :
SavingTracksDbHelper.SaveGpxListener {
override fun onSavingGpxFinish(path: String) {
(activity as MainActivity).shareGpx(path)
}
override fun onSavingGpxError(warnings: MutableList<String>?) {
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
}
})
}
}
}
return mainView
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(START_KEY, startCalendar.timeInMillis)
outState.putLong(END_KEY, endCalendar.timeInMillis)
}
private fun openGpx(path: String) {
val fileUri = AndroidUtils.getUriForFile(app, File(path))
val openGpxIntent = Intent(Intent.ACTION_VIEW)
openGpxIntent.setDataAndType(fileUri, "application/gpx+xml")
openGpxIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val resolved = activity?.packageManager?.resolveActivity(openGpxIntent, PackageManager.MATCH_DEFAULT_ONLY)
if (resolved != null) {
startActivity(openGpxIntent)
}
}
private fun saveCurrentGpxToFile(listener: SavingTracksDbHelper.SaveGpxListener) {
app.savingTracksDbHelper.saveGpx(listener, app.getExternalFilesDir(null), gpxFile)
}
private fun readFromBundle(bundle: Bundle?) {
bundle?.also {
startCalendar.timeInMillis = it.getLong(START_KEY)
endCalendar.timeInMillis = it.getLong(END_KEY)
}
}
private fun setupBtnTextColor(textView: TextView) {
textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light))
}
private fun updateGpxInfo() {
gpxFile = app.savingTracksDbHelper.collectRecordedDataForUser(gpxFile.userId, gpxFile.chatId, startCalendar.timeInMillis, endCalendar.timeInMillis)
updateGPXStatisticRow()
updateDateAndTimeButtons()
updateGPXMap()
}
private fun updateDateAndTimeButtons() {
dateStartBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(startCalendar.timeInMillis)
dateEndBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(endCalendar.timeInMillis)
timeStartBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(startCalendar.timeInMillis)
timeEndBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(endCalendar.timeInMillis)
}
private fun updateGPXStatisticRow() {
val analysis: GPXUtilities.GPXTrackAnalysis = gpxFile.getAnalysis(0)
avgElevationTv.text = OsmandFormatter.getFormattedAlt(analysis.avgElevation, app)
avgSpeedTv.text = if (analysis.isSpeedSpecified) OsmandFormatter.getFormattedSpeed(analysis.avgSpeed, app) else ""
totalDistanceTv.text = OsmandFormatter.getFormattedDistance(analysis.totalDistance, app)
timeSpanTv.text = Algorithms.formatDuration((analysis.timeSpan / 1000).toInt(), true)
}
private fun updateGPXMap() {
saveCurrentGpxToFile(object :
SavingTracksDbHelper.SaveGpxListener {
override fun onSavingGpxFinish(path: String) {
val mgr = activity?.getSystemService(Context.WINDOW_SERVICE)
if (mgr != null) {
val dm = DisplayMetrics()
(mgr as WindowManager).defaultDisplay.getMetrics(dm)
val widthPixels = dm.widthPixels - (2 * app.resources.getDimensionPixelSize(R.dimen.content_padding_standard))
val heightPixels = AndroidUtils.dpToPx(app, 152f)
val gpxUri = AndroidUtils.getUriForFile(app, File(path))
app.osmandAidlHelper.getBitmapForGpx(gpxUri, dm.density , widthPixels, heightPixels, GPX_TRACK_COLOR)
}
}
override fun onSavingGpxError(warnings: MutableList<String>?) {
log.debug("onSavingGpxError ${warnings?.firstOrNull()}")
}
})
}
private fun selectStartDate() {
val dateFromDialog =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
startCalendar.set(Calendar.YEAR, year)
startCalendar.set(Calendar.MONTH, monthOfYear)
startCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
updateGpxInfo()
}
DatePickerDialog(context, dateFromDialog,
startCalendar.get(Calendar.YEAR),
startCalendar.get(Calendar.MONTH),
startCalendar.get(Calendar.DAY_OF_MONTH)).show()
}
private fun selectStartTime() {
TimePickerDialog(context,
TimePickerDialog.OnTimeSetListener { _, hours, minutes ->
startCalendar.set(Calendar.HOUR_OF_DAY, hours)
startCalendar.set(Calendar.MINUTE, minutes)
updateGpxInfo()
}, 0, 0, true).show()
}
private fun selectEndDate() {
val dateFromDialog =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
endCalendar.set(Calendar.YEAR, year)
endCalendar.set(Calendar.MONTH, monthOfYear)
endCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
updateGpxInfo()
}
DatePickerDialog(context, dateFromDialog,
endCalendar.get(Calendar.YEAR),
endCalendar.get(Calendar.MONTH),
endCalendar.get(Calendar.DAY_OF_MONTH)).show()
}
private fun selectEndTime() {
TimePickerDialog(context,
TimePickerDialog.OnTimeSetListener { _, hours, minutes ->
endCalendar.set(Calendar.HOUR_OF_DAY, hours)
endCalendar.set(Calendar.MINUTE, minutes)
updateGpxInfo()
}, 0, 0, true).show()
}
companion object {
private const val TAG = "UserGpxInfoFragment"
private const val START_KEY = "start_key"
private const val END_KEY = "end_key"
private const val GPX_TRACK_COLOR = -65536
fun showInstance(fm: FragmentManager, gpxFile: GPXUtilities.GPXFile, start: Long, end: Long): Boolean {
return try {
val fragment = UserGpxInfoFragment().apply {
arguments = Bundle().apply {
putLong(START_KEY, start)
putLong(END_KEY, end)
}
}
fragment.gpxFile = gpxFile
fragment.show(fm, TAG)
true
} catch (e: RuntimeException) {
false
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
package net.osmand.telegram.utils;
import android.content.Context;
/**
*/
public interface LocationPoint {
public double getLatitude();
public double getLongitude();
public int getColor();
public boolean isVisible();
// public PointDescription getPointDescription(Context ctx);
// public String getSpeakableName();
//public void prepareCommandPlayer(CommandBuilder cmd, String names);
}

View file

@ -23,6 +23,7 @@ object OsmandFormatter {
private const val SHORT_TIME_FORMAT = "%02d:%02d"
private const val SIMPLE_TIME_OF_DAY_FORMAT = "HH:mm"
private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss"
private const val SHORT_DATE_FORMAT = "dd MMM yyyy"
private const val MIN_DURATION_FOR_DATE_FORMAT = 48 * 60 * 60
@ -77,8 +78,13 @@ object OsmandFormatter {
}
}
fun getFormattedDate(seconds: Long): String =
SimpleDateFormat(SIMPLE_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L)
fun getFormattedDate(seconds: Long, shortFormat: Boolean = false): String {
return if (shortFormat) {
SimpleDateFormat(SIMPLE_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L)
} else {
SimpleDateFormat(SHORT_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L)
}
}
fun getListItemLiveTimeDescr(ctx: TelegramApplication, lastUpdated: Int, prefix: String = ""): String {
return if (lastUpdated > 0) {

View file

@ -163,24 +163,12 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.kml" />
<data android:pathPattern=".*\\..*\\.kml" />
<data android:pathPattern=".*\\..*\\..*\\.kml" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kml" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kml" />
</intent-filter>
<intent-filter
android:label="@string/app_name"
android:priority="50">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file"/>
<data android:scheme="data"/>
<data android:host="*"/>
<data android:mimeType="*/*"/>
<data android:pathPattern="*.kml"/>
<data android:pathPattern=".*\\.kml" />
<data android:pathPattern=".*\\..*\\.kml" />
<data android:pathPattern=".*\\..*\\..*\\.kml" />
@ -194,8 +182,11 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:scheme="data"/>
<data android:host="*"/>
<data android:mimeType="*/*"/>
<data android:pathPattern=".*\\.kmz" />
<data android:pathPattern=".*\\..*\\.kmz" />
<data android:pathPattern=".*\\..*\\..*\\.kmz" />
@ -271,9 +262,12 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:scheme="data"/>
<data android:host="*"/>
<data android:mimeType="*/*"/>
<data android:pathPattern="*.sqlitedb" />
<data android:pathPattern=".*\\.sqlitedb" />
<data android:pathPattern=".*\\..*\\.sqlitedb" />
<data android:pathPattern=".*\\..*\\..*\\.sqlitedb" />

View file

@ -21,6 +21,7 @@
<asset source="voice/fa/fa_tts.js" destination="voice/fa-tts/fa_tts.js" mode="overwriteOnlyIfExists" />
<asset source="voice/fi/fi_tts.js" destination="voice/fi-tts/fi_tts.js" mode="overwriteOnlyIfExists" />
<asset source="voice/fr/fr_tts.js" destination="voice/fr-tts/fr_tts.js" mode="alwaysOverwriteOrCopy" />
<asset source="voice/gn-py/gn-py_tts.js" destination="voice/gn-py-tts/gn-py_tts.js" mode="overwriteOnlyIfExists" />
<!--<asset source="voice/he/he_tts.js" destination="voice/he-tts/he_tts.js" mode="overwriteOnlyIfExists" />-->
<asset source="voice/nb/nb_tts.js" destination="voice/nb-tts/nb_tts.js" mode="overwriteOnlyIfExists" />
<asset source="voice/hi/hi_tts.js" destination="voice/hi-tts/hi_tts.js" mode="overwriteOnlyIfExists" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

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