2014-11-16 23:14:06 -08:00
|
|
|
/*
|
|
|
|
* Copyright 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of
|
|
|
|
* the License or (at your option) version 3 or any later version
|
|
|
|
* accepted by the membership of KDE e.V. (or its successor approved
|
|
|
|
* by the membership of KDE e.V.), which shall act as a proxy
|
|
|
|
* defined in Section 14 of version 3 of the license.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2013-09-05 01:37:59 +02:00
|
|
|
package org.kde.kdeconnect;
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.PendingIntent;
|
2013-08-16 10:31:01 +02:00
|
|
|
import android.content.Context;
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.content.Intent;
|
2013-08-16 10:31:01 +02:00
|
|
|
import android.content.SharedPreferences;
|
2013-09-05 01:33:54 +02:00
|
|
|
import android.content.res.Resources;
|
2015-04-12 00:06:47 -07:00
|
|
|
import android.graphics.drawable.Drawable;
|
2013-08-20 13:41:13 +02:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Looper;
|
2013-08-16 10:31:01 +02:00
|
|
|
import android.preference.PreferenceManager;
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.support.v4.app.NotificationCompat;
|
2015-08-10 00:26:58 -07:00
|
|
|
import android.support.v4.content.ContextCompat;
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.util.Base64;
|
2013-07-23 16:11:54 +02:00
|
|
|
import android.util.Log;
|
|
|
|
|
2013-09-16 17:36:26 +02:00
|
|
|
import org.kde.kdeconnect.Backends.BaseLink;
|
2015-09-07 01:49:12 -07:00
|
|
|
import org.kde.kdeconnect.UserInterface.MaterialActivity;
|
2013-09-05 01:37:59 +02:00
|
|
|
import org.kde.kdeconnect.Plugins.Plugin;
|
|
|
|
import org.kde.kdeconnect.Plugins.PluginFactory;
|
|
|
|
import org.kde.kdeconnect_tp.R;
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
import java.security.KeyFactory;
|
|
|
|
import java.security.PrivateKey;
|
|
|
|
import java.security.PublicKey;
|
|
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
|
import java.security.spec.X509EncodedKeySpec;
|
2013-07-23 16:11:54 +02:00
|
|
|
import java.util.ArrayList;
|
2013-08-16 10:31:01 +02:00
|
|
|
import java.util.HashMap;
|
2015-09-09 01:11:46 -07:00
|
|
|
import java.util.HashSet;
|
2013-08-16 10:31:01 +02:00
|
|
|
import java.util.Set;
|
2013-09-03 17:58:59 +02:00
|
|
|
import java.util.Timer;
|
|
|
|
import java.util.TimerTask;
|
2015-09-10 08:05:23 -07:00
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2013-09-16 17:36:26 +02:00
|
|
|
public class Device implements BaseLink.PackageReceiver {
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final Context context;
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2014-03-29 01:47:15 +01:00
|
|
|
private final String deviceId;
|
2015-03-01 20:57:45 -08:00
|
|
|
private String name;
|
2014-01-16 09:51:32 +04:00
|
|
|
public PublicKey publicKey;
|
2013-09-03 17:58:59 +02:00
|
|
|
private int notificationId;
|
2013-10-01 03:24:42 +02:00
|
|
|
private int protocolVersion;
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2015-09-10 08:05:23 -07:00
|
|
|
private DeviceType deviceType;
|
|
|
|
private PairStatus pairStatus;
|
|
|
|
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
|
|
|
|
private Timer pairingTimer;
|
|
|
|
|
|
|
|
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
|
|
|
|
|
|
|
|
private final HashMap<String, Plugin> plugins = new HashMap<>();
|
|
|
|
private final HashMap<String, Plugin> failedPlugins = new HashMap<>();
|
|
|
|
|
|
|
|
private final SharedPreferences settings;
|
|
|
|
|
|
|
|
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
|
|
|
|
|
|
|
|
public interface PluginsChangedListener {
|
|
|
|
void onPluginsChanged(Device device);
|
|
|
|
}
|
|
|
|
|
2015-06-10 12:29:53 +05:30
|
|
|
public enum PairStatus {
|
2013-09-03 17:58:59 +02:00
|
|
|
NotPaired,
|
|
|
|
Requested,
|
|
|
|
RequestedByPeer,
|
|
|
|
Paired
|
|
|
|
}
|
|
|
|
|
2015-06-10 12:29:53 +05:30
|
|
|
public enum DeviceType {
|
2015-04-12 00:06:47 -07:00
|
|
|
Phone,
|
|
|
|
Tablet,
|
|
|
|
Computer;
|
|
|
|
|
|
|
|
public static DeviceType FromString(String s) {
|
|
|
|
if ("tablet".equals(s)) return Tablet;
|
2015-09-09 01:11:46 -07:00
|
|
|
if ("phone".equals(s)) return Phone;
|
|
|
|
return Computer; //Default
|
2015-04-12 00:06:47 -07:00
|
|
|
}
|
|
|
|
public String toString() {
|
|
|
|
switch (this) {
|
|
|
|
case Tablet: return "tablet";
|
2015-09-09 01:11:46 -07:00
|
|
|
case Phone: return "phone";
|
|
|
|
default: return "desktop";
|
2015-04-12 00:06:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public interface PairingCallback {
|
2015-08-10 00:26:58 -07:00
|
|
|
void incomingRequest();
|
|
|
|
void pairingSuccessful();
|
|
|
|
void pairingFailed(String error);
|
|
|
|
void unpaired();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
|
|
|
Device(Context context, String deviceId) {
|
2013-08-20 00:37:08 +02:00
|
|
|
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
2013-08-16 10:31:01 +02:00
|
|
|
|
|
|
|
//Log.e("Device","Constructor A");
|
|
|
|
|
|
|
|
this.context = context;
|
2013-07-23 16:11:54 +02:00
|
|
|
this.deviceId = deviceId;
|
2015-03-01 20:57:45 -08:00
|
|
|
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
|
2013-09-03 17:58:59 +02:00
|
|
|
this.pairStatus = PairStatus.Paired;
|
2013-10-01 03:24:42 +02:00
|
|
|
this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet
|
2015-09-09 01:07:17 -07:00
|
|
|
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
|
|
|
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-04-12 00:05:15 -07:00
|
|
|
unpair();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/Device","Exception");
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
|
|
|
reloadPluginsFromSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
2013-10-01 03:24:42 +02:00
|
|
|
Device(Context context, NetworkPackage np, BaseLink dl) {
|
2013-08-16 10:31:01 +02:00
|
|
|
|
|
|
|
//Log.e("Device","Constructor B");
|
|
|
|
|
|
|
|
this.context = context;
|
2013-10-01 03:24:42 +02:00
|
|
|
this.deviceId = np.getString("deviceId");
|
2015-09-09 01:07:17 -07:00
|
|
|
this.name = context.getString(R.string.unknown_device); //We read it in addLink
|
2013-09-03 17:58:59 +02:00
|
|
|
this.pairStatus = PairStatus.NotPaired;
|
2015-09-09 01:07:17 -07:00
|
|
|
this.protocolVersion = 0;
|
|
|
|
this.deviceType = DeviceType.Computer;
|
2013-09-03 17:58:59 +02:00
|
|
|
this.publicKey = null;
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-10-29 19:44:07 +01:00
|
|
|
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
|
|
|
|
2013-10-01 03:24:42 +02:00
|
|
|
addLink(np, dl);
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getName() {
|
2013-09-05 01:33:54 +02:00
|
|
|
return name != null? name : context.getString(R.string.unknown_device);
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2015-04-12 00:06:47 -07:00
|
|
|
public Drawable getIcon()
|
|
|
|
{
|
2015-08-10 00:26:58 -07:00
|
|
|
int drawableId;
|
2015-04-12 00:06:47 -07:00
|
|
|
switch (deviceType) {
|
2015-08-20 00:17:01 -07:00
|
|
|
case Phone: drawableId = R.drawable.ic_device_phone; break;
|
|
|
|
case Tablet: drawableId = R.drawable.ic_device_tablet; break;
|
2015-08-10 00:26:58 -07:00
|
|
|
default: drawableId = R.drawable.ic_device_laptop;
|
2015-04-12 00:06:47 -07:00
|
|
|
}
|
2015-08-10 00:26:58 -07:00
|
|
|
return ContextCompat.getDrawable(context, drawableId);
|
2015-04-12 00:06:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public DeviceType getDeviceType() {
|
|
|
|
return deviceType;
|
|
|
|
}
|
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
public String getDeviceId() {
|
|
|
|
return deviceId;
|
|
|
|
}
|
|
|
|
|
2013-10-01 03:24:42 +02:00
|
|
|
//Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
|
|
|
|
public int compareProtocolVersion() {
|
|
|
|
return protocolVersion - NetworkPackage.ProtocolVersion;
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Pairing-related functions
|
|
|
|
//
|
|
|
|
|
|
|
|
public boolean isPaired() {
|
|
|
|
return pairStatus == PairStatus.Paired;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public boolean isPairRequested() {
|
|
|
|
return pairStatus == PairStatus.Requested;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
public boolean isPairRequestedByOtherEnd() {
|
|
|
|
return pairStatus == PairStatus.RequestedByPeer;
|
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public void addPairingCallback(PairingCallback callback) {
|
|
|
|
pairingCallback.add(callback);
|
|
|
|
}
|
|
|
|
public void removePairingCallback(PairingCallback callback) {
|
|
|
|
pairingCallback.remove(callback);
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public void requestPairing() {
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-09-05 01:33:54 +02:00
|
|
|
Resources res = context.getResources();
|
|
|
|
|
2015-06-14 19:23:02 -07:00
|
|
|
switch(pairStatus) {
|
|
|
|
case Paired:
|
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(res.getString(R.string.error_already_paired));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case Requested:
|
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(res.getString(R.string.error_already_requested));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case RequestedByPeer:
|
|
|
|
Log.d("requestPairing", "Pairing already started by the other end, accepting their request.");
|
|
|
|
acceptPairing();
|
|
|
|
return;
|
|
|
|
case NotPaired:
|
|
|
|
;
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
2015-06-14 19:23:02 -07:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
if (!isReachable()) {
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(res.getString(R.string.error_not_reachable));
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
//Send our own public key
|
|
|
|
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
2015-09-12 13:21:06 -07:00
|
|
|
sendPackage(np, new SendPackageStatusCallback() {
|
2013-09-27 02:58:16 +02:00
|
|
|
@Override
|
2015-01-29 23:57:58 -08:00
|
|
|
public void onSuccess() {
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification(); //Will stop the pairingTimer if it was running
|
2013-09-27 02:58:16 +02:00
|
|
|
pairingTimer = new Timer();
|
|
|
|
pairingTimer.schedule(new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_timed_out));
|
|
|
|
}
|
2015-09-12 13:21:06 -07:00
|
|
|
Log.e("KDE/Device", "Unpairing (timeout A)");
|
2013-09-27 02:58:16 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
}
|
2015-09-12 13:21:06 -07:00
|
|
|
}, 30 * 1000); //Time to wait for the other to accept
|
2013-09-27 02:58:16 +02:00
|
|
|
pairStatus = PairStatus.Requested;
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
@Override
|
2015-01-29 23:57:58 -08:00
|
|
|
public void onFailure(Throwable e) {
|
2013-09-27 02:58:16 +02:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_could_not_send_package));
|
|
|
|
}
|
2015-09-12 13:21:06 -07:00
|
|
|
Log.e("KDE/Device", "Unpairing (sendFailed A)");
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
}
|
|
|
|
|
2013-09-27 02:58:16 +02:00
|
|
|
});
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
public void hidePairingNotification() {
|
|
|
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
notificationManager.cancel(notificationId);
|
|
|
|
if (pairingTimer != null) {
|
|
|
|
pairingTimer.cancel();
|
|
|
|
}
|
|
|
|
BackgroundService.removeGuiInUseCounter(context);
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void unpair() {
|
|
|
|
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("Device","Unpairing (unpair)");
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
|
|
|
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
2015-01-31 00:16:06 -08:00
|
|
|
preferences.edit().remove(deviceId).apply();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
|
|
|
np.set("pair", false);
|
|
|
|
sendPackage(np);
|
|
|
|
|
|
|
|
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
|
|
|
|
|
|
|
reloadPluginsFromSettings();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-10-29 19:44:07 +01:00
|
|
|
private void pairingDone() {
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("Device", "Storing as trusted, deviceId: "+deviceId);
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
2013-11-06 21:13:37 +01:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.Paired;
|
|
|
|
|
|
|
|
//Store as trusted device
|
|
|
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
2015-01-31 00:16:06 -08:00
|
|
|
preferences.edit().putBoolean(deviceId,true).apply();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
//Store device information needed to create a Device object in a future
|
|
|
|
SharedPreferences.Editor editor = settings.edit();
|
|
|
|
editor.putString("deviceName", getName());
|
2015-04-12 00:06:47 -07:00
|
|
|
editor.putString("deviceType", deviceType.toString());
|
2013-11-06 21:13:37 +01:00
|
|
|
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
|
2013-09-03 17:58:59 +02:00
|
|
|
editor.putString("publicKey", encodedPublicKey);
|
2015-01-31 00:16:06 -08:00
|
|
|
editor.apply();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
reloadPluginsFromSettings();
|
|
|
|
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingSuccessful();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void acceptPairing() {
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Device","Accepted pair request started by the other device");
|
2013-10-29 19:44:07 +01:00
|
|
|
|
|
|
|
//Send our own public key
|
|
|
|
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
2015-01-15 22:57:06 -08:00
|
|
|
sendPackage(np, new SendPackageStatusCallback() {
|
|
|
|
@Override
|
2015-01-29 23:57:58 -08:00
|
|
|
protected void onSuccess() {
|
2013-10-29 19:44:07 +01:00
|
|
|
pairingDone();
|
|
|
|
}
|
|
|
|
@Override
|
2015-01-29 23:57:58 -08:00
|
|
|
protected void onFailure(Throwable e) {
|
2013-11-06 21:13:37 +01:00
|
|
|
Log.e("Device","Unpairing (sendFailed B)");
|
2013-10-29 19:44:07 +01:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_not_reachable));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void rejectPairing() {
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Device","Rejected pair request started by the other device");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("Device","Unpairing (rejectPairing)");
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
|
|
|
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
|
|
|
np.set("pair", false);
|
|
|
|
sendPackage(np);
|
|
|
|
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_canceled_by_user));
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2013-09-03 17:58:59 +02:00
|
|
|
// ComputerLink-related functions
|
2013-08-16 10:31:01 +02:00
|
|
|
//
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public boolean isReachable() {
|
|
|
|
return !links.isEmpty();
|
|
|
|
}
|
|
|
|
|
2013-10-01 03:24:42 +02:00
|
|
|
public void addLink(NetworkPackage identityPackage, BaseLink link) {
|
2015-04-04 11:37:12 -07:00
|
|
|
//FilesHelper.LogOpenFileCount();
|
2013-10-01 03:24:42 +02:00
|
|
|
|
|
|
|
this.protocolVersion = identityPackage.getInt("protocolVersion");
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2015-03-01 20:57:45 -08:00
|
|
|
if (identityPackage.has("deviceName")) {
|
|
|
|
this.name = identityPackage.getString("deviceName", this.name);
|
|
|
|
SharedPreferences.Editor editor = settings.edit();
|
|
|
|
editor.putString("deviceName", this.name);
|
|
|
|
editor.apply();
|
|
|
|
}
|
|
|
|
|
2015-04-12 00:06:47 -07:00
|
|
|
if (identityPackage.has("deviceType")) {
|
2015-09-09 01:07:17 -07:00
|
|
|
this.deviceType = DeviceType.FromString(identityPackage.getString("deviceType", "desktop"));
|
2015-04-12 00:06:47 -07:00
|
|
|
}
|
|
|
|
|
2015-03-01 20:57:45 -08:00
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
links.add(link);
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-09-16 17:36:26 +02:00
|
|
|
try {
|
|
|
|
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
|
|
|
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
|
|
|
|
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
|
|
|
link.setPrivateKey(privateKey);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/Device", "Exception reading our own private key"); //Should not happen
|
2013-09-16 17:36:26 +02:00
|
|
|
}
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
|
2013-08-19 23:59:13 +02:00
|
|
|
|
2014-09-23 16:41:57 +02:00
|
|
|
/*
|
2013-09-16 17:36:26 +02:00
|
|
|
Collections.sort(links, new Comparator<BaseLink>() {
|
2013-07-23 16:11:54 +02:00
|
|
|
@Override
|
2013-09-16 17:36:26 +02:00
|
|
|
public int compare(BaseLink o, BaseLink o2) {
|
2013-07-23 16:11:54 +02:00
|
|
|
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
|
|
|
}
|
|
|
|
});
|
2014-09-23 16:41:57 +02:00
|
|
|
*/
|
2013-07-23 16:11:54 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
link.addPackageReceiver(this);
|
|
|
|
|
|
|
|
if (links.size() == 1) {
|
|
|
|
reloadPluginsFromSettings();
|
|
|
|
}
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2013-09-16 17:36:26 +02:00
|
|
|
public void removeLink(BaseLink link) {
|
2015-04-04 11:37:12 -07:00
|
|
|
//FilesHelper.LogOpenFileCount();
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
link.removePackageReceiver(this);
|
2013-07-23 16:11:54 +02:00
|
|
|
links.remove(link);
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
2013-08-16 10:31:01 +02:00
|
|
|
if (links.isEmpty()) {
|
|
|
|
reloadPluginsFromSettings();
|
|
|
|
}
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
@Override
|
|
|
|
public void onPackageReceived(NetworkPackage np) {
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR)) {
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Device","Pair package");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
boolean wantsPair = np.getBoolean("pair");
|
|
|
|
|
|
|
|
if (wantsPair == isPaired()) {
|
|
|
|
if (pairStatus == PairStatus.Requested) {
|
2013-11-06 21:13:37 +01:00
|
|
|
//Log.e("Device","Unpairing (pair rejected)");
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wantsPair) {
|
|
|
|
|
|
|
|
//Retrieve their public key
|
|
|
|
try {
|
|
|
|
String publicKeyContent = np.getString("publicKey").replace("-----BEGIN PUBLIC KEY-----\n","").replace("-----END PUBLIC KEY-----\n","");
|
|
|
|
byte[] publicKeyBytes = Base64.decode(publicKeyContent, 0);
|
|
|
|
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
|
|
|
} catch(Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/Device","Pairing exception: Received incorrect key");
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_invalid_key));
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pairStatus == PairStatus.Requested) { //We started pairing
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Pairing","Pair answer");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-10-29 19:44:07 +01:00
|
|
|
pairingDone();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Pairing","Pair request");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2015-09-07 00:11:46 -07:00
|
|
|
Intent intent = new Intent(context, MaterialActivity.class);
|
2013-09-03 17:58:59 +02:00
|
|
|
intent.putExtra("deviceId", deviceId);
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
|
|
|
|
|
2013-09-05 01:33:54 +02:00
|
|
|
Resources res = context.getResources();
|
|
|
|
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
Notification noti = new NotificationCompat.Builder(context)
|
2013-09-05 01:33:54 +02:00
|
|
|
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
|
|
|
|
.setContentText(res.getString(R.string.tap_to_answer))
|
2013-09-03 17:58:59 +02:00
|
|
|
.setContentIntent(pendingIntent)
|
2013-09-05 01:33:54 +02:00
|
|
|
.setTicker(res.getString(R.string.pair_requested))
|
2015-09-11 03:44:16 -07:00
|
|
|
.setSmallIcon(R.drawable.ic_notification)
|
2013-09-03 17:58:59 +02:00
|
|
|
.setAutoCancel(true)
|
2014-09-17 16:05:16 +02:00
|
|
|
.setDefaults(Notification.DEFAULT_ALL)
|
2013-09-03 17:58:59 +02:00
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
notificationId = (int)System.currentTimeMillis();
|
2015-06-13 22:16:17 -07:00
|
|
|
try {
|
2015-09-12 13:21:06 -07:00
|
|
|
BackgroundService.addGuiInUseCounter(context);
|
2015-06-13 22:16:17 -07:00
|
|
|
notificationManager.notify(notificationId, noti);
|
|
|
|
} catch(Exception e) {
|
|
|
|
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
|
|
|
|
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
pairingTimer = new Timer();
|
|
|
|
pairingTimer.schedule(new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/Device","Unpairing (timeout B)");
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.NotPaired;
|
|
|
|
}
|
2013-11-06 21:13:37 +01:00
|
|
|
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
|
2013-09-03 17:58:59 +02:00
|
|
|
pairStatus = PairStatus.RequestedByPeer;
|
|
|
|
for (PairingCallback cb : pairingCallback) cb.incomingRequest();
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.i("KDE/Pairing","Unpair request");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
if (pairStatus == PairStatus.Requested) {
|
2015-09-12 13:21:06 -07:00
|
|
|
hidePairingNotification();
|
2013-10-29 19:44:07 +01:00
|
|
|
for (PairingCallback cb : pairingCallback) {
|
|
|
|
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
|
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
} else if (pairStatus == PairStatus.Paired) {
|
|
|
|
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
2015-01-31 00:16:06 -08:00
|
|
|
preferences.edit().remove(deviceId).apply();
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pairStatus = PairStatus.NotPaired;
|
2015-09-07 00:11:46 -07:00
|
|
|
|
|
|
|
reloadPluginsFromSettings();
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
|
|
|
|
|
|
|
}
|
2015-03-22 22:44:17 -07:00
|
|
|
} else if (isPaired()) {
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
for (Plugin plugin : plugins.values()) {
|
2014-06-18 22:53:52 +02:00
|
|
|
try {
|
|
|
|
plugin.onPackageReceived(np);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
|
2014-06-18 22:53:52 +02:00
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
}
|
2015-03-22 22:44:17 -07:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2015-09-09 03:22:34 -07:00
|
|
|
//Log.e("KDE/onPackageReceived","Device not paired, will pass package to unpairedPackageListeners");
|
2015-03-22 22:44:17 -07:00
|
|
|
|
|
|
|
if (pairStatus != PairStatus.Requested) {
|
|
|
|
unpair();
|
|
|
|
}
|
|
|
|
|
2015-08-27 13:06:24 +05:30
|
|
|
for (Plugin plugin : plugins.values()) {
|
2015-08-19 21:27:27 +05:30
|
|
|
try {
|
|
|
|
plugin.onUnpairedDevicePackageReceived(np);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived() in unPairedPackageListeners");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2015-01-29 23:57:58 -08:00
|
|
|
public static abstract class SendPackageStatusCallback {
|
|
|
|
protected abstract void onSuccess();
|
|
|
|
protected abstract void onFailure(Throwable e);
|
|
|
|
protected void onProgressChanged(int percent) { }
|
|
|
|
|
|
|
|
private boolean success = false;
|
|
|
|
public void sendSuccess() {
|
|
|
|
success = true;
|
|
|
|
onSuccess();
|
|
|
|
}
|
|
|
|
public void sendFailure(Throwable e) {
|
|
|
|
if (e != null) {
|
|
|
|
e.printStackTrace();
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/sendPackage", "Exception: " + e.getMessage());
|
2015-01-29 23:57:58 -08:00
|
|
|
} else {
|
2015-04-04 11:37:12 -07:00
|
|
|
Log.e("KDE/sendPackage", "Unknown (null) exception");
|
2015-01-29 23:57:58 -08:00
|
|
|
}
|
|
|
|
onFailure(e);
|
|
|
|
}
|
|
|
|
public void sendProgress(int percent) { onProgressChanged(percent); }
|
2013-09-27 02:58:16 +02:00
|
|
|
}
|
2013-07-31 18:02:22 +02:00
|
|
|
|
2015-01-29 23:57:58 -08:00
|
|
|
|
2013-09-27 02:58:16 +02:00
|
|
|
public void sendPackage(NetworkPackage np) {
|
2015-01-29 23:57:58 -08:00
|
|
|
sendPackage(np,new SendPackageStatusCallback() {
|
|
|
|
@Override
|
|
|
|
protected void onSuccess() { }
|
|
|
|
@Override
|
|
|
|
protected void onFailure(Throwable e) { }
|
|
|
|
});
|
2013-09-27 02:58:16 +02:00
|
|
|
}
|
|
|
|
|
2013-10-05 17:25:59 +02:00
|
|
|
//Async
|
2015-01-15 22:57:06 -08:00
|
|
|
public void sendPackage(final NetworkPackage np, final SendPackageStatusCallback callback) {
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2015-01-29 23:57:58 -08:00
|
|
|
//Log.e("sendPackage", "Sending package...");
|
|
|
|
//Log.e("sendPackage", np.serialize());
|
2013-10-29 19:44:07 +01:00
|
|
|
|
2015-01-31 00:16:06 -08:00
|
|
|
final Throwable backtrace = new Throwable();
|
2013-09-27 00:07:14 +02:00
|
|
|
new Thread(new Runnable() {
|
2013-07-23 16:11:54 +02:00
|
|
|
@Override
|
2013-09-27 00:07:14 +02:00
|
|
|
public void run() {
|
2013-09-16 17:36:26 +02:00
|
|
|
|
2013-10-03 15:50:20 +02:00
|
|
|
boolean useEncryption = (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired());
|
2013-09-16 17:36:26 +02:00
|
|
|
|
2015-01-29 23:57:58 -08:00
|
|
|
//Make a copy to avoid concurrent modification exception if the original list changes
|
2015-09-10 08:05:23 -07:00
|
|
|
for (final BaseLink link : links) {
|
2015-04-04 14:41:22 -07:00
|
|
|
if (link == null) continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile
|
2015-01-29 23:57:58 -08:00
|
|
|
if (useEncryption) {
|
|
|
|
link.sendPackageEncrypted(np, callback, publicKey);
|
|
|
|
} else {
|
|
|
|
link.sendPackage(np, callback);
|
2013-09-27 02:58:16 +02:00
|
|
|
}
|
2015-01-29 23:57:58 -08:00
|
|
|
if (callback.success) break; //If the link didn't call sendSuccess(), try the next one
|
2013-10-05 17:25:59 +02:00
|
|
|
}
|
2013-09-16 17:36:26 +02:00
|
|
|
|
2015-01-31 00:16:06 -08:00
|
|
|
if (!callback.success) {
|
2015-09-10 08:05:23 -07:00
|
|
|
Log.e("KDE/sendPackage", "No device link (of "+links.size()+" available) could send the package. Package "+np.getType()+" to " + name + " lost!");
|
2015-01-31 00:16:06 -08:00
|
|
|
backtrace.printStackTrace();
|
|
|
|
}
|
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
2013-09-27 00:07:14 +02:00
|
|
|
}).start();
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
//
|
|
|
|
// Plugin-related functions
|
|
|
|
//
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
public <T extends Plugin> T getPlugin(Class<T> pluginClass) {
|
|
|
|
return (T)getPlugin(Plugin.getPluginKey(pluginClass));
|
2015-01-10 00:31:07 -08:00
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
public <T extends Plugin> T getPlugin(Class<T> pluginClass, boolean includeFailed) {
|
|
|
|
return (T)getPlugin(Plugin.getPluginKey(pluginClass), includeFailed);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Plugin getPlugin(String pluginKey) {
|
|
|
|
return getPlugin(pluginKey, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Plugin getPlugin(String pluginKey, boolean includeFailed) {
|
|
|
|
Plugin plugin = plugins.get(pluginKey);
|
2015-01-10 00:31:07 -08:00
|
|
|
if (includeFailed && plugin == null) {
|
2015-06-06 00:38:51 -07:00
|
|
|
plugin = failedPlugins.get(pluginKey);
|
2015-01-10 00:31:07 -08:00
|
|
|
}
|
|
|
|
return plugin;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
private synchronized void addPlugin(final String pluginKey) {
|
|
|
|
Plugin existing = plugins.get(pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
if (existing != null) {
|
2015-06-06 00:38:51 -07:00
|
|
|
Log.w("KDE/addPlugin","plugin already present:" + pluginKey);
|
2013-08-20 13:41:13 +02:00
|
|
|
return;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this);
|
2013-08-16 10:31:01 +02:00
|
|
|
if (plugin == null) {
|
2015-06-06 00:38:51 -07:00
|
|
|
Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey);
|
2015-08-10 00:26:58 -07:00
|
|
|
failedPlugins.put(pluginKey, null);
|
2013-08-20 13:41:13 +02:00
|
|
|
return;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2013-08-20 13:41:13 +02:00
|
|
|
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
|
2014-10-10 12:38:11 -07:00
|
|
|
boolean success;
|
2013-08-20 13:41:13 +02:00
|
|
|
try {
|
2014-10-10 12:38:11 -07:00
|
|
|
success = plugin.onCreate();
|
2013-08-20 13:41:13 +02:00
|
|
|
} catch (Exception e) {
|
2014-10-10 12:38:11 -07:00
|
|
|
success = false;
|
2013-08-20 13:41:13 +02:00
|
|
|
e.printStackTrace();
|
2015-06-06 00:38:51 -07:00
|
|
|
Log.e("KDE/addPlugin", "Exception loading plugin " + pluginKey);
|
2013-08-20 13:41:13 +02:00
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2014-10-10 12:38:11 -07:00
|
|
|
if (success) {
|
2015-06-06 00:38:51 -07:00
|
|
|
//Log.e("addPlugin","added " + pluginKey);
|
|
|
|
failedPlugins.remove(pluginKey);
|
|
|
|
plugins.put(pluginKey, plugin);
|
2014-10-10 12:38:11 -07:00
|
|
|
} else {
|
2015-06-06 00:38:51 -07:00
|
|
|
Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey);
|
|
|
|
plugins.remove(pluginKey);
|
|
|
|
failedPlugins.put(pluginKey, plugin);
|
2014-10-10 12:38:11 -07:00
|
|
|
}
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-08-20 13:41:13 +02:00
|
|
|
}
|
|
|
|
});
|
2013-08-19 19:57:29 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
private synchronized boolean removePlugin(String pluginKey) {
|
2013-08-19 19:57:29 +02:00
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
Plugin plugin = plugins.remove(pluginKey);
|
|
|
|
Plugin failedPlugin = failedPlugins.remove(pluginKey);
|
2013-08-19 19:57:29 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
if (plugin == null) {
|
2013-08-19 19:57:29 +02:00
|
|
|
if (failedPlugin == null) {
|
2014-10-10 12:38:11 -07:00
|
|
|
//Not found
|
2013-08-19 19:57:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
plugin = failedPlugin;
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
plugin.onDestroy();
|
2015-06-06 00:38:51 -07:00
|
|
|
//Log.e("removePlugin","removed " + pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2015-06-06 00:38:51 -07:00
|
|
|
Log.e("KDE/removePlugin","Exception calling onDestroy for plugin "+pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
|
|
|
listener.onPluginsChanged(this);
|
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
public void setPluginEnabled(String pluginKey, boolean value) {
|
|
|
|
settings.edit().putBoolean(pluginKey,value).apply();
|
|
|
|
if (value && isPaired() && isReachable()) addPlugin(pluginKey);
|
|
|
|
else removePlugin(pluginKey);
|
2015-09-07 00:11:46 -07:00
|
|
|
|
|
|
|
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
|
|
|
listener.onPluginsChanged(Device.this);
|
|
|
|
}
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
public boolean isPluginEnabled(String pluginKey) {
|
|
|
|
boolean enabledByDefault = PluginFactory.getPluginInfo(context, pluginKey).isEnabledByDefault();
|
|
|
|
boolean enabled = settings.getBoolean(pluginKey, enabledByDefault);
|
2013-08-19 19:57:29 +02:00
|
|
|
return enabled;
|
|
|
|
}
|
|
|
|
|
2014-09-23 16:40:34 +02:00
|
|
|
public boolean hasPluginsLoaded() {
|
2014-09-29 18:50:25 -07:00
|
|
|
return !plugins.isEmpty() || !failedPlugins.isEmpty();
|
2014-09-23 16:40:34 +02:00
|
|
|
}
|
2013-08-19 19:57:29 +02:00
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
public void reloadPluginsFromSettings() {
|
|
|
|
|
2013-08-19 19:57:29 +02:00
|
|
|
failedPlugins.clear();
|
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
Set<String> availablePlugins = PluginFactory.getAvailablePlugins();
|
|
|
|
|
2015-06-06 00:38:51 -07:00
|
|
|
for(String pluginKey : availablePlugins) {
|
2013-08-16 10:31:01 +02:00
|
|
|
boolean enabled = false;
|
2015-08-27 13:06:24 +05:30
|
|
|
boolean listenToUnpaired = PluginFactory.getPluginInfo(context, pluginKey).listenToUnpaired();
|
|
|
|
if ((isPaired() || listenToUnpaired) && isReachable()) {
|
2015-06-06 00:38:51 -07:00
|
|
|
enabled = isPluginEnabled(pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
2015-08-27 13:06:24 +05:30
|
|
|
|
2013-08-16 10:31:01 +02:00
|
|
|
if (enabled) {
|
2015-06-06 00:38:51 -07:00
|
|
|
addPlugin(pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
} else {
|
2015-06-06 00:38:51 -07:00
|
|
|
removePlugin(pluginKey);
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-07 00:11:46 -07:00
|
|
|
for (PluginsChangedListener listener : pluginsChangedListeners) {
|
|
|
|
listener.onPluginsChanged(Device.this);
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
public HashMap<String,Plugin> getLoadedPlugins() {
|
|
|
|
return plugins;
|
|
|
|
}
|
|
|
|
|
2013-08-19 19:57:29 +02:00
|
|
|
public HashMap<String,Plugin> getFailedPlugins() {
|
|
|
|
return failedPlugins;
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-08-19 19:57:29 +02:00
|
|
|
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
|
|
|
pluginsChangedListeners.add(listener);
|
|
|
|
}
|
2013-08-16 10:31:01 +02:00
|
|
|
|
2013-08-19 19:57:29 +02:00
|
|
|
public void removePluginsChangedListener(PluginsChangedListener listener) {
|
|
|
|
pluginsChangedListeners.remove(listener);
|
2013-08-16 10:31:01 +02:00
|
|
|
}
|
|
|
|
|
2015-09-09 00:45:56 +02:00
|
|
|
public void disconnect() {
|
|
|
|
for(BaseLink link : links) {
|
|
|
|
link.disconnect();
|
|
|
|
}
|
|
|
|
}
|
2015-09-12 13:21:06 -07:00
|
|
|
|
|
|
|
public BaseLink.ConnectionStarted getConnectionSource() {
|
|
|
|
for(BaseLink l : links) {
|
|
|
|
if (l.getConnectionSource() == BaseLink.ConnectionStarted.Locally) {
|
|
|
|
return BaseLink.ConnectionStarted.Locally;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BaseLink.ConnectionStarted.Remotely;
|
|
|
|
}
|
|
|
|
|
2013-07-23 16:11:54 +02:00
|
|
|
}
|