File indexing completed on 2024-12-22 04:41:42
0001 /* 0002 * SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 package org.kde.kdeconnect; 0008 0009 import android.Manifest; 0010 import android.app.Application; 0011 import android.content.Context; 0012 import android.content.SharedPreferences; 0013 import android.content.pm.PackageManager; 0014 import android.os.Build; 0015 import android.util.Log; 0016 0017 import androidx.annotation.NonNull; 0018 import androidx.core.app.ActivityCompat; 0019 import androidx.core.content.ContextCompat; 0020 0021 import org.kde.kdeconnect.Backends.BaseLink; 0022 import org.kde.kdeconnect.Backends.BaseLinkProvider; 0023 import org.kde.kdeconnect.Helpers.DeviceHelper; 0024 import org.kde.kdeconnect.Helpers.LifecycleHelper; 0025 import org.kde.kdeconnect.Helpers.NotificationHelper; 0026 import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; 0027 import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; 0028 import org.kde.kdeconnect.Plugins.Plugin; 0029 import org.kde.kdeconnect.Plugins.PluginFactory; 0030 import org.kde.kdeconnect.UserInterface.MainActivity; 0031 import org.kde.kdeconnect.UserInterface.ThemeUtil; 0032 import org.kde.kdeconnect_tp.BuildConfig; 0033 import org.slf4j.impl.HandroidLoggerAdapter; 0034 0035 import java.security.cert.CertificateException; 0036 import java.util.Set; 0037 import java.util.concurrent.ConcurrentHashMap; 0038 0039 /* 0040 * This class holds all the active devices and makes them accessible from every other class. 0041 * It also takes care of initializing all classes that need so when the app boots. 0042 * It provides a ConnectionReceiver that the BackgroundService uses to ping this class every time a new DeviceLink is created. 0043 */ 0044 public class KdeConnect extends Application { 0045 0046 public interface DeviceListChangedCallback { 0047 void onDeviceListChanged(); 0048 } 0049 0050 private static KdeConnect instance = null; 0051 0052 private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>(); 0053 0054 private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>(); 0055 0056 @Override 0057 public void onCreate() { 0058 super.onCreate(); 0059 instance = this; 0060 setupSL4JLogging(); 0061 Log.d("KdeConnect/Application", "onCreate"); 0062 ThemeUtil.setUserPreferredTheme(this); 0063 DeviceHelper.initializeDeviceId(this); 0064 RsaHelper.initialiseRsaKeys(this); 0065 SslHelper.initialiseCertificate(this); 0066 PluginFactory.initPluginInfo(this); 0067 NotificationHelper.initializeChannels(this); 0068 LifecycleHelper.initializeObserver(); 0069 loadRememberedDevicesFromSettings(); 0070 0071 } 0072 0073 private void setupSL4JLogging() { 0074 HandroidLoggerAdapter.DEBUG = BuildConfig.DEBUG; 0075 HandroidLoggerAdapter.ANDROID_API_LEVEL = Build.VERSION.SDK_INT; 0076 HandroidLoggerAdapter.APP_NAME = "KDEConnect"; 0077 } 0078 0079 @Override 0080 public void onTerminate() { 0081 Log.d("KdeConnect/Application", "onTerminate"); 0082 super.onTerminate(); 0083 } 0084 0085 public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) { 0086 deviceListChangedCallbacks.put(key, callback); 0087 } 0088 0089 public void removeDeviceListChangedCallback(String key) { 0090 deviceListChangedCallbacks.remove(key); 0091 } 0092 0093 private void onDeviceListChanged() { 0094 Log.i("MainActivity","Device list changed, notifying "+ deviceListChangedCallbacks.size() +" observers."); 0095 for (DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) { 0096 callback.onDeviceListChanged(); 0097 } 0098 } 0099 0100 public ConcurrentHashMap<String, Device> getDevices() { 0101 return devices; 0102 } 0103 0104 public Device getDevice(String id) { 0105 if (id == null) { 0106 return null; 0107 } 0108 return devices.get(id); 0109 } 0110 0111 public <T extends Plugin> T getDevicePlugin(String deviceId, Class<T> pluginClass) { 0112 if (deviceId == null) { 0113 return null; 0114 } 0115 Device device = devices.get(deviceId); 0116 if (device == null) { 0117 return null; 0118 } 0119 return device.getPlugin(pluginClass); 0120 } 0121 0122 public static KdeConnect getInstance() { 0123 return instance; 0124 } 0125 0126 private void loadRememberedDevicesFromSettings() { 0127 //Log.e("BackgroundService", "Loading remembered trusted devices"); 0128 SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); 0129 Set<String> trustedDevices = preferences.getAll().keySet(); 0130 for (String deviceId : trustedDevices) { 0131 //Log.e("BackgroundService", "Loading device "+deviceId); 0132 if (preferences.getBoolean(deviceId, false)) { 0133 try { 0134 Device device = new Device(this, deviceId); 0135 devices.put(deviceId, device); 0136 device.addPairingCallback(devicePairingCallback); 0137 } catch (CertificateException e) { 0138 Log.w("KdeConnect", "Couldn't load the certificate for a remembered device. Removing from trusted list."); 0139 e.printStackTrace(); 0140 preferences.edit().remove(deviceId).apply(); 0141 } 0142 } 0143 } 0144 } 0145 0146 private final PairingHandler.PairingCallback devicePairingCallback = new PairingHandler.PairingCallback() { 0147 @Override 0148 public void incomingPairRequest() { 0149 onDeviceListChanged(); 0150 } 0151 0152 @Override 0153 public void pairingSuccessful() { 0154 onDeviceListChanged(); 0155 } 0156 0157 @Override 0158 public void pairingFailed(String error) { 0159 onDeviceListChanged(); 0160 } 0161 0162 @Override 0163 public void unpaired() { 0164 onDeviceListChanged(); 0165 } 0166 }; 0167 0168 private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() { 0169 @Override 0170 public void onConnectionReceived(@NonNull final BaseLink link) { 0171 Device device = devices.get(link.getDeviceId()); 0172 if (device != null) { 0173 device.addLink(link); 0174 } else { 0175 device = new Device(KdeConnect.this, link); 0176 devices.put(link.getDeviceId(), device); 0177 device.addPairingCallback(devicePairingCallback); 0178 } 0179 onDeviceListChanged(); 0180 } 0181 0182 @Override 0183 public void onConnectionLost(BaseLink link) { 0184 Device device = devices.get(link.getDeviceId()); 0185 Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId()); 0186 if (device != null) { 0187 device.removeLink(link); 0188 if (!device.isReachable() && !device.isPaired()) { 0189 //Log.e("onConnectionLost","Removing connection device because it was not paired"); 0190 devices.remove(link.getDeviceId()); 0191 device.removePairingCallback(devicePairingCallback); 0192 } 0193 } else { 0194 Log.d("KDE/onConnectionLost","Removing connection to unknown device"); 0195 } 0196 onDeviceListChanged(); 0197 } 0198 }; 0199 0200 public BaseLinkProvider.ConnectionReceiver getConnectionListener() { 0201 return connectionListener; 0202 } 0203 0204 }