mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 01:51:47 +00:00
Migrate BackgroundService to Kotlin
This commit is contained in:
parent
65a71696d5
commit
7ee33de646
@ -1,304 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* This class (still) does 3 things:
|
||||
* - Keeps the app running by creating a foreground notification.
|
||||
* - Holds references to the active LinkProviders, but doesn't handle the DeviceLink those create (the KdeConnect class does that).
|
||||
* - Listens for network connectivity changes and tells the LinkProviders to re-check for devices.
|
||||
* It can be started by the KdeConnectBroadcastReceiver on some events or when the MainActivity is launched.
|
||||
*/
|
||||
public class BackgroundService extends Service {
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
private static BackgroundService instance;
|
||||
|
||||
private KdeConnect applicationInstance;
|
||||
|
||||
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
|
||||
|
||||
public static BackgroundService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
// This indicates when connected over wifi/usb/bluetooth/(anything other than cellular)
|
||||
private final MutableLiveData<Boolean> connectedToNonCellularNetwork = new MutableLiveData<>();
|
||||
public LiveData<Boolean> isConnectedToNonCellularNetwork() {
|
||||
return connectedToNonCellularNetwork;
|
||||
}
|
||||
|
||||
public void updateForegroundNotification() {
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
//Update the foreground notification with the currently connected device list
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
nm.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLinkProviders() {
|
||||
linkProviders.add(new LanLinkProvider(this));
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
linkProviders.add(new BluetoothLinkProvider(this));
|
||||
}
|
||||
|
||||
public void onNetworkChange(@Nullable Network network) {
|
||||
if (!initialized) {
|
||||
Log.d("KDE/BackgroundService", "ignoring onNetworkChange called before the service is initialized");
|
||||
return;
|
||||
}
|
||||
Log.d("KDE/BackgroundService", "onNetworkChange");
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onNetworkChange(network);
|
||||
}
|
||||
}
|
||||
|
||||
public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.addConnectionReceiver(cr);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.removeConnectionReceiver(cr);
|
||||
}
|
||||
}
|
||||
|
||||
//This will called only once, even if we launch the service intent several times
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d("KdeConnect/BgService", "onCreate");
|
||||
instance = this;
|
||||
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("BackgroundService", this::updateForegroundNotification);
|
||||
|
||||
// Register screen on listener
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
// See: https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
}
|
||||
registerReceiver(new KdeConnectBroadcastReceiver(), filter);
|
||||
|
||||
// Watch for changes on all network connections except cellular networks
|
||||
NetworkRequest.Builder networkRequestBuilder = getNonCellularNetworkRequestBuilder();
|
||||
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
cm.registerNetworkCallback(networkRequestBuilder.build(), new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
Log.i("BackgroundService", "Valid network available");
|
||||
connectedToNonCellularNetwork.postValue(true);
|
||||
onNetworkChange(network);
|
||||
}
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
Log.i("BackgroundService", "Valid network lost");
|
||||
connectedToNonCellularNetwork.postValue(false);
|
||||
}
|
||||
});
|
||||
|
||||
applicationInstance = KdeConnect.getInstance();
|
||||
|
||||
registerLinkProviders();
|
||||
addConnectionListener(applicationInstance.getConnectionListener()); // Link Providers need to be already registered
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStart();
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private static NetworkRequest.Builder getNonCellularNetworkRequestBuilder() {
|
||||
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
|
||||
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_USB)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN);
|
||||
}
|
||||
return networkRequestBuilder;
|
||||
}
|
||||
|
||||
public void changePersistentNotificationVisibility(boolean visible) {
|
||||
if (visible) {
|
||||
updateForegroundNotification();
|
||||
} else {
|
||||
Stop();
|
||||
Start(this);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification createForegroundNotification() {
|
||||
|
||||
//Why is this needed: https://developer.android.com/guide/components/services#Foreground
|
||||
|
||||
ArrayList<String> connectedDevices = new ArrayList<>();
|
||||
ArrayList<String> connectedDeviceIds = new ArrayList<>();
|
||||
for (Device device : applicationInstance.getDevices().values()) {
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
connectedDeviceIds.add(device.getDeviceId());
|
||||
connectedDevices.add(device.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
// Force open screen of the only connected device
|
||||
intent.putExtra(MainActivity.EXTRA_DEVICE_ID, connectedDeviceIds.get(0));
|
||||
}
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Builder notification = new NotificationCompat.Builder(this, NotificationHelper.Channels.PERSISTENT);
|
||||
notification
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setOngoing(true)
|
||||
.setContentIntent(pi)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN) //MIN so it's not shown in the status bar before Oreo, on Oreo it will be bumped to LOW
|
||||
.setShowWhen(false)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setAutoCancel(false);
|
||||
notification.setGroup("BackgroundService");
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
//Pre-oreo, the notification will have an empty title line without this
|
||||
notification.setContentTitle(getString(R.string.kde_connect));
|
||||
}
|
||||
|
||||
if (connectedDevices.isEmpty()) {
|
||||
notification.setContentText(getString(R.string.foreground_notification_no_devices));
|
||||
} else {
|
||||
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
|
||||
|
||||
// Adding an action button to send clipboard manually in Android 10 and later.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
|
||||
Intent sendClipboard = ClipboardFloatingActivity.getIntent(this, true);
|
||||
PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
|
||||
}
|
||||
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
String deviceId = connectedDeviceIds.get(0);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
Intent sendFile = new Intent(this, SendFileActivity.class);
|
||||
sendFile.putExtra("deviceId", deviceId);
|
||||
PendingIntent sendPendingFile = PendingIntent.getActivity(this, 1, sendFile, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.send_files), sendPendingFile);
|
||||
|
||||
// Checking if there are registered commands and adding the button.
|
||||
RunCommandPlugin plugin = (RunCommandPlugin) device.getPlugin("RunCommandPlugin");
|
||||
if (plugin != null && !plugin.getCommandList().isEmpty()) {
|
||||
Intent runCommand = new Intent(this, RunCommandActivity.class);
|
||||
runCommand.putExtra("deviceId", connectedDeviceIds.get(0));
|
||||
PendingIntent runPendingCommand = PendingIntent.getActivity(this, 2, runCommand, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.pref_plugin_runcommand), runPendingCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return notification.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d("KdeConnect/BgService", "onDestroy");
|
||||
initialized = false;
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStop();
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d("KDE/BackgroundService", "onStartCommand");
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||
} else {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
if (intent != null && intent.getBooleanExtra("refresh", false)) {
|
||||
onNetworkChange(null);
|
||||
}
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
public static void Start(Context context) {
|
||||
Log.d("KDE/BackgroundService", "Start");
|
||||
Intent intent = new Intent(context, BackgroundService.class);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
}
|
||||
|
||||
public static void ForceRefreshConnections(Context context) {
|
||||
Log.d("KDE/BackgroundService", "ForceRefreshConnections");
|
||||
Intent intent = new Intent(context, BackgroundService.class);
|
||||
intent.putExtra("refresh", true);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
}
|
||||
|
||||
public void Stop() {
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
}
|
291
src/org/kde/kdeconnect/BackgroundService.kt
Normal file
291
src/org/kde/kdeconnect/BackgroundService.kt
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider.ConnectionReceiver
|
||||
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity
|
||||
import org.kde.kdeconnect_tp.R
|
||||
|
||||
/**
|
||||
* This class (still) does 3 things:
|
||||
* - Keeps the app running by creating a foreground notification.
|
||||
* - Holds references to the active LinkProviders, but doesn't handle the DeviceLink those create (the KdeConnect class does that).
|
||||
* - Listens for network connectivity changes and tells the LinkProviders to re-check for devices.
|
||||
* It can be started by the KdeConnectBroadcastReceiver on some events or when the MainActivity is launched.
|
||||
*/
|
||||
class BackgroundService : Service() {
|
||||
private lateinit var applicationInstance: KdeConnect
|
||||
|
||||
private val linkProviders = ArrayList<BaseLinkProvider>()
|
||||
|
||||
private val connectedToNonCellularNetwork = MutableLiveData<Boolean>()
|
||||
/** Indicates whether device is connected over wifi / usb / bluetooth / (anything other than cellular) */
|
||||
val isConnectedToNonCellularNetwork: LiveData<Boolean>
|
||||
get() = connectedToNonCellularNetwork
|
||||
|
||||
fun updateForegroundNotification() {
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
// Update the foreground notification with the currently connected device list
|
||||
val notificationManager = getSystemService<NotificationManager>()
|
||||
notificationManager?.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification())
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerLinkProviders() {
|
||||
linkProviders.add(LanLinkProvider(this))
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
linkProviders.add(BluetoothLinkProvider(this))
|
||||
}
|
||||
|
||||
fun onNetworkChange(network: Network?) {
|
||||
if (!initialized) {
|
||||
Log.d(LOG_TAG, "ignoring onNetworkChange called before the service is initialized")
|
||||
return
|
||||
}
|
||||
Log.d(LOG_TAG, "onNetworkChange")
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onNetworkChange(network)
|
||||
}
|
||||
}
|
||||
|
||||
fun addConnectionListener(connectionReceiver: ConnectionReceiver) {
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.addConnectionReceiver(connectionReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeConnectionListener(connectionReceiver: ConnectionReceiver) {
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.removeConnectionReceiver(connectionReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
/** This will called only once, even if we launch the service intent several times */
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d("KdeConnect/BgService", "onCreate")
|
||||
this.applicationInstance = KdeConnect.getInstance()
|
||||
instance = this
|
||||
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("BackgroundService", this::updateForegroundNotification)
|
||||
|
||||
// Register screen on listener
|
||||
val filter = IntentFilter(Intent.ACTION_SCREEN_ON)
|
||||
// See: https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
|
||||
}
|
||||
registerReceiver(KdeConnectBroadcastReceiver(), filter)
|
||||
|
||||
// Watch for changes on all network connections except cellular networks
|
||||
val networkRequestBuilder = createNonCellularNetworkRequestBuilder()
|
||||
val connectivityManager = this.getSystemService<ConnectivityManager>()
|
||||
connectivityManager?.registerNetworkCallback(networkRequestBuilder.build(), object : NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
Log.i("BackgroundService", "Valid network available")
|
||||
connectedToNonCellularNetwork.postValue(true)
|
||||
onNetworkChange(network)
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
Log.i("BackgroundService", "Valid network lost")
|
||||
connectedToNonCellularNetwork.postValue(false)
|
||||
}
|
||||
})
|
||||
|
||||
registerLinkProviders()
|
||||
addConnectionListener(applicationInstance.connectionListener) // Link Providers need to be already registered
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onStart()
|
||||
}
|
||||
initialized = true
|
||||
}
|
||||
|
||||
fun changePersistentNotificationVisibility(visible: Boolean) {
|
||||
if (visible) {
|
||||
updateForegroundNotification()
|
||||
}
|
||||
else {
|
||||
stopForeground(true)
|
||||
Start(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createForegroundNotification(): Notification {
|
||||
// Why is this needed: https://developer.android.com/guide/components/services#Foreground
|
||||
|
||||
val connectedDevices = ArrayList<String>()
|
||||
val connectedDeviceIds = ArrayList<String>()
|
||||
for (device in applicationInstance.devices.values) {
|
||||
if (device.isReachable && device.isPaired) {
|
||||
connectedDeviceIds.add(device.deviceId)
|
||||
connectedDevices.add(device.name)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
if (connectedDeviceIds.size == 1) {
|
||||
// Force open screen of the only connected device
|
||||
intent.putExtra(MainActivity.EXTRA_DEVICE_ID, connectedDeviceIds[0])
|
||||
}
|
||||
|
||||
val pi = PendingIntent.getActivity(this, 0, intent, UPDATE_MUTABLE_FLAGS)
|
||||
val notification = NotificationCompat.Builder(this, NotificationHelper.Channels.PERSISTENT).apply {
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setOngoing(true)
|
||||
setContentIntent(pi)
|
||||
setPriority(NotificationCompat.PRIORITY_MIN) //MIN so it's not shown in the status bar before Oreo, on Oreo it will be bumped to LOW
|
||||
setShowWhen(false)
|
||||
setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
setAutoCancel(false)
|
||||
setGroup("BackgroundService")
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// Pre-oreo, the notification will have an empty title line without this
|
||||
notification.setContentTitle(getString(R.string.kde_connect))
|
||||
}
|
||||
|
||||
if (connectedDevices.isEmpty()) {
|
||||
notification.setContentText(getString(R.string.foreground_notification_no_devices))
|
||||
}
|
||||
else {
|
||||
notification.setContentText(getString(R.string.foreground_notification_devices, connectedDevices.joinToString(", ")))
|
||||
|
||||
// Adding an action button to send clipboard manually in Android 10 and later.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
|
||||
val sendClipboard = ClipboardFloatingActivity.getIntent(this, true)
|
||||
val sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, UPDATE_MUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard)
|
||||
}
|
||||
|
||||
if (connectedDeviceIds.size == 1) {
|
||||
val deviceId = connectedDeviceIds[0]
|
||||
val device = KdeConnect.getInstance().getDevice(deviceId)
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
val sendFile = Intent(this, SendFileActivity::class.java)
|
||||
sendFile.putExtra("deviceId", deviceId)
|
||||
val sendPendingFile = PendingIntent.getActivity(this, 1, sendFile, UPDATE_MUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.send_files), sendPendingFile)
|
||||
|
||||
// Checking if there are registered commands and adding the button.
|
||||
val plugin = device.getPlugin("RunCommandPlugin") as RunCommandPlugin?
|
||||
if (plugin != null && plugin.commandList.isNotEmpty()) {
|
||||
val runCommand = Intent(this, RunCommandActivity::class.java)
|
||||
runCommand.putExtra("deviceId", connectedDeviceIds[0])
|
||||
val runPendingCommand = PendingIntent.getActivity(this, 2, runCommand, UPDATE_MUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.pref_plugin_runcommand), runPendingCommand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return notification.build()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d("KdeConnect/BgService", "onDestroy")
|
||||
initialized = false
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onStop()
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(LOG_TAG, "onStartCommand")
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
|
||||
}
|
||||
else {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification())
|
||||
}
|
||||
}
|
||||
if (intent != null && intent.getBooleanExtra("refresh", false)) {
|
||||
onNetworkChange(null)
|
||||
}
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOG_TAG = "KDE/BackgroundService"
|
||||
|
||||
const val UPDATE_MUTABLE_FLAGS = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||
private const val FOREGROUND_NOTIFICATION_ID = 1
|
||||
|
||||
@JvmStatic
|
||||
var instance: BackgroundService? = null
|
||||
private set
|
||||
|
||||
private var initialized = false
|
||||
|
||||
private fun createNonCellularNetworkRequestBuilder(): NetworkRequest.Builder {
|
||||
return NetworkRequest.Builder().apply {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_VPN)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_USB)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Start(context: Context) {
|
||||
Log.d(LOG_TAG, "Start")
|
||||
val intent = Intent(context, BackgroundService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ForceRefreshConnections(context: Context) {
|
||||
Log.d(LOG_TAG, "ForceRefreshConnections")
|
||||
val intent = Intent(context, BackgroundService::class.java)
|
||||
intent.putExtra("refresh", true)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user