Подготовка к крупным изменениям: карта, AIS и UI

- Яндекс/MapForge: правки в менеджерах и обёртках маркеров (улучшена отрисовка/логика)
- NMEAParser: корректировки парсинга и стабильности
- Модель AISVessel: уточнение полей/логики
- Настройки: правки в SettingsActivity и SettingsManager, актуализация AppController
- UI: обновлены activity_main, activity_settings, bottom_sheet_ais_vessel; меню main_menu
- Ресурсы: добавлен drawable/targetclassa.xml, обновлён drawable/target.xml
- Конфигурация: правки AndroidManifest и app/build.gradle
- Прочее: изменения в .idea (не влияют на сборку)
This commit is contained in:
2025-09-23 11:53:23 +03:00
parent a2f1775f9f
commit 41432665ea
37 changed files with 6561 additions and 161 deletions
@@ -0,0 +1,237 @@
package com.grigowashere.aismap.services;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.grigowashere.aismap.MainActivity;
import com.grigowashere.aismap.R;
import com.grigowashere.aismap.utils.SettingsManager;
/**
* Сервис для обработки уведомлений о новых AIS целях
* Поддерживает вибрацию и звуковые уведомления
*/
public class NotificationService {
private static final String TAG = "NotificationService";
private static final String ALERT_CHANNEL_ID = "aismap_alerts";
private static final int SAFETY_NOTIFICATION_ID_BASE = 2000;
private Context context;
private SettingsManager settingsManager;
private Vibrator vibrator;
private ToneGenerator toneGenerator;
private boolean isInitialized = false;
public NotificationService(Context context) {
this.context = context;
this.settingsManager = new SettingsManager(context);
initializeService();
}
/**
* Инициализирует сервис уведомлений
*/
private void initializeService() {
try {
createAlertChannel();
// Инициализация вибратора
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
VibratorManager vibratorManager = (VibratorManager) context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
vibrator = vibratorManager.getDefaultVibrator();
} else {
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
// Инициализация генератора тонов
toneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
isInitialized = true;
Log.i(TAG, "Сервис уведомлений инициализирован успешно");
} catch (Exception e) {
Log.e(TAG, "Ошибка инициализации сервиса уведомлений: " + e.getMessage(), e);
isInitialized = false;
}
}
/**
* Создает канал уведомлений для предупреждений (Android O+)
*/
private void createAlertChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
ALERT_CHANNEL_ID,
"AIS Alerts",
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("Сообщения безопасности AIS и предупреждения");
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (nm != null) nm.createNotificationChannel(channel);
}
}
/**
* Воспроизводит уведомление о новой AIS цели
*/
public void notifyNewAISTarget() {
if (!isInitialized) {
Log.w(TAG, "Сервис уведомлений не инициализирован");
return;
}
// Проверяем настройки и воспроизводим соответствующие уведомления
if (settingsManager.isVibrationEnabled()) {
playVibration();
}
if (settingsManager.isSoundEnabled()) {
playSound();
}
Log.i(TAG, "Уведомление о новой AIS цели воспроизведено");
}
/**
* Воспроизводит вибрацию
*/
private void playVibration() {
try {
if (vibrator != null && vibrator.hasVibrator()) {
// Паттерн вибрации: короткая пауза, длинная вибрация, короткая пауза, короткая вибрация
long[] pattern = {0, 200, 100, 100};
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
VibrationEffect effect = VibrationEffect.createWaveform(pattern, -1);
vibrator.vibrate(effect);
} else {
vibrator.vibrate(pattern, -1);
}
Log.d(TAG, "Вибрация воспроизведена");
} else {
Log.w(TAG, "Вибратор недоступен");
}
} catch (Exception e) {
Log.e(TAG, "Ошибка воспроизведения вибрации: " + e.getMessage(), e);
}
}
/**
* Воспроизводит звуковое уведомление
*/
private void playSound() {
try {
if (toneGenerator != null) {
// Воспроизводим тон уведомления (TONE_CDMA_ALERT_CALL_GUARD)
toneGenerator.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 500);
Log.d(TAG, "Звуковое уведомление воспроизведено");
} else {
Log.w(TAG, "Генератор тонов недоступен");
}
} catch (Exception e) {
Log.e(TAG, "Ошибка воспроизведения звука: " + e.getMessage(), e);
}
}
/**
* Проверяет, включены ли уведомления
*/
public boolean areNotificationsEnabled() {
return settingsManager.isVibrationEnabled() || settingsManager.isSoundEnabled();
}
/**
* Проверяет, включена ли вибрация
*/
public boolean isVibrationEnabled() {
return settingsManager.isVibrationEnabled();
}
/**
* Проверяет, включен ли звук
*/
public boolean isSoundEnabled() {
return settingsManager.isSoundEnabled();
}
/**
* Уведомление о сообщении безопасности (AIS 14)
*/
public void notifySafetyMessage(String mmsi, String text) {
if (!isInitialized) {
Log.w(TAG, "Сервис уведомлений не инициализирован");
return;
}
// Подаем сигнал по настройкам (вибро/звук)
if (settingsManager.isVibrationEnabled()) {
playVibration();
}
if (settingsManager.isSoundEnabled()) {
playSound();
}
// Показ системного уведомления в шторке
try {
createAlertChannel();
Intent intent = new Intent(context, MainActivity.class);
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT : 0;
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, flags);
String title = "AIS Safety message";
String content = (text != null && !text.isEmpty()) ? text : ("Сообщение от " + mmsi);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ALERT_CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(content)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
int notificationId = SAFETY_NOTIFICATION_ID_BASE + (mmsi != null ? (mmsi.hashCode() & 0x0FFF) : 0);
NotificationManagerCompat.from(context).notify(notificationId, builder.build());
Log.i(TAG, "Показано системное уведомление о safety-сообщении: MMSI=" + mmsi);
} catch (Exception e) {
Log.e(TAG, "Ошибка показа системного уведомления: " + e.getMessage(), e);
}
}
/**
* Освобождает ресурсы сервиса
*/
public void cleanup() {
try {
if (toneGenerator != null) {
toneGenerator.release();
toneGenerator = null;
}
if (vibrator != null) {
vibrator.cancel();
}
isInitialized = false;
Log.i(TAG, "Ресурсы сервиса уведомлений освобождены");
} catch (Exception e) {
Log.e(TAG, "Ошибка при освобождении ресурсов сервиса уведомлений: " + e.getMessage(), e);
}
}
}