feat: новая архитектура UI и расширенная визуализация AIS

Архитектурные улучшения:
- Внедрен UIRenderingCoordinator с централизованным throttling
- Решены проблемы зависания UI через батчинг операций карты
- Добавлен VesselPathController для отслеживания маршрутов
- Реализован MapLibreMapImpl как альтернатива Яндекс.Картам

Визуализация AIS:
- Добавлены векторные иконки для всех типов судов
- Разделение Class A/B судов с соответствующими иконками
- Иконки навигационных статусов (anchor, moored, engine, sail)
- Улучшенный CursorOverlay с информацией о судах

Производительность:
- Throttling UI обновлений (vessel: 500ms, AIS: 1s, paths: 2s)
- Устранение утечек Handler объектов
- Оптимизация GeoJSON операций в MapLibre
This commit is contained in:
2025-10-02 09:15:33 +03:00
parent 41432665ea
commit b5aee265bc
85 changed files with 7132 additions and 449 deletions
+21
View File
@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="62.13dp"
android:height="77.5dp"
android:viewportWidth="62.13"
android:viewportHeight="77.5">
<path
android:pathData="M31.09,13m-9.5,0a9.5,9.5 0,1 1,19 0a9.5,9.5 0,1 1,-19 0"
android:strokeWidth="7"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M3.09,45.5l4.79,9.05c14.4,27.2 34.78,25.73 48.48,-3.49l2.6,-5.56"
android:strokeWidth="7"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M31.09,22.5L31.09,71"
android:strokeWidth="7"
android:fillColor="#fff"
android:strokeColor="#000"/>
</vector>
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="129dp"
android:height="129dp"
android:viewportWidth="129"
android:viewportHeight="129">
<path
android:pathData="M62.39,72.13L12.02,122.94c-1.86,1.87 -0.53,5.06 2.11,5.06h100.75c2.64,0 3.97,-3.19 2.11,-5.06l-50.38,-50.81c-1.16,-1.17 -3.06,-1.17 -4.22,0Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#000"/>
<path
android:pathData="M56.87,62.39L6.06,12.02c-1.87,-1.86 -5.06,-0.53 -5.06,2.11v100.75c0,2.64 3.19,3.97 5.06,2.11l50.81,-50.38c1.17,-1.16 1.17,-3.06 0,-4.22Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#000"/>
<path
android:pathData="M66.61,56.87L116.98,6.06c1.86,-1.87 0.53,-5.06 -2.11,-5.06H14.12c-2.64,0 -3.97,3.19 -2.11,5.06l50.38,50.81c1.16,1.17 3.06,1.17 4.22,0Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#000"/>
<path
android:pathData="M72.13,66.61l50.81,50.38c1.87,1.86 5.06,0.53 5.06,-2.11V14.12c0,-2.64 -3.19,-3.97 -5.06,-2.11l-50.81,50.38c-1.17,1.16 -1.17,3.06 0,4.22Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#000"/>
</vector>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Горизонтальная линия креста -->
<item android:top="18dp" android:bottom="18dp">
<shape android:shape="rectangle">
<solid android:color="#FF0000" />
<size android:width="40dp" android:height="4dp" />
</shape>
</item>
<!-- Вертикальная линия креста -->
<item android:left="18dp" android:right="18dp">
<shape android:shape="rectangle">
<solid android:color="#FF0000" />
<size android:width="4dp" android:height="40dp" />
</shape>
</item>
<!-- Центральная точка -->
<item android:left="18dp" android:right="18dp" android:top="18dp" android:bottom="18dp">
<shape android:shape="oval">
<solid android:color="#FFFFFF" />
<size android:width="4dp" android:height="4dp" />
</shape>
</item>
</layer-list>
+62
View File
@@ -0,0 +1,62 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="137.43dp"
android:height="137.43dp"
android:viewportWidth="137.43"
android:viewportHeight="137.43">
<path
android:strokeWidth="1"
android:pathData="M69.22,68.97m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0"
android:fillColor="#00000000"
android:strokeColor="#000"/>
<path
android:fillColor="#FF000000"
android:pathData="M50.01,71.14l-6,-0.28l-0.02,-4.4l6,-0.32l0.02,5z"/>
<path
android:fillColor="#FF000000"
android:pathData="M31.34,70.27l-12.67,-0.59l-0.01,-1.86l12.67,-0.68l0.01,3.13z"/>
<path
android:fillColor="#FF000000"
android:pathData="M6,69.09l-6,-0.27l6,-0.33l0,0.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M66.29,50.01l0.28,-6l4.4,-0.02l0.32,6l-5,0.02z"/>
<path
android:fillColor="#FF000000"
android:pathData="M67.16,31.34l0.59,-12.67l1.87,-0.01l0.68,12.67l-3.14,0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M68.34,6l0.28,-6l0.32,6l-0.6,0z"/>
<path
android:fillColor="#FF000000"
android:pathData="M87.43,66.29l6,0.28l0.01,4.4l-6,0.32l-0.01,-5z"/>
<path
android:fillColor="#FF000000"
android:pathData="M106.1,67.16l12.66,0.59l0.01,1.87l-12.66,0.68l-0.01,-3.14z"/>
<path
android:fillColor="#FF000000"
android:pathData="M131.43,68.34l6,0.28l-5.99,0.32l-0.01,-0.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M71.14,87.43l-0.28,6l-4.4,0.01l-0.32,-6l5,-0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M70.27,106.1l-0.59,12.66l-1.86,0.01l-0.68,-12.66l3.13,-0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M69.09,131.43l-0.27,6l-0.33,-5.99l0.6,-0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M69.26,70.79c1.93,0 1.93,-3 0,-3s-1.93,3 0,3h0Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M80.01,134.2c-0.42,-4.29 -0.71,-8.58 -1.01,-12.86 0.01,-6.42 0,-19.31 0,-25.73 0,0 0,-3.22 0,-3.22v-1.61c-0.03,-2.95 1.19,-5.88 3.31,-7.94 1.69,-1.64 3.92,-2.74 6.27,-3.02 2.05,-0.21 4.54,-0.04 6.55,-0.09 0,0 12.86,0 12.86,0h12.86c4.29,0.34 8.58,0.59 12.86,1 -4.29,0.41 -8.58,0.66 -12.86,1 -6.42,0 -19.31,0 -25.73,0 -1.77,0.05 -4.64,-0.1 -6.3,0.08 -1.92,0.23 -3.73,1.12 -5.12,2.47 -1.74,1.7 -2.72,4.08 -2.7,6.51 0,0 0,1.61 0,1.61 0,0 0,3.22 0,3.22 0,6.41 0,19.32 0,25.73 -0.29,4.29 -0.59,8.58 -1.01,12.86h0Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M4.26,79.75c4.29,-0.42 8.58,-0.71 12.86,-1.01 6.42,0.01 19.31,0 25.73,0 0,0 3.22,0 3.22,0h1.61c2.95,-0.03 5.88,1.19 7.94,3.31 1.64,1.69 2.74,3.92 3.02,6.27 0.21,2.05 0.04,4.54 0.09,6.55 0,0 0,12.86 0,12.86v12.86c-0.34,4.29 -0.59,8.58 -1,12.86 -0.41,-4.29 -0.66,-8.58 -1,-12.86 0,-6.42 0,-19.31 0,-25.73 -0.05,-1.77 0.1,-4.64 -0.08,-6.3 -0.23,-1.92 -1.12,-3.73 -2.47,-5.12 -1.7,-1.74 -4.08,-2.72 -6.51,-2.7 0,0 -1.61,0 -1.61,0 0,0 -3.22,0 -3.22,0 -6.41,0 -19.32,0 -25.73,0 -4.29,-0.29 -8.58,-0.59 -12.86,-1.01h0Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M58.72,4c0.42,4.29 0.71,8.58 1.01,12.86 -0.01,6.42 0,19.31 0,25.73 0,0 0,3.22 0,3.22v1.61c0.03,2.95 -1.19,5.88 -3.31,7.94 -1.69,1.64 -3.92,2.74 -6.27,3.02 -2.05,0.21 -4.54,0.04 -6.55,0.09 0,0 -12.86,0 -12.86,0h-12.86c-4.29,-0.34 -8.58,-0.59 -12.86,-1 4.29,-0.41 8.58,-0.66 12.86,-1 6.42,0 19.31,0 25.73,0 1.77,-0.05 4.64,0.1 6.3,-0.08 1.92,-0.23 3.73,-1.12 5.12,-2.47 1.74,-1.7 2.72,-4.08 2.7,-6.51 0,0 0,-1.61 0,-1.61 0,0 0,-3.22 0,-3.22 0,-6.41 0,-19.32 0,-25.73 0.29,-4.29 0.59,-8.58 1.01,-12.86h0Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M134.46,58.46c-4.29,0.42 -8.58,0.71 -12.86,1.01 -6.42,-0.01 -19.31,0 -25.73,0 0,0 -3.22,0 -3.22,0h-1.61c-2.95,0.03 -5.88,-1.19 -7.94,-3.31 -1.64,-1.69 -2.74,-3.92 -3.02,-6.27 -0.21,-2.05 -0.04,-4.54 -0.09,-6.55 0,0 0,-12.86 0,-12.86v-12.86c0.34,-4.29 0.59,-8.58 1,-12.86 0.41,4.29 0.66,8.58 1,12.86 0,6.42 0,19.31 0,25.73 0.05,1.77 -0.1,4.64 0.08,6.3 0.23,1.92 1.12,3.73 2.47,5.12 1.7,1.74 4.08,2.72 6.51,2.7 0,0 1.61,0 1.61,0 0,0 3.22,0 3.22,0 6.41,0 19.32,0 25.73,0 4.29,0.29 8.58,0.59 12.86,1.01h0Z"/>
</vector>
+61
View File
@@ -0,0 +1,61 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="62.24dp"
android:height="62.18dp"
android:viewportWidth="62.24"
android:viewportHeight="62.18">
<path
android:pathData="M30.66,31.22m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M27.25,41.64l-9.4,7.49c-2.27,1.81 -5.4,2.07 -7.94,0.66l-1.42,-0.78c-2.91,-1.61 -2.83,-5.83 0.15,-7.32l7.01,-3.52 11.59,3.47Z"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M19.94,31.19l-10.03,-6.62c-2.42,-1.6 -3.64,-4.5 -3.08,-7.34l0.31,-1.59c0.63,-3.27 4.67,-4.49 7.01,-2.12l5.51,5.58 0.28,12.1Z"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M27.61,21l3.2,-11.58c0.77,-2.79 3.15,-4.85 6.03,-5.2l1.61,-0.2c3.3,-0.41 5.71,3.05 4.18,6.01l-3.6,6.97 -11.42,4.01Z"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M39.68,25.15l12.01,-0.54c2.9,-0.13 5.59,1.5 6.81,4.13l0.68,1.47c1.41,3.02 -1.14,6.37 -4.42,5.83l-7.74,-1.27 -7.34,-9.62Z"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M39.46,37.91l4.22,11.25c1.02,2.71 0.3,5.78 -1.82,7.75l-1.18,1.1c-2.43,2.27 -6.41,0.89 -6.92,-2.4l-1.18,-7.75 6.88,-9.95Z"
android:strokeWidth="4"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M57.93,41.9l-2.71,4.16s3.31,-2.54 2.71,-4.16Z"
android:strokeWidth="4"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M29.15,59.9l-4.8,-1.29s3.44,2.36 4.8,1.29Z"
android:strokeWidth="4"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M3.13,38.09l-0.25,-4.96s-1.19,4 0.25,4.96Z"
android:strokeWidth="4"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M15.84,6.6l4.64,-1.77s-4.17,0.11 -4.64,1.77Z"
android:strokeWidth="4"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M49.71,8.96l3.12,3.87s-1.39,-3.93 -3.12,-3.87Z"
android:strokeWidth="4"
android:fillColor="#fff"
android:strokeColor="#000"/>
</vector>
+22
View File
@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="45.83dp"
android:height="65.94dp"
android:viewportWidth="45.83"
android:viewportHeight="65.94">
<path
android:pathData="M41.75,62.87V4.78l-3.87,2.32 -6.48,3.89c-9.59,5.76 -17.51,13.94 -22.94,23.72l-6.71,12.07"
android:fillColor="#00FFFFFF"/>
<path
android:fillColor="#FF000000"
android:pathData="M41.56,62.87c0,-16.54 0.05,-34.31 -0.59,-50.83 -0.1,-2.42 -0.17,-4.84 -1.23,-7.26 0,0 3.03,1.71 3.03,1.71 -3.66,2.26 -9.09,5.36 -12.6,7.63 -7.99,5.26 -14.73,12.41 -19.49,20.71 0,0 -7.19,12.91 -7.19,12.91 0,0 -3.5,-1.94 -3.5,-1.94 0,0 7.22,-12.97 7.22,-12.97 5.07,-8.83 12.25,-16.45 20.75,-22.05 5.53,-3.51 12.26,-7.38 17.85,-10.79 0,0 -2.08,4.78 -2.08,4.78 -1.8,4.72 -1.15,9.64 -1.49,14.52 -0.19,8.41 -0.24,20.51 -0.33,29.04 -0.02,4.84 -0.03,9.68 0.01,14.52h-0.37Z"/>
<path
android:pathData="M41.47,62.28m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:strokeWidth=".5"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M41.47,63.28v1.8h0c-0.51,0.74 -1.58,0.81 -2.18,0.14l-0.27,-0.31"
android:strokeWidth=".5"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
</vector>
+26
View File
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="107dp"
android:height="127.1dp"
android:viewportWidth="107"
android:viewportHeight="127.1">
<path
android:pathData="M0,115.1L43,115.1"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M64,115.1L107,115.1"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M95,8.1l-12.08,-6.7 -35.92,80.7L15,115.1s11.69,-0.03 28,0c0,0 1,-10 11,-10 9,0 10,10 10,10 9.54,0.02 15.06,0 15,0l-5,-29L95,8.1Z"
android:strokeWidth="2"
android:fillColor="#00ff00"
android:strokeColor="#000"/>
<path
android:pathData="M53.5,115.6m-10.5,0a10.5,10.5 0,1 1,21 0a10.5,10.5 0,1 1,-21 0"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"/>
</vector>
+26
View File
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="37.67dp"
android:height="44.99dp"
android:viewportWidth="37.67"
android:viewportHeight="44.99">
<path
android:pathData="M6.71,43.99V8.86H2.32l6.3,-6.67c0.7,-0.74 1.66,-1.16 2.68,-1.16l14.07,-0.04c1.5,0 2.93,0.6 3.97,1.68l5.97,6.18h-4.6l1,35.13H6.71Z"
android:strokeWidth="2"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M4.21,16.49L33.21,20.49"
android:strokeWidth="2"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M4.21,20.49L33.21,24.12"
android:strokeWidth="2"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:pathData="M4.21,24.12L33.21,27.84"
android:strokeWidth="2"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#80000000" />
<corners android:radius="8dp" />
<stroke
android:width="1dp"
android:color="#40FFFFFF" />
</shape>
+26
View File
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="107dp"
android:height="127.1dp"
android:viewportWidth="107"
android:viewportHeight="127.1">
<path
android:pathData="M0,115.1L43,115.1"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M64,115.1L107,115.1"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M95,8.1l-12.08,-6.7 -35.92,80.7L15,115.1s11.69,-0.03 28,0c0,0 1,-10 11,-10 9,0 10,10 10,10 9.54,0.02 15.06,0 15,0l-5,-29L95,8.1Z"
android:strokeWidth="2"
android:fillColor="#ff0000"
android:strokeColor="#000"/>
<path
android:pathData="M53.5,115.6m-10.5,0a10.5,10.5 0,1 1,21 0a10.5,10.5 0,1 1,-21 0"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"/>
</vector>
+26
View File
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50.57dp"
android:height="51.98dp"
android:viewportWidth="50.57"
android:viewportHeight="51.98">
<path
android:pathData="M21.49,2.23l0,20.35l0.3,29.39"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:pathData="M0.79,41.97L50.57,41.97"
android:strokeWidth="2"
android:fillColor="#fff"
android:strokeColor="#000"/>
<path
android:strokeWidth="1"
android:pathData="M25.68,2.23l0,35.8l23.13,0l-23.13,-35.8z"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
<path
android:strokeWidth="1"
android:pathData="M17.7,2.23l0,35.8l-16.91,0l16.91,-35.8z"
android:fillColor="#00FFFFFF"
android:strokeColor="#000"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#D9913C"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#3A6EA5"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#1D3557"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#B5B8B1"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#2BA9E0"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#35C2A9"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#C23B22"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.5dp"
android:height="152.73dp"
android:viewportWidth="88.5"
android:viewportHeight="152.73">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="9"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M44.25,6.77l-39.75,44.69l0,96.77l79.5,0l0,-96.77l-39.75,-44.69z"
android:strokeWidth="3"
android:fillColor="#E7621B"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#D9913C"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#3A6EA5"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#1D3557"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#B5B8B1"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#2BA9E0"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#35C2A9"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#C23B22"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="91.38dp"
android:height="162.6dp"
android:viewportWidth="91.38"
android:viewportHeight="162.6">
<!-- Внешняя обводка для контраста -->
<path
android:pathData="M45.69,16.63l-39.75,141.47l79.5,0l-39.75,-141.47z"
android:strokeWidth="12"
android:fillColor="#000000"
android:strokeColor="#000000"/>
<!-- Основная форма с внутренней обводкой -->
<path
android:pathData="M45.69,18.63l-37.75,139.47l75.5,0l-37.75,-139.47z"
android:strokeWidth="3"
android:fillColor="#E7621B"
android:strokeColor="#FFFFFF"/>
</vector>
@@ -4,10 +4,23 @@
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_target_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="AIS цели: 0"
android:textSize="16sp"
android:textStyle="bold"
android:padding="12dp"
android:background="#f0f0f0"
android:textColor="#333333"
android:gravity="center" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_ais_targets"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:id="@+id/text_empty_state"
+2 -1
View File
@@ -6,7 +6,7 @@
tools:context=".MainActivity">
<!-- Карта -->
<com.yandex.mapkit.mapview.MapView
<org.maplibre.android.maps.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
@@ -64,6 +64,7 @@
android:background="@android:color/white"
android:layout_marginTop="8dp" />
<!-- Строки возраста последних сообщений GPS ($) и AIS (!) -->
<TextView
android:id="@+id/tv_gps_age"
@@ -72,6 +72,143 @@
</com.google.android.material.card.MaterialCardView>
<!-- Треки и предсказание -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🧭 Путь и предсказание"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:layout_marginBottom="12dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="Максимум точек на судно"
app:helperText="Ограничение размера истории пути">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_path_max_points"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:text="300" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="Толщина линии пути (px)">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_path_width"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:text="3.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="Цвет пути (#RRGGBB)">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_path_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:text="#00FFFF" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="Толщина линии предсказания (px)">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_prediction_width"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:text="2.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="Цвет предсказания (#RRGGBB)">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_prediction_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:text="#FFFF00" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="Горизонт предсказания (сек)">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_prediction_horizon_sec"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:text="60" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Кнопка очистки трекера пути -->
<Button
android:id="@+id/btn_clear_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🗑️ Очистить трекер пути"
android:textSize="16sp"
android:layout_marginTop="8dp"
style="@style/Widget.Material3.Button.OutlinedButton" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Удаляет все сохраненные точки пути собственного судна"
android:textSize="12sp"
android:textColor="@android:color/darker_gray"
android:layout_marginTop="4dp"
android:layout_marginStart="16dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Приоритеты данных -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
@@ -284,6 +421,59 @@
</com.google.android.material.card.MaterialCardView>
<!-- Настройки экрана -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="📱 Управление экраном"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:layout_marginBottom="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Настройте поведение экрана во время навигации:"
android:textSize="14sp"
android:textColor="@android:color/darker_gray"
android:layout_marginBottom="16dp" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_keep_screen_on"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Не давать экрану засыпать"
android:textSize="16sp"
android:checked="true"
android:layout_marginBottom="8dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Экран будет оставаться включенным во время навигации (рекомендуется для навигатора)"
android:textSize="12sp"
android:textColor="@android:color/darker_gray"
android:layout_marginBottom="8dp"
android:layout_marginStart="16dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Настройки уведомлений -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
@@ -355,6 +545,59 @@
</com.google.android.material.card.MaterialCardView>
<!-- Настройки курсора -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🎯 Курсор на карте"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:layout_marginBottom="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Настройте отображение курсора с координатами центра экрана:"
android:textSize="14sp"
android:textColor="@android:color/darker_gray"
android:layout_marginBottom="16dp" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_cursor_enabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Показать курсор"
android:textSize="16sp"
android:checked="false"
android:layout_marginBottom="8dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Отображать крест в центре экрана с координатами и информацией о расстоянии"
android:textSize="12sp"
android:textColor="@android:color/darker_gray"
android:layout_marginBottom="8dp"
android:layout_marginStart="16dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Кнопки -->
<LinearLayout
android:layout_width="match_parent"
@@ -261,6 +261,30 @@
android:background="@android:color/transparent"
android:padding="8dp" />
<!-- Расстояние до судна -->
<TextView
android:id="@+id/bottom_sheet_ais_distance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="📏 Расстояние: --"
android:textSize="14sp"
android:textColor="@android:color/black"
android:layout_marginBottom="8dp"
android:background="@android:color/transparent"
android:padding="8dp" />
<!-- Пеленг (азимут) до судна -->
<TextView
android:id="@+id/bottom_sheet_ais_bearing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🧭 Пеленг: --"
android:textSize="14sp"
android:textColor="@android:color/black"
android:layout_marginBottom="8dp"
android:background="@android:color/transparent"
android:padding="8dp" />
<!-- Последнее обновление -->
<TextView
android:id="@+id/bottom_sheet_ais_last_update"
+150
View File
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<!-- Курсор в виде креста в центре экрана -->
<View
android:id="@+id/cursor_cross"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:background="@drawable/cursorcross" />
<!-- Координаты в первом квадранте (верхний левый) -->
<!-- Расстояние и пеленг в четвертом квадранте (нижний правый) -->
<LinearLayout
android:id="@+id/coordinates_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/panel_background"
android:orientation="vertical"
android:padding="8dp"
android:translationX="-60dp"
android:translationY="-40dp"
android:visibility="visible">
<TextView
android:id="@+id/tv_cursor_latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Широта: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_cursor_longitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Долгота: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/distance_bearing_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginStart="120dp"
android:layout_marginTop="60dp"
android:background="@drawable/panel_background"
android:orientation="vertical"
android:padding="8dp"
android:translationX="60dp"
android:translationY="40dp"
android:visibility="visible">
<TextView
android:id="@+id/tv_distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Rnd:"
android:textColor="@android:color/white"
android:textSize="12sp"
android:visibility="gone"
/>
<TextView
android:id="@+id/tv_bearing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Brg: --"
android:textColor="@android:color/white"
android:textSize="12sp"
android:visibility="gone"
/>
</LinearLayout>
<!-- Информация об AIS судне в левом нижнем углу -->
<LinearLayout
android:id="@+id/ais_vessel_info_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/panel_background"
android:orientation="vertical"
android:padding="8dp"
android:translationX="-75dp"
android:translationY="60dp"
android:visibility="gone">
<TextView
android:id="@+id/tv_ais_mmsi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="MMSI: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_ais_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Название: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_ais_call_sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Позывной: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_ais_cog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="COG: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_ais_sog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="SOG: --"
android:textColor="@android:color/white"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>
@@ -48,6 +48,22 @@
android:textSize="12sp"
android:text="N сек назад" />
<TextView
android:id="@+id/tv_distance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="Расстояние: --"
android:textColor="#666666" />
<TextView
android:id="@+id/tv_bearing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="Азимут: --"
android:textColor="#666666" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+12
View File
@@ -26,4 +26,16 @@
android:icon="@android:drawable/ic_menu_directions"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_service_test"
android:title="Тест сервиса"
android:icon="@android:drawable/ic_menu_manage"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_keep_screen_on"
android:title="Экран"
android:icon="@android:drawable/ic_menu_view"
app:showAsAction="ifRoom" />
</menu>