codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppService.java

361 wiersze
14 KiB
Java

package com.radio.codec2talkie.app;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.preference.PreferenceManager;
import com.radio.codec2talkie.MainActivity;
import com.radio.codec2talkie.R;
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.settings.PreferenceKeys;
import com.radio.codec2talkie.storage.message.MessageItemActivity;
import com.radio.codec2talkie.tracker.Tracker;
import com.radio.codec2talkie.tracker.TrackerFactory;
import com.radio.codec2talkie.transport.TransportFactory;
import java.io.IOException;
public class AppService extends Service {
public static TransportFactory.TransportType transportType;
public static boolean isRunning = false;
private static final String TAG = AppService.class.getSimpleName();
private final int SERVICE_NOTIFICATION_ID = 1;
private final int VOICE_NOTIFICATION_ID = 2;
private final int MESSAGE_NOTIFICATION_ID = 3;
private AppWorker _appWorker;
private Messenger _callbackMessenger;
private Tracker _tracker;
private NotificationManager _notificationManager;
private PowerManager.WakeLock _serviceWakeLock;
private boolean _voiceNotificationsEnabled = false;
private final IBinder _binder = new AppServiceBinder();
public class AppServiceBinder extends Binder {
public AppService getService() {
return AppService.this;
}
}
public String getTransportName() {
if (_appWorker != null) {
return _appWorker.getTransportName();
} else {
return "";
}
}
public void startTransmit() {
if (_appWorker != null)
_appWorker.startTransmit();
}
public void startReceive() {
if (_appWorker != null)
_appWorker.startReceive();
}
public void sendPosition() {
Message msg = Message.obtain();
msg.what = AppMessage.CMD_SEND_SINGLE_TRACKING.toInt();
_onProcess.sendMessage(msg);
}
public void startTracking() {
Message msg = Message.obtain();
msg.what = AppMessage.CMD_START_TRACKING.toInt();
_onProcess.sendMessage(msg);
}
public void stopTracking() {
Message msg = Message.obtain();
msg.what = AppMessage.CMD_STOP_TRACKING.toInt();
_onProcess.sendMessage(msg);
}
public void sendTextMessage(TextMessage textMessage) {
if (_appWorker != null)
_appWorker.sendTextMessage(textMessage);
}
public boolean isTracking() {
if (_tracker == null) return false;
return _tracker.isTracking();
}
public void stopRunning() {
if (_appWorker != null)
_appWorker.stopRunning();
}
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind()");
super.onRebind(intent);
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind()");
_callbackMessenger = null;
return true;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind()");
return _binder;
}
@SuppressLint("WakelockTimeout")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand()");
// update callback from new intent
Bundle extras = intent.getExtras();
_callbackMessenger = (Messenger) extras.get("callback");
// create if not running
if (isRunning) {
Log.i(TAG, "Not starting app service, already running");
} else {
_notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert _notificationManager != null;
SharedPreferences _sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
_voiceNotificationsEnabled = _sharedPreferences.getBoolean(PreferenceKeys.APP_NOTIFICATIONS_VOICE, false);
String trackerName = _sharedPreferences.getString(PreferenceKeys.APRS_LOCATION_SOURCE, "manual");
_tracker = TrackerFactory.create(trackerName);
_tracker.initialize(getApplicationContext(), position -> { if (_appWorker != null) _appWorker.sendPositionToTnc(position); });
Notification notification = buildServiceNotification(getString(R.string.app_service_notif_text_ptt_ready), R.drawable.ic_app_action);
startForeground(SERVICE_NOTIFICATION_ID, notification);
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
boolean noCpuSleep = _sharedPreferences.getBoolean(PreferenceKeys.APP_NO_CPU_SLEEP, false);
if (noCpuSleep) {
_serviceWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "App::Service");
_serviceWakeLock.acquire();
}
isRunning = true;
transportType = (TransportFactory.TransportType) extras.get("transportType");
startAppWorker(transportType);
Log.i(TAG, "App service started");
}
return START_REDELIVER_INTENT;
}
private void startAppWorker(TransportFactory.TransportType transportType) {
try {
Log.i(TAG, "Started app worker: " + transportType.toString());
_appWorker = new AppWorker(transportType,
_onProcess,
getApplicationContext());
_appWorker.start();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(AppService.this, R.string.worker_failed_to_start_processing, Toast.LENGTH_LONG).show();
}
}
private void deliverToParent(Message msg) throws RemoteException {
if (_callbackMessenger != null) {
Message sendMsg = new Message();
sendMsg.copyFrom(msg);
_callbackMessenger.send(sendMsg);
}
}
private final Handler _onProcess = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
try {
deliverToParent(msg);
switch (AppMessage.values()[msg.what]) {
case EV_DISCONNECTED:
isRunning = false;
_appWorker = null;
_tracker.stopTracking();
if (_serviceWakeLock != null) _serviceWakeLock.release();
showServiceNotification(R.string.app_service_notif_connection_lost, R.drawable.ic_app_action_disconnected);
break;
case EV_VOICE_RECEIVED:
showVoiceNotification(R.string.app_notifications_voice_title, R.string.app_notifications_voice_summary);
break;
case EV_TEXT_MESSAGE_RECEIVED:
showMessageNotification(R.string.app_notifications_text_title, (String)msg.obj);
break;
case EV_LISTENING:
hideVoiceNotification();
break;
case CMD_SEND_SINGLE_TRACKING:
_tracker.sendPosition();
break;
case CMD_START_TRACKING:
showServiceNotification(R.string.app_service_notif_text_tracking, R.drawable.ic_app_action);
_tracker.startTracking();
Message startedTrackingMessage = Message.obtain();
startedTrackingMessage.what = AppMessage.EV_STARTED_TRACKING.toInt();
deliverToParent(startedTrackingMessage);
break;
case CMD_STOP_TRACKING:
showServiceNotification(R.string.app_service_notif_text_ptt_ready, R.drawable.ic_app_action);
_tracker.stopTracking();
Message stoppedTrackingMessage = Message.obtain();
stoppedTrackingMessage.what = AppMessage.EV_STOPPED_TRACKING.toInt();
deliverToParent(stoppedTrackingMessage);
break;
default:
break;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
private void showServiceNotification(int resId, int iconResId) {
String text = getString(resId);
_notificationManager.notify(SERVICE_NOTIFICATION_ID, buildServiceNotification(text, iconResId));
}
private void showVoiceNotification(int titleResId, int textResId) {
if (!MainActivity.isPaused || !_voiceNotificationsEnabled) return;
String title = getString(titleResId);
String text = getString(textResId);
_notificationManager.notify(VOICE_NOTIFICATION_ID, buildFullScreenNotification(title, text));
}
private void showMessageNotification(int titleResId, String note) {
if (!MessageItemActivity.isPaused || !_voiceNotificationsEnabled) return;
String title = getString(titleResId);
_notificationManager.notify(MESSAGE_NOTIFICATION_ID, buildMessageNotification(title, note));
}
private void hideVoiceNotification() {
if (!_voiceNotificationsEnabled) return;
_notificationManager.cancel(VOICE_NOTIFICATION_ID);
}
@RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
channel.setImportance(importance);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
_notificationManager.createNotificationChannel(channel);
return channelId;
}
private Notification buildServiceNotification(String text, int iconResId) {
String channelId = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
channelId = createNotificationChannel("alpha", "Service", NotificationManager.IMPORTANCE_LOW);
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(this, channelId)
.setOngoing(true)
.setSmallIcon(iconResId)
.setContentTitle(getString(R.string.app_service_notif_title))
.setContentText(text)
.setPriority(NotificationManagerCompat.IMPORTANCE_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setChannelId(channelId)
.setContentIntent(intent)
.setOnlyAlertOnce(true)
.build();
}
private Notification buildFullScreenNotification(String title, String text) {
String channelId = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
channelId = createNotificationChannel("beta", "Voice", NotificationManager.IMPORTANCE_HIGH);
Intent fullScreenIntent = new Intent(this, MainActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(this, channelId)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_app_action)
.setChannelId(channelId)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setFullScreenIntent(fullScreenPendingIntent, true)
.setAutoCancel(true)
.build();
}
private Notification buildMessageNotification(String title, String text) {
String channelId = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
channelId = createNotificationChannel("gamma", "Message", NotificationManager.IMPORTANCE_HIGH);
// extract group name from the note
String[] srcDstText = text.split(": ");
String groupName = srcDstText[0].split("→")[1];
Intent notificationIntent = new Intent(this, MessageItemActivity.class);
notificationIntent.putExtra("groupName", groupName);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(this, channelId)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_app_action)
.setChannelId(channelId)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
}
}