Created ship vectors (not added yet)

Created menu
Created udp support
Created DockWidgets for compass and SOG/COG
This commit is contained in:
2025-09-03 15:40:02 +03:00
parent 2734560160
commit 25b1dabf73
70 changed files with 3145 additions and 293 deletions
@@ -1,6 +1,7 @@
package com.grigowashere.aismap;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
@@ -9,8 +10,10 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.view.ViewGroup;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
@@ -24,6 +27,9 @@ import com.grigowashere.aismap.models.Vessel;
import com.grigowashere.aismap.models.AISVessel;
import com.grigowashere.aismap.sensors.CompassSensor;
import com.grigowashere.aismap.view.CompassView;
import com.grigowashere.aismap.view.CoordinatesDockWidget;
import com.grigowashere.aismap.view.BaseDockWidget;
import com.grigowashere.aismap.utils.SettingsManager;
import com.yandex.mapkit.mapview.MapView;
import java.util.List;
import java.util.ArrayList;
@@ -32,6 +38,7 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int PERMISSION_REQUEST_CODE = 1001;
private static final int SETTINGS_REQUEST_CODE = 1002;
// Статическая переменная для отслеживания инициализации Яндекс.Карт
private static boolean isYandexMapsInitialized = false;
@@ -40,11 +47,15 @@ public class MainActivity extends AppCompatActivity {
private MapController mapController;
private MapInterface mapInterface;
private MapView mapView;
private SettingsManager settingsManager;
private Button btnCenterOnVessel;
private Button btnTestCompass;
private Button btnMapOrientation;
private Button btnSettings;
private LinearLayout controlPanel;
private CompassView compassView;
private CompassSensor compassSensor;
private CoordinatesDockWidget coordinatesWidget;
// BottomSheet для отображения информации о нашем судне
private BottomSheetDialog ownVesselBottomSheet;
@@ -79,8 +90,11 @@ public class MainActivity extends AppCompatActivity {
private void initializeViews() {
mapView = findViewById(R.id.map_view);
btnCenterOnVessel = findViewById(R.id.btn_center_vessel);
btnTestCompass = findViewById(R.id.btn_test_compass);
btnMapOrientation = findViewById(R.id.btn_map_orientation);
btnSettings = findViewById(R.id.btn_settings);
controlPanel = findViewById(R.id.control_panel);
compassView = findViewById(R.id.compass_view);
coordinatesWidget = findViewById(R.id.coordinates_widget);
// Инициализируем магнитный компас
compassSensor = new CompassSensor(this);
@@ -88,11 +102,13 @@ public class MainActivity extends AppCompatActivity {
initializeBottomSheet();
setupButtonListeners();
setupCompass();
setupCoordinatesWidget();
}
private void setupButtonListeners() {
btnCenterOnVessel.setOnClickListener(v -> centerOnVessel());
btnTestCompass.setOnClickListener(v -> testCompass());
btnMapOrientation.setOnClickListener(v -> toggleMapOrientation());
btnSettings.setOnClickListener(v -> showSettings());
// Кнопка для показа информации о судне
// Button btnShowVesselInfo = findViewById(R.id.btn_show_vessel_info);
@@ -114,6 +130,18 @@ public class MainActivity extends AppCompatActivity {
// Настраиваем слушатель изменения размера док-виджета
compassView.setOnDockResizeListener(newHeight -> {
Log.d(TAG, "Compass dock height changed to: " + newHeight);
// Обновляем позицию панели управления при любом изменении размера docked виджета
updateControlPanelPosition();
});
// Настраиваем слушатель изменения состояния docked
compassView.setOnDockStateChangeListener((isDocked, isTop) -> {
Log.d(TAG, "Compass dock state changed: docked=" + isDocked + ", top=" + isTop);
// Перепозиционируем все docked виджеты
BaseDockWidget.repositionAllDockedWidgets((ViewGroup) compassView.getParent());
updateControlPanelPosition();
});
//smt changed
// Настраиваем магнитный компас
@@ -144,6 +172,48 @@ public class MainActivity extends AppCompatActivity {
// Принудительная отрисовка
compassView.invalidate();
// Инициализируем начальную позицию панели управления
compassView.post(() -> {
updateControlPanelPosition();
});
}
private void setupCoordinatesWidget() {
// Настраиваем слушатель изменения размера dock-виджета
coordinatesWidget.setOnDockResizeListener(newHeight -> {
Log.d(TAG, "Coordinates dock height changed to: " + newHeight);
// Обновляем позицию панели управления при любом изменении размера docked виджета
updateControlPanelPosition();
});
// Настраиваем слушатель изменения состояния docked
coordinatesWidget.setOnDockStateChangeListener((isDocked, isTop) -> {
Log.d(TAG, "Coordinates dock state changed: docked=" + isDocked + ", top=" + isTop);
// Перепозиционируем все docked виджеты
BaseDockWidget.repositionAllDockedWidgets((ViewGroup) coordinatesWidget.getParent());
updateControlPanelPosition();
});
// Устанавливаем виджет координат в dock-режим внизу экрана
coordinatesWidget.post(() -> {
Log.d(TAG, "Setting coordinates widget to dock mode");
coordinatesWidget.setDocked(true, false, 0, 0); // false = dock снизу
coordinatesWidget.invalidate(); // Принудительная отрисовка
// Принудительно обновляем виджет с тестовыми данными
Vessel testVessel = new Vessel();
testVessel.setLatitude(55.7558);
testVessel.setLongitude(37.6176);
testVessel.setSpeed(5.5);
testVessel.setCourse(45.0);
testVessel.setAccuracy(3.0f);
coordinatesWidget.updateVessel(testVessel);
updateControlPanelPosition();
});
}
private void onUpdateCompass(float azimuth, List<AISVessel> nearbyVessels) {
@@ -248,6 +318,9 @@ public class MainActivity extends AppCompatActivity {
}
private void initializeControllers() {
// Инициализация менеджера настроек
settingsManager = new SettingsManager(this);
// Инициализация главного контроллера
appController = new AppController(this);
@@ -292,10 +365,8 @@ public class MainActivity extends AppCompatActivity {
}
private void startControllers() {
// Включаем GPS и UDP по умолчанию
appController.setGPSLocationEnabled(true);
appController.setAndroidNMEAEnabled(true);
appController.setUDPEnabled(true);
// Загружаем настройки и применяем их
applySettings();
// Запускаем все слушатели
appController.startAllListeners();
@@ -331,6 +402,11 @@ public class MainActivity extends AppCompatActivity {
// tvStatus.setText("Статус: GPS активен, данные получены");
// }
// Обновляем виджет координат
if (coordinatesWidget != null) {
coordinatesWidget.updateVessel(vessel);
}
// Обновляем BottomSheet, если он открыт
if (ownVesselBottomSheet != null && ownVesselBottomSheet.isShowing()) {
updateBottomSheetUI();
@@ -387,43 +463,94 @@ public class MainActivity extends AppCompatActivity {
Toast.makeText(this, "Карта центрирована на судне", Toast.LENGTH_SHORT).show();
}
private void testCompass() {
if (compassView != null) {
// Создаем тестовые AIS суда
List<AISVessel> testVessels = new ArrayList<>();
AISVessel testVessel1 = new AISVessel("123456789");
testVessel1.setLatitude(59.9343);
testVessel1.setLongitude(30.3351);
testVessel1.setCourse(45);
testVessel1.setSpeed(10);
testVessel1.setNavigationalStatus("under way using engine");
testVessels.add(testVessel1);
AISVessel testVessel2 = new AISVessel("987654321");
testVessel2.setLatitude(59.9343);
testVessel2.setLongitude(30.3351);
testVessel2.setCourse(180);
testVessel2.setSpeed(5);
testVessel2.setNavigationalStatus("at anchor");
testVessels.add(testVessel2);
compassView.updateNearbyVessels(testVessels);
// Проверяем доступность магнитного компаса
if (compassSensor.isAvailable()) {
Toast.makeText(this, "Магнитный компас доступен и работает", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Магнитный компас недоступен", Toast.LENGTH_SHORT).show();
}
private void toggleMapOrientation() {
// TODO: Реализовать переключение ориентации карты
// Состояния: север, курс, компас
Toast.makeText(this, "Переключение ориентации карты (в разработке)", Toast.LENGTH_SHORT).show();
}
private void showSettings() {
Intent intent = new Intent(this, SettingsActivity.class);
startActivityForResult(intent, SETTINGS_REQUEST_CODE);
}
/**
* Обновляет позицию панели управления в зависимости от состояния docked виджетов
*/
private void updateControlPanelPosition() {
if (controlPanel != null) {
runOnUiThread(() -> {
// Получаем текущие параметры layout
android.widget.RelativeLayout.LayoutParams params =
(android.widget.RelativeLayout.LayoutParams) controlPanel.getLayoutParams();
int topMargin = dpToPx(16); // По умолчанию отступ сверху
int bottomMargin = dpToPx(16); // По умолчанию отступ снизу
// Вычисляем общую высоту всех docked виджетов сверху
int totalTopHeight = 0;
if (compassView != null && compassView.isDocked() && compassView.isDockTop()) {
totalTopHeight += compassView.getHeight();
Log.d(TAG, "Compass docked top, height: " + compassView.getHeight());
}
if (coordinatesWidget != null && coordinatesWidget.isDocked() && coordinatesWidget.isDockTop()) {
totalTopHeight += coordinatesWidget.getHeight();
Log.d(TAG, "Coordinates docked top, height: " + coordinatesWidget.getHeight());
}
// Вычисляем общую высоту всех docked виджетов снизу
int totalBottomHeight = 0;
if (compassView != null && compassView.isDocked() && !compassView.isDockTop()) {
totalBottomHeight += compassView.getHeight();
Log.d(TAG, "Compass docked bottom, height: " + compassView.getHeight());
}
if (coordinatesWidget != null && coordinatesWidget.isDocked() && !coordinatesWidget.isDockTop()) {
totalBottomHeight += coordinatesWidget.getHeight();
Log.d(TAG, "Coordinates docked bottom, height: " + coordinatesWidget.getHeight());
}
// Устанавливаем отступы с учетом всех docked виджетов
if (totalTopHeight > 0) {
topMargin = totalTopHeight + dpToPx(8); // + небольшой отступ
}
if (totalBottomHeight > 0) {
bottomMargin = totalBottomHeight + dpToPx(8); // + небольшой отступ
}
// Устанавливаем отступы
params.topMargin = topMargin;
params.bottomMargin = bottomMargin;
// Применяем новые параметры
controlPanel.setLayoutParams(params);
Log.d(TAG, "Control panel position updated: " +
"topMargin=" + topMargin + "px, " +
"bottomMargin=" + bottomMargin + "px, " +
"totalTopHeight=" + totalTopHeight + "px, " +
"totalBottomHeight=" + totalBottomHeight + "px, " +
"compassDocked=" + (compassView != null ? compassView.isDocked() : false) +
", compassTop=" + (compassView != null ? compassView.isDockTop() : false) +
", coordinatesDocked=" + (coordinatesWidget != null ? coordinatesWidget.isDocked() : false) +
", coordinatesTop=" + (coordinatesWidget != null ? coordinatesWidget.isDockTop() : false));
});
}
}
private void clearAIS() {
appController.clearAISVessels();
Toast.makeText(this, "AIS суда очищены", Toast.LENGTH_SHORT).show();
}
/**
* Конвертирует dp в px
*/
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
@Override
protected void onStart() {
super.onStart();
@@ -548,6 +675,32 @@ public class MainActivity extends AppCompatActivity {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SETTINGS_REQUEST_CODE) {
if (resultCode == RESULT_OK && data != null) {
boolean settingsChanged = data.getBooleanExtra("settings_changed", false);
boolean needsRestart = data.getBooleanExtra("needs_restart", false);
if (settingsChanged) {
Log.i(TAG, "Настройки изменены, применяем изменения");
if (needsRestart) {
Log.i(TAG, "Требуется перезапуск сервисов");
restartServices();
} else {
Log.i(TAG, "Применяем настройки без перезапуска");
applySettings();
}
Toast.makeText(this, "Настройки применены", Toast.LENGTH_SHORT).show();
}
}
}
}
// Меню
@Override
@@ -1082,4 +1235,75 @@ public class MainActivity extends AppCompatActivity {
}
}
}
/**
* Применяет настройки к контроллерам
*/
private void applySettings() {
if (settingsManager == null || appController == null) {
Log.w(TAG, "SettingsManager или AppController не инициализированы");
return;
}
try {
// Применяем UDP настройки
int udpPort = settingsManager.getUDPPort();
boolean udpEnabled = settingsManager.isUDPEnabled();
appController.setUDPPort(udpPort);
appController.setUDPEnabled(udpEnabled);
// Применяем NMEA настройки
boolean androidNMEAEnabled = settingsManager.isAndroidNMEAEnabled();
boolean udpNMEAEnabled = settingsManager.isUDPNMEAEnabled();
appController.setAndroidNMEAEnabled(androidNMEAEnabled);
appController.setUDPNMEAEnabled(udpNMEAEnabled);
// Применяем режим данных
String dataMode = settingsManager.getDataMode();
appController.setDataMode(dataMode);
Log.i(TAG, "Настройки применены: " + settingsManager.getSettingsSummary());
} catch (Exception e) {
Log.e(TAG, "Ошибка при применении настроек: " + e.getMessage(), e);
Toast.makeText(this, "Ошибка при применении настроек", Toast.LENGTH_SHORT).show();
}
}
/**
* Перезапускает сервисы с новыми настройками
*/
private void restartServices() {
if (appController == null) {
Log.w(TAG, "AppController не инициализирован");
return;
}
try {
Log.i(TAG, "Перезапускаем сервисы...");
// Останавливаем все слушатели
appController.stopAllListeners();
// Применяем новые настройки
applySettings();
// Перезапускаем UDP слушатель с новым портом, если нужно
if (settingsManager.shouldRestartUDP(appController.getUDPPort(), appController.isUDPEnabled())) {
appController.restartUDPListener();
}
// Запускаем слушатели с новыми настройками
appController.startAllListeners();
Log.i(TAG, "Сервисы успешно перезапущены");
Log.i(TAG, "Статус настроек: " + appController.getSettingsStatus());
} catch (Exception e) {
Log.e(TAG, "Ошибка при перезапуске сервисов: " + e.getMessage(), e);
Toast.makeText(this, "Ошибка при перезапуске сервисов", Toast.LENGTH_SHORT).show();
}
}
}