Files
arcanechat-android/src/main/java/org/thoughtcrime/securesms/connect/KeepAliveService.java
T
2025-11-05 17:13:44 +01:00

130 lines
5.1 KiB
Java

package org.thoughtcrime.securesms.connect;
import android.annotation.TargetApi;
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.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.notifications.NotificationCenter;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.Prefs;
public class KeepAliveService extends Service {
private static final String TAG = KeepAliveService.class.getSimpleName();
static KeepAliveService s_this = null;
public static void maybeStartSelf(Context context) {
// note, that unfortunately, the check for isIgnoringBatteryOptimizations() is not sufficient,
// this checks only stock-android settings, several os have additional "optimizers" that ignore this setting.
// therefore, the most reliable way to not get killed is a permanent-foreground-notification.
if (Prefs.reliableService(context)) {
startSelf(context);
}
}
public static void startSelf(Context context)
{
try {
ContextCompat.startForegroundService(context, new Intent(context, KeepAliveService.class));
}
catch(Exception e) {
Log.i(TAG, "Error calling ContextCompat.startForegroundService()", e);
}
}
@Override
public void onCreate() {
Log.i("DeltaChat", "*** KeepAliveService.onCreate()");
// there's nothing more to do here as all initialisation stuff is already done in
// ApplicationLoader.onCreate() which is called before this broadcast is sended.
s_this = this;
// set self as foreground
try {
stopForeground(true);
startForeground(NotificationCenter.ID_PERMANENT, createNotification());
}
catch (Exception e) {
Log.i(TAG, "Error in onCreate()", e);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// START_STICKY ensured, the service is recreated as soon it is terminated for any reasons.
// as ApplicationLoader.onCreate() is called before a service starts, there is no more to do here,
// the app is just running fine.
Log.i("DeltaChat", "*** KeepAliveService.onStartCommand()");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onDestroy() {
Log.i("DeltaChat", "*** KeepAliveService.onDestroy()");
// the service will be restarted due to START_STICKY automatically, there's nothing more to do.
}
static public KeepAliveService getInstance()
{
return s_this; // may be null
}
/* The notification
* A notification is required for a foreground service; and without a foreground service,
* ArcaneChat won't get new messages reliable
**********************************************************************************************/
private Notification createNotification()
{
Intent intent = new Intent(this, ConversationListActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE());
// a notification _must_ contain a small icon, a title and a text, see https://developer.android.com/guide/topics/ui/notifiers/notifications.html#Required
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(getString(R.string.app_name));
builder.setContentText(getString(R.string.notify_background_connection_enabled));
builder.setPriority(NotificationCompat.PRIORITY_MIN);
builder.setWhen(0);
builder.setContentIntent(contentIntent);
builder.setSmallIcon(R.drawable.notification_permanent);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) {
createFgNotificationChannel(this);
builder.setChannelId(NotificationCenter.CH_PERMANENT);
}
return builder.build();
}
private static boolean ch_created = false;
@TargetApi(Build.VERSION_CODES.O)
static private void createFgNotificationChannel(Context context) {
if(!ch_created) {
ch_created = true;
NotificationChannel channel = new NotificationChannel(NotificationCenter.CH_PERMANENT,
"Receive messages in background.", NotificationManager.IMPORTANCE_MIN); // IMPORTANCE_DEFAULT will play a sound
channel.setDescription("Ensure reliable message receiving.");
channel.setShowBadge(false);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}