diff --git a/src/org/kde/kdeconnect/Device.java b/src/org/kde/kdeconnect/Device.java index 73b1b818..f5f27662 100644 --- a/src/org/kde/kdeconnect/Device.java +++ b/src/org/kde/kdeconnect/Device.java @@ -1,905 +1,904 @@ /* * Copyright 2014 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.ContextCompat; import android.util.Base64; import android.util.Log; import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Helpers.NotificationHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.UserInterface.MaterialActivity; import org.kde.kdeconnect_tp.R; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; public class Device implements BaseLink.PackageReceiver { private final Context context; private final String deviceId; private String name; public PublicKey publicKey; public Certificate certificate; private int notificationId; private int protocolVersion; private DeviceType deviceType; private PairStatus pairStatus; private final CopyOnWriteArrayList pairingCallback = new CopyOnWriteArrayList<>(); private Map pairingHandlers = new HashMap<>(); private final CopyOnWriteArrayList links = new CopyOnWriteArrayList<>(); private List m_supportedPlugins = new ArrayList<>(); private final ConcurrentHashMap plugins = new ConcurrentHashMap<>(); private final ConcurrentHashMap failedPlugins = new ConcurrentHashMap<>(); private final ConcurrentHashMap pluginsWithoutPermissions = new ConcurrentHashMap<>(); private final ConcurrentHashMap pluginsWithoutOptionalPermissions = new ConcurrentHashMap<>(); private Map> pluginsByIncomingInterface = new HashMap<>(); private final SharedPreferences settings; private final CopyOnWriteArrayList pluginsChangedListeners = new CopyOnWriteArrayList<>(); public interface PluginsChangedListener { void onPluginsChanged(Device device); } public enum PairStatus { NotPaired, Paired } public enum DeviceType { Phone, Tablet, Computer; public static DeviceType FromString(String s) { if ("tablet".equals(s)) return Tablet; if ("phone".equals(s)) return Phone; return Computer; //Default } public String toString() { switch (this) { case Tablet: return "tablet"; case Phone: return "phone"; default: return "desktop"; } } } public interface PairingCallback { void incomingRequest(); void pairingSuccessful(); void pairingFailed(String error); void unpaired(); } //Remembered trusted device, we need to wait for a incoming devicelink to communicate Device(Context context, String deviceId) { settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); //Log.e("Device","Constructor A"); this.context = context; this.deviceId = deviceId; this.name = settings.getString("deviceName", context.getString(R.string.unknown_device)); this.pairStatus = PairStatus.Paired; this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop")); try { String publicKeyStr = settings.getString("publicKey", null); if (publicKeyStr != null) { byte[] publicKeyBytes = Base64.decode(publicKeyStr, 0); publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes)); } } catch (Exception e) { e.printStackTrace(); Log.e("KDE/Device","Exception deserializing stored public key for device"); } //Assume every plugin is supported until addLink is called and we can get the actual list m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); //Do not load plugins yet, the device is not present //reloadPluginsFromSettings(); } //Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet Device(Context context, NetworkPackage np, BaseLink dl) { //Log.e("Device","Constructor B"); this.context = context; this.deviceId = np.getString("deviceId"); this.name = context.getString(R.string.unknown_device); //We read it in addLink this.pairStatus = PairStatus.NotPaired; this.protocolVersion = 0; this.deviceType = DeviceType.Computer; this.publicKey = null; settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); addLink(np, dl); } public String getName() { return name != null? name : context.getString(R.string.unknown_device); } public Drawable getIcon() { int drawableId; switch (deviceType) { case Phone: drawableId = R.drawable.ic_device_phone; break; case Tablet: drawableId = R.drawable.ic_device_tablet; break; default: drawableId = R.drawable.ic_device_laptop; } return ContextCompat.getDrawable(context, drawableId); } public DeviceType getDeviceType() { return deviceType; } public String getDeviceId() { return deviceId; } public Context getContext() { return context; } //Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer public int compareProtocolVersion() { return protocolVersion - NetworkPackage.ProtocolVersion; } // // Pairing-related functions // public boolean isPaired() { return pairStatus == PairStatus.Paired; } /* Asks all pairing handlers that, is pair requested? */ public boolean isPairRequested() { boolean pairRequested = false; for (BasePairingHandler ph: pairingHandlers.values()) { pairRequested = pairRequested || ph.isPairRequested(); } return pairRequested; } /* Asks all pairing handlers that, is pair requested by peer? */ public boolean isPairRequestedByPeer() { boolean pairRequestedByPeer = false; for (BasePairingHandler ph : pairingHandlers.values()) { pairRequestedByPeer = pairRequestedByPeer || ph.isPairRequestedByPeer(); } return pairRequestedByPeer; } public void addPairingCallback(PairingCallback callback) { pairingCallback.add(callback); } public void removePairingCallback(PairingCallback callback) { pairingCallback.remove(callback); } public void requestPairing() { Resources res = context.getResources(); switch(pairStatus) { case Paired: for (PairingCallback cb : pairingCallback) { cb.pairingFailed(res.getString(R.string.error_already_paired)); } return; case NotPaired: ; } if (!isReachable()) { for (PairingCallback cb : pairingCallback) { cb.pairingFailed(res.getString(R.string.error_not_reachable)); } return; } for (BasePairingHandler ph : pairingHandlers.values()) { ph.requestPairing(); } } public void unpair() { for (BasePairingHandler ph : pairingHandlers.values()) { ph.unpair(); } unpairInternal(); // Even if there are no pairing handlers, unpair } /** * This method does not send an unpair package, instead it unpairs internally by deleting trusted device info. . Likely to be called after sending package from * pairing handler */ private void unpairInternal() { //Log.e("Device","Unpairing (unpairInternal)"); pairStatus = PairStatus.NotPaired; SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); preferences.edit().remove(deviceId).apply(); SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); devicePreferences.edit().clear().apply(); for (PairingCallback cb : pairingCallback) cb.unpaired(); reloadPluginsFromSettings(); } /* This method should be called after pairing is done from pairing handler. Calling this method again should not create any problem as most of the things will get over writter*/ private void pairingDone() { //Log.e("Device", "Storing as trusted, deviceId: "+deviceId); hidePairingNotification(); pairStatus = PairStatus.Paired; //Store as trusted device SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); preferences.edit().putBoolean(deviceId,true).apply(); SharedPreferences.Editor editor = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE).edit(); editor.putString("deviceName", name); editor.putString("deviceType", deviceType.toString()); editor.apply(); reloadPluginsFromSettings(); for (PairingCallback cb : pairingCallback) { cb.pairingSuccessful(); } } /* This method is called after accepting pair request form GUI */ public void acceptPairing() { Log.i("KDE/Device", "Accepted pair request started by the other device"); for (BasePairingHandler ph : pairingHandlers.values()) { ph.acceptPairing(); } } /* This method is called after rejecting pairing from GUI */ public void rejectPairing() { Log.i("KDE/Device", "Rejected pair request started by the other device"); //Log.e("Device","Unpairing (rejectPairing)"); pairStatus = PairStatus.NotPaired; for (BasePairingHandler ph : pairingHandlers.values()) { ph.rejectPairing(); } for (PairingCallback cb : pairingCallback) { cb.pairingFailed(context.getString(R.string.error_canceled_by_user)); } } // // Notification related methods used during pairing // public int getNotificationId() { return notificationId; } public void displayPairingNotification() { hidePairingNotification(); notificationId = (int)System.currentTimeMillis(); Intent intent = new Intent(getContext(), MaterialActivity.class); intent.putExtra("deviceId", getDeviceId()); intent.putExtra("notificationId", notificationId); PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); Intent acceptIntent = new Intent(getContext(), MaterialActivity.class); Intent rejectIntent = new Intent(getContext(), MaterialActivity.class); acceptIntent.putExtra("deviceId", getDeviceId()); acceptIntent.putExtra("notificationId", notificationId); acceptIntent.setAction("action "+System.currentTimeMillis()); acceptIntent.putExtra(MaterialActivity.PAIR_REQUEST_STATUS, MaterialActivity.PAIRING_ACCEPTED); rejectIntent.putExtra("deviceId", getDeviceId()); rejectIntent.putExtra("notificationId", notificationId); rejectIntent.setAction("action "+System.currentTimeMillis()); rejectIntent.putExtra(MaterialActivity.PAIR_REQUEST_STATUS, MaterialActivity.PAIRING_REJECTED); PendingIntent acceptedPendingIntent = PendingIntent.getActivity(getContext(), 2, acceptIntent, PendingIntent.FLAG_ONE_SHOT); PendingIntent rejectedPendingIntent = PendingIntent.getActivity(getContext(), 4, rejectIntent, PendingIntent.FLAG_ONE_SHOT); Resources res = getContext().getResources(); Notification noti = new NotificationCompat.Builder(getContext()) .setContentTitle(res.getString(R.string.pairing_request_from, getName())) .setContentText(res.getString(R.string.tap_to_answer)) .setContentIntent(pendingIntent) .setTicker(res.getString(R.string.pair_requested)) .setSmallIcon(R.drawable.ic_notification) .addAction(R.drawable.ic_accept_pairing, res.getString(R.string.pairing_accept), acceptedPendingIntent) .addAction(R.drawable.ic_reject_pairing, res.getString(R.string.pairing_reject), rejectedPendingIntent) .setAutoCancel(true) .setDefaults(Notification.DEFAULT_ALL) .build(); final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE); NotificationHelper.notifyCompat(notificationManager, notificationId, noti); BackgroundService.addGuiInUseCounter(context); } public void hidePairingNotification() { final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(notificationId); BackgroundService.removeGuiInUseCounter(context); } // // ComputerLink-related functions // public boolean isReachable() { return !links.isEmpty(); } public void addLink(NetworkPackage identityPackage, BaseLink link) { //FilesHelper.LogOpenFileCount(); this.protocolVersion = identityPackage.getInt("protocolVersion"); if (identityPackage.has("deviceName")) { this.name = identityPackage.getString("deviceName", this.name); SharedPreferences.Editor editor = settings.edit(); editor.putString("deviceName", this.name); editor.apply(); } if (identityPackage.has("deviceType")) { this.deviceType = DeviceType.FromString(identityPackage.getString("deviceType", "desktop")); } if (identityPackage.has("certificate")) { String certificateString = identityPackage.getString("certificate"); try { byte[] certificateBytes = Base64.decode(certificateString, 0); certificate = SslHelper.parseCertificate(certificateBytes); Log.i("KDE/Device", "Got certificate "); } catch (Exception e) { e.printStackTrace(); Log.e("KDE/Device", "Error getting certificate"); } } links.add(link); 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(); Log.e("KDE/Device", "Exception reading our own private key"); //Should not happen } Log.i("KDE/Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size()); if (!pairingHandlers.containsKey(link.getName())) { BasePairingHandler.PairingHandlerCallback callback = new BasePairingHandler.PairingHandlerCallback() { @Override public void incomingRequest() { for (PairingCallback cb : pairingCallback) { cb.incomingRequest(); } } @Override public void pairingDone() { Device.this.pairingDone(); } @Override public void pairingFailed(String error) { for (PairingCallback cb : pairingCallback) { cb.pairingFailed(error); } } @Override public void unpaired() { unpairInternal(); } }; pairingHandlers.put(link.getName(), link.getPairingHandler(this, callback)); } Set outgoingCapabilities = identityPackage.getStringSet("outgoingCapabilities", null); Set incomingCapabilities = identityPackage.getStringSet("incomingCapabilities", null); if (incomingCapabilities != null && outgoingCapabilities != null) { m_supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(context, incomingCapabilities, outgoingCapabilities)); } else { m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); } link.addPackageReceiver(this); reloadPluginsFromSettings(); } public void removeLink(BaseLink link) { //FilesHelper.LogOpenFileCount(); /* Remove pairing handler corresponding to that link too if it was the only link*/ boolean linkPresent = false; for (BaseLink bl : links) { if (bl.getName().equals(link.getName())) { linkPresent = true; break; } } if (!linkPresent) { pairingHandlers.remove(link.getName()); } link.removePackageReceiver(this); links.remove(link); Log.i("KDE/Device", "removeLink: " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size()); if (links.isEmpty()) { reloadPluginsFromSettings(); } } @Override public void onPackageReceived(NetworkPackage np) { hackToMakeRetrocompatiblePacketTypes(np); if (NetworkPackage.PACKAGE_TYPE_PAIR.equals(np.getType())) { Log.i("KDE/Device", "Pair package"); for (BasePairingHandler ph: pairingHandlers.values()) { try { ph.packageReceived(np); } catch (Exception e) { e.printStackTrace(); Log.e("PairingPackageReceived","Exception"); } } } else if (isPaired()) { //If capabilities are not supported, iterate all plugins Collection targetPlugins = pluginsByIncomingInterface.get(np.getType()); if (targetPlugins != null && !targetPlugins.isEmpty()) { for (String pluginKey : targetPlugins) { Plugin plugin = plugins.get(pluginKey); try { plugin.onPackageReceived(np); } catch (Exception e) { e.printStackTrace(); Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPackageReceived()"); //try { Log.e("KDE/Device", "NetworkPackage:" + np.serialize()); } catch (Exception _) { } } } } else { Log.w("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it"); } } else { //Log.e("KDE/onPackageReceived","Device not paired, will pass package to unpairedPackageListeners"); // If it is pair package, it should be captured by "if" at start // If not and device is paired, it should be captured by isPaired // Else unpair, this handles the situation when one device unpairs, but other dont know like unpairing when wi-fi is off unpair(); //If capabilities are not supported, iterate all plugins Collection targetPlugins = pluginsByIncomingInterface.get(np.getType()); if (targetPlugins != null && !targetPlugins.isEmpty()) { for (String pluginKey : targetPlugins) { Plugin plugin = plugins.get(pluginKey); try { plugin.onUnpairedDevicePackageReceived(np); } catch (Exception e) { e.printStackTrace(); Log.e("KDE/Device", "Exception in " + plugin.getDisplayName() + "'s onPackageReceived() in unPairedPackageListeners"); } } } else { Log.e("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it"); } } } public static abstract class SendPackageStatusCallback { public abstract void onSuccess(); public abstract void onFailure(Throwable e); public void onProgressChanged(int percent) { } } private SendPackageStatusCallback defaultCallback = new SendPackageStatusCallback() { @Override public void onSuccess() { } @Override public void onFailure(Throwable e) { if (e != null) { e.printStackTrace(); } else { Log.e("KDE/sendPackage", "Unknown (null) exception"); } } }; public void sendPackage(NetworkPackage np) { sendPackage(np, defaultCallback); } public boolean sendPackageBlocking(NetworkPackage np) { return sendPackageBlocking(np, defaultCallback); } //Async public void sendPackage(final NetworkPackage np, final SendPackageStatusCallback callback) { new Thread(new Runnable() { @Override public void run() { sendPackageBlocking(np, callback); } }).start(); } public boolean sendPackageBlocking(final NetworkPackage np, final SendPackageStatusCallback callback) { /* if (!m_outgoingCapabilities.contains(np.getType()) && !NetworkPackage.protocolPackageTypes.contains(np.getType())) { Log.e("Device/sendPackage", "Plugin tried to send an undeclared package: " + np.getType()); Log.w("Device/sendPackage", "Declared outgoing package types: " + Arrays.toString(m_outgoingCapabilities.toArray())); } */ hackToMakeRetrocompatiblePacketTypes(np); boolean useEncryption = (protocolVersion < LanLinkProvider.MIN_VERSION_WITH_SSL_SUPPORT && (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired())); boolean success = false; //Make a copy to avoid concurrent modification exception if the original list changes for (final BaseLink link : links) { if (link == null) continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile if (useEncryption) { success = link.sendPackageEncrypted(np, callback, publicKey); } else { success = link.sendPackage(np, callback); } if (success) break; //If the link didn't call sendSuccess(), try the next one } if (!success) { Log.e("KDE/sendPackage", "No device link (of "+links.size()+" available) could send the package. Package "+np.getType()+" to " + name + " lost!"); } return success; } // // Plugin-related functions // public T getPlugin(Class pluginClass) { return (T)getPlugin(Plugin.getPluginKey(pluginClass)); } public T getPlugin(Class 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); if (includeFailed && plugin == null) { plugin = failedPlugins.get(pluginKey); } return plugin; } private synchronized boolean addPlugin(final String pluginKey) { Plugin existing = plugins.get(pluginKey); if (existing != null) { if (existing.getMinSdk() > Build.VERSION.SDK_INT) { Log.i("KDE/addPlugin", "Min API level not fulfilled " + pluginKey); return false; } //Log.w("KDE/addPlugin","plugin already present:" + pluginKey); if (existing.checkOptionalPermissions()) { Log.i("KDE/addPlugin", "Optional Permissions OK " + pluginKey); pluginsWithoutOptionalPermissions.remove(pluginKey); } else { Log.e("KDE/addPlugin", "No optional permission " + pluginKey); pluginsWithoutOptionalPermissions.put(pluginKey, existing); } return true; } final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this); if (plugin == null) { Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey); //Can't put a null //failedPlugins.put(pluginKey, null); return false; } if (plugin.getMinSdk() > Build.VERSION.SDK_INT) { Log.i("KDE/addPlugin", "Min API level not fulfilled" + pluginKey); return false; } boolean success; try { success = plugin.onCreate(); } catch (Exception e) { success = false; e.printStackTrace(); Log.e("KDE/addPlugin", "Exception loading plugin " + pluginKey); } if (success) { //Log.e("addPlugin","added " + pluginKey); failedPlugins.remove(pluginKey); plugins.put(pluginKey, plugin); } else { Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey); plugins.remove(pluginKey); failedPlugins.put(pluginKey, plugin); } if(!plugin.checkRequiredPermissions()){ Log.e("KDE/addPlugin", "No permission " + pluginKey); plugins.remove(pluginKey); pluginsWithoutPermissions.put(pluginKey, plugin); success = false; } else { Log.i("KDE/addPlugin", "Permissions OK " + pluginKey); pluginsWithoutPermissions.remove(pluginKey); if (plugin.checkOptionalPermissions()) { Log.i("KDE/addPlugin", "Optional Permissions OK " + pluginKey); pluginsWithoutOptionalPermissions.remove(pluginKey); } else { Log.e("KDE/addPlugin", "No optional permission " + pluginKey); pluginsWithoutOptionalPermissions.put(pluginKey, plugin); } } return success; } private synchronized boolean removePlugin(String pluginKey) { Plugin plugin = plugins.remove(pluginKey); Plugin failedPlugin = failedPlugins.remove(pluginKey); if (plugin == null) { if (failedPlugin == null) { //Not found return false; } plugin = failedPlugin; } try { plugin.onDestroy(); //Log.e("removePlugin","removed " + pluginKey); } catch (Exception e) { e.printStackTrace(); Log.e("KDE/removePlugin","Exception calling onDestroy for plugin "+pluginKey); } return true; } public void setPluginEnabled(String pluginKey, boolean value) { settings.edit().putBoolean(pluginKey,value).apply(); reloadPluginsFromSettings(); } public boolean isPluginEnabled(String pluginKey) { boolean enabledByDefault = PluginFactory.getPluginInfo(context, pluginKey).isEnabledByDefault(); - boolean enabled = settings.getBoolean(pluginKey, enabledByDefault); - return enabled; + return settings.getBoolean(pluginKey, enabledByDefault); } public void reloadPluginsFromSettings() { failedPlugins.clear(); HashMap> newPluginsByIncomingInterface = new HashMap<>(); for (String pluginKey : m_supportedPlugins) { PluginFactory.PluginInfo pluginInfo = PluginFactory.getPluginInfo(context, pluginKey); boolean pluginEnabled = false; boolean listenToUnpaired = pluginInfo.listenToUnpaired(); if ((isPaired() || listenToUnpaired) && isReachable()) { pluginEnabled = isPluginEnabled(pluginKey); } if (pluginEnabled) { boolean success = addPlugin(pluginKey); if (success) { for (String packageType : pluginInfo.getSupportedPackageTypes()) { packageType = hackToMakeRetrocompatiblePacketTypes(packageType); ArrayList plugins = newPluginsByIncomingInterface.get(packageType); if (plugins == null) plugins = new ArrayList<>(); plugins.add(pluginKey); newPluginsByIncomingInterface.put(packageType, plugins); } } } else { removePlugin(pluginKey); } } pluginsByIncomingInterface = newPluginsByIncomingInterface; onPluginsChanged(); } public void onPluginsChanged() { for (PluginsChangedListener listener : pluginsChangedListeners) { listener.onPluginsChanged(Device.this); } } public ConcurrentHashMap getLoadedPlugins() { return plugins; } public ConcurrentHashMap getFailedPlugins() { return failedPlugins; } public ConcurrentHashMap getPluginsWithoutPermissions() { return pluginsWithoutPermissions; } public ConcurrentHashMap getPluginsWithoutOptionalPermissions() { return pluginsWithoutOptionalPermissions; } public void addPluginsChangedListener(PluginsChangedListener listener) { pluginsChangedListeners.add(listener); } public void removePluginsChangedListener(PluginsChangedListener listener) { pluginsChangedListeners.remove(listener); } public void disconnect() { for(BaseLink link : links) { link.disconnect(); } } public boolean deviceShouldBeKeptAlive() { SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); if (preferences.contains(getDeviceId())) { //Log.e("DeviceShouldBeKeptAlive", "because it's a paired device"); return true; //Already paired } for(BaseLink l : links) { if (l.linkShouldBeKeptAlive()) { return true; } } return false; } public List getSupportedPlugins() { return m_supportedPlugins; } public void hackToMakeRetrocompatiblePacketTypes(NetworkPackage np) { if (protocolVersion >= 6) return; np.mType = np.getType().replace(".request",""); } public String hackToMakeRetrocompatiblePacketTypes(String type) { if (protocolVersion >= 6) return type; return type.replace(".request", ""); } } diff --git a/src/org/kde/kdeconnect/Helpers/DeviceHelper.java b/src/org/kde/kdeconnect/Helpers/DeviceHelper.java index e04e5dba..374e7b75 100644 --- a/src/org/kde/kdeconnect/Helpers/DeviceHelper.java +++ b/src/org/kde/kdeconnect/Helpers/DeviceHelper.java @@ -1,505 +1,504 @@ /* * Copyright 2014 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect.Helpers; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager; import android.provider.Settings; import android.util.Log; import java.util.HashMap; public class DeviceHelper { public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference"; //from https://github.com/meetup/android-device-names //Converted to java using: //cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}' | sed -e 's/\\ /_/g' private final static HashMap humanReadableNames = new HashMap<>(); static { humanReadableNames.put("5860E","Coolpad Quattro 4G"); humanReadableNames.put("831C","HTC One M8"); humanReadableNames.put("9920","Star Alps S9920"); humanReadableNames.put("A0001","OnePlus One"); humanReadableNames.put("A1-810","Acer Iconia A1-810"); humanReadableNames.put("ADR6300","HTC Droid Incredible"); humanReadableNames.put("ADR6330VW","HTC Rhyme"); humanReadableNames.put("ADR6350","HTC Droid Incredible 2"); humanReadableNames.put("ADR6400L","HTC Thunderbolt"); humanReadableNames.put("ADR6410LVW","HTC Droid Incredible 4G"); humanReadableNames.put("ADR6425LVW","HTC Rezound 4G"); humanReadableNames.put("ALCATEL_ONE_TOUCH_5035X","Alcatel One Touch X Pop"); humanReadableNames.put("ALCATEL_ONE_TOUCH_7041X","Alcatel One Touch Pop C7"); humanReadableNames.put("ASUS_T00J","Asus ZenFone 5"); humanReadableNames.put("ASUS_Transformer_Pad_TF300T","Asus Transformer Pad"); humanReadableNames.put("ASUS_Transformer_Pad_TF700T","Asus Transformer Pad"); humanReadableNames.put("Aquaris_E4.5","bq Aquaris E4.5"); humanReadableNames.put("C1905","Sony Xperia M"); humanReadableNames.put("C2105","Sony Xperia L"); humanReadableNames.put("C5155","Kyocera Rise"); humanReadableNames.put("C5170","Kyocera Hydro"); humanReadableNames.put("C5302","Xperia SP"); humanReadableNames.put("C5303","Sony Xperia SP"); humanReadableNames.put("C5306","Xperia SP"); humanReadableNames.put("C6603","Sony Xperia Z"); humanReadableNames.put("C6606","Sony Xperia Z"); humanReadableNames.put("C6833","Sony Xperia Z Ultra"); humanReadableNames.put("C6903","Sony Xperia Z1"); humanReadableNames.put("C6916","Sony Xperia Z1S"); humanReadableNames.put("CM990","Huawei Evolution III"); humanReadableNames.put("CUBOT_ONE","Cubot One"); humanReadableNames.put("D2005","Sony Xperia E1"); humanReadableNames.put("D2302","Xperia M2"); humanReadableNames.put("D2303","Sony Xperia M2"); humanReadableNames.put("D2305","Xperia M2"); humanReadableNames.put("D2306","Xperia M2"); humanReadableNames.put("D2316","Xperia M2"); humanReadableNames.put("D5503","Sony Xperia Z1"); humanReadableNames.put("D5803","Sony Xperia Z3 Compact"); humanReadableNames.put("D5833","Xperia Z3 Compact"); humanReadableNames.put("D6503","Sony Xperia Z2"); humanReadableNames.put("D6603","Sony Xperia Z3"); humanReadableNames.put("D6653","Sony Xperia Z3"); humanReadableNames.put("DROID2","Motorola Droid 2"); humanReadableNames.put("DROID2_GLOBAL","Motorola Droid 2 Global"); humanReadableNames.put("DROID3","Motorola Droid 3"); humanReadableNames.put("DROID4","Motorola Droid 4"); humanReadableNames.put("DROIDX","Motorola Droid X"); humanReadableNames.put("DROID_BIONIC","Motorola Droid Bionic"); humanReadableNames.put("DROID_Pro","Motorola Droid Pro"); humanReadableNames.put("DROID_RAZR","Motorola Droid Razr"); humanReadableNames.put("DROID_RAZR_HD","Motorola Droid Razr HD"); humanReadableNames.put("DROID_X2","Motorola Droid X2"); humanReadableNames.put("Desire_HD","HTC Desire HD"); humanReadableNames.put("Droid","Motorola Droid"); humanReadableNames.put("EVO","HTC Evo"); humanReadableNames.put("GT-I8160","Samsung Galaxy Ace 2"); humanReadableNames.put("GT-I8190","Samsung Galaxy S III Mini"); humanReadableNames.put("GT-I8190L","Samsung Galaxy S3 Mini"); humanReadableNames.put("GT-I8190N","Samsung Galaxy S III Mini"); humanReadableNames.put("GT-I8260","Samsung Galaxy Core"); humanReadableNames.put("GT-I8262","Samsung Galaxy Core"); humanReadableNames.put("GT-I8550L","Samsung Galaxy Win"); humanReadableNames.put("GT-I9000","Samsung Galaxy S"); humanReadableNames.put("GT-I9001","Samsung Galaxy S Plus"); humanReadableNames.put("GT-I9060","Samsung Galaxy Grand Neo"); humanReadableNames.put("GT-I9063T","Samsung Galaxy Grand Neo Duos"); humanReadableNames.put("GT-I9070","Samsung Galaxy S Advance"); humanReadableNames.put("GT-I9082","Samsung Galaxy Grand"); humanReadableNames.put("GT-I9100","Samsung Galaxy S II"); humanReadableNames.put("GT-I9100M","Samsung Galaxy S II"); humanReadableNames.put("GT-I9100P","Samsung Galaxy S II"); humanReadableNames.put("GT-I9100T","Samsung Galaxy S II"); humanReadableNames.put("GT-I9105P","Samsung Galaxy S2 Plus"); humanReadableNames.put("GT-I9190","Samsung Galaxy S4 Mini"); humanReadableNames.put("GT-I9192","Samsung Galaxy S4 Mini Duos"); humanReadableNames.put("GT-I9195","Samsung Galaxy S4 Mini"); humanReadableNames.put("GT-I9197","Galaxy S4 Mini"); humanReadableNames.put("GT-I9198","Galaxy S4 Mini"); humanReadableNames.put("GT-I9210","Galaxy S2"); humanReadableNames.put("GT-I9295","Samsung Galaxy S4 Active"); humanReadableNames.put("GT-I9300","Samsung Galaxy S III"); humanReadableNames.put("GT-I9300T","Samsung Galaxy S III"); humanReadableNames.put("GT-I9305","Samsung Galaxy S III"); humanReadableNames.put("GT-I9305T","Samsung Galaxy S III"); humanReadableNames.put("GT-I9500","Samsung Galaxy S4"); humanReadableNames.put("GT-I9505","Samsung Galaxy S4"); humanReadableNames.put("GT-I9506","Samsung Galaxy S4"); humanReadableNames.put("GT-I9507","Samsung Galaxy S4"); humanReadableNames.put("GT-N5110","Samsung Galaxy Note 8.0"); humanReadableNames.put("GT-N7000","Samsung Galaxy Note"); humanReadableNames.put("GT-N7100","Samsung Galaxy Note II"); humanReadableNames.put("GT-N7105","Samsung Galaxy Note II"); humanReadableNames.put("GT-N7105T","Samsung Galaxy Note II"); humanReadableNames.put("GT-N8000","Samsung Galaxy Note 10.1"); humanReadableNames.put("GT-N8010","Samsung Galaxy Note 10.1"); humanReadableNames.put("GT-N8013","Samsung Galaxy Note 10.1"); humanReadableNames.put("GT-P3100","Samsung Galaxy Tab 2"); humanReadableNames.put("GT-P3110","Samsung Galaxy Tab 2"); humanReadableNames.put("GT-P3113","Samsung Galaxy Tab 2 7.0"); humanReadableNames.put("GT-P5110","Samsung Galaxy Tab 2"); humanReadableNames.put("GT-P5113","Samsnung Galaxy Tab 2 10.1"); humanReadableNames.put("GT-P5210","Samsung Galaxy Tab 3 10.1"); humanReadableNames.put("GT-P7510","Samsung Galaxy Tab 10.1"); humanReadableNames.put("GT-S5301L","Samsung Galaxy Pocket Plus"); humanReadableNames.put("GT-S5360","Samsung Galaxy Y"); humanReadableNames.put("GT-S5570","Samsung Galaxy Mini"); humanReadableNames.put("GT-S5830","Samsung Galaxy Ace"); humanReadableNames.put("GT-S5830i","Samsung Galaxy Ace"); humanReadableNames.put("GT-S6310","Samsung Galaxy Young"); humanReadableNames.put("GT-S6310N","Samsung Galaxy Young"); humanReadableNames.put("GT-S6810P","Samsung Galaxy Fame"); humanReadableNames.put("GT-S7560M","Samsung Galaxy Ace II X"); humanReadableNames.put("GT-S7562","Samsung Galaxy S Duos"); humanReadableNames.put("GT-S7580","Samsung Galaxy Trend Plus"); humanReadableNames.put("Galaxy_Nexus","Samsung Galaxy Nexus"); humanReadableNames.put("HM_1SW","Xiaomi Redmi"); humanReadableNames.put("HTC6435LVW","HTC Droid DNA"); humanReadableNames.put("HTC6500LVW","HTC One"); humanReadableNames.put("HTC6525LVW","HTC One M8"); humanReadableNames.put("HTCEVODesign4G","HTC Evo Design 4G"); humanReadableNames.put("HTCEVOV4G","HTC Evo V 4G"); humanReadableNames.put("HTCONE","HTC One"); humanReadableNames.put("HTC_Desire_500","HTC Desire 500"); humanReadableNames.put("HTC_Desire_HD_A9191","HTC Desire HD"); humanReadableNames.put("HTC_One_mini","HTC One mini"); humanReadableNames.put("HTC_PH39100","HTC Vivid 4G"); humanReadableNames.put("HTC_PN071","HTC One"); humanReadableNames.put("HTC_Sensation_Z710e","HTC Sensation"); humanReadableNames.put("HTC_Sensation_4G","HTC Sensation"); humanReadableNames.put("HTC_VLE_U","HTC One S"); humanReadableNames.put("HUAWEI_G510-0251","Huawei Ascend G510"); humanReadableNames.put("HUAWEI_P6-U06","Huawei Ascend P6"); humanReadableNames.put("HUAWEI_Y300-0100","Huawei Ascend Y300"); humanReadableNames.put("ISW11SC","Galaxy S2"); humanReadableNames.put("KFJWA","Kindle Fire HD 8.9"); humanReadableNames.put("KFJWI","Kindle Fire HD 8.9"); humanReadableNames.put("KFOT","Kindle Fire"); humanReadableNames.put("KFTT","Kindle Fire HD 7"); humanReadableNames.put("L-01F","G2"); humanReadableNames.put("LG-C800","LG myTouch Q"); humanReadableNames.put("LG-D415","LG Optimus L90"); humanReadableNames.put("LG-D620","LG G2 Mini"); humanReadableNames.put("LG-D686","LG G Pro Lite Dual"); humanReadableNames.put("LG-D800","LG G2"); humanReadableNames.put("LG-D801","LG G2"); humanReadableNames.put("LG-D802","LG G2"); humanReadableNames.put("LG-D803","G2"); humanReadableNames.put("LG-D805","G2"); humanReadableNames.put("LG-D850","LG G3"); humanReadableNames.put("LG-D851","LG G3"); humanReadableNames.put("LG-D852","G3"); humanReadableNames.put("LG-D855","LG G3"); humanReadableNames.put("LG-E411g","LG Optimus L1 II"); humanReadableNames.put("LG-E425g","LG Optimus L3 II"); humanReadableNames.put("LG-E440g","LG Optimus L4 II"); humanReadableNames.put("LG-E460","LG Optimus L5 II"); humanReadableNames.put("LG-E610","LG Optimus L5"); humanReadableNames.put("LG-E612g","LG Optimus L5 Dual"); humanReadableNames.put("LG-E739","LG MyTouch e739"); humanReadableNames.put("LG-E970","LG Optimus G"); humanReadableNames.put("LG-E971","Optimus G"); humanReadableNames.put("LG-E980","LG Optimus G Pro"); humanReadableNames.put("LG-H815","G4"); humanReadableNames.put("LG-LG730","LG Venice"); humanReadableNames.put("LG-LS720","LG Optimus F3"); humanReadableNames.put("LG-LS840","LG Viper"); humanReadableNames.put("LG-LS970","LG Optimus G"); humanReadableNames.put("LG-LS980","LG G2"); humanReadableNames.put("LG-MS770","LG Motion 4G"); humanReadableNames.put("LG-MS910","LG Esteem"); humanReadableNames.put("LG-P509","LG Optimus T"); humanReadableNames.put("LG-P760","LG Optimus L9"); humanReadableNames.put("LG-P768","LG Optimus L9"); humanReadableNames.put("LG-P769","LG Optimus L9"); humanReadableNames.put("LG-P999","LG G2X P999"); humanReadableNames.put("LG-VM696","LG Optimus Elite"); humanReadableNames.put("LGL34C","LG Optimus Fuel"); humanReadableNames.put("LGL55C","LG LGL55C"); humanReadableNames.put("LGLS740","LG Volt"); humanReadableNames.put("LGLS990","LG G3"); humanReadableNames.put("LGMS323","LG Optimus L70"); humanReadableNames.put("LGMS500","LG Optimus F6"); humanReadableNames.put("LGMS769","LG Optimus L9"); humanReadableNames.put("LS670","LG Optimus S"); humanReadableNames.put("LT22i","Sony Xperia P"); humanReadableNames.put("LT25i","Sony Xperia V"); humanReadableNames.put("LT26i","Sony Xperia S"); humanReadableNames.put("LT30p","Sony Xperia T"); humanReadableNames.put("MB855","Motorola Photon 4G"); humanReadableNames.put("MB860","Motorola Atrix 4G"); humanReadableNames.put("MB865","Motorola Atrix 2"); humanReadableNames.put("MB886","Motorola Atrix HD"); humanReadableNames.put("ME173X","Asus MeMO Pad HD 7"); humanReadableNames.put("MI_3W","Xiaomi Mi 3"); humanReadableNames.put("MOTWX435KT","Motorola Triumph"); humanReadableNames.put("N3","Star NO.1 N3"); humanReadableNames.put("N860","ZTE Warp N860"); humanReadableNames.put("NEXUS_4","Nexus 4"); humanReadableNames.put("NEXUS_5","Nexus 5"); humanReadableNames.put("NEXUS_5X","Nexus 5X"); humanReadableNames.put("LG-D820","Nexus 5"); humanReadableNames.put("LG-D821","Nexus 5"); humanReadableNames.put("NEXUS_6","Nexus 6"); humanReadableNames.put("NEXUS_6P","Nexus 6P"); humanReadableNames.put("Nexus_10","Google Nexus 10"); humanReadableNames.put("Nexus_4","Google Nexus 4"); humanReadableNames.put("Nexus_7","Asus Nexus 7"); humanReadableNames.put("Nexus_S","Samsung Nexus S"); humanReadableNames.put("Nexus_S_4G","Samsung Nexus S 4G"); humanReadableNames.put("Orange_Daytona","Huawei Ascend G510"); humanReadableNames.put("PC36100","HTC Evo 4G"); humanReadableNames.put("PG06100","HTC EVO Shift 4G"); humanReadableNames.put("PG86100","HTC Evo 3D"); humanReadableNames.put("PH44100","HTC Evo Design 4G"); humanReadableNames.put("PantechP9070","Pantech Burst"); humanReadableNames.put("QMV7A","Verizon Ellipsis 7"); humanReadableNames.put("SAMSUNG-SGH-I317","Samsung Galaxy Note II"); humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S4"); humanReadableNames.put("SAMSUNG-SGH-I527","Samsung Galaxy Mega"); humanReadableNames.put("SAMSUNG-SGH-I537","Samsung Galaxy S4 Active"); humanReadableNames.put("SAMSUNG-SGH-I717","Samsung Galaxy Note"); humanReadableNames.put("SAMSUNG-SGH-I727","Samsung Skyrocket"); humanReadableNames.put("SAMSUNG-SGH-I747","Samsung Galaxy S III"); humanReadableNames.put("SAMSUNG-SGH-I777","Samsung Galaxy S II"); humanReadableNames.put("SAMSUNG-SGH-I897","Samsung Captivate"); humanReadableNames.put("SAMSUNG-SGH-I927","Samsung Captivate Glide"); humanReadableNames.put("SAMSUNG-SGH-I997","Samsung Infuse 4G"); humanReadableNames.put("SAMSUNG-SM-G730A","Samsung Galaxy S3 Mini"); humanReadableNames.put("SAMSUNG-SM-G870A","Samsung Galaxy S5 Active"); humanReadableNames.put("SAMSUNG-SM-G900A","Samsung Galaxy S5"); humanReadableNames.put("SAMSUNG-SM-G920A","Samsung Galaxy S6"); humanReadableNames.put("SAMSUNG-SM-N900A","Samsung Galaxy Note 3"); humanReadableNames.put("SAMSUNG-SM-N910A","Samsung Galaxy Note 4"); humanReadableNames.put("SC-02C","Galaxy S2"); humanReadableNames.put("SC-03E","Galaxy S3"); humanReadableNames.put("SC-04E","Galaxy S4"); humanReadableNames.put("SC-06D","Galaxy S3"); humanReadableNames.put("SCH-I200","Samsung Galaxy Stellar"); humanReadableNames.put("SCH-I337","Galaxy S4"); humanReadableNames.put("SCH-I405","Samsung Stratosphere"); humanReadableNames.put("SCH-I415","Samsung Galaxy Stratosphere II"); humanReadableNames.put("SCH-I435","Samsung Galaxy S4 Mini"); humanReadableNames.put("SCH-I500","Samsung Fascinate"); humanReadableNames.put("SCH-I510","Samsung Droid Charge"); humanReadableNames.put("SCH-I535","Samsung Galaxy S III"); humanReadableNames.put("SCH-I545","Samsung Galaxy S4"); humanReadableNames.put("SCH-I605","Samsung Galaxy Note II"); humanReadableNames.put("SCH-I800","Samsung Galaxy Tab 7.0"); humanReadableNames.put("SCH-I939","Galaxy S3"); humanReadableNames.put("SCH-I959","Galaxy S4"); humanReadableNames.put("SCH-J021","Galaxy S3"); humanReadableNames.put("SCH-R530C","Samsung Galaxy S3"); humanReadableNames.put("SCH-R530M","Samsung Galaxy S III"); humanReadableNames.put("SCH-R530U","Samsung Galaxy S III"); humanReadableNames.put("SCH-R720","Samsung Admire"); humanReadableNames.put("SCH-R760","Galaxy S2"); humanReadableNames.put("SCH-R970","Samsung Galaxy S4"); humanReadableNames.put("SCH-S720C","Samsung Proclaim"); humanReadableNames.put("SCH-S738C","Samsung Galaxy Centura"); humanReadableNames.put("SCH-S968C","Samsung Galaxy S III"); humanReadableNames.put("SCL21","Galaxy S3"); humanReadableNames.put("SGH-I257M","Samsung Galaxy S4 Mini"); humanReadableNames.put("SGH-I317M","Samsung Galaxy Note II"); humanReadableNames.put("SGH-I337M","Samsung Galaxy S4"); humanReadableNames.put("SGH-I727R","Samsung Galaxy S II"); humanReadableNames.put("SGH-I747M","Samsung Galaxy S III"); humanReadableNames.put("SGH-I757M","Galaxy S2"); humanReadableNames.put("SGH-I777M","Galaxy S2"); humanReadableNames.put("SGH-M919","Samsung Galaxy S4"); humanReadableNames.put("SGH-M919N","Samsung Galaxy S4"); humanReadableNames.put("SGH-N035","Galaxy S3"); humanReadableNames.put("SGH-N045","Galaxy S4"); humanReadableNames.put("SGH-N064","Galaxy S3"); humanReadableNames.put("SGH-T399","Samsung Galaxy Light"); humanReadableNames.put("SGH-T399N","Samsung Galaxy Light"); humanReadableNames.put("SGH-T599N","Samsung Galaxy Exhibit"); humanReadableNames.put("SGH-T679","Samsung Exhibit II"); humanReadableNames.put("SGH-T769","Samsung Galaxy S Blaze"); humanReadableNames.put("SGH-T889","Samsung Galaxy Note II"); humanReadableNames.put("SGH-T959","Samsung Galaxy S Vibrant"); humanReadableNames.put("SGH-T959V","Samsung Galaxy S 4G"); humanReadableNames.put("SGH-T989","Samsung Galaxy S II"); humanReadableNames.put("SGH-T989D","Samsung Galaxy S II"); humanReadableNames.put("SGH-T999","Samsung Galaxy S III"); humanReadableNames.put("SGH-T999L","Samsung Galaxy S III"); humanReadableNames.put("SGH-T999V","Samsung Galaxy S III"); humanReadableNames.put("SGP312","Sony Xperia Tablet Z"); humanReadableNames.put("SHV-E210K","Samsung Galaxy S3"); humanReadableNames.put("SHV-E210S","Samsung Galaxy S III"); humanReadableNames.put("SHV-E250K","Samsung Galaxy Note 2"); humanReadableNames.put("SHV-E250S","Samsung Galaxy Note II"); humanReadableNames.put("SHV-E300","Galaxy S4"); humanReadableNames.put("SHW-M250","Galaxy S2"); humanReadableNames.put("SM-G3815","Samsung Galaxy Express II"); humanReadableNames.put("SM-G386T","Samsung Galaxy Avant"); humanReadableNames.put("SM-G386T1","Samsung Galaxy Avant"); humanReadableNames.put("SM-G7102","Samsung Galaxy Grand II"); humanReadableNames.put("SM-G800F","Samsung Galaxy S5 Mini"); humanReadableNames.put("SM-G860P","Samsung Galaxy S5 Sport"); humanReadableNames.put("SM-G900F","Samsung Galaxy S5"); humanReadableNames.put("SM-G900H","Samsung Galaxy S5"); humanReadableNames.put("SM-G900I","Samsung Galaxy S5"); humanReadableNames.put("SM-G900P","Samsung Galaxy S5"); humanReadableNames.put("SM-G900R4","Galaxy S5"); humanReadableNames.put("SM-G900RZWAUSC","Galaxy S5"); humanReadableNames.put("SM-G900T","Samsung Galaxy S5"); humanReadableNames.put("SM-G900V","Samsung Galaxy S5"); humanReadableNames.put("SM-G900W8","Samsung Galaxy S5"); humanReadableNames.put("SM-G9200","Galaxy S6"); humanReadableNames.put("SM-G920F","Galaxy S6"); humanReadableNames.put("SM-G920I","Galaxy S6"); humanReadableNames.put("SM-G920P","Samsung Galaxy S6"); humanReadableNames.put("SM-G920R","Galaxy S6"); humanReadableNames.put("SM-G920T","Samsung Galaxy S6"); humanReadableNames.put("SM-G920V","Samsung Galaxy S6"); humanReadableNames.put("SM-G920W8","Galaxy S6"); humanReadableNames.put("SM-G9250","Galaxy S6 Edge"); humanReadableNames.put("SM-G925A","Galaxy S6 Edge"); humanReadableNames.put("SM-G925F","Galaxy S6 Edge"); humanReadableNames.put("SM-G925P","Galaxy S6 Edge"); humanReadableNames.put("SM-G925R","Galaxy S6 Edge"); humanReadableNames.put("SM-G925T","Galaxy S6 Edge"); humanReadableNames.put("SM-G925V","Galaxy S6 Edge"); humanReadableNames.put("SM-G925W8","Galaxy S6 Edge"); humanReadableNames.put("SM-N7505","Samsung Galaxy Note 3 Neo"); humanReadableNames.put("SM-N900","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N9005","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N9006","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N900P","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N900T","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N900V","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N900W8","Samsung Galaxy Note 3"); humanReadableNames.put("SM-N910C","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910F","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910G","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910P","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910T","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910V","Samsung Galaxy Note 4"); humanReadableNames.put("SM-N910W8","Samsung Galaxy Note 4"); humanReadableNames.put("SM-P600","Samsung Galaxy Note 10.1"); humanReadableNames.put("SM-T210R","Samsung Galaxy Tab 3 7.0"); humanReadableNames.put("SM-T217S","Samsung Galaxy Tab 3 7.0"); humanReadableNames.put("SM-T230NU","Samsung Galaxy Tab 4"); humanReadableNames.put("SM-T310","Samsung Galaxy Tab 3 8.0"); humanReadableNames.put("SM-T530NU","Samsung Galaxy Tab 4 10.1"); humanReadableNames.put("SM-T800","Samsung Galaxy Tab S 10.5"); humanReadableNames.put("SPH-D600","Samsung Conquer 4G"); humanReadableNames.put("SPH-D700","Samsung Epic 4G"); humanReadableNames.put("SPH-D710","Samsung Epic"); humanReadableNames.put("SPH-D710BST","Samsung Galaxy S II"); humanReadableNames.put("SPH-D710VMUB","Samsung Galaxy S II"); humanReadableNames.put("SPH-L300","Samsung Galaxy Victory"); humanReadableNames.put("SPH-L520","Samsung Galaxy S4 Mini"); humanReadableNames.put("SPH-L710","Samsung Galaxy S III"); humanReadableNames.put("SPH-L710T","Samsung Galaxy S III"); humanReadableNames.put("SPH-L720","Samsung Galaxy S4"); humanReadableNames.put("SPH-L720T","Samsung Galaxy S4"); humanReadableNames.put("SPH-L900","Samsung Galaxy Note II"); humanReadableNames.put("SPH-M820-BST","Samsung Galaxy Prevail"); humanReadableNames.put("SPH-M830","Samsung Galaxy Rush"); humanReadableNames.put("SPH-M840","Samsung Galaxy Prevail 2"); humanReadableNames.put("SPH-M930BST","Samsung Transform Ultra"); humanReadableNames.put("ST21i","Sony Xperia Tipo"); humanReadableNames.put("ST25i","Sony Xperia U"); humanReadableNames.put("ST26i","Sony Xperia J"); humanReadableNames.put("Transformer_Prime_TF201","Asus Transformer Prime"); humanReadableNames.put("Transformer_TF101","Asus Transformer"); humanReadableNames.put("VM670","LG Optimus V"); humanReadableNames.put("VS840_4G","LG Lucid 4G"); humanReadableNames.put("VS870_4G","LG Lucid 2"); humanReadableNames.put("VS910_4G","LG Revolution 4G"); humanReadableNames.put("VS920_4G","LG Spectrum 4G"); humanReadableNames.put("VS930_4G","LG Spectrum 2"); humanReadableNames.put("VS980_4G","LG G2"); humanReadableNames.put("VS985_4G","LG G3 4G"); humanReadableNames.put("XT1022","Motorola Moto E"); humanReadableNames.put("XT1028","Motorola Moto G"); humanReadableNames.put("XT1030","Motorola Droid Mini"); humanReadableNames.put("XT1031","Motorola Moto G"); humanReadableNames.put("XT1032","Motorola Moto G"); humanReadableNames.put("XT1033","Motorola Moto G"); humanReadableNames.put("XT1034","Motorola Moto G"); humanReadableNames.put("XT1039","Motorola Moto G"); humanReadableNames.put("XT1045","Motorola Moto G"); humanReadableNames.put("XT1049","Motorola Moto X"); humanReadableNames.put("XT1053","Motorola Moto X"); humanReadableNames.put("XT1056","Motorola Moto X"); humanReadableNames.put("XT1058","Motorola Moto X"); humanReadableNames.put("XT1060","Motorola Moto X"); humanReadableNames.put("XT1068","Motorola Moto G"); humanReadableNames.put("XT1080","Motorola Droid Ultra"); humanReadableNames.put("XT1095","Motorola Moto X"); humanReadableNames.put("XT1096","Motorola Moto X"); humanReadableNames.put("XT1097","Motorola Moto X"); humanReadableNames.put("XT1254","Motorola Droid Turbo"); humanReadableNames.put("XT897","Motorola Photo Q"); humanReadableNames.put("XT907","Motorola Droid Razr M"); humanReadableNames.put("Xoom","Motorola Xoom"); humanReadableNames.put("Z970","ZTE ZMax"); humanReadableNames.put("bq_Aquaris_5","bq Aquaris 5"); humanReadableNames.put("bq_Aquaris_5_HD","bq Aquaris 5 HD"); humanReadableNames.put("google_sdk","Android Emulator"); humanReadableNames.put("myTouch_4G_Slide","HTC myTouch 4G Slide"); } public static String getAndroidDeviceName() { String deviceName = null; try { String internalName = Build.MODEL.replace(' ', '_'); String dictName = humanReadableNames.get(internalName); if (dictName != null) { deviceName = dictName; } else { Log.w("getAndroidDeviceName", "Not found human readable name for device '" + internalName + "'"); if (Build.BRAND.equalsIgnoreCase("samsung")) { deviceName = "Samsung " + Build.MODEL; } else { deviceName = Build.BRAND; } } } catch (Exception e) { //Some phones might not define BRAND or MODEL, ignore exceptions Log.e("Exception", e.getMessage()); e.printStackTrace(); } if (deviceName == null || deviceName.isEmpty()) { return "Android"; //Could not find a name } else { return deviceName; } } public static boolean isTablet() { Configuration config = Resources.getSystem().getConfiguration(); //This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE - boolean isLarge = ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE); - return isLarge; + return ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE); } //It returns getAndroidDeviceName() if no user-defined name has been set with setDeviceName(). public static String getDeviceName(Context context){ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); // Could use prefrences.contains but would need to check for empty String anyway. String deviceName = preferences.getString(KEY_DEVICE_NAME_PREFERENCE, ""); if (deviceName.isEmpty()){ deviceName = DeviceHelper.getAndroidDeviceName(); Log.i("MainSettingsActivity", "New device name: " + deviceName); preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, deviceName).apply(); } return deviceName; } public static void setDeviceName(Context context, String name){ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply(); } public static String getDeviceId(Context context) { return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); } } diff --git a/src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.java b/src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.java index a4f1ac1e..96ee2a95 100644 --- a/src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.java +++ b/src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.java @@ -1,161 +1,159 @@ /* * Copyright 2015 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect.Helpers.SecurityHelpers; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Base64; import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.kde.kdeconnect.NetworkPackage; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; public class RsaHelper { public static void initialiseRsaKeys(Context context) { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); if (!settings.contains("publicKey") || !settings.contains("privateKey")) { KeyPair keyPair; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); keyPair = keyGen.genKeyPair(); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); Log.e("KDE/initializeRsaKeys", "Exception"); return; } byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded(); SharedPreferences.Editor edit = settings.edit(); - edit.putString("publicKey", Base64.encodeToString(publicKey, 0).trim()+"\n"); - edit.putString("privateKey",Base64.encodeToString(privateKey, 0)); + edit.putString("publicKey", Base64.encodeToString(publicKey, 0).trim() + "\n"); + edit.putString("privateKey", Base64.encodeToString(privateKey, 0)); edit.apply(); } } - public static PublicKey getPublicKey (Context context) throws Exception{ + public static PublicKey getPublicKey(Context context) throws Exception { try { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0); - PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes)); - return publicKey; - }catch (Exception e){ + + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes)); + } catch (Exception e) { throw e; } } - public static PublicKey getPublicKey(Context context, String deviceId) throws Exception{ + public static PublicKey getPublicKey(Context context, String deviceId) throws Exception { try { SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0); - PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes)); - return publicKey; + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes)); } catch (Exception e) { throw e; } } - public static PrivateKey getPrivateKey(Context context) throws Exception{ + public static PrivateKey getPrivateKey(Context context) throws Exception { try { SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context); byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0); - PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); - return privateKey; + return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); } catch (Exception e) { throw e; } } public static NetworkPackage encrypt(NetworkPackage np, PublicKey publicKey) throws GeneralSecurityException, JSONException { String serialized = np.serialize(); int chunkSize = 128; Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); JSONArray chunks = new JSONArray(); while (serialized.length() > 0) { if (serialized.length() < chunkSize) { chunkSize = serialized.length(); } String chunk = serialized.substring(0, chunkSize); serialized = serialized.substring(chunkSize); byte[] chunkBytes = chunk.getBytes(Charset.defaultCharset()); byte[] encryptedChunk; encryptedChunk = cipher.doFinal(chunkBytes); chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP)); } //Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks"); NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED); encrypted.set("data", chunks); encrypted.setPayload(np.getPayload(), np.getPayloadSize()); return encrypted; } - public static NetworkPackage decrypt(NetworkPackage np, PrivateKey privateKey) throws GeneralSecurityException, JSONException { + public static NetworkPackage decrypt(NetworkPackage np, PrivateKey privateKey) throws GeneralSecurityException, JSONException { JSONArray chunks = np.getJSONArray("data"); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); cipher.init(Cipher.DECRYPT_MODE, privateKey); String decryptedJson = ""; for (int i = 0; i < chunks.length(); i++) { byte[] encryptedChunk = Base64.decode(chunks.getString(i), Base64.NO_WRAP); String decryptedChunk = new String(cipher.doFinal(encryptedChunk)); decryptedJson += decryptedChunk; } NetworkPackage decrypted = NetworkPackage.unserialize(decryptedJson); decrypted.setPayload(np.getPayload(), np.getPayloadSize()); return decrypted; } } diff --git a/src/org/kde/kdeconnect/NetworkPackage.java b/src/org/kde/kdeconnect/NetworkPackage.java index f2975ac7..6b5a557b 100644 --- a/src/org/kde/kdeconnect/NetworkPackage.java +++ b/src/org/kde/kdeconnect/NetworkPackage.java @@ -1,242 +1,241 @@ /* * Copyright 2014 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect; import android.content.Context; import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Plugins.PluginFactory; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class NetworkPackage { public final static int ProtocolVersion = 7; public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity"; public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair"; public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted"; public static Set protocolPackageTypes = new HashSet() {{ add(PACKAGE_TYPE_IDENTITY); add(PACKAGE_TYPE_PAIR); add(PACKAGE_TYPE_ENCRYPTED); }}; private long mId; String mType; private JSONObject mBody; private InputStream mPayload; private JSONObject mPayloadTransferInfo; private long mPayloadSize; private NetworkPackage() { } public NetworkPackage(String type) { mId = System.currentTimeMillis(); mType = type; mBody = new JSONObject(); mPayload = null; mPayloadSize = 0; mPayloadTransferInfo = new JSONObject(); } public String getType() { return mType; } public long getId() { return mId; } //Most commons getters and setters defined for convenience public String getString(String key) { return mBody.optString(key,""); } public String getString(String key, String defaultValue) { return mBody.optString(key,defaultValue); } public void set(String key, String value) { if (value == null) return; try { mBody.put(key,value); } catch(Exception e) { } } public int getInt(String key) { return mBody.optInt(key,-1); } public int getInt(String key, int defaultValue) { return mBody.optInt(key,defaultValue); } public long getLong(String key) { return mBody.optLong(key,-1); } public long getLong(String key,long defaultValue) { return mBody.optLong(key,defaultValue); } public void set(String key, int value) { try { mBody.put(key,value); } catch(Exception e) { } } public boolean getBoolean(String key) { return mBody.optBoolean(key,false); } public boolean getBoolean(String key, boolean defaultValue) { return mBody.optBoolean(key,defaultValue); } public void set(String key, boolean value) { try { mBody.put(key,value); } catch(Exception e) { } } public double getDouble(String key) { return mBody.optDouble(key,Double.NaN); } public double getDouble(String key, double defaultValue) { return mBody.optDouble(key,defaultValue); } public void set(String key, double value) { try { mBody.put(key,value); } catch(Exception e) { } } public JSONArray getJSONArray(String key) { return mBody.optJSONArray(key); } public void set(String key, JSONArray value) { try { mBody.put(key,value); } catch(Exception e) { } } public Set getStringSet(String key) { JSONArray jsonArray = mBody.optJSONArray(key); if (jsonArray == null) return null; Set list = new HashSet<>(); int length = jsonArray.length(); for (int i = 0; i < length; i++) { try { String str = jsonArray.getString(i); list.add(str); } catch(Exception e) { } } return list; } public Set getStringSet(String key, Set defaultValue) { if (mBody.has(key)) return getStringSet(key); else return defaultValue; } public void set(String key, Set value) { try { JSONArray jsonArray = new JSONArray(); for(String str : value) { jsonArray.put(str); } mBody.put(key,jsonArray); } catch(Exception e) { } } public List getStringList(String key) { JSONArray jsonArray = mBody.optJSONArray(key); if (jsonArray == null) return null; List list = new ArrayList<>(); int length = jsonArray.length(); for (int i = 0; i < length; i++) { try { String str = jsonArray.getString(i); list.add(str); } catch(Exception e) { } } return list; } public List getStringList(String key, List defaultValue) { if (mBody.has(key)) return getStringList(key); else return defaultValue; } public void set(String key, List value) { try { JSONArray jsonArray = new JSONArray(); for(String str : value) { jsonArray.put(str); } mBody.put(key,jsonArray); } catch(Exception e) { } } public boolean has(String key) { return mBody.has(key); } public String serialize() throws JSONException { JSONObject jo = new JSONObject(); jo.put("id", mId); jo.put("type", mType); jo.put("body", mBody); if (hasPayload()) { jo.put("payloadSize", mPayloadSize); jo.put("payloadTransferInfo", mPayloadTransferInfo); } //QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format. - String json = jo.toString().replace("\\/","/")+"\n"; - return json; + return jo.toString().replace("\\/", "/") + "\n"; } static public NetworkPackage unserialize(String s) throws JSONException { NetworkPackage np = new NetworkPackage(); JSONObject jo = new JSONObject(s); np.mId = jo.getLong("id"); np.mType = jo.getString("type"); np.mBody = jo.getJSONObject("body"); if (jo.has("payloadSize")) { np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo"); np.mPayloadSize = jo.getLong("payloadSize"); } else { np.mPayloadTransferInfo = new JSONObject(); np.mPayloadSize = 0; } return np; } static public NetworkPackage createIdentityPackage(Context context) { NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY); String deviceId = DeviceHelper.getDeviceId(context); try { np.mBody.put("deviceId", deviceId); np.mBody.put("deviceName", DeviceHelper.getDeviceName(context)); np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion); np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone"); np.mBody.put("incomingCapabilities", new JSONArray(PluginFactory.getIncomingCapabilities(context))); np.mBody.put("outgoingCapabilities", new JSONArray(PluginFactory.getOutgoingCapabilities(context))); } catch (Exception e) { e.printStackTrace(); Log.e("NetworkPacakge","Exception on createIdentityPackage"); } return np; } public void setPayload(byte[] data) { setPayload(new ByteArrayInputStream(data), data.length); } public void setPayload(InputStream stream, long size) { mPayload = stream; mPayloadSize = size; } /*public void setPayload(InputStream stream) { setPayload(stream, -1); }*/ public InputStream getPayload() { return mPayload; } public long getPayloadSize() { return mPayloadSize; } public boolean hasPayload() { return (mPayload != null); } public boolean hasPayloadTransferInfo() { return (mPayloadTransferInfo.length() > 0); } public JSONObject getPayloadTransferInfo() { return mPayloadTransferInfo; } public void setPayloadTransferInfo(JSONObject payloadTransferInfo) { mPayloadTransferInfo = payloadTransferInfo; } } diff --git a/src/org/kde/kdeconnect/Plugins/BatteryPlugin/BatteryPlugin.java b/src/org/kde/kdeconnect/Plugins/BatteryPlugin/BatteryPlugin.java index 9ea1f776..ee0f8fe3 100644 --- a/src/org/kde/kdeconnect/Plugins/BatteryPlugin/BatteryPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/BatteryPlugin/BatteryPlugin.java @@ -1,124 +1,122 @@ /* * Copyright 2014 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect.Plugins.BatteryPlugin; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect_tp.R; public class BatteryPlugin extends Plugin { public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery"; public final static String PACKAGE_TYPE_BATTERY_REQUEST = "kdeconnect.battery.request"; // keep these fields in sync with kdeconnect-kded:BatteryPlugin.h:ThresholdBatteryEvent private static final int THRESHOLD_EVENT_NONE= 0; private static final int THRESHOLD_EVENT_BATTERY_LOW = 1; private NetworkPackage batteryInfo = new NetworkPackage(PACKAGE_TYPE_BATTERY); @Override public String getDisplayName() { return context.getResources().getString(R.string.pref_plugin_battery); } @Override public String getDescription() { return context.getResources().getString(R.string.pref_plugin_battery_desc); } private final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent batteryIntent) { int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); int plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); int currentCharge = (level == -1)? batteryInfo.getInt("currentCharge") : level*100 / scale; boolean isCharging = (plugged == -1)? batteryInfo.getBoolean("isCharging") : (0 != plugged); boolean lowBattery = Intent.ACTION_BATTERY_LOW.equals(batteryIntent.getAction()); int thresholdEvent = lowBattery? THRESHOLD_EVENT_BATTERY_LOW : THRESHOLD_EVENT_NONE; if (isCharging == batteryInfo.getBoolean("isCharging") && currentCharge == batteryInfo.getInt("currentCharge") && thresholdEvent == batteryInfo.getInt("thresholdEvent") ) { //Do not send again if nothing has changed } else { batteryInfo.set("currentCharge", currentCharge); batteryInfo.set("isCharging", isCharging); batteryInfo.set("thresholdEvent", thresholdEvent); device.sendPackage(batteryInfo); } } }; @Override public boolean onCreate() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); intentFilter.addAction(Intent.ACTION_BATTERY_LOW); context.registerReceiver(receiver, intentFilter); return true; } @Override public void onDestroy() { //It's okay to call this only once, even though we registered it for two filters context.unregisterReceiver(receiver); } @Override public boolean onPackageReceived(NetworkPackage np) { if (np.getBoolean("request")) { device.sendPackage(batteryInfo); } return true; } @Override public String[] getSupportedPackageTypes() { - String[] packetTypes = {PACKAGE_TYPE_BATTERY_REQUEST}; - return packetTypes; + return new String[]{PACKAGE_TYPE_BATTERY_REQUEST}; } @Override public String[] getOutgoingPackageTypes() { - String[] packetTypes = {PACKAGE_TYPE_BATTERY}; - return packetTypes; + return new String[]{PACKAGE_TYPE_BATTERY}; } } diff --git a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java index 97551e18..5bcb2abc 100644 --- a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java @@ -1,89 +1,87 @@ /* * Copyright 2014 Albert Vaca Cintora * * 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 . */ package org.kde.kdeconnect.Plugins.ClibpoardPlugin; import android.os.Build; import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect_tp.R; public class ClipboardPlugin extends Plugin { public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard"; @Override public String getDisplayName() { return context.getResources().getString(R.string.pref_plugin_clipboard); } @Override public String getDescription() { return context.getResources().getString(R.string.pref_plugin_clipboard_desc); } @Override public boolean isEnabledByDefault() { //Disabled by default due to just one direction sync(incoming clipboard change) in early version of android. return (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB); } @Override public boolean onPackageReceived(NetworkPackage np) { String content = np.getString("content"); ClipboardListener.instance(context).setText(content); return true; } private ClipboardListener.ClipboardObserver observer = new ClipboardListener.ClipboardObserver() { @Override public void clipboardChanged(String content) { NetworkPackage np = new NetworkPackage(ClipboardPlugin.PACKAGE_TYPE_CLIPBOARD); np.set("content", content); device.sendPackage(np); } }; @Override public boolean onCreate() { ClipboardListener.instance(context).registerObserver(observer); return true; } @Override public void onDestroy() { ClipboardListener.instance(context).removeObserver(observer); } @Override public String[] getSupportedPackageTypes() { - String[] packetTypes = {PACKAGE_TYPE_CLIPBOARD}; - return packetTypes; + return new String[]{PACKAGE_TYPE_CLIPBOARD}; } @Override public String[] getOutgoingPackageTypes() { - String[] packetTypes = {PACKAGE_TYPE_CLIPBOARD}; - return packetTypes; + return new String[]{PACKAGE_TYPE_CLIPBOARD}; } } diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SftpPlugin.java b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SftpPlugin.java index aaa3211f..0ec83789 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SftpPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SftpPlugin.java @@ -1,150 +1,149 @@ /* * Copyright 2014 Samoilenko Yuri * * 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 . */ package org.kde.kdeconnect.Plugins.SftpPlugin; import android.Manifest; import android.os.Environment; import org.kde.kdeconnect.Helpers.StorageHelper; import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect_tp.R; import java.io.File; import java.util.ArrayList; import java.util.List; public class SftpPlugin extends Plugin { public final static String PACKAGE_TYPE_SFTP = "kdeconnect.sftp"; public final static String PACKAGE_TYPE_SFTP_REQUEST = "kdeconnect.sftp.request"; private static final SimpleSftpServer server = new SimpleSftpServer(); private int sftpPermissionExplanation = R.string.sftp_permission_explanation; @Override public String getDisplayName() { return context.getResources().getString(R.string.pref_plugin_sftp); } @Override public String getDescription() { return context.getResources().getString(R.string.pref_plugin_sftp_desc); } @Override public boolean onCreate() { server.init(context, device); permissionExplanation = sftpPermissionExplanation; return true; } @Override public void onDestroy() { server.stop(); } @Override public boolean onPackageReceived(NetworkPackage np) { if (np.getBoolean("startBrowsing")) { if (server.start()) { NetworkPackage np2 = new NetworkPackage(PACKAGE_TYPE_SFTP); np2.set("ip", server.getLocalIpAddress()); np2.set("port", server.getPort()); np2.set("user", SimpleSftpServer.USER); np2.set("password", server.getPassword()); //Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath()); List storageList = StorageHelper.getStorageList(); ArrayList paths = new ArrayList<>(); ArrayList pathNames = new ArrayList<>(); for (StorageHelper.StorageInfo storage : storageList) { paths.add(storage.path); StringBuilder res = new StringBuilder(); if (storageList.size() > 1) { if (!storage.removable) { res.append(context.getString(R.string.sftp_internal_storage)); } else if (storage.number > 1) { res.append(context.getString(R.string.sftp_sdcard_num, storage.number)); } else { res.append(context.getString(R.string.sftp_sdcard)); } } else { res.append(context.getString(R.string.sftp_all_files)); } String pathName = res.toString(); if (storage.readonly) { res.append(" "); res.append(context.getString(R.string.sftp_readonly)); } pathNames.add(res.toString()); //Shortcut for users that only want to browse camera pictures String dcim = storage.path + "/DCIM/Camera"; if (new File(dcim).exists()) { paths.add(dcim); if (storageList.size() > 1) { pathNames.add(context.getString(R.string.sftp_camera) + "(" + pathName + ")"); } else { pathNames.add(context.getString(R.string.sftp_camera)); } } } if (paths.size() > 0) { np2.set("multiPaths", paths); np2.set("pathNames", pathNames); } device.sendPackage(np2); return true; } } return false; } @Override public String[] getRequiredPermissions() { - String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE}; - return perms; + return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}; } @Override public String[] getSupportedPackageTypes() { return new String[]{PACKAGE_TYPE_SFTP_REQUEST}; } @Override public String[] getOutgoingPackageTypes() { return new String[]{PACKAGE_TYPE_SFTP}; } }