Compare commits
24 commits
markers_re
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
9d39e2f805 | ||
|
3ce4082997 | ||
|
f528ae4b43 | ||
|
ed0f6d6882 | ||
|
f0dc959bdc | ||
|
aa2c111e88 | ||
|
74efa9f2b8 | ||
|
92d59f6521 | ||
|
d1fb9d5129 | ||
|
22237493a6 | ||
|
3754cf7822 | ||
|
2437fb722a | ||
|
8accf25b60 | ||
|
974ad80734 | ||
|
500cb007b8 | ||
|
e6be5dcab5 | ||
|
e2d659e859 | ||
|
4de1aa9193 | ||
|
13b6b74e13 | ||
|
fbec2abd19 | ||
|
0bdf2dfa8a | ||
|
aa6b04a924 | ||
|
149b89090b | ||
|
ff7aa932a3 |
43 changed files with 1099 additions and 278 deletions
|
@ -186,7 +186,7 @@ public class RouteColorize {
|
||||||
public List<RouteColorizationPoint> getResult(boolean simplify) {
|
public List<RouteColorizationPoint> getResult(boolean simplify) {
|
||||||
List<RouteColorizationPoint> result = new ArrayList<>();
|
List<RouteColorizationPoint> result = new ArrayList<>();
|
||||||
if (simplify) {
|
if (simplify) {
|
||||||
result = simplify();
|
result = simplify(zoom);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < latitudes.length; i++) {
|
for (int i = 0; i < latitudes.length; i++) {
|
||||||
result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
|
result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
|
||||||
|
@ -200,7 +200,7 @@ public class RouteColorize {
|
||||||
|
|
||||||
public int getColorByValue(double value) {
|
public int getColorByValue(double value) {
|
||||||
if (Double.isNaN(value)) {
|
if (Double.isNaN(value)) {
|
||||||
value = (minValue + maxValue) / 2;
|
value = colorizationType == ColorizationType.SLOPE ? minValue : (minValue + maxValue) / 2;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < palette.length - 1; i++) {
|
for (int i = 0; i < palette.length - 1; i++) {
|
||||||
if (value == palette[i][VALUE_INDEX])
|
if (value == palette[i][VALUE_INDEX])
|
||||||
|
@ -242,7 +242,7 @@ public class RouteColorize {
|
||||||
return rgbaToDecimal(0, 0, 0, 0);
|
return rgbaToDecimal(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RouteColorizationPoint> simplify() {
|
public List<RouteColorizationPoint> simplify(int zoom) {
|
||||||
if (dataList == null) {
|
if (dataList == null) {
|
||||||
dataList = new ArrayList<>();
|
dataList = new ArrayList<>();
|
||||||
for (int i = 0; i < latitudes.length; i++) {
|
for (int i = 0; i < latitudes.length; i++) {
|
||||||
|
@ -266,6 +266,8 @@ public class RouteColorize {
|
||||||
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
|
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
|
||||||
simplified.addAll(getExtremums(sublist));
|
simplified.addAll(getExtremums(sublist));
|
||||||
}
|
}
|
||||||
|
Node lastSurvivedPoint = result.get(result.size() - 1);
|
||||||
|
simplified.add(dataList.get((int) lastSurvivedPoint.getId()));
|
||||||
return simplified;
|
return simplified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
OsmAnd/res/drawable/ic_action_alert_circle.xml
Normal file
10
OsmAnd/res/drawable/ic_action_alert_circle.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22ZM11,14V7H13V14H11ZM11,18V16H13V18H11Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
18
OsmAnd/res/drawable/ic_action_cellular.xml
Normal file
18
OsmAnd/res/drawable/ic_action_cellular.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M20,12C20,15.4738 17.7859,18.4304 14.692,19.5358L14.9449,21.5593C19.0304,20.3022 22,16.4979 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,16.4979 4.9696,20.3022 9.0551,21.5593L9.308,19.5358C6.2141,18.4304 4,15.4738 4,12C4,7.5817 7.5817,4 12,4C16.4183,4 20,7.5817 20,12Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M18,12C18,14.4466 16.5357,16.5511 14.4356,17.485L14.1701,15.3607C15.2713,14.6482 16,13.4092 16,12C16,9.7909 14.2091,8 12,8C9.7909,8 8,9.7909 8,12C8,13.4092 8.7287,14.6482 9.8299,15.3607L9.5644,17.485C7.4643,16.5511 6,14.4466 6,12C6,8.6863 8.6863,6 12,6C15.3137,6 18,8.6863 18,12Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14,12C14,13.1046 13.1046,14 12,14C10.8954,14 10,13.1046 10,12C10,10.8954 10.8954,10 12,10C13.1046,10 14,10.8954 14,12Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11,15V22H13V15H11Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
15
OsmAnd/res/drawable/ic_action_cellular_disable.xml
Normal file
15
OsmAnd/res/drawable/ic_action_cellular_disable.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M22.0001,12C22.0001,13.9623 21.4349,15.7926 20.4583,17.337L18.9991,15.8777C19.6369,14.7291 20.0001,13.407 20.0001,12C20.0001,7.5817 16.4183,4 12.0001,4C10.5931,4 9.271,4.3632 8.1223,5.0009L6.6631,3.5417C8.2075,2.5652 10.0378,2 12.0001,2C17.5229,2 22.0001,6.4771 22.0001,12Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M18.0001,12C18.0001,12.8478 17.8242,13.6545 17.507,14.3857L15.9203,12.7989C15.9726,12.5407 16.0001,12.2736 16.0001,12C16.0001,9.7909 14.2092,8 12.0001,8C11.7265,8 11.4593,8.0275 11.2012,8.0798L9.6144,6.493C10.3456,6.1758 11.1523,6 12.0001,6C15.3138,6 18.0001,8.6863 18.0001,12Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14.3449,16.759L14.4357,17.485C14.5806,17.4206 14.7225,17.3506 14.8611,17.2752L16.3203,18.7344C15.8138,19.0599 15.2682,19.33 14.692,19.5358L14.945,21.5593C15.9623,21.2463 16.9105,20.7753 17.7609,20.1749L21.293,23.7071L22.7072,22.2928L2.7072,2.2929L1.293,3.7071L3.8251,6.2392C2.6754,7.8677 2.0001,9.855 2.0001,12C2.0001,16.4979 4.9696,20.3022 9.0552,21.5593L9.3081,19.5358C6.2141,18.4304 4.0001,15.4738 4.0001,12C4.0001,10.4087 4.4647,8.9258 5.2657,7.6798L6.7248,9.1389C6.2626,9.9894 6.0001,10.964 6.0001,12C6.0001,14.4466 7.4644,16.5511 9.5644,17.485L9.83,15.3607C8.7288,14.6482 8.0001,13.4092 8.0001,12C8.0001,11.5256 8.0826,11.0705 8.2342,10.6483L10.0555,12.4696C10.2305,13.197 10.8031,13.7695 11.5305,13.9446L12.5859,15H11.0001V22H13.0001V15.4142L14.3449,16.759Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
9
OsmAnd/res/drawable/ic_action_cloud.xml
Normal file
9
OsmAnd/res/drawable/ic_action_cloud.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6,18V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H6Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
10
OsmAnd/res/drawable/ic_action_cloud_alert.xml
Normal file
10
OsmAnd/res/drawable/ic_action_cloud_alert.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6,17.9725V18H19C20.6569,18 22,16.6569 22,15C22,13.3431 20.6569,12 19,12C18.6485,12 18.3111,12.0605 17.9976,12.1716C17.9992,12.1146 18,12.0574 18,12C18,8.6863 15.3137,6 12,6C9.7752,6 7.8332,7.2109 6.7971,9.0097C6.6989,9.0033 6.5998,9 6.5,9C4.0147,9 2,11.0147 2,13.5C2,15.8163 3.75,17.7238 6,17.9725ZM11,13V8H13V13H11ZM11,17V15H13V17H11Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
9
OsmAnd/res/drawable/ic_action_cloud_upload.xml
Normal file
9
OsmAnd/res/drawable/ic_action_cloud_upload.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M14,18V15H16.5L12.5,10L8.5,15H11V18H6V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H14Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
12
OsmAnd/res/drawable/ic_action_cloud_upload_colored.xml
Normal file
12
OsmAnd/res/drawable/ic_action_cloud_upload_colored.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6,18V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H6Z"
|
||||||
|
android:fillColor="#6C19FF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11,15H8.5L12.5,9.5L16.5,15H14V20H11V15Z"
|
||||||
|
android:fillColor="#FFC30D"/>
|
||||||
|
</vector>
|
15
OsmAnd/res/drawable/ic_action_read_from_file.xml
Normal file
15
OsmAnd/res/drawable/ic_action_read_from_file.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M14,2L20,8H16C14.8954,8 14,7.1046 14,6V2Z"
|
||||||
|
android:strokeAlpha="0.5"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillAlpha="0.5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M4,4C4,2.8954 4.8954,2 6,2H14V6C14,7.1046 14.8954,8 16,8H20V20C20,21.1046 19.1046,22 18,22H6C4.8954,22 4,21.1046 4,20V4ZM11,18H13V13H15L12,9L9,13H11V18Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
37
OsmAnd/res/drawable/ic_action_restore.xml
Normal file
37
OsmAnd/res/drawable/ic_action_restore.xml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,5C12.5523,5 13,4.5523 13,4H17V8.0549C16.6717,8.0186 16.338,8 16,8C11.0294,8 7,12.0294 7,17V4H11C11,4.5523 11.4477,5 12,5Z"
|
||||||
|
android:strokeAlpha="0.3"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillAlpha="0.3"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M7,17C7,18.0519 7.1805,19.0617 7.5121,20H7V17Z"
|
||||||
|
android:strokeAlpha="0.3"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillAlpha="0.3"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M17,2.01L7,2C5.9,2 5,2.9 5,4V20C5,21.1 5.9,22 7,22H8.5155C8.1025,21.383 7.7638,20.7121 7.5121,20H7V4H11C11,4.5523 11.4477,5 12,5C12.5523,5 13,4.5523 13,4H17V8.0549C17.6935,8.1316 18.3632,8.287 19,8.5121V4C19,2.9 18.1,2.01 17,2.01Z"
|
||||||
|
android:strokeAlpha="0.7"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillAlpha="0.7"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M16,21C18.2091,21 20,19.2091 20,17C20,14.7909 18.2091,13 16,13C13.7909,13 12,14.7909 12,17H14L11,20L8,17H10C10,13.6863 12.6863,11 16,11C19.3137,11 22,13.6863 22,17C22,20.3137 19.3137,23 16,23C14.598,23 13.3082,22.5191 12.2868,21.7132L13.7159,20.2841C14.3635,20.7354 15.1508,21 16,21Z"
|
||||||
|
android:fillType="evenOdd">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:gradientRadius="6.36396"
|
||||||
|
android:centerX="16"
|
||||||
|
android:centerY="17"
|
||||||
|
android:type="radial">
|
||||||
|
<item android:offset="0" android:color="#FFFFFFFF"/>
|
||||||
|
<item android:offset="0.8125" android:color="#FFFFFFFF"/>
|
||||||
|
<item android:offset="1" android:color="#00FFFFFF"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
15
OsmAnd/res/drawable/ic_action_user_folder.xml
Normal file
15
OsmAnd/res/drawable/ic_action_user_folder.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6,3C4.8954,3 4,3.8954 4,5V7H20C20,5.8954 19.1046,5 18,5H13L11.4,3H6Z"
|
||||||
|
android:strokeAlpha="0.5"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillAlpha="0.5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M2,7C2,5.8954 2.8954,5 4,5H10L12,7H20C21.1046,7 22,7.8954 22,9V19C22,20.1046 21.1046,21 20,21H4C2.8954,21 2,20.1046 2,19V7ZM17,12C17,13.1046 16.1046,14 15,14C13.8954,14 13,13.1046 13,12C13,10.8954 13.8954,10 15,10C16.1046,10 17,10.8954 17,12ZM10,17.5V19H20V17.5C20,15.5 17,15 15,15C13,15 10,15.5 10,17.5Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
|
@ -5,8 +5,10 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/content_padding"
|
|
||||||
android:paddingStart="@dimen/content_padding"
|
android:paddingStart="@dimen/content_padding"
|
||||||
|
android:paddingLeft="@dimen/content_padding"
|
||||||
|
android:paddingTop="@dimen/content_padding"
|
||||||
|
android:paddingRight="@dimen/content_padding"
|
||||||
android:paddingEnd="@dimen/content_padding">
|
android:paddingEnd="@dimen/content_padding">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
@ -43,4 +45,9 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/space"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/content_padding" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -1088,7 +1088,7 @@
|
||||||
<string name="lang_ja">Японская</string>
|
<string name="lang_ja">Японская</string>
|
||||||
<string name="lang_ko">Карэйская</string>
|
<string name="lang_ko">Карэйская</string>
|
||||||
<string name="lang_lv">Латышская</string>
|
<string name="lang_lv">Латышская</string>
|
||||||
<string name="lang_lt">Летувісская</string>
|
<string name="lang_lt">Летувіская</string>
|
||||||
<string name="lang_mr">Маратхі</string>
|
<string name="lang_mr">Маратхі</string>
|
||||||
<string name="lang_no">Нарвежская (Bokmål)</string>
|
<string name="lang_no">Нарвежская (Bokmål)</string>
|
||||||
<string name="lang_pl">Польская</string>
|
<string name="lang_pl">Польская</string>
|
||||||
|
|
|
@ -3843,7 +3843,7 @@ POIの更新は利用できません</string>
|
||||||
<string name="subscription_expired_title">OsmAndLiveサブスクリプションの有効期限が切れました</string>
|
<string name="subscription_expired_title">OsmAndLiveサブスクリプションの有効期限が切れました</string>
|
||||||
<string name="subscription_paused_title">OsmAndLiveサブスクリプションが一時停止されました</string>
|
<string name="subscription_paused_title">OsmAndLiveサブスクリプションが一時停止されました</string>
|
||||||
<string name="subscription_on_hold_title">OsmAndLiveサブスクリプションは保留中です</string>
|
<string name="subscription_on_hold_title">OsmAndLiveサブスクリプションは保留中です</string>
|
||||||
<string name="markers_history">マーカーの履歴</string>
|
<string name="markers_history">マーカー履歴</string>
|
||||||
<string name="send_files_to_openstreetmap">GPXファイルをOpenStreetMapに送信</string>
|
<string name="send_files_to_openstreetmap">GPXファイルをOpenStreetMapに送信</string>
|
||||||
<string name="enter_text_separated">タグはカンマで区切って入力してください。</string>
|
<string name="enter_text_separated">タグはカンマで区切って入力してください。</string>
|
||||||
<string name="gpx_upload_public_visibility_descr">\"公開 \"状態は、追跡機能にてユーザーのGPS追跡、公開GPS追跡リスト、および生データのタイムスタンプ付き公開追跡リストに公開されることを意味します。APIを介して提供されるデータはユーザーの追跡ページを参照しません。追跡ポイントのタイムスタンプはパブリックGPS APIでは利用できず、また追跡ポイントは時系列に並んでいません。</string>
|
<string name="gpx_upload_public_visibility_descr">\"公開 \"状態は、追跡機能にてユーザーのGPS追跡、公開GPS追跡リスト、および生データのタイムスタンプ付き公開追跡リストに公開されることを意味します。APIを介して提供されるデータはユーザーの追跡ページを参照しません。追跡ポイントのタイムスタンプはパブリックGPS APIでは利用できず、また追跡ポイントは時系列に並んでいません。</string>
|
||||||
|
|
|
@ -3927,4 +3927,5 @@
|
||||||
<string name="poi_cliff_diving">Mergulho de falésia</string>
|
<string name="poi_cliff_diving">Mergulho de falésia</string>
|
||||||
<string name="poi_zurkhaneh_sport">Zurkhaneh</string>
|
<string name="poi_zurkhaneh_sport">Zurkhaneh</string>
|
||||||
<string name="poi_bay_filter">Tipo de baía</string>
|
<string name="poi_bay_filter">Tipo de baía</string>
|
||||||
|
<string name="poi_ultimate">Ultimate</string>
|
||||||
</resources>
|
</resources>
|
|
@ -4049,15 +4049,19 @@
|
||||||
<string name="shared_string_route_line">Linha de rota</string>
|
<string name="shared_string_route_line">Linha de rota</string>
|
||||||
<string name="route_line_use_map_style_appearance">A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s.</string>
|
<string name="route_line_use_map_style_appearance">A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s.</string>
|
||||||
<string name="specify_color_for_map_mode">Especifique a cor para o modo de mapa: %1$s.</string>
|
<string name="specify_color_for_map_mode">Especifique a cor para o modo de mapa: %1$s.</string>
|
||||||
<string name="release_4_0_beta">• As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\"
|
<string name="release_4_0_beta">"• Adicionada opção para baixar curvas de nível em pés
|
||||||
\n
|
\n
|
||||||
\n• As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação.
|
\n • Plano de paisagem da rota: guias adicionadas para alternar entre pontos ou gráficos
|
||||||
\n
|
\n
|
||||||
\n• Adicionada opção para alterar a aparência da linha da rota de navegação
|
\n • As atualizações do OsmAnd Live foram movidas para \"Downloads> Atualizações\"
|
||||||
\n
|
\n
|
||||||
\n• Caixa de diálogo \"Gravação de viagem\" atualizada
|
\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação.
|
||||||
\n
|
\n
|
||||||
\n</string>
|
\n • Adicionada opção para alterar a aparência da linha da rota de navegação
|
||||||
|
\n
|
||||||
|
\n • Caixa de diálogo \"Gravação de viagem\" atualizada
|
||||||
|
\n
|
||||||
|
\n"</string>
|
||||||
<string name="no_purchases">Você não tem nenhuma compra</string>
|
<string name="no_purchases">Você não tem nenhuma compra</string>
|
||||||
<string name="new_device_account">Novo dispositivo / nova conta</string>
|
<string name="new_device_account">Novo dispositivo / nova conta</string>
|
||||||
<string name="contact_support_description">Se você tiver alguma dúvida, entre em contato conosco em %1$s.</string>
|
<string name="contact_support_description">Se você tiver alguma dúvida, entre em contato conosco em %1$s.</string>
|
||||||
|
@ -4081,4 +4085,8 @@
|
||||||
<string name="user_points">Pontos do usuário</string>
|
<string name="user_points">Pontos do usuário</string>
|
||||||
<string name="output">Saída</string>
|
<string name="output">Saída</string>
|
||||||
<string name="map_quick_action_pattern">%1$s → …</string>
|
<string name="map_quick_action_pattern">%1$s → …</string>
|
||||||
|
<string name="shared_string_feet">pés</string>
|
||||||
|
<string name="srtm_unit_format">Formato da unidade de curvas de nível</string>
|
||||||
|
<string name="srtm_download_list_help_message">OsmAnd fornece dados de linhas de contorno em metros e pés. Você precisará baixar novamente o arquivo para alterar o formato.</string>
|
||||||
|
<string name="srtm_download_single_help_message">Selecione o formato necessário. Você precisará baixar novamente o arquivo para alterar o formato.</string>
|
||||||
</resources>
|
</resources>
|
|
@ -193,7 +193,7 @@
|
||||||
<string name="poi_ford_stepping_stones">Ponte pedonal em pedras separadas</string>
|
<string name="poi_ford_stepping_stones">Ponte pedonal em pedras separadas</string>
|
||||||
<string name="poi_mountain_pass">Passo de montanha</string>
|
<string name="poi_mountain_pass">Passo de montanha</string>
|
||||||
<string name="poi_gate">Portão</string>
|
<string name="poi_gate">Portão</string>
|
||||||
<string name="poi_city_wall">Muralha de cidade</string>
|
<string name="poi_city_wall">Muralha/cerca de cidade</string>
|
||||||
<string name="poi_lift_gate">Cancela elevatória</string>
|
<string name="poi_lift_gate">Cancela elevatória</string>
|
||||||
<string name="poi_toll_booth">Cabine de portagem</string>
|
<string name="poi_toll_booth">Cabine de portagem</string>
|
||||||
<string name="poi_border_control">Controlo aduaneiro</string>
|
<string name="poi_border_control">Controlo aduaneiro</string>
|
||||||
|
@ -637,13 +637,13 @@
|
||||||
<string name="poi_australian_football">Futebol australiano</string>
|
<string name="poi_australian_football">Futebol australiano</string>
|
||||||
<string name="poi_base">Base jumping</string>
|
<string name="poi_base">Base jumping</string>
|
||||||
<string name="poi_baseball">Beisebol</string>
|
<string name="poi_baseball">Beisebol</string>
|
||||||
<string name="poi_basketball">Basquete</string>
|
<string name="poi_basketball">Basquetebol</string>
|
||||||
<string name="poi_beachvolleyball">Voleibol de praia</string>
|
<string name="poi_beachvolleyball">Voleibol de praia</string>
|
||||||
<string name="poi_bmx">BMX</string>
|
<string name="poi_bmx">BMX</string>
|
||||||
<string name="poi_boules">Bocha</string>
|
<string name="poi_boules">Bocha</string>
|
||||||
<string name="poi_bowls">Lawn bowls</string>
|
<string name="poi_bowls">Lawn bowls</string>
|
||||||
<string name="poi_canadian_football">Futebol canadiano</string>
|
<string name="poi_canadian_football">Futebol canadiano</string>
|
||||||
<string name="poi_canoe">Canoa</string>
|
<string name="poi_canoe">Canoagem</string>
|
||||||
<string name="poi_chess">Xadrez</string>
|
<string name="poi_chess">Xadrez</string>
|
||||||
<string name="poi_climbing">Escalada</string>
|
<string name="poi_climbing">Escalada</string>
|
||||||
<string name="poi_cricket">Críquete</string>
|
<string name="poi_cricket">Críquete</string>
|
||||||
|
@ -694,7 +694,7 @@
|
||||||
<string name="poi_boundary_stone">Marco de fronteira</string>
|
<string name="poi_boundary_stone">Marco de fronteira</string>
|
||||||
<string name="poi_historic_cannon">Canhão histórico</string>
|
<string name="poi_historic_cannon">Canhão histórico</string>
|
||||||
<string name="poi_castle">Castelo</string>
|
<string name="poi_castle">Castelo</string>
|
||||||
<string name="poi_city_gate">Portão/arco de cidade</string>
|
<string name="poi_city_gate">Portão/porta/arco de cidade</string>
|
||||||
<string name="poi_fort">Forte</string>
|
<string name="poi_fort">Forte</string>
|
||||||
<string name="poi_fountain">Chafariz</string>
|
<string name="poi_fountain">Chafariz</string>
|
||||||
<string name="poi_historic_ruins">Ruínas históricas</string>
|
<string name="poi_historic_ruins">Ruínas históricas</string>
|
||||||
|
@ -707,7 +707,7 @@
|
||||||
<string name="poi_aquarium">Aquário</string>
|
<string name="poi_aquarium">Aquário</string>
|
||||||
<string name="poi_theme_park">Parque de diversões</string>
|
<string name="poi_theme_park">Parque de diversões</string>
|
||||||
<string name="poi_attraction">Atração turística</string>
|
<string name="poi_attraction">Atração turística</string>
|
||||||
<string name="poi_tourism_yes">Objeto turístico</string>
|
<string name="poi_tourism_yes">Elemento turístico</string>
|
||||||
<string name="poi_attraction_amusement_ride">Atracão de feira</string>
|
<string name="poi_attraction_amusement_ride">Atracão de feira</string>
|
||||||
<string name="poi_attraction_animal">Animal (atração)</string>
|
<string name="poi_attraction_animal">Animal (atração)</string>
|
||||||
<string name="poi_attraction_big_wheel">Roda gigante</string>
|
<string name="poi_attraction_big_wheel">Roda gigante</string>
|
||||||
|
@ -2866,9 +2866,9 @@
|
||||||
<string name="poi_aquaculture_mussels">Aquicultura: mexilhões</string>
|
<string name="poi_aquaculture_mussels">Aquicultura: mexilhões</string>
|
||||||
<string name="poi_mdf">Rede de distribuição principal (MDF)</string>
|
<string name="poi_mdf">Rede de distribuição principal (MDF)</string>
|
||||||
<string name="poi_min_age">Idade mínima</string>
|
<string name="poi_min_age">Idade mínima</string>
|
||||||
<string name="poi_organic_yes">Sim</string>
|
<string name="poi_organic_yes">Produtos orgânicos: sim</string>
|
||||||
<string name="poi_organic_no">Não</string>
|
<string name="poi_organic_no">Produtos orgânicos: não</string>
|
||||||
<string name="poi_organic_only">Unicamente</string>
|
<string name="poi_organic_only">Produtos orgânicos: unicamente</string>
|
||||||
<string name="poi_traffic_mirror">Espelho de tráfego</string>
|
<string name="poi_traffic_mirror">Espelho de tráfego</string>
|
||||||
<string name="poi_diplomatic_consulate">Consulado</string>
|
<string name="poi_diplomatic_consulate">Consulado</string>
|
||||||
<string name="poi_diplomatic_consulate_general">Consulado geral</string>
|
<string name="poi_diplomatic_consulate_general">Consulado geral</string>
|
||||||
|
@ -3746,7 +3746,7 @@
|
||||||
<string name="poi_community_gender_male">Sexo comunitário: masculino</string>
|
<string name="poi_community_gender_male">Sexo comunitário: masculino</string>
|
||||||
<string name="poi_community_gender_mixed">Sexo comunitário: misto</string>
|
<string name="poi_community_gender_mixed">Sexo comunitário: misto</string>
|
||||||
<string name="poi_grave">Sepultura</string>
|
<string name="poi_grave">Sepultura</string>
|
||||||
<string name="poi_parking_space">Espaço de estacionamento</string>
|
<string name="poi_parking_space">Lugar de estacionamento (1 veículo)</string>
|
||||||
<string name="poi_url">URL</string>
|
<string name="poi_url">URL</string>
|
||||||
<string name="poi_volcano_type">Tipo</string>
|
<string name="poi_volcano_type">Tipo</string>
|
||||||
<string name="poi_volcano_status">Estado</string>
|
<string name="poi_volcano_status">Estado</string>
|
||||||
|
|
|
@ -2013,7 +2013,7 @@
|
||||||
<string name="routing_attr_height_obstacles_name">Utilizar dados de elevação</string>
|
<string name="routing_attr_height_obstacles_name">Utilizar dados de elevação</string>
|
||||||
<string name="rendering_attr_depthContours_description">Mostrar pontos e contornos de profundidade.</string>
|
<string name="rendering_attr_depthContours_description">Mostrar pontos e contornos de profundidade.</string>
|
||||||
<string name="rendering_attr_depthContours_name">Contornos de profundidade náuticos</string>
|
<string name="rendering_attr_depthContours_name">Contornos de profundidade náuticos</string>
|
||||||
<string name="show_transparency_seekbar">Mostra a transparência da barra de navegação</string>
|
<string name="show_transparency_seekbar">Mostrar barra deslizante de transparência</string>
|
||||||
<string name="shared_string_widgets">Widgets</string>
|
<string name="shared_string_widgets">Widgets</string>
|
||||||
<string name="rendering_attr_hideUnderground_name">Objetos subterrâneos</string>
|
<string name="rendering_attr_hideUnderground_name">Objetos subterrâneos</string>
|
||||||
<string name="auto_split_recording_title">Dividir automaticamente as gravações após quebras</string>
|
<string name="auto_split_recording_title">Dividir automaticamente as gravações após quebras</string>
|
||||||
|
|
|
@ -2872,7 +2872,7 @@
|
||||||
<string name="sit_on_the_stop">Посадка на остановке</string>
|
<string name="sit_on_the_stop">Посадка на остановке</string>
|
||||||
<string name="use_osm_live_public_transport_description">Включить общественный транспорт с учётом автообновлений OsmAnd Live.</string>
|
<string name="use_osm_live_public_transport_description">Включить общественный транспорт с учётом автообновлений OsmAnd Live.</string>
|
||||||
<string name="use_osm_live_public_transport">Общественный транспорт OsmAnd Live</string>
|
<string name="use_osm_live_public_transport">Общественный транспорт OsmAnd Live</string>
|
||||||
<string name="transfers_size">%1$d пересадки</string>
|
<string name="transfers_size">пересадки: %1$d</string>
|
||||||
<string name="rendering_attr_surface_unpaved_name">Грунтовая</string>
|
<string name="rendering_attr_surface_unpaved_name">Грунтовая</string>
|
||||||
<string name="rendering_attr_surface_sand_name">Песок</string>
|
<string name="rendering_attr_surface_sand_name">Песок</string>
|
||||||
<string name="rendering_attr_surface_grass_name">Трава</string>
|
<string name="rendering_attr_surface_grass_name">Трава</string>
|
||||||
|
@ -3263,7 +3263,7 @@
|
||||||
<string name="selected_profile">Выбранный профиль</string>
|
<string name="selected_profile">Выбранный профиль</string>
|
||||||
<string name="personal_category_name">Персональный</string>
|
<string name="personal_category_name">Персональный</string>
|
||||||
<string name="shared_string_downloading_formatted">Скачивание %s</string>
|
<string name="shared_string_downloading_formatted">Скачивание %s</string>
|
||||||
<string name="rendering_value_thick_name">Толсто</string>
|
<string name="rendering_value_thick_name">Толстая</string>
|
||||||
<string name="default_speed_dialog_msg">Используется для оценки времени прибытия для неизвестного типа дорог и ограничения скорости для всех дорог (может изменить маршрут)</string>
|
<string name="default_speed_dialog_msg">Используется для оценки времени прибытия для неизвестного типа дорог и ограничения скорости для всех дорог (может изменить маршрут)</string>
|
||||||
<string name="routing_attr_allow_intermediate_name">Разрешить промежуточные маршруты</string>
|
<string name="routing_attr_allow_intermediate_name">Разрешить промежуточные маршруты</string>
|
||||||
<string name="routing_attr_allow_advanced_name">Разрешить расширенные маршруты</string>
|
<string name="routing_attr_allow_advanced_name">Разрешить расширенные маршруты</string>
|
||||||
|
@ -3302,7 +3302,7 @@
|
||||||
<string name="rendering_attr_piste_difficulty_connection_name">Соединение</string>
|
<string name="rendering_attr_piste_difficulty_connection_name">Соединение</string>
|
||||||
<string name="simulate_your_location_gpx_descr">Симулировать свою позицию используя записанный GPX трек.</string>
|
<string name="simulate_your_location_gpx_descr">Симулировать свою позицию используя записанный GPX трек.</string>
|
||||||
<string name="route_start_point">Начало маршрута</string>
|
<string name="route_start_point">Начало маршрута</string>
|
||||||
<string name="shared_string_revert">Вернуться</string>
|
<string name="shared_string_revert">Сброс</string>
|
||||||
<string name="suggested_maps_descr">Эти карты необходимо использовать с плагином.</string>
|
<string name="suggested_maps_descr">Эти карты необходимо использовать с плагином.</string>
|
||||||
<string name="added_profiles">Добавленные профили</string>
|
<string name="added_profiles">Добавленные профили</string>
|
||||||
<string name="added_profiles_descr">Профили, добавленные плагином</string>
|
<string name="added_profiles_descr">Профили, добавленные плагином</string>
|
||||||
|
@ -4056,7 +4056,11 @@
|
||||||
<string name="next_billing_date">Следующая дата оплаты: %1$s</string>
|
<string name="next_billing_date">Следующая дата оплаты: %1$s</string>
|
||||||
<string name="osmand_live">OsmAnd Live</string>
|
<string name="osmand_live">OsmAnd Live</string>
|
||||||
<string name="annual_subscription">Годовая подписка</string>
|
<string name="annual_subscription">Годовая подписка</string>
|
||||||
<string name="release_4_0_beta">• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
|
<string name="release_4_0_beta">• Добавлена возможность скачать контурные линии в футах.
|
||||||
|
\n
|
||||||
|
\n• Планирование маршрута: добавлены вкладки для переключения между точками и графиками.
|
||||||
|
\n
|
||||||
|
\n• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
|
||||||
\n
|
\n
|
||||||
\n• Теперь треки можно раскрашивать по высоте, скорости или уклону.
|
\n• Теперь треки можно раскрашивать по высоте, скорости или уклону.
|
||||||
\n
|
\n
|
||||||
|
@ -4087,4 +4091,8 @@
|
||||||
<string name="output">Вывод</string>
|
<string name="output">Вывод</string>
|
||||||
<string name="map_quick_action_pattern">%1$s → …</string>
|
<string name="map_quick_action_pattern">%1$s → …</string>
|
||||||
<string name="exit_number">Номер съезда</string>
|
<string name="exit_number">Номер съезда</string>
|
||||||
|
<string name="srtm_unit_format">Формат единиц на контурных линиях</string>
|
||||||
|
<string name="srtm_download_single_help_message">Выберите необходимый формат. Для изменения формата потребуется повторно загрузить файл.</string>
|
||||||
|
<string name="shared_string_feet">футы</string>
|
||||||
|
<string name="srtm_download_list_help_message">OsmAnd предоставляет данные изолиний в метрах и футах. Вам нужно будет повторно загрузить файл, чтобы изменить формат.</string>
|
||||||
</resources>
|
</resources>
|
|
@ -4045,7 +4045,11 @@
|
||||||
<string name="lost_data_warning">Všetky neuložené údaje budú stratené.</string>
|
<string name="lost_data_warning">Všetky neuložené údaje budú stratené.</string>
|
||||||
<string name="show_start_dialog">Zobraziť úvodné okno</string>
|
<string name="show_start_dialog">Zobraziť úvodné okno</string>
|
||||||
<string name="trip_recording_show_start_dialog_setting">Ak je vypnuté, záznam začne hneď po stlačení nástroja alebo položky v menu a preskočí okno nastavenia.</string>
|
<string name="trip_recording_show_start_dialog_setting">Ak je vypnuté, záznam začne hneď po stlačení nástroja alebo položky v menu a preskočí okno nastavenia.</string>
|
||||||
<string name="release_4_0_beta">• Aktualizácie OsmAnd Live presunuté do \"Sťahovania > Aktualizácie\"
|
<string name="release_4_0_beta">• Pridaná možnosť stiahnutia Vrstevníc v stopách
|
||||||
|
\n
|
||||||
|
\n• Plánovať trasy: pridané prepínače medzi bodmi a grafmi
|
||||||
|
\n
|
||||||
|
\n• Aktualizácie OsmAnd Live presunuté do \"Sťahovania > Aktualizácie\"
|
||||||
\n
|
\n
|
||||||
\n • Stopy je teraz možné vyfarbiť podľa nadmorskej výšky, rýchlosti alebo sklonu svahu.
|
\n • Stopy je teraz možné vyfarbiť podľa nadmorskej výšky, rýchlosti alebo sklonu svahu.
|
||||||
\n
|
\n
|
||||||
|
@ -4081,4 +4085,8 @@
|
||||||
<string name="user_points">Body používateľa</string>
|
<string name="user_points">Body používateľa</string>
|
||||||
<string name="output">Výstup</string>
|
<string name="output">Výstup</string>
|
||||||
<string name="map_quick_action_pattern">%1$s → …</string>
|
<string name="map_quick_action_pattern">%1$s → …</string>
|
||||||
|
<string name="shared_string_feet">stopy</string>
|
||||||
|
<string name="srtm_unit_format">Formát jednotiek vrstevníc</string>
|
||||||
|
<string name="srtm_download_list_help_message">OsmAnd poskytuje údaje vrstevníc v metroch a stopách. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
|
||||||
|
<string name="srtm_download_single_help_message">Prosím zvoľte požadovaný formát. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
|
||||||
</resources>
|
</resources>
|
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<string name="shared_string_max_height">Max. height</string>
|
||||||
|
<string name="shared_string_min_height">Min. height</string>
|
||||||
|
<string name="route_line_use_gradient_coloring">Route line will be colorized depending on the elevation profile of the route.</string>
|
||||||
<string name="output">Output</string>
|
<string name="output">Output</string>
|
||||||
<string name="user_points">User points</string>
|
<string name="user_points">User points</string>
|
||||||
<string name="announce_when_exceeded">Announce when exceeded</string>
|
<string name="announce_when_exceeded">Announce when exceeded</string>
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
@ -47,13 +48,14 @@ public class AndroidNetworkUtils {
|
||||||
private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class);
|
private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class);
|
||||||
|
|
||||||
public interface OnRequestResultListener {
|
public interface OnRequestResultListener {
|
||||||
void onResult(String result);
|
void onResult(@Nullable String result, @Nullable String error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnFilesUploadCallback {
|
public interface OnFilesUploadCallback {
|
||||||
@Nullable
|
@Nullable
|
||||||
Map<String, String> getAdditionalParams(@NonNull File file);
|
Map<String, String> getAdditionalParams(@NonNull File file);
|
||||||
void onFileUploadProgress(@NonNull File file, int percent);
|
void onFileUploadProgress(@NonNull File file, int percent);
|
||||||
|
void onFileUploadDone(@NonNull File file);
|
||||||
void onFilesUploadDone(@NonNull Map<File, String> errors);
|
void onFilesUploadDone(@NonNull Map<File, String> errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,16 +65,26 @@ public class AndroidNetworkUtils {
|
||||||
void onFileDownloadProgress(@NonNull File file, int percent);
|
void onFileDownloadProgress(@NonNull File file, int percent);
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
void onFileDownloadedAsync(@NonNull File file);
|
void onFileDownloadedAsync(@NonNull File file);
|
||||||
|
|
||||||
|
void onFileDownloadDone(@NonNull File file);
|
||||||
void onFilesDownloadDone(@NonNull Map<File, String> errors);
|
void onFilesDownloadDone(@NonNull Map<File, String> errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RequestResponse {
|
public static class RequestResponse {
|
||||||
private Request request;
|
private final Request request;
|
||||||
private String response;
|
private final String response;
|
||||||
|
private final String error;
|
||||||
|
|
||||||
RequestResponse(@NonNull Request request, @Nullable String response) {
|
RequestResponse(@NonNull Request request, @Nullable String response) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.response = response;
|
this.response = response;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestResponse(@NonNull Request request, @Nullable String response, @Nullable String error) {
|
||||||
|
this.request = request;
|
||||||
|
this.response = response;
|
||||||
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Request getRequest() {
|
public Request getRequest() {
|
||||||
|
@ -82,6 +94,10 @@ public class AndroidNetworkUtils {
|
||||||
public String getResponse() {
|
public String getResponse() {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnSendRequestsListener {
|
public interface OnSendRequestsListener {
|
||||||
|
@ -92,6 +108,13 @@ public class AndroidNetworkUtils {
|
||||||
public static void sendRequestsAsync(@Nullable final OsmandApplication ctx,
|
public static void sendRequestsAsync(@Nullable final OsmandApplication ctx,
|
||||||
@NonNull final List<Request> requests,
|
@NonNull final List<Request> requests,
|
||||||
@Nullable final OnSendRequestsListener listener) {
|
@Nullable final OnSendRequestsListener listener) {
|
||||||
|
sendRequestsAsync(ctx, requests, listener, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendRequestsAsync(@Nullable final OsmandApplication ctx,
|
||||||
|
@NonNull final List<Request> requests,
|
||||||
|
@Nullable final OnSendRequestsListener listener,
|
||||||
|
final Executor executor) {
|
||||||
|
|
||||||
new AsyncTask<Void, RequestResponse, List<RequestResponse>>() {
|
new AsyncTask<Void, RequestResponse, List<RequestResponse>>() {
|
||||||
|
|
||||||
|
@ -101,11 +124,18 @@ public class AndroidNetworkUtils {
|
||||||
for (Request request : requests) {
|
for (Request request : requests) {
|
||||||
RequestResponse requestResponse;
|
RequestResponse requestResponse;
|
||||||
try {
|
try {
|
||||||
String response = sendRequest(ctx, request.getUrl(), request.getParameters(),
|
final String[] response = {null, null};
|
||||||
request.getUserOperation(), request.isToastAllowed(), request.isPost());
|
sendRequest(ctx, request.getUrl(), request.getParameters(),
|
||||||
requestResponse = new RequestResponse(request, response);
|
request.getUserOperation(), request.isToastAllowed(), request.isPost(), new OnRequestResultListener() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
|
response[0] = result;
|
||||||
|
response[1] = error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
requestResponse = new RequestResponse(request, response[0], response[1]);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
requestResponse = new RequestResponse(request, null);
|
requestResponse = new RequestResponse(request, null, "Unexpected error");
|
||||||
}
|
}
|
||||||
responses.add(requestResponse);
|
responses.add(requestResponse);
|
||||||
publishProgress(requestResponse);
|
publishProgress(requestResponse);
|
||||||
|
@ -127,7 +157,7 @@ public class AndroidNetworkUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
}.executeOnExecutor(executor, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendRequestAsync(final OsmandApplication ctx,
|
public static void sendRequestAsync(final OsmandApplication ctx,
|
||||||
|
@ -137,26 +167,45 @@ public class AndroidNetworkUtils {
|
||||||
final boolean toastAllowed,
|
final boolean toastAllowed,
|
||||||
final boolean post,
|
final boolean post,
|
||||||
final OnRequestResultListener listener) {
|
final OnRequestResultListener listener) {
|
||||||
|
sendRequestAsync(ctx, url, parameters, userOperation, toastAllowed, post, listener,
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
new AsyncTask<Void, Void, String>() {
|
public static void sendRequestAsync(final OsmandApplication ctx,
|
||||||
|
final String url,
|
||||||
|
final Map<String, String> parameters,
|
||||||
|
final String userOperation,
|
||||||
|
final boolean toastAllowed,
|
||||||
|
final boolean post,
|
||||||
|
final OnRequestResultListener listener,
|
||||||
|
final Executor executor) {
|
||||||
|
new AsyncTask<Void, Void, String[]>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doInBackground(Void... params) {
|
protected String[] doInBackground(Void... params) {
|
||||||
|
final String[] res = {null, null};
|
||||||
try {
|
try {
|
||||||
return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post);
|
sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, new OnRequestResultListener() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
|
res[0] = result;
|
||||||
|
res[1] = error;
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
// ignore
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(String response) {
|
protected void onPostExecute(String[] response) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onResult(response);
|
listener.onResult(response[0], response[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
}.executeOnExecutor(executor, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void downloadFileAsync(final String url,
|
public static void downloadFileAsync(final String url,
|
||||||
|
@ -183,6 +232,14 @@ public class AndroidNetworkUtils {
|
||||||
final @NonNull List<File> files,
|
final @NonNull List<File> files,
|
||||||
final @NonNull Map<String, String> parameters,
|
final @NonNull Map<String, String> parameters,
|
||||||
final @Nullable OnFilesDownloadCallback callback) {
|
final @Nullable OnFilesDownloadCallback callback) {
|
||||||
|
downloadFilesAsync(url, files, parameters, callback, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void downloadFilesAsync(final @NonNull String url,
|
||||||
|
final @NonNull List<File> files,
|
||||||
|
final @NonNull Map<String, String> parameters,
|
||||||
|
final @Nullable OnFilesDownloadCallback callback,
|
||||||
|
final Executor executor) {
|
||||||
|
|
||||||
new AsyncTask<Void, Object, Map<File, String>>() {
|
new AsyncTask<Void, Object, Map<File, String>>() {
|
||||||
|
|
||||||
|
@ -228,7 +285,7 @@ public class AndroidNetworkUtils {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.put(file, e.getMessage());
|
errors.put(file, e.getMessage());
|
||||||
}
|
}
|
||||||
publishProgress(file, Integer.MAX_VALUE);
|
publishProgress(file, -1);
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +293,13 @@ public class AndroidNetworkUtils {
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(Object... objects) {
|
protected void onProgressUpdate(Object... objects) {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onFileDownloadProgress((File) objects[0], (Integer) objects[1]);
|
File file = (File) objects[0];
|
||||||
|
Integer progress = (Integer) objects[1];
|
||||||
|
if (progress >= 0) {
|
||||||
|
callback.onFileDownloadProgress(file, progress);
|
||||||
|
} else {
|
||||||
|
callback.onFileDownloadDone(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,15 +310,23 @@ public class AndroidNetworkUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
}.executeOnExecutor(executor, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
|
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
|
||||||
@Nullable Map<String, String> parameters,
|
@Nullable Map<String, String> parameters,
|
||||||
@Nullable String userOperation, boolean toastAllowed, boolean post) {
|
@Nullable String userOperation, boolean toastAllowed, boolean post) {
|
||||||
|
return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
|
||||||
|
@Nullable Map<String, String> parameters,
|
||||||
|
@Nullable String userOperation, boolean toastAllowed, boolean post,
|
||||||
|
@Nullable OnRequestResultListener listener) {
|
||||||
|
String result = null;
|
||||||
|
String error = null;
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
String params = null;
|
String params = null;
|
||||||
if (parameters != null && parameters.size() > 0) {
|
if (parameters != null && parameters.size() > 0) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -285,68 +356,66 @@ public class AndroidNetworkUtils {
|
||||||
output.write(params.getBytes("UTF-8"));
|
output.write(params.getBytes("UTF-8"));
|
||||||
output.flush();
|
output.flush();
|
||||||
output.close();
|
output.close();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
connection.setRequestMethod("GET");
|
connection.setRequestMethod("GET");
|
||||||
connection.connect();
|
connection.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
|
if (ctx != null) {
|
||||||
|
error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
|
||||||
|
+ ctx.getString(R.string.failed_op) + ": " + connection.getResponseMessage();
|
||||||
|
} else {
|
||||||
|
error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
|
||||||
|
+ "failed: " + connection.getResponseMessage();
|
||||||
|
}
|
||||||
if (toastAllowed && ctx != null) {
|
if (toastAllowed && ctx != null) {
|
||||||
String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
|
showToast(ctx, error);
|
||||||
+ ctx.getString(R.string.failed_op) + ": "
|
}
|
||||||
+ connection.getResponseMessage();
|
InputStream errorStream = connection.getErrorStream();
|
||||||
showToast(ctx, msg);
|
if (errorStream != null) {
|
||||||
|
error = streamToString(errorStream);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StringBuilder responseBody = new StringBuilder();
|
result = streamToString(connection.getInputStream());
|
||||||
responseBody.setLength(0);
|
|
||||||
InputStream i = connection.getInputStream();
|
|
||||||
if (i != null) {
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256);
|
|
||||||
String s;
|
|
||||||
boolean f = true;
|
|
||||||
while ((s = in.readLine()) != null) {
|
|
||||||
if (!f) {
|
|
||||||
responseBody.append("\n");
|
|
||||||
} else {
|
|
||||||
f = false;
|
|
||||||
}
|
|
||||||
responseBody.append(s);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
i.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return responseBody.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
// that's tricky case why NPE is thrown to fix that problem httpClient could be used
|
// that's tricky case why NPE is thrown to fix that problem httpClient could be used
|
||||||
|
if (ctx != null) {
|
||||||
|
error = ctx.getString(R.string.auth_failed);
|
||||||
|
} else {
|
||||||
|
error = "Authorization failed";
|
||||||
|
}
|
||||||
if (toastAllowed && ctx != null) {
|
if (toastAllowed && ctx != null) {
|
||||||
String msg = ctx.getString(R.string.auth_failed);
|
showToast(ctx, error);
|
||||||
showToast(ctx, msg);
|
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
|
if (ctx != null) {
|
||||||
|
error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
|
||||||
|
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation);
|
||||||
|
} else {
|
||||||
|
error = "Action " + userOperation + ": Unexpected error";
|
||||||
|
}
|
||||||
if (toastAllowed && ctx != null) {
|
if (toastAllowed && ctx != null) {
|
||||||
showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
|
showToast(ctx, error);
|
||||||
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation));
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
if (ctx != null) {
|
||||||
|
error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
|
||||||
|
+ ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation);
|
||||||
|
} else {
|
||||||
|
error = "Action " + userOperation + ": I/O error";
|
||||||
|
}
|
||||||
if (toastAllowed && ctx != null) {
|
if (toastAllowed && ctx != null) {
|
||||||
showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
|
showToast(ctx, error);
|
||||||
+ ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation));
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onResult(result, error);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,35 +443,64 @@ public class AndroidNetworkUtils {
|
||||||
public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) {
|
public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) {
|
||||||
String error = null;
|
String error = null;
|
||||||
try {
|
try {
|
||||||
URLConnection connection = NetworkUtils.getHttpURLConnection(url);
|
HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
|
||||||
connection.setConnectTimeout(CONNECTION_TIMEOUT);
|
connection.setConnectTimeout(CONNECTION_TIMEOUT);
|
||||||
connection.setReadTimeout(CONNECTION_TIMEOUT);
|
connection.setReadTimeout(CONNECTION_TIMEOUT);
|
||||||
if (gzip) {
|
if (gzip) {
|
||||||
connection.setRequestProperty("Accept-Encoding", "deflate, gzip");
|
connection.setRequestProperty("Accept-Encoding", "deflate, gzip");
|
||||||
}
|
}
|
||||||
InputStream inputStream = gzip
|
connection.connect();
|
||||||
? new GZIPInputStream(connection.getInputStream())
|
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
: new BufferedInputStream(connection.getInputStream(), 8 * 1024);
|
return streamToString(connection.getErrorStream());
|
||||||
fileToSave.getParentFile().mkdirs();
|
} else {
|
||||||
OutputStream stream = null;
|
InputStream inputStream = gzip
|
||||||
try {
|
? new GZIPInputStream(connection.getInputStream())
|
||||||
stream = new FileOutputStream(fileToSave);
|
: new BufferedInputStream(connection.getInputStream(), 8 * 1024);
|
||||||
Algorithms.streamCopy(inputStream, stream, progress, 1024);
|
fileToSave.getParentFile().mkdirs();
|
||||||
stream.flush();
|
OutputStream stream = null;
|
||||||
} finally {
|
try {
|
||||||
Algorithms.closeStream(inputStream);
|
stream = new FileOutputStream(fileToSave);
|
||||||
Algorithms.closeStream(stream);
|
Algorithms.streamCopy(inputStream, stream, progress, 1024);
|
||||||
|
stream.flush();
|
||||||
|
} finally {
|
||||||
|
Algorithms.closeStream(inputStream);
|
||||||
|
Algorithms.closeStream(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
error = e.getMessage();
|
error = e.getMessage();
|
||||||
LOG.error("UnknownHostException, cannot download file " + url + " " + error);
|
LOG.error("UnknownHostException, cannot download file " + url + " " + error);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
error = e.getMessage();
|
error = e.getMessage();
|
||||||
LOG.warn("Cannot download file : " + url, e);
|
LOG.warn("Cannot download file: " + url, e);
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String streamToString(InputStream inputStream) throws IOException {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
if (inputStream != null) {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 256);
|
||||||
|
String buffer;
|
||||||
|
boolean f = true;
|
||||||
|
while ((buffer = in.readLine()) != null) {
|
||||||
|
if (!f) {
|
||||||
|
result.append("\n");
|
||||||
|
} else {
|
||||||
|
f = false;
|
||||||
|
}
|
||||||
|
result.append(buffer);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
inputStream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static final String BOUNDARY = "CowMooCowMooCowCowCow";
|
private static final String BOUNDARY = "CowMooCowMooCowCowCow";
|
||||||
|
|
||||||
public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip,
|
public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip,
|
||||||
|
@ -417,7 +515,6 @@ public class AndroidNetworkUtils {
|
||||||
@NonNull Map<String, String> additionalParams,
|
@NonNull Map<String, String> additionalParams,
|
||||||
@Nullable Map<String, String> headers,
|
@Nullable Map<String, String> headers,
|
||||||
@Nullable IProgress progress) {
|
@Nullable IProgress progress) {
|
||||||
URL url;
|
|
||||||
try {
|
try {
|
||||||
boolean firstPrm = !urlText.contains("?");
|
boolean firstPrm = !urlText.contains("?");
|
||||||
StringBuilder sb = new StringBuilder(urlText);
|
StringBuilder sb = new StringBuilder(urlText);
|
||||||
|
@ -428,7 +525,7 @@ public class AndroidNetworkUtils {
|
||||||
urlText = sb.toString();
|
urlText = sb.toString();
|
||||||
|
|
||||||
LOG.info("Start uploading file to " + urlText + " " + fileName);
|
LOG.info("Start uploading file to " + urlText + " " + fileName);
|
||||||
url = new URL(urlText);
|
URL url = new URL(urlText);
|
||||||
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
conn.setDoInput(true);
|
conn.setDoInput(true);
|
||||||
|
@ -469,6 +566,10 @@ public class AndroidNetworkUtils {
|
||||||
LOG.info("Finish uploading file " + fileName);
|
LOG.info("Finish uploading file " + fileName);
|
||||||
LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
|
LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
|
||||||
if (conn.getResponseCode() != 200) {
|
if (conn.getResponseCode() != 200) {
|
||||||
|
InputStream errorStream = conn.getErrorStream();
|
||||||
|
if (errorStream != null) {
|
||||||
|
return streamToString(errorStream);
|
||||||
|
}
|
||||||
return conn.getResponseMessage();
|
return conn.getResponseMessage();
|
||||||
}
|
}
|
||||||
InputStream is = conn.getInputStream();
|
InputStream is = conn.getInputStream();
|
||||||
|
@ -503,6 +604,16 @@ public class AndroidNetworkUtils {
|
||||||
final @NonNull Map<String, String> parameters,
|
final @NonNull Map<String, String> parameters,
|
||||||
final @Nullable Map<String, String> headers,
|
final @Nullable Map<String, String> headers,
|
||||||
final OnFilesUploadCallback callback) {
|
final OnFilesUploadCallback callback) {
|
||||||
|
uploadFilesAsync(url, files, gzip, parameters, headers, callback, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uploadFilesAsync(final @NonNull String url,
|
||||||
|
final @NonNull List<File> files,
|
||||||
|
final boolean gzip,
|
||||||
|
final @NonNull Map<String, String> parameters,
|
||||||
|
final @Nullable Map<String, String> headers,
|
||||||
|
final OnFilesUploadCallback callback,
|
||||||
|
final Executor executor) {
|
||||||
|
|
||||||
new AsyncTask<Void, Object, Map<File, String>>() {
|
new AsyncTask<Void, Object, Map<File, String>>() {
|
||||||
|
|
||||||
|
@ -538,7 +649,7 @@ public class AndroidNetworkUtils {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.put(file, e.getMessage());
|
errors.put(file, e.getMessage());
|
||||||
}
|
}
|
||||||
publishProgress(file, Integer.MAX_VALUE);
|
publishProgress(file, -1);
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
@ -546,7 +657,13 @@ public class AndroidNetworkUtils {
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(Object... objects) {
|
protected void onProgressUpdate(Object... objects) {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onFileUploadProgress((File) objects[0], (Integer) objects[1]);
|
File file = (File) objects[0];
|
||||||
|
Integer progress = (Integer) objects[1];
|
||||||
|
if (progress >= 0) {
|
||||||
|
callback.onFileUploadProgress(file, progress);
|
||||||
|
} else {
|
||||||
|
callback.onFileUploadDone(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,7 +674,7 @@ public class AndroidNetworkUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
}.executeOnExecutor(executor, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void showToast(OsmandApplication ctx, String message) {
|
private static void showToast(OsmandApplication ctx, String message) {
|
||||||
|
@ -565,11 +682,11 @@ public class AndroidNetworkUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Request {
|
public static class Request {
|
||||||
private String url;
|
private final String url;
|
||||||
private Map<String, String> parameters;
|
private final Map<String, String> parameters;
|
||||||
private String userOperation;
|
private final String userOperation;
|
||||||
private boolean toastAllowed;
|
private final boolean toastAllowed;
|
||||||
private boolean post;
|
private final boolean post;
|
||||||
|
|
||||||
public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) {
|
public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
|
|
@ -225,7 +225,7 @@ public class CustomRegion extends WorldRegion {
|
||||||
&& app.getSettings().isInternetConnectionAvailable()) {
|
&& app.getSettings().isInternetConnectionAvailable()) {
|
||||||
OnRequestResultListener resultListener = new OnRequestResultListener() {
|
OnRequestResultListener resultListener = new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
if (!Algorithms.isEmpty(result)) {
|
if (!Algorithms.isEmpty(result)) {
|
||||||
if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) {
|
if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) {
|
||||||
dynamicItemsJson = mapJsonItems(result);
|
dynamicItemsJson = mapJsonItems(result);
|
||||||
|
|
|
@ -37,6 +37,10 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class BackupHelper {
|
public class BackupHelper {
|
||||||
|
|
||||||
|
@ -45,6 +49,9 @@ public class BackupHelper {
|
||||||
private final FavouritesDbHelper favouritesHelper;
|
private final FavouritesDbHelper favouritesHelper;
|
||||||
private final GpxDbHelper gpxHelper;
|
private final GpxDbHelper gpxHelper;
|
||||||
|
|
||||||
|
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 0L,
|
||||||
|
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
|
||||||
|
|
||||||
private static final String SERVER_URL = "https://osmand.net";
|
private static final String SERVER_URL = "https://osmand.net";
|
||||||
|
|
||||||
private static final String USER_REGISTER_URL = SERVER_URL + "/userdata/user-register";
|
private static final String USER_REGISTER_URL = SERVER_URL + "/userdata/user-register";
|
||||||
|
@ -59,10 +66,6 @@ public class BackupHelper {
|
||||||
public final static int STATUS_EMPTY_RESPONSE_ERROR = 2;
|
public final static int STATUS_EMPTY_RESPONSE_ERROR = 2;
|
||||||
public final static int STATUS_SERVER_ERROR = 3;
|
public final static int STATUS_SERVER_ERROR = 3;
|
||||||
|
|
||||||
public interface OnResultListener {
|
|
||||||
void onResult(int status, @Nullable String message, @Nullable JSONObject json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface OnRegisterUserListener {
|
public interface OnRegisterUserListener {
|
||||||
void onRegisterUser(int status, @Nullable String message);
|
void onRegisterUser(int status, @Nullable String message);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +80,6 @@ public class BackupHelper {
|
||||||
|
|
||||||
public interface OnCollectLocalFilesListener {
|
public interface OnCollectLocalFilesListener {
|
||||||
void onFileCollected(@NonNull GpxFileInfo fileInfo);
|
void onFileCollected(@NonNull GpxFileInfo fileInfo);
|
||||||
|
|
||||||
void onFilesCollected(@NonNull List<GpxFileInfo> fileInfos);
|
void onFilesCollected(@NonNull List<GpxFileInfo> fileInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,20 +89,21 @@ public class BackupHelper {
|
||||||
|
|
||||||
public interface OnUploadFilesListener {
|
public interface OnUploadFilesListener {
|
||||||
void onFileUploadProgress(@NonNull File file, int progress);
|
void onFileUploadProgress(@NonNull File file, int progress);
|
||||||
|
void onFileUploadDone(@NonNull File file);
|
||||||
void onFilesUploadDone(@NonNull Map<File, String> errors);
|
void onFilesUploadDone(@NonNull Map<File, String> errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnDeleteFilesListener {
|
public interface OnDeleteFilesListener {
|
||||||
void onFileDeleteProgress(@NonNull UserFile file);
|
void onFileDeleteProgress(@NonNull UserFile file);
|
||||||
|
|
||||||
void onFilesDeleteDone(@NonNull Map<UserFile, String> errors);
|
void onFilesDeleteDone(@NonNull Map<UserFile, String> errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnDownloadFileListener {
|
public interface OnDownloadFileListener {
|
||||||
void onFileDownloadProgress(@NonNull UserFile userFile, int progress);
|
void onFileDownloadProgress(@NonNull UserFile userFile, int progress);
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
void onFileDownloadedAsync(@NonNull File file);
|
void onFileDownloadedAsync(@NonNull File file);
|
||||||
|
void onFileDownloaded(@NonNull File file);
|
||||||
void onFilesDownloadDone(@NonNull Map<File, String> errors);
|
void onFilesDownloadDone(@NonNull Map<File, String> errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,20 +171,22 @@ public class BackupHelper {
|
||||||
params.put("orderid", orderId);
|
params.put("orderid", orderId);
|
||||||
}
|
}
|
||||||
params.put("deviceid", app.getUserAndroidId());
|
params.put("deviceid", app.getUserAndroidId());
|
||||||
AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", true, true, new OnRequestResultListener() {
|
AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", false, true, new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String resultJson) {
|
public void onResult(@Nullable String resultJson, @Nullable String error) {
|
||||||
int status;
|
int status;
|
||||||
String message;
|
String message;
|
||||||
if (!Algorithms.isEmpty(resultJson)) {
|
if (!Algorithms.isEmpty(error)) {
|
||||||
|
message = "User registration error: " + parseServerError(error);
|
||||||
|
status = STATUS_SERVER_ERROR;
|
||||||
|
} else if (!Algorithms.isEmpty(resultJson)) {
|
||||||
try {
|
try {
|
||||||
JSONObject result = new JSONObject(resultJson);
|
JSONObject result = new JSONObject(resultJson);
|
||||||
String statusStr = result.getString("status");
|
if (result.has("status") && "ok".equals(result.getString("status"))) {
|
||||||
if (statusStr.equals("ok")) {
|
|
||||||
message = "You have been registered successfully. Please check for email with activation code.";
|
message = "You have been registered successfully. Please check for email with activation code.";
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
message = "User registration error: " + statusStr;
|
message = "User registration error: unknown";
|
||||||
status = STATUS_SERVER_ERROR;
|
status = STATUS_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
|
@ -196,7 +201,7 @@ public class BackupHelper {
|
||||||
listener.onRegisterUser(status, message);
|
listener.onRegisterUser(status, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerDevice(String token, @Nullable final OnRegisterDeviceListener listener) {
|
public void registerDevice(String token, @Nullable final OnRegisterDeviceListener listener) {
|
||||||
|
@ -211,12 +216,15 @@ public class BackupHelper {
|
||||||
params.put("deviceid", androidId);
|
params.put("deviceid", androidId);
|
||||||
}
|
}
|
||||||
params.put("token", token);
|
params.put("token", token);
|
||||||
AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", true, true, new OnRequestResultListener() {
|
AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", false, true, new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String resultJson) {
|
public void onResult(@Nullable String resultJson, @Nullable String error) {
|
||||||
int status;
|
int status;
|
||||||
String message;
|
String message;
|
||||||
if (!Algorithms.isEmpty(resultJson)) {
|
if (!Algorithms.isEmpty(error)) {
|
||||||
|
message = "Device registration error: " + parseServerError(error);
|
||||||
|
status = STATUS_SERVER_ERROR;
|
||||||
|
} else if (!Algorithms.isEmpty(resultJson)) {
|
||||||
try {
|
try {
|
||||||
JSONObject result = new JSONObject(resultJson);
|
JSONObject result = new JSONObject(resultJson);
|
||||||
settings.BACKUP_DEVICE_ID.set(result.getString("id"));
|
settings.BACKUP_DEVICE_ID.set(result.getString("id"));
|
||||||
|
@ -224,8 +232,9 @@ public class BackupHelper {
|
||||||
settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid"));
|
settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid"));
|
||||||
settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken"));
|
settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken"));
|
||||||
settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime"));
|
settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime"));
|
||||||
status = STATUS_SUCCESS;
|
|
||||||
message = "Device have been registered successfully";
|
message = "Device have been registered successfully";
|
||||||
|
status = STATUS_SUCCESS;
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
message = "Device registration error: json parsing";
|
message = "Device registration error: json parsing";
|
||||||
status = STATUS_PARSE_JSON_ERROR;
|
status = STATUS_PARSE_JSON_ERROR;
|
||||||
|
@ -238,7 +247,7 @@ public class BackupHelper {
|
||||||
listener.onRegisterDevice(status, message);
|
listener.onRegisterDevice(status, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadFiles(@NonNull List<GpxFileInfo> gpxFiles, @Nullable final OnUploadFilesListener listener) throws UserNotRegisteredException {
|
public void uploadFiles(@NonNull List<GpxFileInfo> gpxFiles, @Nullable final OnUploadFilesListener listener) throws UserNotRegisteredException {
|
||||||
|
@ -265,14 +274,6 @@ public class BackupHelper {
|
||||||
additionaParams.put("name", gpxFileInfo.getFileName(true));
|
additionaParams.put("name", gpxFileInfo.getFileName(true));
|
||||||
additionaParams.put("type", Algorithms.getFileExtension(file));
|
additionaParams.put("type", Algorithms.getFileExtension(file));
|
||||||
gpxFileInfo.uploadTime = System.currentTimeMillis();
|
gpxFileInfo.uploadTime = System.currentTimeMillis();
|
||||||
if (file.equals(favoritesFile)) {
|
|
||||||
favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime);
|
|
||||||
} else {
|
|
||||||
GpxDataItem gpxItem = gpxHelper.getItem(file);
|
|
||||||
if (gpxItem != null) {
|
|
||||||
gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime));
|
additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime));
|
||||||
}
|
}
|
||||||
return additionaParams;
|
return additionaParams;
|
||||||
|
@ -285,16 +286,34 @@ public class BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileUploadDone(@NonNull File file) {
|
||||||
|
if (listener != null) {
|
||||||
|
GpxFileInfo gpxFileInfo = gpxInfos.get(file);
|
||||||
|
if (gpxFileInfo != null) {
|
||||||
|
if (file.equals(favoritesFile)) {
|
||||||
|
favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime);
|
||||||
|
} else {
|
||||||
|
GpxDataItem gpxItem = gpxHelper.getItem(file);
|
||||||
|
if (gpxItem != null) {
|
||||||
|
gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.onFileUploadDone(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
|
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1);
|
settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1);
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onFilesUploadDone(errors);
|
listener.onFilesUploadDone(resolveServerErrors(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteFiles(@NonNull List<UserFile> userFiles, @Nullable final OnDeleteFilesListener listener) throws UserNotRegisteredException {
|
public void deleteFiles(@NonNull List<UserFile> userFiles, @Nullable final OnDeleteFilesListener listener) throws UserNotRegisteredException {
|
||||||
|
@ -332,24 +351,36 @@ public class BackupHelper {
|
||||||
for (RequestResponse response : results) {
|
for (RequestResponse response : results) {
|
||||||
UserFile userFile = filesMap.get(response.getRequest());
|
UserFile userFile = filesMap.get(response.getRequest());
|
||||||
if (userFile != null) {
|
if (userFile != null) {
|
||||||
String responseStr = response.getResponse();
|
|
||||||
boolean success;
|
boolean success;
|
||||||
try {
|
String message = null;
|
||||||
JSONObject json = new JSONObject(responseStr);
|
String errorStr = response.getError();
|
||||||
String status = json.getString("status");
|
if (!Algorithms.isEmpty(errorStr)) {
|
||||||
success = status.equalsIgnoreCase("ok");
|
message = parseServerError(errorStr);
|
||||||
} catch (JSONException e) {
|
|
||||||
success = false;
|
success = false;
|
||||||
|
} else {
|
||||||
|
String responseStr = response.getResponse();
|
||||||
|
try {
|
||||||
|
JSONObject result = new JSONObject(responseStr);
|
||||||
|
if (result.has("status") && "ok".equals(result.getString("status"))) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
message = "Unknown error";
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
message = "Json parsing error";
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!success) {
|
if (!success) {
|
||||||
errors.put(userFile, responseStr);
|
errors.put(userFile, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.onFilesDeleteDone(errors);
|
listener.onFilesDeleteDone(errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadFileList(@Nullable final OnDownloadFileListListener listener) throws UserNotRegisteredException {
|
public void downloadFileList(@Nullable final OnDownloadFileListListener listener) throws UserNotRegisteredException {
|
||||||
|
@ -358,13 +389,16 @@ public class BackupHelper {
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new HashMap<>();
|
||||||
params.put("deviceid", getDeviceId());
|
params.put("deviceid", getDeviceId());
|
||||||
params.put("accessToken", getAccessToken());
|
params.put("accessToken", getAccessToken());
|
||||||
AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", true, false, new OnRequestResultListener() {
|
AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", false, false, new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String resultJson) {
|
public void onResult(@Nullable String resultJson, @Nullable String error) {
|
||||||
int status;
|
int status;
|
||||||
String message;
|
String message;
|
||||||
List<UserFile> userFiles = new ArrayList<>();
|
List<UserFile> userFiles = new ArrayList<>();
|
||||||
if (!Algorithms.isEmpty(resultJson)) {
|
if (!Algorithms.isEmpty(error)) {
|
||||||
|
status = STATUS_SERVER_ERROR;
|
||||||
|
message = "Download file list error: " + parseServerError(error);
|
||||||
|
} else if (!Algorithms.isEmpty(resultJson)) {
|
||||||
try {
|
try {
|
||||||
JSONObject result = new JSONObject(resultJson);
|
JSONObject result = new JSONObject(resultJson);
|
||||||
String totalZipSize = result.getString("totalZipSize");
|
String totalZipSize = result.getString("totalZipSize");
|
||||||
|
@ -391,7 +425,7 @@ public class BackupHelper {
|
||||||
listener.onDownloadFileList(status, message, userFiles);
|
listener.onDownloadFileList(status, message, userFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadFiles(@NonNull final Map<File, UserFile> filesMap, @Nullable final OnDownloadFileListener listener) throws UserNotRegisteredException {
|
public void downloadFiles(@NonNull final Map<File, UserFile> filesMap, @Nullable final OnDownloadFileListener listener) throws UserNotRegisteredException {
|
||||||
|
@ -419,6 +453,13 @@ public class BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileDownloadDone(@NonNull File file) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onFileDownloaded(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileDownloadedAsync(@NonNull File file) {
|
public void onFileDownloadedAsync(@NonNull File file) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
|
@ -429,10 +470,10 @@ public class BackupHelper {
|
||||||
@Override
|
@Override
|
||||||
public void onFilesDownloadDone(@NonNull Map<File, String> errors) {
|
public void onFilesDownloadDone(@NonNull Map<File, String> errors) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onFilesDownloadDone(errors);
|
listener.onFilesDownloadDone(resolveServerErrors(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@ -514,7 +555,37 @@ public class BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<File, String> resolveServerErrors(@NonNull Map<File, String> errors) {
|
||||||
|
Map<File, String> resolvedErrors = new HashMap<>();
|
||||||
|
for (Entry<File, String> fileError : errors.entrySet()) {
|
||||||
|
File file = fileError.getKey();
|
||||||
|
String errorStr = fileError.getValue();
|
||||||
|
try {
|
||||||
|
JSONObject errorJson = new JSONObject(errorStr);
|
||||||
|
JSONObject error = errorJson.getJSONObject("error");
|
||||||
|
errorStr = "Error " + error.getInt("errorCode") + " (" + error.getString("message") + ")";
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
resolvedErrors.put(file, errorStr);
|
||||||
|
}
|
||||||
|
return resolvedErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseServerError(@NonNull String error) {
|
||||||
|
try {
|
||||||
|
JSONObject resultError = new JSONObject(error);
|
||||||
|
if (resultError.has("error")) {
|
||||||
|
JSONObject errorObj = resultError.getJSONObject("error");
|
||||||
|
return errorObj.getInt("errorCode") + " (" + errorObj.getString("message") + ")";
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@ -575,6 +646,6 @@ public class BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(EXECUTOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import net.osmand.AndroidUtils;
|
||||||
import net.osmand.GPXUtilities;
|
import net.osmand.GPXUtilities;
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||||
import net.osmand.plus.GpxDbHelper;
|
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.ProgressImplementation;
|
import net.osmand.plus.ProgressImplementation;
|
||||||
import net.osmand.plus.backup.BackupHelper.BackupInfo;
|
import net.osmand.plus.backup.BackupHelper.BackupInfo;
|
||||||
|
@ -113,7 +112,7 @@ public class BackupTask {
|
||||||
tasks.push(backupTasks[i]);
|
tasks.push(backupTasks[i]);
|
||||||
}
|
}
|
||||||
this.runningTasks = tasks;
|
this.runningTasks = tasks;
|
||||||
onTasksInit();
|
onBackupTasksInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initRestoreTasks() {
|
private void initRestoreTasks() {
|
||||||
|
@ -123,7 +122,7 @@ public class BackupTask {
|
||||||
tasks.push(restoreTasks[i]);
|
tasks.push(restoreTasks[i]);
|
||||||
}
|
}
|
||||||
this.runningTasks = tasks;
|
this.runningTasks = tasks;
|
||||||
onTasksInit();
|
onRestoreTasksInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initData() {
|
private void initData() {
|
||||||
|
@ -180,6 +179,11 @@ public class BackupTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileUploadDone(@NonNull File file) {
|
||||||
|
onTaskProgressDone();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
|
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
|
||||||
uploadErrors = errors;
|
uploadErrors = errors;
|
||||||
|
@ -224,6 +228,11 @@ public class BackupTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileDownloaded(@NonNull File file) {
|
||||||
|
onTaskProgressDone();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileDownloadedAsync(@NonNull File file) {
|
public void onFileDownloadedAsync(@NonNull File file) {
|
||||||
UserFile userFile = filesMap.get(file);
|
UserFile userFile = filesMap.get(file);
|
||||||
|
@ -275,14 +284,22 @@ public class BackupTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onTasksInit() {
|
private void onBackupTasksInit() {
|
||||||
Context ctx = contextRef.get();
|
Context ctx = contextRef.get();
|
||||||
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
|
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) {
|
||||||
progress = ProgressImplementation.createProgressDialog(ctx,
|
progress = ProgressImplementation.createProgressDialog(ctx,
|
||||||
"Backup data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL);
|
"Backup data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onRestoreTasksInit() {
|
||||||
|
Context ctx = contextRef.get();
|
||||||
|
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) {
|
||||||
|
progress = ProgressImplementation.createProgressDialog(ctx,
|
||||||
|
"Restore data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onTaskProgressUpdate(Object... objects) {
|
private void onTaskProgressUpdate(Object... objects) {
|
||||||
Context ctx = contextRef.get();
|
Context ctx = contextRef.get();
|
||||||
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
|
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
|
||||||
|
@ -292,7 +309,7 @@ public class BackupTask {
|
||||||
progress.startTask((String) objects[0], -1);
|
progress.startTask((String) objects[0], -1);
|
||||||
} else if (objects[0] instanceof Integer) {
|
} else if (objects[0] instanceof Integer) {
|
||||||
int progressValue = (Integer) objects[0];
|
int progressValue = (Integer) objects[0];
|
||||||
if (progressValue < Integer.MAX_VALUE) {
|
if (progressValue >= 0) {
|
||||||
progress.progress(progressValue);
|
progress.progress(progressValue);
|
||||||
} else {
|
} else {
|
||||||
progress.finishTask();
|
progress.finishTask();
|
||||||
|
@ -305,6 +322,13 @@ public class BackupTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onTaskProgressDone() {
|
||||||
|
Context ctx = contextRef.get();
|
||||||
|
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
|
||||||
|
progress.finishTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onError(@NonNull String message) {
|
private void onError(@NonNull String message) {
|
||||||
this.error = message;
|
this.error = message;
|
||||||
runningTasks.clear();
|
runningTasks.clear();
|
||||||
|
|
|
@ -137,6 +137,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
a.buttonVerify.setVisibility(View.VISIBLE);
|
a.buttonVerify.setVisibility(View.VISIBLE);
|
||||||
a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS);
|
a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS);
|
||||||
a.tokenEditText.requestFocus();
|
a.tokenEditText.requestFocus();
|
||||||
|
a.infoView.setText(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -162,10 +163,11 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
a.progressBar.setVisibility(View.GONE);
|
a.progressBar.setVisibility(View.GONE);
|
||||||
a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS);
|
a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS);
|
||||||
if (status == BackupHelper.STATUS_SUCCESS) {
|
if (status == BackupHelper.STATUS_SUCCESS) {
|
||||||
tokenEdit.setVisibility(View.GONE);
|
a.tokenEdit.setVisibility(View.GONE);
|
||||||
buttonVerify.setVisibility(View.GONE);
|
a.buttonVerify.setVisibility(View.GONE);
|
||||||
|
a.prepareBackup();
|
||||||
}
|
}
|
||||||
a.prepareBackup();
|
a.infoView.setText(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -186,6 +188,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (backupInfo != null) {
|
if (backupInfo != null) {
|
||||||
|
buttonBackup.setEnabled(false);
|
||||||
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
|
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
|
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
|
||||||
|
@ -195,14 +198,19 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
String description;
|
String description;
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
description = error;
|
description = error;
|
||||||
} else if (uploadErrors == null && downloadErrors == null) {
|
} else if (uploadErrors == null && deleteErrors == null) {
|
||||||
description = "No data";
|
description = "No data";
|
||||||
} else {
|
} else {
|
||||||
description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error);
|
description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error);
|
||||||
}
|
}
|
||||||
a.infoView.setText(description);
|
a.infoView.setText(description);
|
||||||
a.infoView.requestFocus();
|
a.infoView.requestFocus();
|
||||||
a.prepareBackup();
|
a.buttonBackup.setEnabled(true);
|
||||||
|
if (Algorithms.isEmpty(description)) {
|
||||||
|
a.prepareBackup();
|
||||||
|
} else {
|
||||||
|
a.backupInfo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -214,6 +222,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (backupInfo != null) {
|
if (backupInfo != null) {
|
||||||
|
buttonRestore.setEnabled(false);
|
||||||
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
|
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
|
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
|
||||||
|
@ -230,7 +239,12 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
}
|
}
|
||||||
a.infoView.setText(description);
|
a.infoView.setText(description);
|
||||||
a.infoView.requestFocus();
|
a.infoView.requestFocus();
|
||||||
a.prepareBackup();
|
a.buttonRestore.setEnabled(true);
|
||||||
|
if (Algorithms.isEmpty(description)) {
|
||||||
|
a.prepareBackup();
|
||||||
|
} else {
|
||||||
|
a.backupInfo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -245,21 +259,21 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
private String getBackupErrorsDescription(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors, @Nullable Map<UserFile, String> deleteErrors, @Nullable String error) {
|
private String getBackupErrorsDescription(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors, @Nullable Map<UserFile, String> deleteErrors, @Nullable String error) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (!Algorithms.isEmpty(uploadErrors)) {
|
if (!Algorithms.isEmpty(uploadErrors)) {
|
||||||
sb.append("--- Upload errors ---").append("\n");
|
sb.append("--- Upload errors ---").append("\n\n");
|
||||||
for (Entry<File, String> uploadEntry : uploadErrors.entrySet()) {
|
for (Entry<File, String> uploadEntry : uploadErrors.entrySet()) {
|
||||||
sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n");
|
sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(downloadErrors)) {
|
if (!Algorithms.isEmpty(downloadErrors)) {
|
||||||
sb.append("--- Download errors ---").append("\n");
|
sb.append("--- Download errors ---").append("\n\n");
|
||||||
for (Entry<File, String> downloadEntry : downloadErrors.entrySet()) {
|
for (Entry<File, String> downloadEntry : downloadErrors.entrySet()) {
|
||||||
sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n");
|
sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(deleteErrors)) {
|
if (!Algorithms.isEmpty(deleteErrors)) {
|
||||||
sb.append("--- Delete errors ---").append("\n");
|
sb.append("--- Delete errors ---").append("\n\n");
|
||||||
for (Entry<UserFile, String> deleteEntry : deleteErrors.entrySet()) {
|
for (Entry<UserFile, String> deleteEntry : deleteErrors.entrySet()) {
|
||||||
sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n");
|
sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.length() == 0 ? "OK" : sb.toString();
|
return sb.length() == 0 ? "OK" : sb.toString();
|
||||||
|
@ -268,32 +282,32 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
private String getBackupDescription(@NonNull BackupInfo backupInfo) {
|
private String getBackupDescription(@NonNull BackupInfo backupInfo) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (!Algorithms.isEmpty(backupInfo.filesToUpload)) {
|
if (!Algorithms.isEmpty(backupInfo.filesToUpload)) {
|
||||||
sb.append("\n").append("--- Upload ---").append("\n");
|
sb.append("\n").append("--- Upload ---").append("\n\n");
|
||||||
for (GpxFileInfo info : backupInfo.filesToUpload) {
|
for (GpxFileInfo info : backupInfo.filesToUpload) {
|
||||||
sb.append(info.getFileName(true))
|
sb.append(info.getFileName(true))
|
||||||
.append(" L: ").append(DF.format(new Date(info.getFileDate())))
|
.append(" L: ").append(DF.format(new Date(info.getFileDate())))
|
||||||
.append(" U: ").append(DF.format(new Date(info.uploadTime)))
|
.append(" U: ").append(DF.format(new Date(info.uploadTime)))
|
||||||
.append("\n");
|
.append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(backupInfo.filesToDownload)) {
|
if (!Algorithms.isEmpty(backupInfo.filesToDownload)) {
|
||||||
sb.append("\n").append("--- Download ---").append("\n");
|
sb.append("\n").append("--- Download ---").append("\n\n");
|
||||||
for (UserFile userFile : backupInfo.filesToDownload) {
|
for (UserFile userFile : backupInfo.filesToDownload) {
|
||||||
sb.append(userFile.getName())
|
sb.append(userFile.getName())
|
||||||
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
|
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
|
||||||
.append("\n");
|
.append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(backupInfo.filesToDelete)) {
|
if (!Algorithms.isEmpty(backupInfo.filesToDelete)) {
|
||||||
sb.append("\n").append("--- Delete ---").append("\n");
|
sb.append("\n").append("--- Delete ---").append("\n\n");
|
||||||
for (UserFile userFile : backupInfo.filesToDelete) {
|
for (UserFile userFile : backupInfo.filesToDelete) {
|
||||||
sb.append(userFile.getName())
|
sb.append(userFile.getName())
|
||||||
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
|
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
|
||||||
.append("\n");
|
.append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(backupInfo.filesToMerge)) {
|
if (!Algorithms.isEmpty(backupInfo.filesToMerge)) {
|
||||||
sb.append("\n").append("--- Conflicts ---").append("\n");
|
sb.append("\n").append("--- Conflicts ---").append("\n\n");
|
||||||
for (Pair<GpxFileInfo, UserFile> localRemote : backupInfo.filesToMerge) {
|
for (Pair<GpxFileInfo, UserFile> localRemote : backupInfo.filesToMerge) {
|
||||||
GpxFileInfo local = localRemote.first;
|
GpxFileInfo local = localRemote.first;
|
||||||
UserFile remote = localRemote.second;
|
UserFile remote = localRemote.second;
|
||||||
|
@ -301,7 +315,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
.append(" L: ").append(DF.format(new Date(local.getFileDate())))
|
.append(" L: ").append(DF.format(new Date(local.getFileDate())))
|
||||||
.append(" U: ").append(DF.format(new Date(local.uploadTime)))
|
.append(" U: ").append(DF.format(new Date(local.uploadTime)))
|
||||||
.append(" R: ").append(DF.format(new Date(remote.getClienttimems())))
|
.append(" R: ").append(DF.format(new Date(remote.getClienttimems())))
|
||||||
.append("\n");
|
.append("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
@ -309,6 +323,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
|
|
||||||
private void prepareBackup() {
|
private void prepareBackup() {
|
||||||
final WeakReference<TestBackupActivity> activityRef = new WeakReference<>(this);
|
final WeakReference<TestBackupActivity> activityRef = new WeakReference<>(this);
|
||||||
|
buttonRefresh.setEnabled(false);
|
||||||
PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() {
|
PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error) {
|
public void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error) {
|
||||||
|
@ -329,6 +344,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
|
||||||
}
|
}
|
||||||
a.infoView.setText(description);
|
a.infoView.setText(description);
|
||||||
a.infoView.requestFocus();
|
a.infoView.requestFocus();
|
||||||
|
a.buttonRefresh.setEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2125,9 +2125,12 @@ public class GpxUiHelper {
|
||||||
|
|
||||||
|
|
||||||
public static GPXFile makeGpxFromRoute(RouteCalculationResult route, OsmandApplication app) {
|
public static GPXFile makeGpxFromRoute(RouteCalculationResult route, OsmandApplication app) {
|
||||||
|
return makeGpxFromLocations(route.getRouteLocations(), app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GPXFile makeGpxFromLocations(List<Location> locations, OsmandApplication app) {
|
||||||
double lastHeight = HEIGHT_UNDEFINED;
|
double lastHeight = HEIGHT_UNDEFINED;
|
||||||
GPXFile gpx = new GPXUtilities.GPXFile(Version.getFullVersion(app));
|
GPXFile gpx = new GPXUtilities.GPXFile(Version.getFullVersion(app));
|
||||||
List<Location> locations = route.getRouteLocations();
|
|
||||||
if (locations != null) {
|
if (locations != null) {
|
||||||
GPXUtilities.Track track = new GPXUtilities.Track();
|
GPXUtilities.Track track = new GPXUtilities.Track();
|
||||||
GPXUtilities.TrkSegment seg = new GPXUtilities.TrkSegment();
|
GPXUtilities.TrkSegment seg = new GPXUtilities.TrkSegment();
|
||||||
|
@ -2147,6 +2150,8 @@ public class GpxUiHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastHeight = h;
|
lastHeight = h;
|
||||||
|
} else {
|
||||||
|
lastHeight = HEIGHT_UNDEFINED;
|
||||||
}
|
}
|
||||||
seg.points.add(point);
|
seg.points.add(point);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import android.os.AsyncTask;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.AndroidNetworkUtils;
|
import net.osmand.AndroidNetworkUtils;
|
||||||
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
|
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
|
||||||
import net.osmand.AndroidNetworkUtils.OnSendRequestsListener;
|
import net.osmand.AndroidNetworkUtils.OnSendRequestsListener;
|
||||||
|
@ -41,9 +44,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
public abstract class InAppPurchaseHelper {
|
public abstract class InAppPurchaseHelper {
|
||||||
// Debug tag, for logging
|
// Debug tag, for logging
|
||||||
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class);
|
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class);
|
||||||
|
@ -466,7 +466,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
|
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
|
||||||
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
|
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
||||||
notifyGetItems();
|
notifyGetItems();
|
||||||
stop(true);
|
stop(true);
|
||||||
|
@ -477,7 +477,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
if (purchaseInfoList.size() > 0) {
|
if (purchaseInfoList.size() > 0) {
|
||||||
sendTokens(purchaseInfoList, listener);
|
sendTokens(purchaseInfoList, listener);
|
||||||
} else {
|
} else {
|
||||||
listener.onResult("OK");
|
listener.onResult("OK", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED);
|
liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED);
|
||||||
sendTokens(Collections.singletonList(info), new OnRequestResultListener() {
|
sendTokens(Collections.singletonList(info), new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
|
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
|
||||||
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
|
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
|
||||||
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
|
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
|
||||||
|
@ -642,7 +642,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onResult("OK");
|
listener.onResult("OK", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,7 +695,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logError("SendToken Error", e);
|
logError("SendToken Error", e);
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onResult("Error");
|
listener.onResult("Error", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.AndroidNetworkUtils;
|
import net.osmand.AndroidNetworkUtils;
|
||||||
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
|
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
|
||||||
|
@ -194,7 +195,7 @@ public class PerformLiveUpdateAsyncTask
|
||||||
AndroidNetworkUtils.sendRequestAsync(
|
AndroidNetworkUtils.sendRequestAsync(
|
||||||
app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() {
|
app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
if (!Algorithms.isEmpty(result)) {
|
if (!Algorithms.isEmpty(result)) {
|
||||||
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
|
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
|
||||||
source.setTimeZone(TimeZone.getTimeZone("UTC"));
|
source.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
|
|
@ -208,7 +208,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
|
||||||
"https://osmand.net/subscription/update",
|
"https://osmand.net/subscription/update",
|
||||||
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
|
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
dismissProgress(null);
|
dismissProgress(null);
|
||||||
OsmandApplication app = getMyApplication();
|
OsmandApplication app = getMyApplication();
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
|
@ -6,12 +6,14 @@ import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
public class RouteLineDrawInfo {
|
public class RouteLineDrawInfo {
|
||||||
|
|
||||||
private static final String LINE_COLOR_DAY = "line_color_day";
|
private static final String LINE_COLOR_DAY = "line_color_day";
|
||||||
private static final String LINE_COLOR_NIGHT = "line_color_night";
|
private static final String LINE_COLOR_NIGHT = "line_color_night";
|
||||||
|
private static final String LINE_COLOR_GRADIENT = "line_color_gradient";
|
||||||
private static final String LINE_WIDTH = "line_width";
|
private static final String LINE_WIDTH = "line_width";
|
||||||
private static final String NAVIGATION_ICON_ID = "navigation_icon_id";
|
private static final String NAVIGATION_ICON_ID = "navigation_icon_id";
|
||||||
private static final String NAVIGATION_ICON_COLOR = "navigation_icon_color";
|
private static final String NAVIGATION_ICON_COLOR = "navigation_icon_color";
|
||||||
|
@ -24,6 +26,7 @@ public class RouteLineDrawInfo {
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private Integer colorDay;
|
private Integer colorDay;
|
||||||
private Integer colorNight;
|
private Integer colorNight;
|
||||||
|
private GradientScaleType scaleType;
|
||||||
private String width;
|
private String width;
|
||||||
|
|
||||||
// temporally parameters to show in preview
|
// temporally parameters to show in preview
|
||||||
|
@ -37,9 +40,11 @@ public class RouteLineDrawInfo {
|
||||||
|
|
||||||
public RouteLineDrawInfo(@Nullable @ColorInt Integer colorDay,
|
public RouteLineDrawInfo(@Nullable @ColorInt Integer colorDay,
|
||||||
@Nullable @ColorInt Integer colorNight,
|
@Nullable @ColorInt Integer colorNight,
|
||||||
|
@Nullable GradientScaleType gradientScaleType,
|
||||||
@Nullable String width) {
|
@Nullable String width) {
|
||||||
this.colorDay = colorDay;
|
this.colorDay = colorDay;
|
||||||
this.colorNight = colorNight;
|
this.colorNight = colorNight;
|
||||||
|
this.scaleType = gradientScaleType;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +55,7 @@ public class RouteLineDrawInfo {
|
||||||
public RouteLineDrawInfo(@NonNull RouteLineDrawInfo existed) {
|
public RouteLineDrawInfo(@NonNull RouteLineDrawInfo existed) {
|
||||||
this.colorDay = existed.colorDay;
|
this.colorDay = existed.colorDay;
|
||||||
this.colorNight = existed.colorNight;
|
this.colorNight = existed.colorNight;
|
||||||
|
this.scaleType = existed.scaleType;
|
||||||
this.width = existed.width;
|
this.width = existed.width;
|
||||||
this.iconId = existed.iconId;
|
this.iconId = existed.iconId;
|
||||||
this.iconColor = existed.iconColor;
|
this.iconColor = existed.iconColor;
|
||||||
|
@ -71,6 +77,10 @@ public class RouteLineDrawInfo {
|
||||||
this.useDefaultColor = useDefaultColor;
|
this.useDefaultColor = useDefaultColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setGradientScaleType(@Nullable GradientScaleType scaleType) {
|
||||||
|
this.scaleType = scaleType;
|
||||||
|
}
|
||||||
|
|
||||||
public void setWidth(@Nullable String width) {
|
public void setWidth(@Nullable String width) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
}
|
}
|
||||||
|
@ -108,6 +118,11 @@ public class RouteLineDrawInfo {
|
||||||
return nightMode ? colorNight : colorDay;
|
return nightMode ? colorNight : colorDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GradientScaleType getGradientScaleType() {
|
||||||
|
return scaleType;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getWidth() {
|
public String getWidth() {
|
||||||
return width;
|
return width;
|
||||||
|
@ -141,6 +156,9 @@ public class RouteLineDrawInfo {
|
||||||
if (bundle.containsKey(LINE_COLOR_NIGHT)) {
|
if (bundle.containsKey(LINE_COLOR_NIGHT)) {
|
||||||
colorNight = bundle.getInt(LINE_COLOR_NIGHT);
|
colorNight = bundle.getInt(LINE_COLOR_NIGHT);
|
||||||
}
|
}
|
||||||
|
if (bundle.containsKey(LINE_COLOR_GRADIENT)) {
|
||||||
|
scaleType = GradientScaleType.getGradientTypeByName(bundle.getString(LINE_COLOR_GRADIENT));
|
||||||
|
}
|
||||||
width = bundle.getString(LINE_WIDTH);
|
width = bundle.getString(LINE_WIDTH);
|
||||||
iconId = bundle.getInt(NAVIGATION_ICON_ID);
|
iconId = bundle.getInt(NAVIGATION_ICON_ID);
|
||||||
iconColor = bundle.getInt(NAVIGATION_ICON_COLOR);
|
iconColor = bundle.getInt(NAVIGATION_ICON_COLOR);
|
||||||
|
@ -157,6 +175,9 @@ public class RouteLineDrawInfo {
|
||||||
if (colorNight != null) {
|
if (colorNight != null) {
|
||||||
bundle.putInt(LINE_COLOR_NIGHT, colorNight);
|
bundle.putInt(LINE_COLOR_NIGHT, colorNight);
|
||||||
}
|
}
|
||||||
|
if (scaleType != null) {
|
||||||
|
bundle.putString(LINE_COLOR_GRADIENT, scaleType.getTypeName());
|
||||||
|
}
|
||||||
if (width != null) {
|
if (width != null) {
|
||||||
bundle.putString(LINE_WIDTH, width);
|
bundle.putString(LINE_WIDTH, width);
|
||||||
}
|
}
|
||||||
|
@ -177,6 +198,7 @@ public class RouteLineDrawInfo {
|
||||||
|
|
||||||
if (!Algorithms.objectEquals(getColor(false), that.getColor(false))) return false;
|
if (!Algorithms.objectEquals(getColor(false), that.getColor(false))) return false;
|
||||||
if (!Algorithms.objectEquals(getColor(true), that.getColor(true))) return false;
|
if (!Algorithms.objectEquals(getColor(true), that.getColor(true))) return false;
|
||||||
|
if (!Algorithms.objectEquals(scaleType, that.scaleType)) return false;
|
||||||
return Algorithms.objectEquals(width, that.width);
|
return Algorithms.objectEquals(width, that.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +206,7 @@ public class RouteLineDrawInfo {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = colorDay != null ? colorDay.hashCode() : 0;
|
int result = colorDay != null ? colorDay.hashCode() : 0;
|
||||||
result = 31 * result + (colorNight != null ? colorNight.hashCode() : 0);
|
result = 31 * result + (colorNight != null ? colorNight.hashCode() : 0);
|
||||||
|
result = 31 * result + (scaleType != null ? scaleType.getTypeName().hashCode() : 0);
|
||||||
result = 31 * result + (width != null ? width.hashCode() : 0);
|
result = 31 * result + (width != null ? width.hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import net.osmand.AndroidUtils;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.UiUtilities;
|
import net.osmand.plus.UiUtilities;
|
||||||
import net.osmand.plus.activities.MapActivity;
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||||
import net.osmand.plus.helpers.ColorDialogs;
|
import net.osmand.plus.helpers.ColorDialogs;
|
||||||
import net.osmand.plus.helpers.enums.DayNightMode;
|
import net.osmand.plus.helpers.enums.DayNightMode;
|
||||||
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
||||||
|
@ -32,6 +33,8 @@ import net.osmand.plus.settings.fragments.HeaderUiAdapter;
|
||||||
import net.osmand.plus.track.AppearanceViewHolder;
|
import net.osmand.plus.track.AppearanceViewHolder;
|
||||||
import net.osmand.plus.track.ColorsCard;
|
import net.osmand.plus.track.ColorsCard;
|
||||||
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
|
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
|
||||||
|
import net.osmand.plus.track.GradientCard;
|
||||||
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
import net.osmand.plus.widgets.multistatetoggle.RadioItem;
|
import net.osmand.plus.widgets.multistatetoggle.RadioItem;
|
||||||
import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener;
|
import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener;
|
||||||
import net.osmand.plus.widgets.multistatetoggle.TextToggleButton;
|
import net.osmand.plus.widgets.multistatetoggle.TextToggleButton;
|
||||||
|
@ -51,6 +54,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
private HeaderUiAdapter headerUiAdapter;
|
private HeaderUiAdapter headerUiAdapter;
|
||||||
|
|
||||||
private ColorsCard colorsCard;
|
private ColorsCard colorsCard;
|
||||||
|
private GradientCard gradientCard;
|
||||||
private ColorTypeAdapter colorAdapter;
|
private ColorTypeAdapter colorAdapter;
|
||||||
private RecyclerView groupRecyclerView;
|
private RecyclerView groupRecyclerView;
|
||||||
private TextView tvDescription;
|
private TextView tvDescription;
|
||||||
|
@ -64,7 +68,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
|
|
||||||
private enum ColorMode {
|
private enum ColorMode {
|
||||||
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style),
|
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style),
|
||||||
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings);
|
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings),
|
||||||
|
ALTITUDE(R.string.altitude, R.drawable.ic_action_hillshade_dark),
|
||||||
|
SLOPE(R.string.shared_string_slope, R.drawable.ic_action_altitude_ascent);
|
||||||
|
|
||||||
ColorMode(int titleId, int iconId) {
|
ColorMode(int titleId, int iconId) {
|
||||||
this.titleId = titleId;
|
this.titleId = titleId;
|
||||||
|
@ -108,28 +114,43 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
setupRadioGroup(radioGroup);
|
setupRadioGroup(radioGroup);
|
||||||
|
|
||||||
cardsContainer = (ViewGroup) view.findViewById(R.id.colors_card_container);
|
cardsContainer = (ViewGroup) view.findViewById(R.id.colors_card_container);
|
||||||
createColorSelector(cardsContainer);
|
createCards(cardsContainer);
|
||||||
|
|
||||||
initSelectedMode();
|
initSelectedMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSelectedMode() {
|
private void initSelectedMode() {
|
||||||
selectedMode = getRouteLineColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
|
if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.ALTITUDE) {
|
||||||
|
selectedMode = ColorMode.ALTITUDE;
|
||||||
|
} else if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.SLOPE) {
|
||||||
|
selectedMode = ColorMode.SLOPE;
|
||||||
|
} else {
|
||||||
|
selectedMode = getRouteLineColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
|
||||||
|
}
|
||||||
modeChanged();
|
modeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void modeChanged() {
|
private void modeChanged() {
|
||||||
if (selectedMode == ColorMode.DEFAULT) {
|
if (selectedMode == ColorMode.DEFAULT) {
|
||||||
themeToggleContainer.setVisibility(View.GONE);
|
themeToggleContainer.setVisibility(View.GONE);
|
||||||
cardsContainer.setVisibility(View.GONE);
|
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
|
||||||
|
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
|
||||||
routeLineDrawInfo.setUseDefaultColor(true);
|
routeLineDrawInfo.setUseDefaultColor(true);
|
||||||
changeMapTheme(initMapTheme);
|
changeMapTheme(initMapTheme);
|
||||||
} else {
|
} else if (selectedMode == ColorMode.CUSTOM) {
|
||||||
themeToggleContainer.setVisibility(View.VISIBLE);
|
themeToggleContainer.setVisibility(View.VISIBLE);
|
||||||
cardsContainer.setVisibility(View.VISIBLE);
|
AndroidUiHelper.updateVisibility(colorsCard.getView(), true);
|
||||||
|
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
|
||||||
routeLineDrawInfo.setUseDefaultColor(false);
|
routeLineDrawInfo.setUseDefaultColor(false);
|
||||||
changeMapTheme(isNightMap() ? DayNightMode.NIGHT : DayNightMode.DAY);
|
changeMapTheme(isNightMap() ? DayNightMode.NIGHT : DayNightMode.DAY);
|
||||||
|
} else {
|
||||||
|
gradientCard.setSelectedScaleType(getGradientScaleTypeFromMode());
|
||||||
|
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
|
||||||
|
AndroidUiHelper.updateVisibility(gradientCard.getView(), true);
|
||||||
|
themeToggleContainer.setVisibility(View.GONE);
|
||||||
|
routeLineDrawInfo.setUseDefaultColor(false);
|
||||||
}
|
}
|
||||||
|
routeLineDrawInfo.setGradientScaleType(getGradientScaleTypeFromMode());
|
||||||
updateColorItems();
|
updateColorItems();
|
||||||
updateDescription();
|
updateDescription();
|
||||||
}
|
}
|
||||||
|
@ -170,7 +191,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createColorSelector(ViewGroup container) {
|
private void createCards(ViewGroup container) {
|
||||||
MapActivity mapActivity = getMapActivity();
|
MapActivity mapActivity = getMapActivity();
|
||||||
if (mapActivity != null) {
|
if (mapActivity != null) {
|
||||||
List<Integer> colors = new ArrayList<>();
|
List<Integer> colors = new ArrayList<>();
|
||||||
|
@ -184,6 +205,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
colorsCard = new ColorsCard(mapActivity, selectedColor, targetFragment, colors, preference, null);
|
colorsCard = new ColorsCard(mapActivity, selectedColor, targetFragment, colors, preference, null);
|
||||||
colorsCard.setListener(this);
|
colorsCard.setListener(this);
|
||||||
container.addView(colorsCard.build(mapActivity));
|
container.addView(colorsCard.build(mapActivity));
|
||||||
|
|
||||||
|
gradientCard = new GradientCard(mapActivity, routeLineDrawInfo.getGradientScaleType());
|
||||||
|
container.addView(gradientCard.build(mapActivity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +236,17 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
return routeLineDrawInfo.getColor(isNightMap());
|
return routeLineDrawInfo.getColor(isNightMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private GradientScaleType getGradientScaleTypeFromMode() {
|
||||||
|
if (selectedMode == ColorMode.ALTITUDE) {
|
||||||
|
return GradientScaleType.ALTITUDE;
|
||||||
|
} else if (selectedMode == ColorMode.SLOPE) {
|
||||||
|
return GradientScaleType.SLOPE;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSelectedColor() {
|
private void updateSelectedColor() {
|
||||||
int selectedColor = colorsCard.getSelectedColor();
|
int selectedColor = colorsCard.getSelectedColor();
|
||||||
routeLineDrawInfo.setColor(selectedColor, isNightMap());
|
routeLineDrawInfo.setColor(selectedColor, isNightMap());
|
||||||
|
@ -235,6 +270,8 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
String colorName = "";
|
String colorName = "";
|
||||||
if (selectedMode == ColorMode.DEFAULT) {
|
if (selectedMode == ColorMode.DEFAULT) {
|
||||||
colorName = app.getString(R.string.map_widget_renderer);
|
colorName = app.getString(R.string.map_widget_renderer);
|
||||||
|
} else if (selectedMode == ColorMode.ALTITUDE || selectedMode == ColorMode.SLOPE) {
|
||||||
|
colorName = app.getString(selectedMode.titleId);
|
||||||
} else if (getRouteLineColor() != null) {
|
} else if (getRouteLineColor() != null) {
|
||||||
int colorNameId = ColorDialogs.getColorName(getRouteLineColor());
|
int colorNameId = ColorDialogs.getColorName(getRouteLineColor());
|
||||||
colorName = app.getString(colorNameId);
|
colorName = app.getString(colorNameId);
|
||||||
|
@ -248,10 +285,12 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
|
||||||
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
|
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
|
||||||
String color = app.getString(R.string.shared_string_color).toLowerCase();
|
String color = app.getString(R.string.shared_string_color).toLowerCase();
|
||||||
description = String.format(pattern, color, app.getRendererRegistry().getSelectedRendererName());
|
description = String.format(pattern, color, app.getRendererRegistry().getSelectedRendererName());
|
||||||
} else {
|
} else if (selectedMode == ColorMode.CUSTOM) {
|
||||||
String pattern = app.getString(R.string.specify_color_for_map_mode);
|
String pattern = app.getString(R.string.specify_color_for_map_mode);
|
||||||
String mapModeTitle = app.getString(isNightMap() ? NIGHT_TITLE_ID : DAY_TITLE_ID);
|
String mapModeTitle = app.getString(isNightMap() ? NIGHT_TITLE_ID : DAY_TITLE_ID);
|
||||||
description = String.format(pattern, mapModeTitle.toLowerCase());
|
description = String.format(pattern, mapModeTitle.toLowerCase());
|
||||||
|
} else {
|
||||||
|
description = app.getString(R.string.route_line_use_gradient_coloring);
|
||||||
}
|
}
|
||||||
tvDescription.setText(description);
|
tvDescription.setText(description);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.AndroidNetworkUtils;
|
import net.osmand.AndroidNetworkUtils;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
@ -71,7 +73,7 @@ public class SendSearchQueryBottomSheet extends MenuBottomSheetDialogFragment {
|
||||||
AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params,
|
AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params,
|
||||||
null, true, true, new AndroidNetworkUtils.OnRequestResultListener() {
|
null, true, true, new AndroidNetworkUtils.OnRequestResultListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(String result) {
|
public void onResult(@Nullable String result, @Nullable String error) {
|
||||||
if (result != null && isAdded()) {
|
if (result != null && isAdded()) {
|
||||||
try {
|
try {
|
||||||
JSONObject obj = new JSONObject(result);
|
JSONObject obj = new JSONObject(result);
|
||||||
|
|
|
@ -2708,6 +2708,7 @@ public class OsmandSettings {
|
||||||
public final ListStringPreference CUSTOM_ROUTE_LINE_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_route_line_colors", null, ",").makeShared().makeGlobal();
|
public final ListStringPreference CUSTOM_ROUTE_LINE_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_route_line_colors", null, ",").makeShared().makeGlobal();
|
||||||
public final CommonPreference<Integer> ROUTE_LINE_COLOR_DAY = new IntPreference(this, "route_line_color", 0).cache().makeProfile();
|
public final CommonPreference<Integer> ROUTE_LINE_COLOR_DAY = new IntPreference(this, "route_line_color", 0).cache().makeProfile();
|
||||||
public final CommonPreference<Integer> ROUTE_LINE_COLOR_NIGHT = new IntPreference(this, "route_line_color_night", 0).cache().makeProfile();
|
public final CommonPreference<Integer> ROUTE_LINE_COLOR_NIGHT = new IntPreference(this, "route_line_color_night", 0).cache().makeProfile();
|
||||||
|
public final CommonPreference<GradientScaleType> ROUTE_LINE_GRADIENT = new EnumStringPreference<>(this, "route_line_gradient", null, new GradientScaleType[] {GradientScaleType.ALTITUDE, GradientScaleType.SLOPE}).cache().makeProfile();
|
||||||
public final CommonPreference<String> ROUTE_LINE_WIDTH = new StringPreference(this, "route_line_width", null).makeProfile();
|
public final CommonPreference<String> ROUTE_LINE_WIDTH = new StringPreference(this, "route_line_width", null).makeProfile();
|
||||||
|
|
||||||
public final OsmandPreference<Boolean> USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile();
|
public final OsmandPreference<Boolean> USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile();
|
||||||
|
|
|
@ -63,6 +63,7 @@ import net.osmand.plus.settings.backend.backup.SettingsHelper;
|
||||||
import net.osmand.plus.settings.fragments.RouteLineAppearanceFragment.OnApplyRouteLineListener;
|
import net.osmand.plus.settings.fragments.RouteLineAppearanceFragment.OnApplyRouteLineListener;
|
||||||
import net.osmand.plus.track.ColorsCard;
|
import net.osmand.plus.track.ColorsCard;
|
||||||
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
|
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
|
||||||
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
import net.osmand.plus.widgets.FlowLayout;
|
import net.osmand.plus.widgets.FlowLayout;
|
||||||
import net.osmand.plus.widgets.OsmandTextFieldBoxes;
|
import net.osmand.plus.widgets.OsmandTextFieldBoxes;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -1021,8 +1022,9 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
|
||||||
private RouteLineDrawInfo createRouteLineDrawInfo(@NonNull ApplicationMode appMode) {
|
private RouteLineDrawInfo createRouteLineDrawInfo(@NonNull ApplicationMode appMode) {
|
||||||
Integer colorDay = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY);
|
Integer colorDay = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY);
|
||||||
Integer colorNight = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT);
|
Integer colorNight = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT);
|
||||||
|
GradientScaleType scaleType = settings.ROUTE_LINE_GRADIENT.getModeValue(appMode);
|
||||||
String widthKey = settings.ROUTE_LINE_WIDTH.getModeValue(appMode);
|
String widthKey = settings.ROUTE_LINE_WIDTH.getModeValue(appMode);
|
||||||
return new RouteLineDrawInfo(colorDay, colorNight, widthKey);
|
return new RouteLineDrawInfo(colorDay, colorNight, scaleType, widthKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer getRouteLineColor(@NonNull ApplicationMode appMode,
|
private Integer getRouteLineColor(@NonNull ApplicationMode appMode,
|
||||||
|
@ -1035,6 +1037,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
|
||||||
@NonNull RouteLineDrawInfo drawInfo) {
|
@NonNull RouteLineDrawInfo drawInfo) {
|
||||||
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY, drawInfo.getColor(false));
|
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY, drawInfo.getColor(false));
|
||||||
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT, drawInfo.getColor(true));
|
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT, drawInfo.getColor(true));
|
||||||
|
settings.ROUTE_LINE_GRADIENT.setModeValue(appMode, drawInfo.getGradientScaleType());
|
||||||
settings.ROUTE_LINE_WIDTH.setModeValue(appMode, drawInfo.getWidth());
|
settings.ROUTE_LINE_WIDTH.setModeValue(appMode, drawInfo.getWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,24 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
public class GradientCard extends BaseCard {
|
public class GradientCard extends BaseCard {
|
||||||
|
|
||||||
private GPXTrackAnalysis gpxTrackAnalysis;
|
private final GPXTrackAnalysis gpxTrackAnalysis;
|
||||||
private GradientScaleType selectedScaleType;
|
private GradientScaleType selectedScaleType;
|
||||||
|
|
||||||
|
private final int minSlope = 0;
|
||||||
|
private final int maxSlope = 60;
|
||||||
|
|
||||||
public GradientCard(@NonNull MapActivity mapActivity, @NonNull GPXTrackAnalysis gpxTrackAnalysis, @Nullable GradientScaleType selectedScaleType) {
|
public GradientCard(@NonNull MapActivity mapActivity, @NonNull GPXTrackAnalysis gpxTrackAnalysis, @Nullable GradientScaleType selectedScaleType) {
|
||||||
super(mapActivity);
|
super(mapActivity);
|
||||||
this.gpxTrackAnalysis = gpxTrackAnalysis;
|
this.gpxTrackAnalysis = gpxTrackAnalysis;
|
||||||
this.selectedScaleType = selectedScaleType;
|
this.selectedScaleType = selectedScaleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GradientCard(@NonNull MapActivity mapActivity, @Nullable GradientScaleType scaleType) {
|
||||||
|
super(mapActivity);
|
||||||
|
this.gpxTrackAnalysis = null;
|
||||||
|
this.selectedScaleType = scaleType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCardLayoutId() {
|
public int getCardLayoutId() {
|
||||||
return R.layout.gradient_card;
|
return R.layout.gradient_card;
|
||||||
|
@ -40,14 +49,27 @@ public class GradientCard extends BaseCard {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidUiHelper.updateVisibility(view, true);
|
|
||||||
TextView minValue = view.findViewById(R.id.min_value);
|
TextView minValue = view.findViewById(R.id.min_value);
|
||||||
TextView maxValue = view.findViewById(R.id.max_value);
|
TextView maxValue = view.findViewById(R.id.max_value);
|
||||||
double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis);
|
|
||||||
double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(),
|
if (gpxTrackAnalysis != null) {
|
||||||
gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed());
|
AndroidUiHelper.updateVisibility(view, true);
|
||||||
minValue.setText(formatValue(min));
|
double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis);
|
||||||
maxValue.setText(formatValue(max));
|
double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(),
|
||||||
|
gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed());
|
||||||
|
minValue.setText(formatValue(min));
|
||||||
|
maxValue.setText(formatValue(max));
|
||||||
|
AndroidUiHelper.updateVisibility(view.findViewById(R.id.space), true);
|
||||||
|
} else {
|
||||||
|
if (selectedScaleType == GradientScaleType.ALTITUDE) {
|
||||||
|
minValue.setText(R.string.shared_string_min_height);
|
||||||
|
maxValue.setText(R.string.shared_string_max_height);
|
||||||
|
} else if (selectedScaleType == GradientScaleType.SLOPE) {
|
||||||
|
minValue.setText(formatValue(minSlope));
|
||||||
|
maxValue.setText(formatValue(maxSlope));
|
||||||
|
}
|
||||||
|
AndroidUiHelper.updateVisibility(view.findViewById(R.id.space), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedScaleType(GradientScaleType type) {
|
public void setSelectedScaleType(GradientScaleType type) {
|
||||||
|
@ -59,7 +81,7 @@ public class GradientCard extends BaseCard {
|
||||||
if (selectedScaleType == GradientScaleType.ALTITUDE) {
|
if (selectedScaleType == GradientScaleType.ALTITUDE) {
|
||||||
return OsmAndFormatter.getFormattedAlt(value, app);
|
return OsmAndFormatter.getFormattedAlt(value, app);
|
||||||
} else if (selectedScaleType == GradientScaleType.SLOPE) {
|
} else if (selectedScaleType == GradientScaleType.SLOPE) {
|
||||||
return (int) value + " %";
|
return app.getString(R.string.ltr_or_rtl_combine_via_space, String.valueOf((int) value), "%");
|
||||||
}
|
}
|
||||||
String speed = OsmAndFormatter.getFormattedSpeed((float) value, app);
|
String speed = OsmAndFormatter.getFormattedSpeed((float) value, app);
|
||||||
String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app);
|
String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app);
|
||||||
|
|
|
@ -357,19 +357,7 @@ public class POIMapLayer extends OsmandMapLayer implements ContextMenuLayer.ICon
|
||||||
@Override
|
@Override
|
||||||
public PointDescription getObjectName(Object o) {
|
public PointDescription getObjectName(Object o) {
|
||||||
if (o instanceof Amenity) {
|
if (o instanceof Amenity) {
|
||||||
Amenity amenity = (Amenity) o;
|
return new PointDescription(PointDescription.POINT_TYPE_POI, getAmenityName((Amenity) o));
|
||||||
String preferredLang = app.getSettings().MAP_PREFERRED_LOCALE.get();
|
|
||||||
boolean transliterateNames = app.getSettings().MAP_TRANSLITERATE_NAMES.get();
|
|
||||||
|
|
||||||
if (amenity.getType().isWiki()) {
|
|
||||||
if (Algorithms.isEmpty(preferredLang)) {
|
|
||||||
preferredLang = app.getLanguage();
|
|
||||||
}
|
|
||||||
preferredLang = OsmandPlugin.onGetMapObjectsLocale(amenity, preferredLang);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PointDescription(PointDescription.POINT_TYPE_POI,
|
|
||||||
amenity.getName(preferredLang, transliterateNames));
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -431,8 +419,20 @@ public class POIMapLayer extends OsmandMapLayer implements ContextMenuLayer.ICon
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getText(Amenity o) {
|
public String getText(Amenity o) {
|
||||||
return o.getName(view.getSettings().MAP_PREFERRED_LOCALE.get(),
|
return getAmenityName(o);
|
||||||
view.getSettings().MAP_TRANSLITERATE_NAMES.get());
|
}
|
||||||
|
|
||||||
|
private String getAmenityName(Amenity amenity) {
|
||||||
|
String locale = app.getSettings().MAP_PREFERRED_LOCALE.get();
|
||||||
|
|
||||||
|
if (amenity.getType().isWiki()) {
|
||||||
|
if (Algorithms.isEmpty(locale)) {
|
||||||
|
locale = app.getLanguage();
|
||||||
|
}
|
||||||
|
locale = OsmandPlugin.onGetMapObjectsLocale(amenity, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return amenity.getName(locale, app.getSettings().MAP_TRANSLITERATE_NAMES.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Paint.Cap;
|
import android.graphics.Paint.Cap;
|
||||||
|
@ -11,6 +12,7 @@ import android.graphics.Path;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.PorterDuff.Mode;
|
import android.graphics.PorterDuff.Mode;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.graphics.Shader;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.LayerDrawable;
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ import net.osmand.plus.routing.RouteService;
|
||||||
import net.osmand.plus.routing.RoutingHelper;
|
import net.osmand.plus.routing.RoutingHelper;
|
||||||
import net.osmand.plus.routing.TransportRoutingHelper;
|
import net.osmand.plus.routing.TransportRoutingHelper;
|
||||||
import net.osmand.plus.settings.backend.CommonPreference;
|
import net.osmand.plus.settings.backend.CommonPreference;
|
||||||
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
import net.osmand.plus.views.OsmandMapLayer;
|
import net.osmand.plus.views.OsmandMapLayer;
|
||||||
import net.osmand.plus.views.OsmandMapTileView;
|
import net.osmand.plus.views.OsmandMapTileView;
|
||||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||||
|
@ -54,6 +57,7 @@ import net.osmand.plus.views.layers.geometry.RouteGeometryWayContext;
|
||||||
import net.osmand.render.RenderingRuleProperty;
|
import net.osmand.render.RenderingRuleProperty;
|
||||||
import net.osmand.render.RenderingRuleSearchRequest;
|
import net.osmand.render.RenderingRuleSearchRequest;
|
||||||
import net.osmand.render.RenderingRulesStorage;
|
import net.osmand.render.RenderingRulesStorage;
|
||||||
|
import net.osmand.router.RouteColorize;
|
||||||
import net.osmand.router.TransportRouteResult;
|
import net.osmand.router.TransportRouteResult;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
import net.osmand.util.MapUtils;
|
import net.osmand.util.MapUtils;
|
||||||
|
@ -109,6 +113,7 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
|
||||||
|
|
||||||
private int routeLineColor;
|
private int routeLineColor;
|
||||||
private Integer directionArrowsColor;
|
private Integer directionArrowsColor;
|
||||||
|
private GradientScaleType gradientScaleType = null;
|
||||||
|
|
||||||
public RouteLayer(RoutingHelper helper) {
|
public RouteLayer(RoutingHelper helper) {
|
||||||
this.helper = helper;
|
this.helper = helper;
|
||||||
|
@ -136,6 +141,8 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
|
||||||
attrs.defaultWidth = (int) (12 * density);
|
attrs.defaultWidth = (int) (12 * density);
|
||||||
attrs.defaultWidth3 = (int) (7 * density);
|
attrs.defaultWidth3 = (int) (7 * density);
|
||||||
attrs.defaultColor = view.getResources().getColor(R.color.nav_track);
|
attrs.defaultColor = view.getResources().getColor(R.color.nav_track);
|
||||||
|
attrs.shadowPaint.setColor(0x80000000);
|
||||||
|
attrs.shadowPaint.setStrokeCap(Cap.ROUND);
|
||||||
attrs.paint3.setStrokeCap(Cap.BUTT);
|
attrs.paint3.setStrokeCap(Cap.BUTT);
|
||||||
attrs.paint3.setColor(Color.WHITE);
|
attrs.paint3.setColor(Color.WHITE);
|
||||||
attrs.paint2.setStrokeCap(Cap.BUTT);
|
attrs.paint2.setStrokeCap(Cap.BUTT);
|
||||||
|
@ -331,14 +338,24 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
|
||||||
DrawSettings settings,
|
DrawSettings settings,
|
||||||
RouteLineDrawInfo drawInfo) {
|
RouteLineDrawInfo drawInfo) {
|
||||||
updateAttrs(settings, tileBox);
|
updateAttrs(settings, tileBox);
|
||||||
updateRouteColors(nightMode);
|
|
||||||
paintRouteLinePreview.setColor(getRouteLineColor());
|
|
||||||
paintRouteLinePreview.setStrokeWidth(getRouteLineWidth(tileBox));
|
paintRouteLinePreview.setStrokeWidth(getRouteLineWidth(tileBox));
|
||||||
|
|
||||||
int centerX = drawInfo.getCenterX();
|
int centerX = drawInfo.getCenterX();
|
||||||
int centerY = drawInfo.getCenterY();
|
int centerY = drawInfo.getCenterY();
|
||||||
int screenHeight = drawInfo.getScreenHeight();
|
int screenHeight = drawInfo.getScreenHeight();
|
||||||
|
|
||||||
|
updateRouteColors(nightMode);
|
||||||
|
updateRouteGradient();
|
||||||
|
|
||||||
|
LinearGradient gradient = null;
|
||||||
|
if (gradientScaleType == GradientScaleType.ALTITUDE || gradientScaleType == GradientScaleType.SLOPE) {
|
||||||
|
int[] colors = new int[] {RouteColorize.RED, RouteColorize.YELLOW, RouteColorize.GREEN};
|
||||||
|
float[] positions = new float[] {0, 0.5f, 1};
|
||||||
|
gradient = new LinearGradient(centerX, 0, centerX, screenHeight, colors, positions, Shader.TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
paintRouteLinePreview.setShader(gradient);
|
||||||
|
paintRouteLinePreview.setColor(getRouteLineColor());
|
||||||
|
|
||||||
canvas.drawLine(centerX, 0, centerX, screenHeight, paintRouteLinePreview);
|
canvas.drawLine(centerX, 0, centerX, screenHeight, paintRouteLinePreview);
|
||||||
|
|
||||||
if (previewIcon == null) {
|
if (previewIcon == null) {
|
||||||
|
@ -460,6 +477,14 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
|
||||||
routeLineColor = color;
|
routeLineColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateRouteGradient() {
|
||||||
|
if (routeLineDrawInfo != null) {
|
||||||
|
gradientScaleType = routeLineDrawInfo.getGradientScaleType();
|
||||||
|
} else {
|
||||||
|
gradientScaleType = view.getSettings().ROUTE_LINE_GRADIENT.getModeValue(helper.getAppMode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float getRouteLineWidth(@NonNull RotatedTileBox tileBox) {
|
private float getRouteLineWidth(@NonNull RotatedTileBox tileBox) {
|
||||||
String widthKey;
|
String widthKey;
|
||||||
if (routeLineDrawInfo != null) {
|
if (routeLineDrawInfo != null) {
|
||||||
|
@ -524,9 +549,10 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
|
||||||
boolean directTo = route.getRouteService() == RouteService.DIRECT_TO;
|
boolean directTo = route.getRouteService() == RouteService.DIRECT_TO;
|
||||||
boolean straight = route.getRouteService() == RouteService.STRAIGHT;
|
boolean straight = route.getRouteService() == RouteService.STRAIGHT;
|
||||||
publicTransportRouteGeometry.clearRoute();
|
publicTransportRouteGeometry.clearRoute();
|
||||||
routeGeometry.updateRoute(tb, route);
|
|
||||||
updateRouteColors(nightMode);
|
updateRouteColors(nightMode);
|
||||||
routeGeometry.setRouteStyleParams(getRouteLineColor(), getRouteLineWidth(tb), getDirectionArrowsColor());
|
updateRouteGradient();
|
||||||
|
routeGeometry.setRouteStyleParams(getRouteLineColor(), getRouteLineWidth(tb), getDirectionArrowsColor(), gradientScaleType);
|
||||||
|
routeGeometry.updateRoute(tb, route, view.getApplication());
|
||||||
if (directTo) {
|
if (directTo) {
|
||||||
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
||||||
null, 0);
|
null, 0);
|
||||||
|
|
|
@ -126,7 +126,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
zooms.clear();
|
zooms.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PathGeometryZoom getGeometryZoom(RotatedTileBox tb) {
|
protected PathGeometryZoom getGeometryZoom(RotatedTileBox tb, Map<Integer, PathGeometryZoom> zooms) {
|
||||||
int zoom = tb.getZoom();
|
int zoom = tb.getZoom();
|
||||||
PathGeometryZoom zm = zooms.size() > zoom ? zooms.get(zoom) : null;
|
PathGeometryZoom zm = zooms.size() > zoom ? zooms.get(zoom) : null;
|
||||||
if (zm == null) {
|
if (zm == null) {
|
||||||
|
@ -139,6 +139,11 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
@NonNull
|
@NonNull
|
||||||
public abstract GeometryWayStyle<?> getDefaultWayStyle();
|
public abstract GeometryWayStyle<?> getDefaultWayStyle();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected Map<Integer, GeometryWayStyle<?>> getStyleMap() {
|
||||||
|
return styleMap;
|
||||||
|
}
|
||||||
|
|
||||||
public Location getNextVisiblePoint() {
|
public Location getNextVisiblePoint() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +153,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
if (locationProvider == null || locationProvider.getSize() == 0) {
|
if (locationProvider == null || locationProvider.getSize() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PathGeometryZoom geometryZoom = getGeometryZoom(tb);
|
PathGeometryZoom geometryZoom = getGeometryZoom(tb, zooms);
|
||||||
TByteArrayList simplification = geometryZoom.getSimplifyPoints();
|
TByteArrayList simplification = geometryZoom.getSimplifyPoints();
|
||||||
List<Double> odistances = geometryZoom.getDistances();
|
List<Double> odistances = geometryZoom.getDistances();
|
||||||
|
|
||||||
|
@ -168,32 +173,27 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
int previous = -1;
|
int previous = -1;
|
||||||
for (int i = startLocationIndex; i < locationProvider.getSize(); i++) {
|
for (int i = startLocationIndex; i < locationProvider.getSize(); i++) {
|
||||||
style = getStyle(i, defaultWayStyle);
|
style = getStyle(i, defaultWayStyle);
|
||||||
if (simplification.getQuick(i) == 0 && !styleMap.containsKey(i)) {
|
if (shouldSkipLocation(simplification, styleMap, i)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double lat = locationProvider.getLatitude(i);
|
|
||||||
double lon = locationProvider.getLongitude(i);
|
|
||||||
if (shouldAddLocation(simplification, leftLongitude, rightLongitude, bottomLatitude, topLatitude,
|
if (shouldAddLocation(simplification, leftLongitude, rightLongitude, bottomLatitude, topLatitude,
|
||||||
locationProvider, i)) {
|
locationProvider, i)) {
|
||||||
double dist = previous == -1 ? 0 : odistances.get(i);
|
double dist = previous == -1 ? 0 : odistances.get(i);
|
||||||
if (!previousVisible) {
|
if (!previousVisible) {
|
||||||
double prevLat = Double.NaN;
|
|
||||||
double prevLon = Double.NaN;
|
|
||||||
if (previous != -1) {
|
if (previous != -1) {
|
||||||
prevLat = locationProvider.getLatitude(previous);
|
addLocation(tb, previous, style, tx, ty, angles, distances, dist, styles);
|
||||||
prevLon = locationProvider.getLongitude(previous);
|
|
||||||
} else if (lastProjection != null) {
|
} else if (lastProjection != null) {
|
||||||
prevLat = lastProjection.getLatitude();
|
double prevLat = lastProjection.getLatitude();
|
||||||
prevLon = lastProjection.getLongitude();
|
double prevLon = lastProjection.getLongitude();
|
||||||
}
|
if (!Double.isNaN(prevLat) && !Double.isNaN(prevLon)) {
|
||||||
if (!Double.isNaN(prevLat) && !Double.isNaN(prevLon)) {
|
addLocation(tb, prevLat, prevLon, getStyle(i - 1, style), tx, ty, angles, distances, dist, styles); // first point
|
||||||
addLocation(tb, prevLat, prevLon, getStyle(i - 1, style), tx, ty, angles, distances, dist, styles); // first point
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addLocation(tb, lat, lon, style, tx, ty, angles, distances, dist, styles);
|
addLocation(tb, i, style, tx, ty, angles, distances, dist, styles);
|
||||||
previousVisible = true;
|
previousVisible = true;
|
||||||
} else if (previousVisible) {
|
} else if (previousVisible) {
|
||||||
addLocation(tb, lat, lon, style, tx, ty, angles, distances, previous == -1 ? 0 : odistances.get(i), styles);
|
addLocation(tb, i, style, tx, ty, angles, distances, previous == -1 ? 0 : odistances.get(i), styles);
|
||||||
double distToFinish = 0;
|
double distToFinish = 0;
|
||||||
for (int ki = i + 1; ki < odistances.size(); ki++) {
|
for (int ki = i + 1; ki < odistances.size(); ki++) {
|
||||||
distToFinish += odistances.get(ki);
|
distToFinish += odistances.get(ki);
|
||||||
|
@ -207,6 +207,10 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
drawRouteSegment(tb, canvas, tx, ty, angles, distances, 0, styles);
|
drawRouteSegment(tb, canvas, tx, ty, angles, distances, 0, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean shouldSkipLocation(TByteArrayList simplification, Map<Integer, GeometryWayStyle<?>> styleMap, int locationIdx) {
|
||||||
|
return simplification.getQuick(locationIdx) == 0 && !styleMap.containsKey(locationIdx);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean shouldAddLocation(TByteArrayList simplification, double leftLon, double rightLon, double bottomLat,
|
protected boolean shouldAddLocation(TByteArrayList simplification, double leftLon, double rightLon, double bottomLat,
|
||||||
double topLat, GeometryWayProvider provider, int currLocationIdx) {
|
double topLat, GeometryWayProvider provider, int currLocationIdx) {
|
||||||
double lat = provider.getLatitude(currLocationIdx);
|
double lat = provider.getLatitude(currLocationIdx);
|
||||||
|
@ -214,7 +218,14 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
return leftLon <= lon && lon <= rightLon && bottomLat <= lat && lat <= topLat;
|
return leftLon <= lon && lon <= rightLon && bottomLat <= lat && lat <= topLat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style,
|
protected void addLocation(RotatedTileBox tb, int locationIdx, GeometryWayStyle<?> style,
|
||||||
|
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
||||||
|
double dist, List<GeometryWayStyle<?>> styles) {
|
||||||
|
addLocation(tb, locationProvider.getLatitude(locationIdx), locationProvider.getLongitude(locationIdx),
|
||||||
|
style, tx, ty, angles, distances, dist, styles);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style,
|
||||||
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
||||||
double dist, List<GeometryWayStyle<?>> styles) {
|
double dist, List<GeometryWayStyle<?>> styles) {
|
||||||
float x = tb.getPixXFromLatLon(latitude, longitude);
|
float x = tb.getPixXFromLatLon(latitude, longitude);
|
||||||
|
@ -356,13 +367,26 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
List<Pair<Path, GeometryWayStyle<?>>> paths = new ArrayList<>();
|
List<Pair<Path, GeometryWayStyle<?>>> paths = new ArrayList<>();
|
||||||
canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
||||||
calculatePath(tb, tx, ty, styles, paths);
|
calculatePath(tb, tx, ty, styles, paths);
|
||||||
for (Pair<Path, GeometryWayStyle<?>> pc : paths) {
|
|
||||||
|
drawer.drawFullBorder(canvas, tb.getZoom(), paths);
|
||||||
|
drawer.drawSegmentBorder(canvas, tb.getZoom(), paths.get(0).first, paths.get(0).second);
|
||||||
|
for (int i = 1; i <= paths.size(); i++) {
|
||||||
|
if (i != paths.size()) {
|
||||||
|
Pair<Path, GeometryWayStyle<?>> prev = paths.get(i);
|
||||||
|
if (prev.second.hasPathLine()) {
|
||||||
|
drawer.drawSegmentBorder(canvas, tb.getZoom(), prev.first, prev.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<Path, GeometryWayStyle<?>> pc = paths.get(i - 1);
|
||||||
GeometryWayStyle<?> style = pc.second;
|
GeometryWayStyle<?> style = pc.second;
|
||||||
if (style.hasPathLine()) {
|
if (style.hasPathLine()) {
|
||||||
drawer.drawPath(canvas, pc.first, style);
|
drawer.drawPath(canvas, pc.first, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.clearCustomColor();
|
context.clearCustomColor();
|
||||||
|
context.clearCustomShader();
|
||||||
|
|
||||||
}
|
}
|
||||||
drawer.drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles);
|
drawer.drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -372,7 +396,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PathGeometryZoom {
|
protected static class PathGeometryZoom {
|
||||||
|
|
||||||
private static final float EPSILON_IN_DPI = 2;
|
private static final float EPSILON_IN_DPI = 2;
|
||||||
|
|
||||||
|
@ -390,12 +414,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
angles = new ArrayList<>(size);
|
angles = new ArrayList<>(size);
|
||||||
if (simplify) {
|
if (simplify) {
|
||||||
simplifyPoints.fill(0, size, (byte) 0);
|
simplifyPoints.fill(0, size, (byte) 0);
|
||||||
if (size > 0) {
|
simplify(tb, locationProvider, simplifyPoints);
|
||||||
simplifyPoints.set(0, (byte) 1);
|
|
||||||
}
|
|
||||||
double distInPix = (tb.getDistance(0, 0, tb.getPixWidth(), 0) / tb.getPixWidth());
|
|
||||||
double cullDistance = (distInPix * (EPSILON_IN_DPI * Math.max(1, tb.getDensity())));
|
|
||||||
cullRamerDouglasPeucker(simplifyPoints, locationProvider, 0, size - 1, cullDistance);
|
|
||||||
} else {
|
} else {
|
||||||
simplifyPoints.fill(0, size, (byte) 1);
|
simplifyPoints.fill(0, size, (byte) 1);
|
||||||
}
|
}
|
||||||
|
@ -422,6 +441,16 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void simplify(RotatedTileBox tb, GeometryWayProvider locationProvider, TByteArrayList simplifyPoints) {
|
||||||
|
int size = locationProvider.getSize();
|
||||||
|
if (size > 0) {
|
||||||
|
simplifyPoints.set(0, (byte) 1);
|
||||||
|
}
|
||||||
|
double distInPix = (tb.getDistance(0, 0, tb.getPixWidth(), 0) / tb.getPixWidth());
|
||||||
|
double cullDistance = (distInPix * (EPSILON_IN_DPI * Math.max(1, tb.getDensity())));
|
||||||
|
cullRamerDouglasPeucker(simplifyPoints, locationProvider, 0, size - 1, cullDistance);
|
||||||
|
}
|
||||||
|
|
||||||
public List<Double> getDistances() {
|
public List<Double> getDistances() {
|
||||||
return distances;
|
return distances;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,10 @@ public abstract class GeometryWayContext {
|
||||||
attrs.customColor = 0;
|
attrs.customColor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearCustomShader() {
|
||||||
|
attrs.customColorPaint.setShader(null);
|
||||||
|
}
|
||||||
|
|
||||||
public int getStrokeColor(int sourceColor) {
|
public int getStrokeColor(int sourceColor) {
|
||||||
return ColorUtils.blendARGB(sourceColor, Color.BLACK, 0.6f);
|
return ColorUtils.blendARGB(sourceColor, Color.BLACK, 0.6f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import net.osmand.data.RotatedTileBox;
|
import net.osmand.data.RotatedTileBox;
|
||||||
|
|
||||||
|
@ -89,6 +90,12 @@ public class GeometryWayDrawer<T extends GeometryWayContext> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void drawFullBorder(Canvas canvas, int zoom, List<Pair<Path, GeometryWayStyle<?>>> paths) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void drawSegmentBorder(Canvas canvas, int zoom, Path path, GeometryWayStyle<?> style) {
|
||||||
|
}
|
||||||
|
|
||||||
protected PathPoint getArrowPathPoint(float iconx, float icony, GeometryWayStyle<?> style, double angle) {
|
protected PathPoint getArrowPathPoint(float iconx, float icony, GeometryWayStyle<?> style, double angle) {
|
||||||
return new PathPoint(iconx, icony, angle, style);
|
return new PathPoint(iconx, icony, angle, style);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,32 @@ package net.osmand.plus.views.layers.geometry;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PointF;
|
||||||
|
|
||||||
|
import net.osmand.GPXUtilities;
|
||||||
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.Location;
|
||||||
|
import net.osmand.data.RotatedTileBox;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper;
|
||||||
|
import net.osmand.plus.routing.RouteCalculationResult;
|
||||||
|
import net.osmand.plus.routing.RoutingHelper;
|
||||||
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
|
import net.osmand.router.RouteColorize;
|
||||||
|
import net.osmand.router.RouteColorize.RouteColorizationPoint;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import gnu.trove.list.array.TByteArrayList;
|
||||||
|
|
||||||
import net.osmand.Location;
|
public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, RouteGeometryWayDrawer> {
|
||||||
import net.osmand.data.RotatedTileBox;
|
|
||||||
import net.osmand.plus.routing.RouteCalculationResult;
|
|
||||||
import net.osmand.plus.routing.RoutingHelper;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, GeometryWayDrawer<RouteGeometryWayContext>> {
|
|
||||||
|
|
||||||
private RoutingHelper helper;
|
private RoutingHelper helper;
|
||||||
private RouteCalculationResult route;
|
private RouteCalculationResult route;
|
||||||
|
@ -23,34 +35,101 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
|
||||||
private Integer customColor;
|
private Integer customColor;
|
||||||
private Float customWidth;
|
private Float customWidth;
|
||||||
private Integer customPointColor;
|
private Integer customPointColor;
|
||||||
|
private GradientScaleType scaleType;
|
||||||
|
private GradientScaleType prevScaleType;
|
||||||
|
|
||||||
public RouteGeometryWay(RouteGeometryWayContext context) {
|
public RouteGeometryWay(RouteGeometryWayContext context) {
|
||||||
super(context, new GeometryWayDrawer<>(context));
|
super(context, new RouteGeometryWayDrawer(context, true));
|
||||||
this.helper = context.getApp().getRoutingHelper();
|
this.helper = context.getApp().getRoutingHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRouteStyleParams(@Nullable @ColorInt Integer color,
|
public void setRouteStyleParams(@Nullable @ColorInt Integer color,
|
||||||
@Nullable Float width,
|
@Nullable Float width,
|
||||||
@Nullable @ColorInt Integer pointColor) {
|
@Nullable @ColorInt Integer pointColor,
|
||||||
|
@Nullable GradientScaleType scaleType) {
|
||||||
|
if (scaleType != null && !Algorithms.objectEquals(customWidth, width)) {
|
||||||
|
for (GeometryWayStyle<?> style : getStyleMap().values()) {
|
||||||
|
style.width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.customColor = color;
|
this.customColor = color;
|
||||||
this.customWidth = width;
|
this.customWidth = width;
|
||||||
this.customPointColor = pointColor;
|
this.customPointColor = pointColor;
|
||||||
|
this.prevScaleType = this.scaleType;
|
||||||
|
this.scaleType = scaleType;
|
||||||
|
if (width != null) {
|
||||||
|
getContext().getAttrs().shadowPaint.setStrokeWidth(width + getContext().getDensity() * 2);
|
||||||
|
}
|
||||||
|
getContext().getAttrs().customColorPaint.setStrokeCap(scaleType != null ? Paint.Cap.ROUND : Paint.Cap.BUTT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route, OsmandApplication app) {
|
||||||
@Override
|
if (tb.getMapDensity() != getMapDensity() || this.route != route || prevScaleType != scaleType) {
|
||||||
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
|
|
||||||
Paint paint = getContext().getAttrs().paint;
|
|
||||||
int color = customColor != null ? customColor : paint.getColor();
|
|
||||||
float width = customWidth != null ? customWidth : paint.getStrokeWidth();
|
|
||||||
return new GeometrySolidWayStyle(getContext(), color, width, customPointColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) {
|
|
||||||
if (tb.getMapDensity() != getMapDensity() || this.route != route) {
|
|
||||||
this.route = route;
|
this.route = route;
|
||||||
List<Location> locations = route != null ? route.getImmutableAllLocations() : Collections.<Location>emptyList();
|
List<Location> locations = route != null ? route.getImmutableAllLocations() : Collections.<Location>emptyList();
|
||||||
updateWay(locations, tb);
|
|
||||||
|
if (scaleType == null || locations.size() < 2) {
|
||||||
|
updateWay(locations, tb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GPXFile gpxFile = GpxUiHelper.makeGpxFromLocations(locations, app);
|
||||||
|
if (!gpxFile.hasAltitude) {
|
||||||
|
updateWay(locations, tb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start point can have wrong zero altitude
|
||||||
|
List<GPXUtilities.WptPt> pts = gpxFile.tracks.get(0).segments.get(0).points;
|
||||||
|
GPXUtilities.WptPt firstPt = pts.get(0);
|
||||||
|
if (firstPt.ele == 0) {
|
||||||
|
firstPt.ele = pts.get(1).ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteColorize routeColorize = new RouteColorize(tb.getZoom(), gpxFile, null, scaleType.toColorizationType(), 0);
|
||||||
|
List<RouteColorizationPoint> points = routeColorize.getResult(false);
|
||||||
|
|
||||||
|
updateWay(new GradientGeometryWayProvider(routeColorize, points), createStyles(points), tb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, GeometryWayStyle<?>> createStyles(List<RouteColorizationPoint> points) {
|
||||||
|
Map<Integer, GeometryWayStyle<?>> styleMap = new TreeMap<>();
|
||||||
|
for (int i = 1; i < points.size(); i++) {
|
||||||
|
GeometryGradientWayStyle style = getGradientWayStyle();
|
||||||
|
style.startColor = points.get(i - 1).color;
|
||||||
|
style.endColor = points.get(i).color;
|
||||||
|
styleMap.put(i, style);
|
||||||
|
}
|
||||||
|
return styleMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldSkipLocation(TByteArrayList simplification, Map<Integer, GeometryWayStyle<?>> styleMap, int locationIdx) {
|
||||||
|
return scaleType == null ?
|
||||||
|
super.shouldSkipLocation(simplification, styleMap, locationIdx) :
|
||||||
|
simplification.getQuick(locationIdx) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addLocation(RotatedTileBox tb, int locationIdx, GeometryWayStyle<?> style, List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances, double dist, List<GeometryWayStyle<?>> styles) {
|
||||||
|
super.addLocation(tb, getLocationProvider().getLatitude(locationIdx),
|
||||||
|
getLocationProvider().getLongitude(locationIdx), style, tx, ty, angles, distances, dist, styles);
|
||||||
|
if (scaleType != null && tx.size() - 1 > 0) {
|
||||||
|
int idx = tx.size() - 1;
|
||||||
|
((GeometryGradientWayStyle) style).startXY = new PointF(tx.get(idx - 1), ty.get(idx - 1));
|
||||||
|
((GeometryGradientWayStyle) style).endXY = new PointF(tx.get(idx), ty.get(idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style, List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances, double dist, List<GeometryWayStyle<?>> styles) {
|
||||||
|
super.addLocation(tb, latitude, longitude, style, tx, ty, angles, distances, dist, styles);
|
||||||
|
if (scaleType != null) {
|
||||||
|
int lastIdx = tx.size() - 1;
|
||||||
|
((GeometryGradientWayStyle) style).startXY = new PointF(tx.get(lastIdx), ty.get(lastIdx));
|
||||||
|
((GeometryGradientWayStyle) style).endXY = new PointF(tx.get(lastIdx), ty.get(lastIdx));
|
||||||
|
((GeometryGradientWayStyle) style).startColor = getGradientLocationProvider().getColor(0);
|
||||||
|
((GeometryGradientWayStyle) style).endColor = getGradientLocationProvider().getColor(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,17 +140,125 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
|
||||||
|
Paint paint = getContext().getAttrs().paint;
|
||||||
|
int color = customColor != null ? customColor : paint.getColor();
|
||||||
|
float width = customWidth != null ? customWidth : paint.getStrokeWidth();
|
||||||
|
return scaleType == null ?
|
||||||
|
new GeometrySolidWayStyle(getContext(), color, width, customPointColor) :
|
||||||
|
new GeometryGradientWayStyle(getContext(), color, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GeometryGradientWayStyle getGradientWayStyle() {
|
||||||
|
return (GeometryGradientWayStyle) getDefaultWayStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Location getNextVisiblePoint() {
|
public Location getNextVisiblePoint() {
|
||||||
return helper.getRoute().getCurrentStraightAnglePoint();
|
return helper.getRoute().getCurrentStraightAnglePoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GradientGeometryWayProvider getGradientLocationProvider() {
|
||||||
|
return (GradientGeometryWayProvider) getLocationProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PathGeometryZoom getGeometryZoom(RotatedTileBox tb, Map<Integer, PathGeometryZoom> zooms) {
|
||||||
|
if (scaleType == null) {
|
||||||
|
return super.getGeometryZoom(tb, zooms);
|
||||||
|
}
|
||||||
|
int zoom = tb.getZoom();
|
||||||
|
PathGeometryZoom zm = zooms.get(zoom);
|
||||||
|
if (zm == null) {
|
||||||
|
zm = new GradientPathGeometryZoom(getLocationProvider(), tb, true);
|
||||||
|
zooms.put(zoom, zm);
|
||||||
|
}
|
||||||
|
return zm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GradientGeometryWayProvider implements GeometryWayProvider {
|
||||||
|
|
||||||
|
private final RouteColorize routeColorize;
|
||||||
|
private final List<RouteColorizationPoint> locations;
|
||||||
|
|
||||||
|
public GradientGeometryWayProvider(RouteColorize routeColorize, List<RouteColorizationPoint> locations) {
|
||||||
|
this.routeColorize = routeColorize;
|
||||||
|
this.locations = locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RouteColorizationPoint> simplify(int zoom) {
|
||||||
|
return routeColorize.simplify(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor(int index) {
|
||||||
|
return locations.get(index).color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getLatitude(int index) {
|
||||||
|
return locations.get(index).lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getLongitude(int index) {
|
||||||
|
return locations.get(index).lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return locations.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GradientPathGeometryZoom extends PathGeometryZoom {
|
||||||
|
|
||||||
|
public GradientPathGeometryZoom(GeometryWayProvider locationProvider, RotatedTileBox tb, boolean simplify) {
|
||||||
|
super(locationProvider, tb, simplify);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void simplify(RotatedTileBox tb, GeometryWayProvider locationProvider, TByteArrayList simplifyPoints) {
|
||||||
|
if (locationProvider instanceof GradientGeometryWayProvider) {
|
||||||
|
GradientGeometryWayProvider provider = (GradientGeometryWayProvider) locationProvider;
|
||||||
|
List<RouteColorizationPoint> simplified = provider.simplify(tb.getZoom());
|
||||||
|
for (RouteColorizationPoint location : simplified) {
|
||||||
|
simplifyPoints.set(location.id, (byte) 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GeometryGradientWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
|
||||||
|
|
||||||
|
public int startColor;
|
||||||
|
public int endColor;
|
||||||
|
|
||||||
|
public PointF startXY;
|
||||||
|
public PointF endXY;
|
||||||
|
|
||||||
|
public GeometryGradientWayStyle(RouteGeometryWayContext context, Integer color, Float width) {
|
||||||
|
super(context, color, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bitmap getPointBitmap() {
|
||||||
|
return getContext().getArrowBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return this == other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class GeometrySolidWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
|
private static class GeometrySolidWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
|
||||||
|
|
||||||
private Integer pointColor;
|
private Integer pointColor;
|
||||||
|
|
||||||
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color, Float width,
|
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color, Float width,
|
||||||
Integer pointColor) {
|
Integer pointColor) {
|
||||||
super(context, color, width);
|
super(context, color, width);
|
||||||
this.pointColor = pointColor;
|
this.pointColor = pointColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package net.osmand.plus.views.layers.geometry;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import net.osmand.plus.views.MapTileLayer;
|
||||||
|
import net.osmand.plus.views.layers.geometry.RouteGeometryWay.GeometryGradientWayStyle;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RouteGeometryWayDrawer extends GeometryWayDrawer<RouteGeometryWayContext> {
|
||||||
|
|
||||||
|
private static final int BORDER_TYPE_ZOOM_THRESHOLD = MapTileLayer.DEFAULT_MAX_ZOOM + MapTileLayer.OVERZOOM_IN;
|
||||||
|
|
||||||
|
private final boolean drawBorder;
|
||||||
|
|
||||||
|
public RouteGeometryWayDrawer(RouteGeometryWayContext context, boolean drawBorder) {
|
||||||
|
super(context);
|
||||||
|
this.drawBorder = drawBorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawFullBorder(Canvas canvas, int zoom, List<Pair<Path, GeometryWayStyle<?>>> paths) {
|
||||||
|
if (drawBorder && zoom < BORDER_TYPE_ZOOM_THRESHOLD) {
|
||||||
|
Paint borderPaint = getContext().getAttrs().shadowPaint;
|
||||||
|
Path fullPath = new Path();
|
||||||
|
for (Pair<Path, GeometryWayStyle<?>> path : paths) {
|
||||||
|
if (path.second instanceof GeometryGradientWayStyle) {
|
||||||
|
fullPath.addPath(path.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.drawPath(fullPath, borderPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawPath(Canvas canvas, Path path, GeometryWayStyle<?> s) {
|
||||||
|
if (s instanceof GeometryGradientWayStyle) {
|
||||||
|
GeometryGradientWayStyle style = (GeometryGradientWayStyle) s;
|
||||||
|
LinearGradient gradient = new LinearGradient(style.startXY.x, style.startXY.y, style.endXY.x, style.endXY.y,
|
||||||
|
style.startColor, style.endColor, Shader.TileMode.CLAMP);
|
||||||
|
getContext().getAttrs().customColorPaint.setShader(gradient);
|
||||||
|
}
|
||||||
|
super.drawPath(canvas, path, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawSegmentBorder(Canvas canvas, int zoom, Path path, GeometryWayStyle<?> style) {
|
||||||
|
if (drawBorder && zoom >= BORDER_TYPE_ZOOM_THRESHOLD) {
|
||||||
|
canvas.drawPath(path, getContext().getAttrs().shadowPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue