2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-05 00:25:09 +00:00

Compare commits

...

60 Commits

Author SHA1 Message Date
Albert Vaca Cintora
a2ef4659d9 Release 1.27.1 2023-07-29 12:00:27 +02:00
Albert Vaca Cintora
e769a512eb Fix crash in loadRememberedDevicesFromSettings 2023-07-29 11:38:46 +02:00
Albert Vaca Cintora
4da0599f0e Fix crash on start in API 31+
Sometimes we can't start the foreground service at boot for some reason.
I have a couple untested theories: either the phone is slow at boot and
we don't get to call startForeground in time, or the user has never
started the app before and we are not allowed to start it or to create a
notification.
2023-07-29 11:28:50 +02:00
Albert Vaca Cintora
b00a0771fd Fix NPE when mediaSession was set to null in a separate thread 2023-07-29 11:08:23 +02:00
l10n daemon script
9d4e4cda3a GIT_SILENT made messages (after extraction) 2023-07-29 00:45:28 +00:00
l10n daemon script
06f8d47b00 GIT_SILENT Sync po/docbooks with svn 2023-07-27 01:46:36 +00:00
l10n daemon script
e46a413630 GIT_SILENT Add new file (after extraction) 2023-07-27 00:46:15 +00:00
l10n daemon script
d40e3122cc GIT_SILENT made messages (after extraction) 2023-07-27 00:46:10 +00:00
l10n daemon script
d84ac8b5f0 GIT_SILENT made messages (after extraction) 2023-07-26 00:46:30 +00:00
l10n daemon script
7128c9e298 GIT_SILENT Sync po/docbooks with svn 2023-07-24 09:02:25 +00:00
l10n daemon script
affc019c9c GIT_SILENT made messages (after extraction) 2023-07-24 07:33:05 +00:00
l10n daemon script
8ffd1f5b20 GIT_SILENT made messages (after extraction) 2023-07-21 00:49:36 +00:00
l10n daemon script
ca2a622de5 GIT_SILENT made messages (after extraction) 2023-07-20 00:48:35 +00:00
l10n daemon script
40fc12797a GIT_SILENT Sync po/docbooks with svn 2023-07-19 02:36:08 +00:00
Albert Vaca Cintora
f825a37c8e Better explanation for the mouse receiver permissions 2023-07-18 11:14:15 +02:00
Albert Vaca Cintora
7c52260efe Remove request packets from Battery and Connectivity Report plugins
We already send updates right after connecting and once there's a
change, so this was redundant.
2023-07-17 14:07:55 +02:00
Albert Vaca Cintora
49e2b2d9a2 Release 1.27.0 2023-07-16 18:15:18 +02:00
Albert Vaca Cintora
96fc4016ff Add missing import 2023-07-16 17:35:02 +02:00
Albert Vaca Cintora
1706a4c10d Revert "Disable MouseReceiver Plugin so we don't need accessibility API"
This reverts commit 32d6a346ab.
2023-07-16 17:29:25 +02:00
Albert Vaca Cintora
13b5fc4550 Revert "Remove REQUEST_INSTALL_PACKAGES permission"
This reverts commit b5d9293103.
2023-07-16 10:57:17 +02:00
Albert Vaca Cintora
11ffab5502 Revert "Enable the loopback link on Google test devices"
This reverts commit f26bd54be5.
2023-07-16 10:49:04 +02:00
l10n daemon script
377f91bd23 GIT_SILENT Sync po/docbooks with svn 2023-07-16 01:57:34 +00:00
l10n daemon script
8e078e608a GIT_SILENT made messages (after extraction) 2023-07-16 00:48:17 +00:00
l10n daemon script
12d08a17c2 GIT_SILENT Sync po/docbooks with svn 2023-07-14 02:00:18 +00:00
l10n daemon script
4b6ad1cdaa GIT_SILENT Add new file (after extraction) 2023-07-14 00:47:21 +00:00
l10n daemon script
1fcfd4b879 GIT_SILENT made messages (after extraction) 2023-07-12 00:47:11 +00:00
Albert Vaca Cintora
541ee4d3cc Remove Esperanto localization from fastlane
Esperanto isn't supported by Google
2023-07-08 00:47:45 +02:00
Albert Vaca Cintora
af922a4277 Release 1.26.4 2023-07-08 00:41:42 +02:00
Albert Vaca Cintora
48cccf3fca Catch SecurityException when calling getActiveNotifications
It's okay to ignore this since we were only using it to check if Spotify
was running.
2023-07-08 00:39:13 +02:00
Albert Vaca Cintora
1c3389efa0 Do not listen for PACKAGE_REPLACED broadcasts
We already listen for MY_PACKAGE_REPLACED which is sent since API 12
2023-07-08 00:29:09 +02:00
Albert Vaca Cintora
f54ebdb39b Trying random things to try to fix ForegroundServiceStartNotAllowedException 2023-07-08 00:24:17 +02:00
Albert Vaca Cintora
ea80000a4e Add a null check as mentioned in !377 2023-07-08 00:03:12 +02:00
Albert Vaca Cintora
d9db7e4ad9 Catch TextParsingException (which wrapps SocketException)
Could happen if the download of the CSV file failed or got interrupted
2023-07-08 00:00:06 +02:00
Łukasz Patron
1273cb641a Unbreak Mpris media notifications
Because notificationDevice is only set on updateCurrentPlayer(), we
ought to call it before checking if the device is null.
2023-07-07 20:48:43 +02:00
l10n daemon script
cafbfcaee8 GIT_SILENT Sync po/docbooks with svn 2023-07-07 01:53:59 +00:00
l10n daemon script
ac4c997efd GIT_SILENT made messages (after extraction) 2023-07-07 00:50:20 +00:00
l10n daemon script
d05feaa6d0 GIT_SILENT Sync po/docbooks with svn 2023-07-06 02:28:00 +00:00
l10n daemon script
4967cc7a81 GIT_SILENT Add new file (after extraction) 2023-07-06 00:58:05 +00:00
l10n daemon script
59cc3f2d4a GIT_SILENT made messages (after extraction) 2023-07-06 00:57:50 +00:00
l10n daemon script
0b9880d9b8 GIT_SILENT Sync po/docbooks with svn 2023-07-04 02:31:15 +00:00
Albert Vaca Cintora
90f89c653d Try to fix NPE in removeOnSubscriptionsChangedListener 2023-07-02 23:40:18 +02:00
Albert Vaca Cintora
e53338c70e Release 1.26.3 2023-07-02 23:34:46 +02:00
Albert Vaca Cintora
e641ff5a0a Don't call put if we are going to call remove right after
Note we still want the remove in case the plugin already was loaded
2023-07-02 23:26:56 +02:00
Albert Vaca Cintora
2ebaf6ae5b Ignore exceptions in pairingSuccessful callbacks
Bubbling up the exception to the PairingHandler makes the device become
unpaired, and we only want this if the error happens persisting device info
2023-07-02 23:01:05 +02:00
Albert Vaca Cintora
dd89463d75 Add logging to help debug a crash restoring saved devices 2023-07-02 22:57:40 +02:00
Albert Vaca Cintora
7194b308cb Fix NPE 2023-07-02 20:46:11 +02:00
Albert Vaca Cintora
328b708083 Fix NPE by checking if there's a Device 2023-07-02 10:27:52 +02:00
l10n daemon script
6404b86373 GIT_SILENT Sync po/docbooks with svn 2023-07-01 01:53:29 +00:00
l10n daemon script
3263b37c8a GIT_SILENT Sync po/docbooks with svn 2023-06-30 01:53:09 +00:00
Albert Vaca Cintora
a31476951a Release 1.26.2 2023-06-29 10:52:37 +02:00
Albert Vaca Cintora
1d105bbb3d Re-add nowPlaying for backwards compat with GSConnect 50 2023-06-29 10:48:02 +02:00
l10n daemon script
3154eef6a2 GIT_SILENT Sync po/docbooks with svn 2023-06-29 01:51:01 +00:00
l10n daemon script
97a0389d04 GIT_SILENT made messages (after extraction) 2023-06-29 00:46:34 +00:00
Albert Vaca Cintora
8c1603f6e4 Add DeviceInfo class
`DeviceInfo` contains all the properties we need to instantiate a `Device`:
id, name, type, cert, capabilities and protocol version. Before, we had a mix
of passing those around as arguments or passing identity packets (ie: json).
This simplifies the `Device` class quite a bit.

Now, `BaseLink` subclasses need to implement the `getDeviceInfo()` interface
that returns a `DeviceInfo`, which is what we will pass around and eventually
use to instantiate a `Device`.

This means that identity packets are an implementation detail of the
`LanLinkProvider` and that other implementations could get the `DeviceInfo`
in a different way.

In a future, we can add a mechanism for links to notify when their `DeviceInfo` changed.
This will allow us to better support device renames (which now are implemented by 
reconnecting to the device and don't always work) or sending the capabilities fields
later in the case of MacOS/iOS where we can't send them in a UDP packet due to the
size limit of 1500 bytes.
2023-06-27 11:14:36 +00:00
l10n daemon script
310e61b570 GIT_SILENT Sync po/docbooks with svn 2023-06-27 01:58:07 +00:00
l10n daemon script
bfa4d05e0d GIT_SILENT made messages (after extraction) 2023-06-27 00:47:27 +00:00
Albert Vaca Cintora
6568bb486c Synchronize access to fetchUrlList
Otherwise we could get ArrayIndexOutOfBoundsException
2023-06-27 02:34:00 +02:00
Albert Vaca Cintora
a46fa23419 Copy the ref to MpriMprisPlayer since the field can become null
Create a local copy of the reference, otherwise other fields can set the
field to null while the function is running and cause a NPE.
2023-06-27 02:26:10 +02:00
Krut Patel
a29aeaad92 mpris-receiver: Deregister callbacks when clearing players
## Summary
### Main issue
In essence, clearing `players` field inside `onActiveSessionsChanged` is not enough - we need to also deregister the `MprisReceiverCallback` from `MediaController` to ensure the `MprisReceiverPlayer` is truly destroyed.

### Details
* Spotify has a bad habit of causing lots of calls to `onActiveSessionsChanged` on song change. In each call, we clear the existing players and create new instances of `MprisReceiverPlayer` and `MprisReceiverCallback` to track media changes.
* Each `MprisReceiverCallback` instance gets registered with `MediaController` inside `createPlayer`.
* Inside `onActiveSessionsChanged`, if the underlying `MediaController` object remained alive, we would now end up with _two_ instances of `MprisReceiverCallback` that internally reference the same `MprisReceiverPlayer` object.

### Fix
Deregister the callbacks inside `onActiveSessionsChanged` before clearing `players` hashmap.

## Test Plan
* Add log line `Log.d(TAG, "sending Metadata");` just above `device.sendPacket(np);` inside `sendMetadata` of `MprisReceiverPlugin`.
* Start playing a song on spotify, and after a few secs, hit Next.\
* Monitor our logcat.

### Before:
<details><summary>Relevant logcat after Next was pressed</summary>

```txt
2023-06-25 18:56:57.395 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.403 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.413 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.422 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.436 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.454 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.484 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.499 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.524 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.544 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.554 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.562 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.569 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.579 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.592 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.603 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.613 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.629 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.641 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.653 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.666 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.671 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.679 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.686 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.694 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.699 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.704 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.713 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.719 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.724 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.730 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.743 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.750 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.759 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.770 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.785 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.793 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.799 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.808 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.812 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.819 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.831 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.838 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.844 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.849 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.855 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.861 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.866 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.872 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.877 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.884 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.892 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.898 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.903 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.907 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.913 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.917 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.921 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.928 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.934 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.938 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.944 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.949 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.954 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.959 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.966 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.971 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.976 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.983 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.990 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:57.995 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.000 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.007 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.013 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.021 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.026 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.032 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.041 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.047 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.055 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.062 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.067 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.072 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.078 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.083 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.088 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.096 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.106 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.114 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.120 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.127 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.135 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.143 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.149 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.157 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.164 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.174 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.180 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.186 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.195 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.201 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.209 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.216 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.223 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.231 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.238 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.243 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.251 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.262 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.269 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.275 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.281 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.287 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.294 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.302 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.310 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.319 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.327 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.337 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.345 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.350 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.357 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.363 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.369 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.374 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.382 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.389 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.398 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.404 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.411 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.419 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.427 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.434 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.441 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.449 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.453 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.458 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.464 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.471 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.475 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.483 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.490 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.497 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.502 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.510 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.519 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.532 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.536 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.543 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.552 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.560 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.567 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.574 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.580 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.587 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.592 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.599 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.606 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.613 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.620 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.626 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.633 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.642 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.652 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.659 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.667 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.675 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.683 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.690 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.699 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.706 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.712 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.718 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.726 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.734 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.742 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.748 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.753 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.759 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.765 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.774 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.781 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.787 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.794 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.801 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.806 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.811 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.817 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.823 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.831 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.839 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.847 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.854 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.862 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.870 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.875 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.879 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.886 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.892 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.898 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.904 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.911 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.919 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.927 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.934 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.939 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.945 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.953 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.959 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.969 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.979 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.986 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:58.993 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.001 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.007 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.014 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.021 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.027 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.034 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.040 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.046 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.053 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.060 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.067 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.075 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.083 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.092 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.099 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.107 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.114 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.121 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.129 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.136 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.142 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:56:59.226 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.228 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.229 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.231 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.233 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.234 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.234 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.235 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.236 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.236 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.237 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.237 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.238 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.238 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.239 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:56:59.239 31955-31955 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
```

</details>

### After:
<details><summary>Relevant logcat after Next was pressed</summary>

```txt
2023-06-25 18:54:57.412 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.534 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.547 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.558 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.567 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.579 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.590 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.595 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.603 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendMetadata
2023-06-25 18:54:57.655 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.656 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.667 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.678 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.680 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.683 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.701 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.711 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.714 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.716 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.726 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.729 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.739 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.742 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.743 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
2023-06-25 18:54:57.746 30742-30742 MprisReceiver           org.kde.kdeconnect_tp                D  sendPlayerList
```
</details>


## Future work
There are still some redundant calls to `sendMetadata`. These are coming from multiple calls to `onPlaybackStateChanged` from Spotify. To reduce these, we'd need to add a debouncer.

Another thing- we can see that `sendPlayerList` packet is also being sent quite a lot.
1. We probably need to be smarter about creating new instances of `MprisReceiverPlayer` inside `onActiveSessionsChanged` instead of the nuke-all-and-rebuild approach we have today.
2. Maybe we can implement a debouncer inside `onActiveSessionsChanged` to avoid sending too many packets.
2023-06-25 15:20:11 +00:00
l10n daemon script
79744dc17b GIT_SILENT made messages (after extraction) 2023-06-25 00:51:53 +00:00
88 changed files with 930 additions and 460 deletions

View File

@@ -9,8 +9,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.kde.kdeconnect_tp"
android:versionCode="12601"
android:versionName="1.26.1">
android:versionCode="12701"
android:versionName="1.27.1">
<uses-feature
android:name="android.hardware.telephony"
@@ -38,6 +38,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -138,14 +139,6 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:host="kdeconnect"
android:path="/"
android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
@@ -366,7 +359,6 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
<!--
<service
android:name="org.kde.kdeconnect.Plugins.MouseReceiverPlugin.MouseReceiverService"
android:exported="true"
@@ -378,7 +370,6 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
android:name="android.accessibilityservice"
android:resource="@xml/mouse_receiver_service" />
</service>
-->
<activity
android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationFilterActivity"

View File

@@ -12,7 +12,7 @@ function import_po_dirs # First parameter will be a path that will be a director
{
podir=$1
# Some languages don't exist in Google Play or have different codes
declare -a to_delete=( "bs" "ca@valencia" "sr@ijekavian" "sr@ijekavianlatin" "sr@latin" "ia" "tg" )
declare -a to_delete=( "bs" "ca@valencia" "sr@ijekavian" "sr@ijekavianlatin" "sr@latin" "ia" "eo" "tg" )
for lang in "${to_delete[@]}"; do
if [ -d $podir/$lang ]; then
rm $podir/$lang/*

View File

@@ -0,0 +1,12 @@
1.26.2:
* Fixed several bugs and crashes related to media controls.
1.26.1:
* Fix infinite loop that would cause high CPU usage.
1.26.0:
* Allow having different widgets for diferent devices.
* Add stats about network packets sent and received.
* Add the option to cancel a pairing request after sending it.
* Fix device name set initially not being human-friendly.
* Rewrite some of the internals to improve performance.

View File

@@ -0,0 +1,12 @@
1.26.3:
* Fixed several bugs and crashes related to media controls.
1.26.1:
* Fix infinite loop that would cause high CPU usage.
1.26.0:
* Allow having different widgets for diferent devices.
* Add stats about network packets sent and received.
* Add the option to cancel a pairing request after sending it.
* Fix device name set initially not being human-friendly.
* Rewrite some of the internals to improve performance.

View File

@@ -0,0 +1,12 @@
1.26.4:
* Fixed several bugs and crashes related to media controls.
1.26.1:
* Fix infinite loop that would cause high CPU usage.
1.26.0:
* Allow having different widgets for diferent devices.
* Add stats about network packets sent and received.
* Add the option to cancel a pairing request after sending it.
* Fix device name set initially not being human-friendly.
* Rewrite some of the internals to improve performance.

View File

@@ -0,0 +1,9 @@
1.27:
* Added back the mouse receiver plugin.
1.26:
* Allow having different widgets for diferent devices.
* Add stats about network packets sent and received.
* Add the option to cancel a pairing request after sending it.
* Fix device name set initially not being human-friendly.
* Rewrite some of the internals to improve performance.

View File

@@ -0,0 +1,12 @@
1.27.1:
* Fixed crashes.
1.27:
* Added back the mouse receiver plugin.
1.26:
* Allow having different widgets for diferent devices.
* Add stats about network packets sent and received.
* Add the option to cancel a pairing request after sending it.
* Fix device name set initially not being human-friendly.
* Rewrite some of the internals to improve performance.

View File

@@ -0,0 +1,14 @@
KDE Connect tarjoaa ominaisuudet työnvuosi eheyttämiseksi laitteiden kesken:
Jaettu leikepöytä: kopioi ja liitä laitteelta toiselle.
Jaa tiedostoja ja verkko-osoitteita tietokoneeseesi mistä sovelluksesta vain.
Saa ilmoitukset saapuvista puheluista ja tekstiviesteistä tietokoneellesi.
Näyttönäppäimistö: käytä puhelimen näyttöä tietokoneesi osoitinlaitteena.
Ilmoitusten tahdistus: lue Android-ilmoituksesi työpöydältä.
Multimedian etähallinta: käytä puhelinta Linux-mediasoitinten kaukosäätimenä.
Langaton verkkoyhteys: USB-johtoa tai Bluetoothia ei tarvita.
Päästä päähän -TLS-salaus: tietosi ovat turvassa.
Huomaa, että sovelluksen toimimiseksi KDE Connect tulee asentaa tietokoneeseen ja pitää ajan tasalla Android-version kanssa, jotta kaikki ominaisuudet toimisivat.
Sovellus on avoimen lähdekoodin projekti ja on olemassa sitä avustaneiden ihmisten ansiosta. Lähdekoodin saat noudettua kotisivulta.

View File

@@ -0,0 +1 @@
KDE Connect eheyttää älypuhelimen ja tietokoneen

View File

@@ -0,0 +1 @@
KDE Connect

View File

@@ -1 +1 @@
KDE Connect integrates your smartphone and computer
KDE Connect intègre votre téléphone et votre ordinateur.

View File

@@ -0,0 +1,14 @@
O KDE Connect fornece um conjunto de recursos para integrar seu fluxo de trabalho entre dispositivos:
- Área de transferência compartilhada: copie e cole entre seus dispositivos.
- Compartilhe arquivos e URLs em seu computador a partir de qualquer app.
- Receba notificações de chamadas recebidas e mensagens SMS no seu PC.
- Touchpad virtual: use a tela do telefone como touchpad do computador.
- Sincronização de notificações: leia as notificações do seu Android na área de trabalho.
- Controle remoto multimídia: use seu telefone como controle remoto para reprodutores de mídia Linux.
- Conexão Wi-Fi: sem necessidade de cabos USB ou bluetooth.
- Criptografia TLS de ponta a ponta: suas informações estão seguras.
Observe que você precisará instalar o KDE Connect no seu computador para que este aplicativo funcione e mantenha a versão para desktop atualizada com a versão do Android para que os recursos mais recentes funcionem.
Este aplicativo faz parte de um projeto de código aberto e existe graças a todas as pessoas que contribuíram para ele. Visite o site para obter o código-fonte.

View File

@@ -0,0 +1 @@
O KDE Connect integra seu celular e computador

View File

@@ -0,0 +1 @@
KDE Connect

View File

@@ -0,0 +1,61 @@
# translation of kdeconnect-android-store-full.pot to esperanto
# Copyright (C) 2023 Free Software Foundation, Inc.
# This file is distributed under the same license as the kdeconnect-android package.
# Oliver Kellogg <okellogg@users.sourceforge.net, 2023.
#
#. extracted from ./metadata/android/en-US/full_description.txt
msgid ""
msgstr ""
"Project-Id-Version: kdeconnect-android\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-06 06:18+0100\n"
"Last-Translator: Oliver Kellogg <okellogg@users.sourceforge.net>\n"
"Language-Team: Esperanto <kde-i18n-eo@kde.org>\n"
"Language: eo\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
msgid ""
"KDE Connect provides a set of features to integrate your workflow across "
"devices:\n"
"\n"
"- Shared clipboard: copy and paste between your devices.\n"
"- Share files and URLs to your computer from any app.\n"
"- Get notifications for incoming calls and SMS messages on your PC.\n"
"- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
"- Notifications sync: Read your Android notifications from the desktop.\n"
"- Multimedia remote control: Use your phone as a remote for Linux media "
"players.\n"
"- WiFi connection: no USB wire or bluetooth needed.\n"
"- End-to-end TLS encryption: your information is safe.\n"
"\n"
"Please note you will need to install KDE Connect on your computer for this "
"app to work, and keep the desktop version up-to-date with the Android "
"version for the latest features to work.\n"
"\n"
"This app is part of an open source project and it exists thanks to all the "
"people who contributed to it. Visit the website to grab the source code."
msgstr ""
"KDE Connect provizas aron da funkcioj por integri vian laborfluon trans "
"aparatoj:\n"
"\n"
"- Komuna tondujo: kopiu kaj algluu inter viaj aparatoj.\n"
"- Kunhavigu dosierojn kaj URL-ojn al via komputilo de iu ajn aplikaĵo.\n"
"- Ricevu sciigojn pri envenantaj vokoj kaj SMS-mesaĝoj en via komputilo.\n"
"- Virtuala tuŝplato: Uzu vian telefonan ekranon kiel la tuŝplaton de via "
"komputilo.\n"
"- Sinkronigo de sciigoj: Legu viajn Android-sciigojn de la labortablo.\n"
"- Plurmedia teleregilo: Uzu vian telefonon kiel teleregilon por Linuks-"
"komunikilaj ludantoj.\n"
"- WiFi-konekto: ne necesas USB-drato aŭ bluetooth.\n"
"- Fin-al-fina TLS-ĉifrado: viaj informoj estas sekuraj.\n"
"\n"
"Bonvolu noti, ke vi devos instali KDE Connect sur via komputilo por tiu "
"aplikaĵo por funkcii, kaj tenu la labortablan version ĝisdatigita kun la "
"Android versio por ke la plej novaj kapabloj funkciu.\n"
"\n"
"Ĉi tiu programo estas parto de malfermkoda projekto kaj ĝi ekzistas danke al "
"ĉiuj homoj kiuj kontribuis al ĝi. Vizitu la retejon por kapti la fontkodon."

View File

@@ -0,0 +1,22 @@
# translation of kdeconnect-android-store-short.pot to esperanto
# Copyright (C) 2023 Free Software Foundation, Inc.
# This file is distributed under the same license as the kdeconnect-android package.
# Oliver Kellogg <okellogg@users.sourceforge.net, 2023.
#
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: kdeconnect-android\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-05 07:30+0100\n"
"Last-Translator: Oliver Kellogg <okellogg@users.sourceforge.net>\n"
"Language-Team: Esperanto <kde-i18n-eo@kde.org>\n"
"Language: eo\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr "KDE Connect integras vian poŝtelefonon kaj komputilon"

View File

@@ -0,0 +1,59 @@
# Tommi Nieminen <translator@legisign.org>, 2023.
#. extracted from ./metadata/android/en-US/full_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-04 21:29+0300\n"
"Last-Translator: Tommi Nieminen <translator@legisign.org>\n"
"Language-Team: Finnish <kde-i18n-doc@kde.org>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.12.3\n"
msgid ""
"KDE Connect provides a set of features to integrate your workflow across "
"devices:\n"
"\n"
"- Shared clipboard: copy and paste between your devices.\n"
"- Share files and URLs to your computer from any app.\n"
"- Get notifications for incoming calls and SMS messages on your PC.\n"
"- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
"- Notifications sync: Read your Android notifications from the desktop.\n"
"- Multimedia remote control: Use your phone as a remote for Linux media "
"players.\n"
"- WiFi connection: no USB wire or bluetooth needed.\n"
"- End-to-end TLS encryption: your information is safe.\n"
"\n"
"Please note you will need to install KDE Connect on your computer for this "
"app to work, and keep the desktop version up-to-date with the Android "
"version for the latest features to work.\n"
"\n"
"This app is part of an open source project and it exists thanks to all the "
"people who contributed to it. Visit the website to grab the source code."
msgstr ""
"KDE Connect tarjoaa ominaisuudet työnvuosi eheyttämiseksi laitteiden "
"kesken:\n"
"\n"
" Jaettu leikepöytä: kopioi ja liitä laitteelta toiselle.\n"
" Jaa tiedostoja ja verkko-osoitteita tietokoneeseesi mistä sovelluksesta "
"vain.\n"
" Saa ilmoitukset saapuvista puheluista ja tekstiviesteistä "
"tietokoneellesi.\n"
" Näyttönäppäimistö: käytä puhelimen näyttöä tietokoneesi osoitinlaitteena.\n"
" Ilmoitusten tahdistus: lue Android-ilmoituksesi työpöydältä.\n"
" Multimedian etähallinta: käytä puhelinta Linux-mediasoitinten "
"kaukosäätimenä.\n"
" Langaton verkkoyhteys: USB-johtoa tai Bluetoothia ei tarvita.\n"
" Päästä päähän -TLS-salaus: tietosi ovat turvassa.\n"
"\n"
"Huomaa, että sovelluksen toimimiseksi KDE Connect tulee asentaa "
"tietokoneeseen ja pitää ajan tasalla Android-version kanssa, jotta kaikki "
"ominaisuudet toimisivat.\n"
"\n"
"Sovellus on avoimen lähdekoodin projekti ja on olemassa sitä avustaneiden "
"ihmisten ansiosta. Lähdekoodin saat noudettua kotisivulta."

View File

@@ -0,0 +1,19 @@
# Tommi Nieminen <translator@legisign.org>, 2023.
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-04 21:29+0300\n"
"Last-Translator: Tommi Nieminen <translator@legisign.org>\n"
"Language-Team: Finnish <kde-i18n-doc@kde.org>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.12.3\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr "KDE Connect eheyttää älypuhelimen ja tietokoneen"

View File

@@ -1,17 +1,19 @@
# Xavier BESNARD <xavier.besnard@neuf.fr>, 2023.
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: kdeconnect-android-store-short\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-06-08 05:31+0200\n"
"Last-Translator: KDE Francophone <kde-francophone@kde.org>\n"
"Language-Team: KDE Francophone <kde-francophone@kde.org>\n"
"PO-Revision-Date: 2023-06-27 08:54+0200\n"
"Last-Translator: Xavier BESNARD <xavier.besnard@neuf.fr>\n"
"Language-Team: fr\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Lokalize 23.04.2\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr ""
msgstr "KDE Connect intègre votre téléphone et votre ordinateur."

View File

@@ -0,0 +1,19 @@
# Shinjo Park <kde@peremen.name>, 2023.
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-23 00:47+0200\n"
"Last-Translator: Shinjo Park <kde@peremen.name>\n"
"Language-Team: Korean <kde-kr@kde.org>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Lokalize 22.12.3\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr "KDE Connect는 스마트폰과 컴퓨터를 통합합니다"

View File

@@ -0,0 +1,24 @@
# Translation of kdeconnect-android-store-short to Norwegian Nynorsk
#
# Karl Ove Hufthammer <karl@huftis.org>, 2023.
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-15 14:30+0200\n"
"Last-Translator: Karl Ove Hufthammer <karl@huftis.org>\n"
"Language-Team: Norwegian Nynorsk <l10n-no@lister.huftis.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: nn\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 23.04.3\n"
"X-Environment: kde\n"
"X-Accelerator-Marker: &\n"
"X-Text-Markup: kde4\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr "KDE Connect koplar telefonen din saman med datamaskina"

View File

@@ -0,0 +1,60 @@
# Geraldo Simiao <geraldosimiao@fedoraproject.org>, 2023.
#. extracted from ./metadata/android/en-US/full_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-07-25 22:01-0300\n"
"Last-Translator: Geraldo Simiao <geraldosimiao@fedoraproject.org>\n"
"Language-Team: Brazilian Portuguese <kde-i18n-pt_BR@kde.org>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 23.04.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid ""
"KDE Connect provides a set of features to integrate your workflow across "
"devices:\n"
"\n"
"- Shared clipboard: copy and paste between your devices.\n"
"- Share files and URLs to your computer from any app.\n"
"- Get notifications for incoming calls and SMS messages on your PC.\n"
"- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
"- Notifications sync: Read your Android notifications from the desktop.\n"
"- Multimedia remote control: Use your phone as a remote for Linux media "
"players.\n"
"- WiFi connection: no USB wire or bluetooth needed.\n"
"- End-to-end TLS encryption: your information is safe.\n"
"\n"
"Please note you will need to install KDE Connect on your computer for this "
"app to work, and keep the desktop version up-to-date with the Android "
"version for the latest features to work.\n"
"\n"
"This app is part of an open source project and it exists thanks to all the "
"people who contributed to it. Visit the website to grab the source code."
msgstr ""
"O KDE Connect fornece um conjunto de recursos para integrar seu fluxo de "
"trabalho entre dispositivos:\n"
"\n"
"- Área de transferência compartilhada: copie e cole entre seus "
"dispositivos.\n"
"- Compartilhe arquivos e URLs em seu computador a partir de qualquer app.\n"
"- Receba notificações de chamadas recebidas e mensagens SMS no seu PC.\n"
"- Touchpad virtual: use a tela do telefone como touchpad do computador.\n"
"- Sincronização de notificações: leia as notificações do seu Android na área "
"de trabalho.\n"
"- Controle remoto multimídia: use seu telefone como controle remoto para "
"reprodutores de mídia Linux.\n"
"- Conexão Wi-Fi: sem necessidade de cabos USB ou bluetooth.\n"
"- Criptografia TLS de ponta a ponta: suas informações estão seguras.\n"
"\n"
"Observe que você precisará instalar o KDE Connect no seu computador para que "
"este aplicativo funcione e mantenha a versão para desktop atualizada com a "
"versão do Android para que os recursos mais recentes funcionem.\n"
"\n"
"Este aplicativo faz parte de um projeto de código aberto e existe graças a "
"todas as pessoas que contribuíram para ele. Visite o site para obter o "
"código-fonte."

View File

@@ -0,0 +1,19 @@
# Luiz Fernando Ranghetti <elchevive@opensuse.org>, 2023.
#. extracted from ./metadata/android/en-US/short_description.txt
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-06-26 17:19-0300\n"
"Last-Translator: Luiz Fernando Ranghetti <elchevive@opensuse.org>\n"
"Language-Team: Brazilian Portuguese <kde-i18n-pt_BR@kde.org>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 22.12.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "KDE Connect integrates your smartphone and computer"
msgstr "O KDE Connect integra seu celular e computador"

View File

@@ -4,7 +4,7 @@ msgstr ""
"Project-Id-Version: kdeorg\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-06-17 04:11\n"
"PO-Revision-Date: 2023-07-23 12:28\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"

View File

@@ -4,7 +4,7 @@ msgstr ""
"Project-Id-Version: kdeorg\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-07 00:47+0000\n"
"PO-Revision-Date: 2023-06-17 04:11\n"
"PO-Revision-Date: 2023-07-23 12:28\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Düymə vuruşu kimi göndərmək</string>
<string name="mouse_receiver_plugin_description">Uzaqdakı siçanın hərəkətini qəbul etmək</string>
<string name="mouse_receiver_plugin_name">Siçanı qəbul edən</string>
<string name="mouse_receiver_no_permissions">Xüsusi İmkanlarda xidməti aktiv etməlisiniz</string>
<string name="view_status_title">Vəziyyət</string>
<string name="battery_status_format">Batareya: %d%%</string>
<string name="battery_status_low_format">Batareya: %d%% Zəif batareya</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Изпращане като клавишни комбинации</string>
<string name="mouse_receiver_plugin_description">Получаване на дистанционно движение на мишката</string>
<string name="mouse_receiver_plugin_name">Приемане на движение на мишката</string>
<string name="mouse_receiver_no_permissions">Трябва да активирате услугата за достъпност</string>
<string name="mouse_receiver_no_permissions">"За отдалечено управление с тъчскрийн, трябва да предоставите разрешения за достъп за пълен контрол на устройството."</string>
<string name="view_status_title">Състояние</string>
<string name="battery_status_format">Батерия: %d%%</string>
<string name="battery_status_low_format">Батерия: %d%% Ниско ниво на батерията</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Envia com a pulsacions de tecla</string>
<string name="mouse_receiver_plugin_description">Rep el moviment del ratolí remot</string>
<string name="mouse_receiver_plugin_name">Receptor del ratolí</string>
<string name="mouse_receiver_no_permissions">Cal que habiliteu el servei Accessibilitat</string>
<string name="mouse_receiver_no_permissions">Per a rebre entrades tàctils remotament cal atorgar el permís d\'Accessibilitat a control complet del vostre dispositiu</string>
<string name="view_status_title">Estat</string>
<string name="battery_status_format">Bateria: %d%%</string>
<string name="battery_status_low_format">Bateria: %d%% bateria baixa</string>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Posílat jako úhozy kláves</string>
<string name="mouse_receiver_plugin_description">Přijímat vzdálený pohyb myši</string>
<string name="mouse_receiver_plugin_name">Příjemce myši</string>
<string name="mouse_receiver_no_permissions">Musíte povolit Službu Zpřístupnění.</string>
<string name="view_status_title">Stav</string>
<string name="battery_status_format">Baterie: %d%%</string>
<string name="battery_status_low_format">Baterie: %d%% Téměř vybitá baterie</string>

View File

@@ -95,7 +95,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Als Tastendruck senden</string>
<string name="mouse_receiver_plugin_description">Empfänger für entfernte Mauseingaben</string>
<string name="mouse_receiver_plugin_name">Maus-Empfänger</string>
<string name="mouse_receiver_no_permissions">Sie müssen den Zugangshilfendienst aktivieren</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Akku: %d%%</string>
<string name="battery_status_low_format">Akku: %d%% Niedriger Ladestand</string>

View File

@@ -84,7 +84,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Αποστολή ως πληκτρολογήσεις</string>
<string name="mouse_receiver_plugin_description">Λήψη απομακρυσμένων κινήσεων του ποντικιού</string>
<string name="mouse_receiver_plugin_name">Δέκτης ποντικιού</string>
<string name="mouse_receiver_no_permissions">Απαιτείται η ενεργοποίηση της υπηρεσίας προσβασιμότητας</string>
<string name="view_status_title">Κατάσταση</string>
<string name="battery_status_format">Μπαταρία: %d%%</string>
<string name="battery_status_low_format">Μπαταρία: %d%% Χαμηλή συάθμη</string>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Send as keystrokes</string>
<string name="mouse_receiver_plugin_description">Receive remote mouse movement</string>
<string name="mouse_receiver_plugin_name">Mouse receiver</string>
<string name="mouse_receiver_no_permissions">You need to enable Accessibility Service</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Battery: %d%%</string>
<string name="battery_status_low_format">Battery: %d%% Low Battery</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Enviar como pulsaciones</string>
<string name="mouse_receiver_plugin_description">Recibir el movimiento de un ratón remoto</string>
<string name="mouse_receiver_plugin_name">Receptor del ratón</string>
<string name="mouse_receiver_no_permissions">Necesita activar el servicio de accesibilidad</string>
<string name="mouse_receiver_no_permissions">Para recibir entradas táctiles de manera remota necesita conceder permisos de accesibilidad para controlar totalmente su dispositivo</string>
<string name="view_status_title">Estado</string>
<string name="battery_status_format">Batería: %d%%</string>
<string name="battery_status_low_format">Batería: %d%% Batería baja</string>

View File

@@ -93,7 +93,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Bidali tekla-joaldi gisa</string>
<string name="mouse_receiver_plugin_description">Jaso urrutiko saguaren mugimenduak</string>
<string name="mouse_receiver_plugin_name">Sagu jasotzailea</string>
<string name="mouse_receiver_no_permissions">Irisgarritasun zerbitzua gaitu behar duzu</string>
<string name="view_status_title">Egoera</string>
<string name="battery_status_format">Bateria: %%%d</string>
<string name="battery_status_low_format">Bateria: %%%d bateria baxu</string>

View File

@@ -60,6 +60,8 @@
<string name="mousepad_mouse_buttons_title">Näytä hiiripainikkeet</string>
<string name="mousepad_acceleration_profile_settings_title">Aseta osoittimen kiihdytys</string>
<string name="mousepad_scroll_direction_title">Käänteinen vierityssuunta</string>
<string name="gyro_mouse_enabled_title">Käytä gyroskooppihiirtä</string>
<string name="gyro_mouse_sensitivity_title">Gyroskoopin herkkyys</string>
<string-array name="mousepad_tap_entries">
<item>Vasen napsautus</item>
<item>Oikea napsautus</item>
@@ -95,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Lähetä näppäinpainalluksina</string>
<string name="mouse_receiver_plugin_description">Vastaanota etähiiren liike</string>
<string name="mouse_receiver_plugin_name">Hiirivastaanotin</string>
<string name="mouse_receiver_no_permissions">Esteettömyyspalvelu on otettava käyttöön</string>
<string name="view_status_title">Tila</string>
<string name="battery_status_format">Varaus: %d %%</string>
<string name="battery_status_low_format">Varaus: %d %%, vähissä</string>
@@ -106,6 +107,7 @@
<string name="device_menu_plugins">Liitännäisten asetukset</string>
<string name="device_menu_unpair">Poista laitepari</string>
<string name="pair_new_device">Kytke uusi laite pariksi</string>
<string name="cancel_pairing">Peru paritus</string>
<string name="unknown_device">Tuntematon laite</string>
<string name="error_not_reachable">Laite tavoittamattomissa</string>
<string name="error_already_paired">Laite on jo kytketty pariksi</string>
@@ -374,6 +376,7 @@
<string name="click_here_to_type">Napauta ja kirjoita</string>
<string name="clear_compose">Tyhjennä</string>
<string name="send_compose">Lähetä</string>
<string name="compose_send_title">Kirjoita teksti</string>
<string name="open_compose_send">Kirjoita teksti</string>
<string name="about_kde_about">&lt;h1&gt;Tietoa&lt;/h1&gt; &lt;p&gt;KDE on ohjelmoijien, taiteilijoiden, kirjoittajien, kääntäjien ja muiden sisällönluojien kansainvälinen yhteisö, joka on sitoutunut &lt;a href=https://www.gnu.org/philosophy/free-sw.html&gt;vapaiden ohjelmien&lt;/a&gt; kehitykseen. KDE tuottaa Plasma-työpöytäympäristöä, satoja sovelluksia ja monia niitä tukevia ohjelmakirjastoja.&lt;/p&gt; &lt;p&gt;KDE pyrkii yhteistyöhön: mikään yksittäinen toimija ei hallitse sen suuntaa tai tuotteita, vaan teemme yhdessä työtä yhteisen päämäärän hyväksi: tuottaaksemme maailman hienointa vapaata ohjelmistoa. Kaikki ovat tervetulleita &lt;a href=https://community.kde.org/Get_Involved&gt;liittymään ja avustamaan&lt;/a&gt; KDE:ta myös sinä.&lt;/p&gt; Sivulta &lt;a href=https://www.kde.org/&gt;https://www.kde.org/&lt;/a&gt; löytyy KDE-yhteisöstä ja tuottamistamme ohjelmista lisätietoa.</string>
<string name="about_kde_report_bugs_or_wishes">&lt;h1&gt;Ilmoita ohjelmavirheistä tai -toiveista&lt;/h1&gt; &lt;p&gt;Ohjelmia voi aina parantaa, ja KDE-yhteisö on siihen valmis. Sinun käyttäjän on kuitenkin kerrottava meille, kun jokin ei toimi odotetusti tai voisi toimia paremmin.&lt;/p&gt; &lt;p&gt;KDE:lla on virheenseurantajärjestelmä. Käy sivulla &lt;a href=https://bugs.kde.org/&gt;https://bugs.kde.org/&lt;/a&gt; tai käytä Tietoa-sivun painiketta ”Ilmoita ohjelmavirheestä”.&lt;/p&gt; Parannusehdotuksissakin olet tervetullut käyttämään virheenseurantajärjestelmää kirjataksesi toiveesi. Varmista, että käytät vakavuustasoa ”Wishlist”.</string>
@@ -392,4 +395,5 @@
<string name="everyone_else">Kaikki muut vuosien varrella KDE Connectia avustaneet</string>
<string name="send_clipboard">Lähetä leikepöytä</string>
<string name="tap_to_execute">Suorita napauttamalla</string>
<string name="plugin_stats">Liitännäisen tilastot</string>
</resources>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Envoyez comme appuis de touches</string>
<string name="mouse_receiver_plugin_description">Recevoir les mouvements de la souri distante</string>
<string name="mouse_receiver_plugin_name">Récepteur de souris</string>
<string name="mouse_receiver_no_permissions">Vous avez besoin d\'accéder au service « Accessibilité »</string>
<string name="view_status_title">État</string>
<string name="battery_status_format">Batterie : %d %%</string>
<string name="battery_status_low_format">Batterie : %d %% Batterie faible</string>
@@ -396,4 +395,5 @@
<string name="everyone_else">Toutes les autres personnes ayant contribué à « KDE Connect » depuis plusieurs années</string>
<string name="send_clipboard">Envoyer le presse-papier</string>
<string name="tap_to_execute">Tapotez pour lancer</string>
<string name="plugin_stats">Statistiques des modules externes</string>
</resources>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Enviar como pulsacións de tecla</string>
<string name="mouse_receiver_plugin_description">Recibir movementos de rato remotos</string>
<string name="mouse_receiver_plugin_name">Receptor de rato</string>
<string name="mouse_receiver_no_permissions">Ten que activar o servizo de accesibilidade</string>
<string name="view_status_title">Estado</string>
<string name="battery_status_format">Batería: %d%%</string>
<string name="battery_status_low_format">Batería: %d%% (baixa)</string>

View File

@@ -92,7 +92,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Küldés billentyűleütésként</string>
<string name="mouse_receiver_plugin_description">Távoli egérmozgások fogadása</string>
<string name="mouse_receiver_plugin_name">Egérvevő</string>
<string name="mouse_receiver_no_permissions">Engedélyeznie kell az akadálymentesítési szolgáltatást</string>
<string name="view_status_title">Állapot</string>
<string name="battery_status_format">Akku: %d%%</string>
<string name="battery_status_low_format">Akku: %d%% alacsony töltöttség</string>

View File

@@ -177,7 +177,7 @@
<string name="thanks_to">Gratias a</string>
<string name="easter_egg">Ovo de Pascha</string>
<string name="version">Version %s</string>
<string name="about_kde">A proposio de KDE</string>
<string name="about_kde">A proposito de KDE</string>
<string name="kde_be_free">KDE- Vos Sia Libere!</string>
<string name="kde">KDE</string>
<string name="konqi">Konqi</string>

View File

@@ -92,7 +92,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Kirim sebagai penekanan tombol</string>
<string name="mouse_receiver_plugin_description">Terima penggerakan mouse jarak jauh</string>
<string name="mouse_receiver_plugin_name">Penerima mouse</string>
<string name="mouse_receiver_no_permissions">Anda harus mengaktifkan Layanan Aksesibilitas</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Baterai: %d%%</string>
<string name="battery_status_low_format">Baterai: %d%% Baterai Lemah</string>

View File

@@ -86,7 +86,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Senda sem lyklaáslátt</string>
<string name="mouse_receiver_plugin_description">"Taka á móti fjartengdum músarhreyfingum"</string>
<string name="mouse_receiver_plugin_name">Móttakari músarmerkja</string>
<string name="mouse_receiver_no_permissions">Þú þarft að virkja þjónustuna fyrir aukið aðgengi</string>
<string name="view_status_title">Staða</string>
<string name="battery_status_format">Rafhlaða: %d%%</string>
<string name="battery_status_low_format">Rafhlaða: %d%% lítil hleðsla</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Invia come combinazioni di tasti</string>
<string name="mouse_receiver_plugin_description">Ricevi i movimenti remoti del mouse</string>
<string name="mouse_receiver_plugin_name">Ricevitore del mouse</string>
<string name="mouse_receiver_no_permissions">Devi abilitare Servizio di accessibilità</string>
<string name="mouse_receiver_no_permissions">Per ricevere input tattili da remoto devi concedere i permessi di accessibilità per controllare completamente il tuo dispositivo</string>
<string name="view_status_title">Stato</string>
<string name="battery_status_format">Batteria: %d%%</string>
<string name="battery_status_low_format">Batteria: %d%% livello basso</string>

View File

@@ -93,7 +93,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">キー入力として送信</string>
<string name="mouse_receiver_plugin_description">リモートからマウスの動きを受信</string>
<string name="mouse_receiver_plugin_name">マウスレシーバ</string>
<string name="mouse_receiver_no_permissions">アクセシビリティサービスを有効化する必要があります</string>
<string name="view_status_title">状態</string>
<string name="battery_status_format">バッテリ: %d%%</string>
<string name="battery_status_low_format">バッテリ: %d%% 残量低下</string>

View File

@@ -60,6 +60,8 @@
<string name="mousepad_mouse_buttons_title">마우스 단추 표시</string>
<string name="mousepad_acceleration_profile_settings_title">포인터 가속 설정</string>
<string name="mousepad_scroll_direction_title">스크롤 방향 뒤집기</string>
<string name="gyro_mouse_enabled_title">자이로스코프 마우스 활성화</string>
<string name="gyro_mouse_sensitivity_title">자이로스코프 감도</string>
<string-array name="mousepad_tap_entries">
<item>왼쪽 클릭</item>
<item>오른쪽 단추 클릭</item>
@@ -95,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">키 입력으로 보내기</string>
<string name="mouse_receiver_plugin_description">원격 마우스 움직임 받기</string>
<string name="mouse_receiver_plugin_name">마우스 수신기</string>
<string name="mouse_receiver_no_permissions">접근성 서비스를 활성화해야 합니다</string>
<string name="mouse_receiver_no_permissions">원격으로 터치 입력을 받으려면 장치 제어를 위해서 접근성 권한을 허용해야 합니다</string>
<string name="view_status_title">상태</string>
<string name="battery_status_format">배터리: %d%%</string>
<string name="battery_status_low_format">배터리: %d%% 배터리 부족</string>
@@ -106,6 +108,7 @@
<string name="device_menu_plugins">플러그인 설정</string>
<string name="device_menu_unpair">연결 해제</string>
<string name="pair_new_device">새 장치 연결</string>
<string name="cancel_pairing">페어링 취소</string>
<string name="unknown_device">알 수 없는 장치</string>
<string name="error_not_reachable">장치에 접근할 수 없음</string>
<string name="error_already_paired">장치가 이미 연결됨</string>
@@ -366,8 +369,9 @@
<string name="click_here_to_type">입력하려면 누르십시오</string>
<string name="clear_compose">지우기</string>
<string name="send_compose">보내기</string>
<string name="compose_send_title">텍스트 보내기</string>
<string name="open_compose_send">텍스트 작성</string>
<string name="about_kde_about">&lt;h1&gt;정보&lt;h1&gt; &lt;p&gt;KDE는 &lt;a href=https://www.gnu.org/philosophy/free-sw.html&gt;자유 소프트웨어&lt;/a&gt;를 개발하려고 모인 소프트웨어 개발자, 예술가, 집필가, 번역가 및 기타 인원의 모임입니다. KDE 커뮤니티에서는 Plasma 데스크톱 환경, 다양한 프로그램 및 지원 라이브러리를 개발합니다.&lt;p&gt; &lt;p&gt;KDE는 협동 조합입니다. 어떠한 단일 집단도 방향이나 제품을 결정하지 않습니다. 우리는 전 세계에서 가장 뛰어난 자유 소프트웨어 개발이라는 공통 목표를 향해 함께 힘을 모으고 있습니다. KDE에는 이 글을 읽는 여러분과 같은 누구나 &lt;a href=https://community.kde.org/Get_Involved&gt;참여하고 기여&lt;/a&gt;할 수 있습니다.&lt;p&gt; &lt;a href=https://www.kde.org/&gt;https://www.kde.org/&lt;/a&gt; 페이지를 방문하셔서 KDE 커뮤니티와 소프트웨어에 대해 알아 보십시오.</string>
<string name="about_kde_about">&lt;h1&gt;정보&lt;h1&gt; &lt;p&gt;KDE는 &lt;a href=https://www.gnu.org/philosophy/free-sw.html&gt;자유 소프트웨어&lt;/a&gt;를 개발하려고 모인 소프트웨어 개발자, 예술가, 집필가, 번역가 및 기타 인원의 모임입니다. KDE 커뮤니티에서는 Plasma 데스크톱 환경, 다양한 및 지원 라이브러리를 개발합니다.&lt;p&gt; &lt;p&gt;KDE는 협동 조합입니다. 어떠한 단일 집단도 방향이나 제품을 결정하지 않습니다. 우리는 전 세계에서 가장 뛰어난 자유 소프트웨어 개발이라는 공통 목표를 향해 함께 힘을 모으고 있습니다. KDE에는 이 글을 읽는 여러분과 같은 누구나 &lt;a href=https://community.kde.org/Get_Involved&gt;참여하고 기여&lt;/a&gt;할 수 있습니다.&lt;p&gt; &lt;a href=https://www.kde.org/&gt;https://www.kde.org/&lt;/a&gt; 페이지를 방문하셔서 KDE 커뮤니티와 소프트웨어에 대해 알아 보십시오.</string>
<string name="about_kde_report_bugs_or_wishes">"&lt;h1&gt;버그나 요구 사항 보고&lt;/h1&gt; &lt;p&gt;소프트웨어는 항상 개선되며, KDE 팀도 그럴 준비가 되어 있습니다. 따라서 사용자 여러분은 무언가가 예상한 대로 작동하지 않거나 더 잘 작동하기를 바라면 개발자에게 알려 주십시오.&lt;/p&gt; &lt;p&gt;KDE는 버그 추적 시스템을 가지고 있습니다. &lt;a href=https://bugs.kde.org/&gt;https://bugs.kde.org/&lt;/a&gt;를 방문하시거나 \"도움말\" 메뉴의 \"버그 보고...\" 대화 상자를 이용하셔서 버그를 보고해 주십시오.&lt;/p&gt; 개선 사항 제안을 하고 싶으시다면 버그 보고 시스템을 통해서 알려 주십시오. 이 경우 심각성 항목에서 \"Wishlist\"를 선택하셔야 합니다."</string>
<string name="about_kde_join_kde">&lt;h1&gt;KDE에 참여하기&lt;/h1&gt; &lt;p&gt;소프트웨어 개발자만이 KDE에 참가할 수 있는 것은 아닙니다. 프로그램 인터페이스를 번역하는 각 나라 번역팀을 도울 수도 있습니다. 또한 그래픽, 테마, 소리, 더 나은 문서 등을 기여할 수도 있습니다. 직접 결정하십시오!&lt;/p&gt; &lt;p&gt;&lt;a href=https://community.kde.org/Get_Involved&gt;https://community.kde.org/Get_Involved&lt;/a&gt; 페이지를 방문하셔서 참여할 수 있는 프로젝트를 찾아 보십시오.&lt;/p&gt; 만약 더 많은 정보나 문서가 필요하다면, &lt;a href=https://techbase.kde.org/&gt;https://techbase.kde.org/&lt;/a&gt; 사이트를 방문하셔서 원하는 정보를 찾으십시오.</string>
<string name="about_kde_support_kde">"&lt;h1&gt;KDE 지원&lt;/h1&gt; &lt;p&gt;KDE는 무료로 사용 가능하지만, 만드는 것은 무료가 아닙니다.&lt;/p&gt; &lt;p&gt;따라서 KDE 커뮤니티는 독일에 비영리 재단 KDE e.V.를 설립했습니다. KDE e.V.는 KDE 커뮤니티를 법적, 재정적인 면에서 후원합니다. &lt;a href=https://ev.kde.org/&gt;https://ev.kde.org/&lt;/a&gt; 사이트를 방문하셔서 KDE e.V.에 관한 정보를 확인하십시오.&lt;/p&gt; &lt;p&gt;KDE는 재정적인 보조가 필요합니다. 대부분의 지원금은 구성원에게 대가를 지급하거나 KDE에 기여하는 데 드는 돈을 대는 데 사용됩니다. &lt;a href=https://www.kde.org/community/donations/&gt;https://www.kde.org/community/donations/&lt;/a&gt;에 있는 방법을 사용하여 재정적인 지원을 해 주십시오.&lt;/p&gt;여러분의 협조에 미리 감사드립니다."</string>
@@ -384,4 +388,5 @@
<string name="everyone_else">그 외 오랫동안 KDE Connect에 기여한 사람들</string>
<string name="send_clipboard">클립보드 보내기</string>
<string name="tap_to_execute">실행하려면 누르십시오</string>
<string name="plugin_stats">플러그인 통계</string>
</resources>

View File

@@ -93,7 +93,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Siųsti kaip klavišų paspaudimus</string>
<string name="mouse_receiver_plugin_description">Gauti nuotolinius pelės judesius</string>
<string name="mouse_receiver_plugin_name">Pelės gavėjas</string>
<string name="mouse_receiver_no_permissions">Jūs turite įjungti prieinamumo tarnybą</string>
<string name="view_status_title">Būsena</string>
<string name="battery_status_format">Akumuliatorius: %d%%</string>
<string name="battery_status_low_format">Akumuliatorius: %d%% baigia išsikrauti</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Als toetsaanslagen verzenden</string>
<string name="mouse_receiver_plugin_description">Muisbewegingen van afstand ontvangen</string>
<string name="mouse_receiver_plugin_name">Muisontvanger</string>
<string name="mouse_receiver_no_permissions">U moet service toegankelijkheid inschakelen</string>
<string name="mouse_receiver_no_permissions">Om aanraakinvoer op afstand te ontvangen moet u toegangsrechten toekennen om uw apparaat volledig te besturen</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Batterij: %d%%</string>
<string name="battery_status_low_format">Batterij: %d%% lage batterij</string>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Send som tastetrykk</string>
<string name="mouse_receiver_plugin_description">Ta imot eksterne muserørsler</string>
<string name="mouse_receiver_plugin_name">Muserørsle-mottakar</string>
<string name="mouse_receiver_no_permissions">Du må slå på tilgjenge-tenesta</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Batteri: %d %%</string>
<string name="battery_status_low_format">Batteri: %d %%  lågt batterinivå</string>
@@ -108,6 +107,7 @@
<string name="device_menu_plugins">Programtillegg-oppsett</string>
<string name="device_menu_unpair">Løys paring</string>
<string name="pair_new_device">Par ny eining</string>
<string name="cancel_pairing">Avbryt paring</string>
<string name="unknown_device">Ukjend eining</string>
<string name="error_not_reachable">Får ikkje kontakt med eininga</string>
<string name="error_already_paired">Eininga er alt para</string>
@@ -394,4 +394,5 @@
<string name="everyone_else">Alle andre som har hjelpt til med utviklinga av KDE Connect opp gjennom åra</string>
<string name="send_clipboard">Send utklippstavla</string>
<string name="tap_to_execute">Tapp for å utføra handlinga</string>
<string name="plugin_stats">Programtillegg-statistikk</string>
</resources>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Wysyłaj jako naciśnięcia klawiszy</string>
<string name="mouse_receiver_plugin_description">Odbiera ruchy myszy z innego urządzenia do sterowania tym urządzeniem</string>
<string name="mouse_receiver_plugin_name">Odbieranie myszy</string>
<string name="mouse_receiver_no_permissions">Musisz włączyć usługę dostępności</string>
<string name="view_status_title">Stan</string>
<string name="battery_status_format">Bateria: %d%%</string>
<string name="battery_status_low_format">Bateria: %d%% niski poziom</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Enviar como pressionamento de teclas</string>
<string name="mouse_receiver_plugin_description">Receber movimento de mouse remoto</string>
<string name="mouse_receiver_plugin_name">Receptor de mouse</string>
<string name="mouse_receiver_no_permissions">Você precisa habilitar o serviço de acessibilidade</string>
<string name="mouse_receiver_no_permissions">Para receber entradas de toque remotas você precisa conceder permissões de acessibilidade para controlar totalmente seu dispositivo</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Bateria: %d%%</string>
<string name="battery_status_low_format">Bateria: %d%% bateria baixa</string>
@@ -108,6 +108,7 @@
<string name="device_menu_plugins">Configuração dos plugins</string>
<string name="device_menu_unpair">Cancelar emparelhamento</string>
<string name="pair_new_device">Emparelhar novo dispositivo</string>
<string name="cancel_pairing">Cancelar emparelhamento</string>
<string name="unknown_device">Dispositivo desconhecido</string>
<string name="error_not_reachable">Dispositivo inacessível</string>
<string name="error_already_paired">Dispositivo já emparelhado</string>
@@ -395,4 +396,5 @@
<string name="everyone_else">Todos os outros que contribuíram para o KDE Connect ao longo dos anos</string>
<string name="send_clipboard">Enviar para área de transferência</string>
<string name="tap_to_execute">Toque para executar</string>
<string name="plugin_stats">Estatísticas do plugin</string>
</resources>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Enviar como eventos de teclado</string>
<string name="mouse_receiver_plugin_description">Receber o movimento remoto do rato</string>
<string name="mouse_receiver_plugin_name">Receptor do rato</string>
<string name="mouse_receiver_no_permissions">Precisa de activar o Serviço de Acessibilidade</string>
<string name="view_status_title">Estado</string>
<string name="battery_status_format">Bateria: %d%%</string>
<string name="battery_status_low_format">Bateria: %d%% Bateria Fraca</string>

View File

@@ -92,7 +92,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Trimite ca apăsări de taste</string>
<string name="mouse_receiver_plugin_description">Primește mișcări de maus de la distanță</string>
<string name="mouse_receiver_plugin_name">Receptor pentru maus</string>
<string name="mouse_receiver_no_permissions">Trebuie să activați Serviciul de Accesibilitate</string>
<string name="view_status_title">Stare</string>
<string name="battery_status_format">Acumulator: %d%%</string>
<string name="battery_status_low_format">Acumulator: %d%% Acumulator scăzut</string>

File diff suppressed because one or more lines are too long

View File

@@ -76,7 +76,6 @@
<string name="sendkeystrokes_pref_enabled">Povoliť odosielanie stlačení klávesov</string>
<string name="mouse_receiver_plugin_description">Prijímanie pohybu vzdialenej myši</string>
<string name="mouse_receiver_plugin_name">Prijímač myši</string>
<string name="mouse_receiver_no_permissions">Musíte povoliť službu dostupnosti</string>
<string name="view_status_title">Stav</string>
<string name="battery_status_format">Batéria: %d%%</string>
<string name="battery_status_low_format">Batéria: %d%% Nízka úroveň batérie</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Pošlji kot pritiske tipk</string>
<string name="mouse_receiver_plugin_description">Sprejemaj gibanje oddaljene miške</string>
<string name="mouse_receiver_plugin_name">Sprejemnik miške</string>
<string name="mouse_receiver_no_permissions">Omogočiti morate storitev dostopnosti</string>
<string name="mouse_receiver_no_permissions">Če želite prejemati vnose dotikov na daljavo, morate podeliti dovoljenja za dostopnost za popoln nadzor vaše naprave</string>
<string name="view_status_title">Stanje</string>
<string name="battery_status_format">Baterija: %d%%</string>
<string name="battery_status_low_format">Baterija: %d%% skoraj prazna</string>

View File

@@ -92,7 +92,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">Skicka som tangentnedtryckningar</string>
<string name="mouse_receiver_plugin_description">Ta emot externa musrörelser</string>
<string name="mouse_receiver_plugin_name">Musmottagare</string>
<string name="mouse_receiver_no_permissions">Du måste aktivera åtkomsttjänsten</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Batteri: %d %%</string>
<string name="battery_status_low_format">Batteri: %d %% låg laddning</string>

View File

@@ -97,7 +97,6 @@
<string name="pref_plugin_mousepad_send_keystrokes">விசைகளாக அனுப்பு</string>
<string name="mouse_receiver_plugin_description">தொலை சுட்டி அசைவைப் பெறு</string>
<string name="mouse_receiver_plugin_name">சுட்டி பெறுநர்</string>
<string name="mouse_receiver_no_permissions">அணுகல்தன்மை சேவையை நீங்கள் இயக்க வேண்டும்</string>
<string name="view_status_title">நிலை</string>
<string name="battery_status_format">மின்கலம்: %d%%</string>
<string name="battery_status_low_format">மின்கலம்: %d%% குறைந்த மின்கலம்</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Düğme basımları olarak gönder</string>
<string name="mouse_receiver_plugin_description">Uzaktan fare hareketi al</string>
<string name="mouse_receiver_plugin_name">Fare alıcısı</string>
<string name="mouse_receiver_no_permissions">Erişilebilirlik Hizmeti\'ni etkinleştirmeniz gerekiyor</string>
<string name="mouse_receiver_no_permissions">Dokunma girdilerini uzaktan almak için aygıtı tümüyle denetlemek üzere Erişilebilirlik izinleri sağlamanız gerekir</string>
<string name="view_status_title">Durum</string>
<string name="battery_status_format">Pil: %d%%</string>
<string name="battery_status_low_format">Pil: %d%% Düşük pil</string>
@@ -379,9 +379,9 @@
<string name="send_compose">Gönder</string>
<string name="compose_send_title">Gönderi oluştur</string>
<string name="open_compose_send">Metin oluştur</string>
<string name="about_kde_about">&lt;h1&gt;Hakkında&lt;/h1&gt; &lt;p&gt;KDE, &lt;a href=https://www.gnu.org/philosophy/free-sw.html&gt;Özgür Yazılım&lt;/a&gt; hareketine destek veren yazılım mühendislerinin, sanatçıların, yazarların, çevirmenlerin ve yaratıcıların bir araya geldiği dünya çapında bir topluluktur KDE, Plasma masaüstü ortamını, yüzlerce uygulamayı ve onları destekleyen sayısız yazılım kitaplığını üretir.&lt;/p&gt; &lt;p&gt;KDE, işbirlikçi bir kurumdur: Tek bir varlık yönünü veya ürünlerini kontrol etmez. Bunun yerine, dünyanın en kaliteli Özgür Yazılım\'larını üretme hedefi için birlikte çalışırız. Herkes, sen de dahil olmak üzere, KDE\'ye &lt;a href=https://community.kde.org/Get_Involved&gt;katılıp katkıda bulunmakta özgürdür&lt;/a&gt;.&lt;/p&gt; KDE topluluğu ve ürettiğimiz yazılımlar hakkında daha fazla bilgi için &lt;a href=https://www.kde.org/&gt;https://www.kde.org/&lt;/a&gt; adresini ziyaret edin.</string>
<string name="about_kde_about">&lt;h1&gt;Hakkında&lt;/h1&gt; &lt;p&gt;KDE, &lt;a href=https://www.gnu.org/philosophy/free-sw.html&gt;Özgür Yazılım&lt;/a&gt; hareketine destek veren yazılım mühendislerinin, sanatçıların, yazarların, çevirmenlerin ve yaratıcıların bir araya geldiği dünya çapında bir topluluktur KDE, Plasma masaüstü ortamını, yüzlerce uygulamayı ve onları destekleyen sayısız yazılım kitaplığını üretir.&lt;/p&gt; &lt;p&gt;KDE, işbirliğine dayalı bir kuruluştur: yönünü veya ürünlerini tek başına denetleyen bir kuruluş yoktur. Bunun yerine, dünyanın en iyi Özgür Yazılımını oluşturma ortak hedefine ulaşmak için birlikte çalışıyoruz. Siz de dahil olmak üzere herkes &lt;a href=https://community.kde.org/Get_Involved&gt;katılabilir&lt;/a&gt; ve katkıda bulunabilir.&lt;/p&gt; KDE topluluğu ve ürettiğimiz yazılımlar hakkında daha fazla bilgi için &lt;a href=https://www.kde.org/&gt;https://www.kde.org/&lt;/a&gt; adresini ziyaret edin.</string>
<string name="about_kde_report_bugs_or_wishes">&lt;h1&gt;Hataları veya İsteklerinizi Bildirin&lt;/h1&gt; &lt;p&gt;Yazılım her zaman iyileştirilebilir ve KDE takımın bunu yapmaya hazır. Ancak siz de bir şey beklendiği gibi gitmezse veya hata verirse bize bildirin.&lt;/p&gt; &lt;p&gt;KDE\'nin bir hata takip sistemi vardır. &lt;a href=https://bugs.kde.org/&gt;https://bugs.kde.org/&lt;/a&gt; adresini ziyaret edin veya hakkında ekranının \"Hata Bildir\" düğmesini kullanarak hataları bildirin.&lt;/p&gt; Bir iyileştirme için öneriniz varsa bunu bildirmek için hata takip sistemini kullanabilirsiniz; yalnızca \"Wishlist\" ciddiyet düzeyini kullandığınızdan emin olun.</string>
<string name="about_kde_join_kde">"&lt;h1&gt;KDE\'ye Katılın&lt;/h1&gt; &lt;p&gt;KDE takımının bir üyesi olmak için yazılım geliştirici olmanıza gerek yok. Program arayüzlerini çeviren yerel takımlara katılabilirsiniz. Grafikler, temalar, sesler ve iyileştirilmiş belgelendirme sağlayabilirsiniz. Siz karar verin!&lt;/p&gt; &lt;p&gt;Katılabileceğiniz bazı projeler hakkında bilgi almak için &lt;a href=https://community.kde.org/Get_Involved&gt;https://community.kde.org/Get_Involved&lt;/a&gt; sayfasını ziyaret edin.&lt;/p&gt; Daha fazla bilgiye veya belgeye gereksiniminiz varsa &lt;a href=https://techbase.kde.org/&gt;https://techbase.kde.org/&lt;/a&gt; sayfasında aradığınızı bulabilirsiniz."</string>
<string name="about_kde_join_kde">"&lt;h1&gt;KDE\'ye Katılın&lt;/h1&gt; &lt;p&gt;KDE takımının bir üyesi olmak için yazılım geliştirici olmanıza gerek yok. Program arayüzlerini çeviren yerel takımlara katılabilirsiniz. Grafikler, temalar, sesler ve iyileştirilmiş belgelendirme sağlayabilirsiniz. Karar sizin!&lt;/p&gt; &lt;p&gt;Katılabileceğiniz bazı projeler hakkında bilgi almak için &lt;a href=https://community.kde.org/Get_Involved&gt;https://community.kde.org/Get_Involved&lt;/a&gt; sayfasını ziyaret edin.&lt;/p&gt; Daha fazla bilgiye veya belgeye gereksiniminiz varsa &lt;a href=https://techbase.kde.org/&gt;https://techbase.kde.org/&lt;/a&gt; sayfasında aradığınızı bulabilirsiniz."</string>
<string name="about_kde_support_kde">"&lt;h1&gt;KDE\'yi Destekleyin&lt;/h1&gt; &lt;p&gt;KDE yazılımları her zaman ücretsiz kalmayı sürdürecektir; ancak bunu oluşturmak bedava değildir. &lt;/p&gt; &lt;p&gt;Geliştirmeyi desteklemek için KDE topluluğu, kar amacı gütmeyen bir kuruluş olan KDE e.V.\'yi kurmuştur, bu topluluk KDE topluğunu yasal ve finansal konularda temsil eder. KDE e.V. hakkında daha fazla bilgi için &lt;a href=https://ev.kde.org/&gt;https://ev.kde.org/&lt;/a&gt; adresini ziyadet edin.&lt;/p&gt; &lt;p&gt;KDE, finansal da dahil olmak üzere her türlü katkıdan yarar sağlar. Maddi kaynaklarımızla, geliştiricilerimizin ve diğerlerinin katkıda bulunurken oluşan masraflarını karşılıyoruz. Ayrıca yasal destek ve konferanslar ve toplantılar için de kullanılmaktadır.&lt;/p&gt; &lt;p&gt;Emeklerimizi, finansal destekle desteklemeniz için &lt;a href=https://www.kde.org/community/donations/&gt;https://www.kde.org/community/donations/&lt;/a&gt; adresinde bulunan yollardan birini kullanabilirsiniz.&lt;/p&gt; Desteğiniz için şimdiden teşekkürler."</string>
<string name="maintainer_and_developer">Projeyi sürdüren ve geliştirici</string>
<string name="developer">Geliştirici</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">Надсилати як натискання клавіш</string>
<string name="mouse_receiver_plugin_description">Отримувати віддалені рухи мишею</string>
<string name="mouse_receiver_plugin_name">Отримання даних миші</string>
<string name="mouse_receiver_no_permissions">Вам слід увімкнути службу доступності</string>
<string name="mouse_receiver_no_permissions">Щоб отримувати сигнали про торкання віддалено, вам слід надати засобам доступності дозвіл на повне керування вашим пристроєм</string>
<string name="view_status_title">Стан</string>
<string name="battery_status_format">Акумулятор: %d%%</string>
<string name="battery_status_low_format">Акумулятор: %d%%, низький заряд</string>

View File

@@ -97,7 +97,7 @@
<string name="pref_plugin_mousepad_send_keystrokes">作为按键发送</string>
<string name="mouse_receiver_plugin_description">接收远程鼠标移动</string>
<string name="mouse_receiver_plugin_name">鼠标接收器</string>
<string name="mouse_receiver_no_permissions">您需要启用无障碍模式</string>
<string name="mouse_receiver_no_permissions">要远程接受触摸输入,您必须授予完全控制此设备的访问权限</string>
<string name="view_status_title">状态</string>
<string name="battery_status_format">电池:%d%%</string>
<string name="battery_status_low_format">电池:%d%% 电量低</string>

View File

@@ -154,7 +154,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<string name="mouse_receiver_plugin_description">Receive remote mouse movement</string>
<string name="mouse_receiver_plugin_name">Mouse receiver</string>
<string name="mouse_receiver_no_permissions">You need to enable Accessibility Service</string>
<string name="mouse_receiver_no_permissions">To receive touch inputs remotely you need to grant Accessibility permissions to fully control your device</string>
<string name="view_status_title">Status</string>
<string name="battery_status_format">Battery: %d%%</string>

View File

@@ -12,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.NetworkPacket;
import java.io.IOException;
@@ -26,20 +27,20 @@ public abstract class BaseLink {
protected final Context context;
private final BaseLinkProvider linkProvider;
private final String deviceId;
private final ArrayList<PacketReceiver> receivers = new ArrayList<>();
protected BaseLink(@NonNull Context context, @NonNull String deviceId, @NonNull BaseLinkProvider linkProvider) {
protected BaseLink(@NonNull Context context, @NonNull BaseLinkProvider linkProvider) {
this.context = context;
this.linkProvider = linkProvider;
this.deviceId = deviceId;
}
/* To be implemented by each link for pairing handlers */
public abstract String getName();
public abstract DeviceInfo getDeviceInfo();
public String getDeviceId() {
return deviceId;
return getDeviceInfo().id;
}
public BaseLinkProvider getLinkProvider() {
@@ -54,7 +55,7 @@ public abstract class BaseLink {
}
//Should be called from a background thread listening for packets
protected void packetReceived(@NonNull NetworkPacket np) {
public void packetReceived(@NonNull NetworkPacket np) {
for(PacketReceiver pr : receivers) {
pr.onPacketReceived(np);
}

View File

@@ -8,23 +8,17 @@ package org.kde.kdeconnect.Backends;
import androidx.annotation.NonNull;
import org.kde.kdeconnect.NetworkPacket;
import java.security.cert.Certificate;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class BaseLinkProvider {
private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
public interface ConnectionReceiver {
void onConnectionReceived(@NonNull final String deviceId,
@NonNull final Certificate certificate,
@NonNull final NetworkPacket identityPacket,
@NonNull final BaseLink link);
void onConnectionReceived(@NonNull final BaseLink link);
void onConnectionLost(BaseLink link);
}
private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
public void addConnectionReceiver(ConnectionReceiver cr) {
connectionReceivers.add(cr);
}
@@ -36,13 +30,10 @@ public abstract class BaseLinkProvider {
/**
* To be called from the child classes when a link to a new device is established
*/
protected void onConnectionReceived(@NonNull final String deviceId,
@NonNull final Certificate certificate,
@NonNull final NetworkPacket identityPacket,
@NonNull final BaseLink link) {
protected void onConnectionReceived(@NonNull final BaseLink link) {
//Log.i("KDE/LinkProvider", "onConnectionReceived");
for(ConnectionReceiver cr : connectionReceivers) {
cr.onConnectionReceived(deviceId, certificate, identityPacket, link);
cr.onConnectionReceived(link);
}
}

View File

@@ -17,6 +17,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.NetworkPacket;
import java.io.IOException;
@@ -34,6 +35,7 @@ public class BluetoothLink extends BaseLink {
private final OutputStream output;
private final BluetoothDevice remoteAddress;
private final BluetoothLinkProvider linkProvider;
private final DeviceInfo deviceInfo;
private boolean continueAccepting = true;
@@ -93,11 +95,12 @@ public class BluetoothLink extends BaseLink {
}
});
public BluetoothLink(Context context, ConnectionMultiplexer connection, InputStream input, OutputStream output, BluetoothDevice remoteAddress, String deviceId, BluetoothLinkProvider linkProvider) {
super(context, deviceId, linkProvider);
public BluetoothLink(Context context, ConnectionMultiplexer connection, InputStream input, OutputStream output, BluetoothDevice remoteAddress, DeviceInfo deviceInfo, BluetoothLinkProvider linkProvider) {
super(context, linkProvider);
this.connection = connection;
this.input = input;
this.output = output;
this.deviceInfo = deviceInfo;
this.remoteAddress = remoteAddress;
this.linkProvider = linkProvider;
}
@@ -111,6 +114,11 @@ public class BluetoothLink extends BaseLink {
return "BluetoothLink";
}
@Override
public DeviceInfo getDeviceInfo() {
return deviceInfo;
}
public void disconnect() {
if (connection == null) {
return;
@@ -120,7 +128,7 @@ public class BluetoothLink extends BaseLink {
connection.close();
} catch (IOException ignored) {
}
linkProvider.disconnectedLink(this, getDeviceId(), remoteAddress);
linkProvider.disconnectedLink(this, remoteAddress);
}
private void sendMessage(NetworkPacket np) throws JSONException, IOException {

View File

@@ -20,6 +20,8 @@ import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.ThreadHelper;
import org.kde.kdeconnect.NetworkPacket;
@@ -54,10 +56,6 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
private void addLink(NetworkPacket identityPacket, BluetoothLink link) throws CertificateException {
String deviceId = identityPacket.getString("deviceId");
String certificateString = identityPacket.getString("certificate");
byte[] certificateBytes = Base64.decode(certificateString, 0);
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
Log.i("BluetoothLinkProvider", "addLink to " + deviceId);
BluetoothLink oldLink = visibleDevices.get(deviceId);
if (oldLink == link) {
@@ -65,8 +63,9 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
return;
}
visibleDevices.put(deviceId, link);
onConnectionReceived(deviceId, certificate, identityPacket, link);
onConnectionReceived(link);
link.startListening();
link.packetReceived(identityPacket);
if (oldLink != null) {
Log.i("BluetoothLinkProvider", "Removing old connection to same device");
oldLink.disconnect();
@@ -127,9 +126,9 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
return "BluetoothLinkProvider";
}
public void disconnectedLink(BluetoothLink link, String deviceId, BluetoothDevice remoteAddress) {
public void disconnectedLink(BluetoothLink link, BluetoothDevice remoteAddress) {
sockets.remove(remoteAddress);
visibleDevices.remove(deviceId);
visibleDevices.remove(link.getDeviceId());
onConnectionLost(link);
}
@@ -196,8 +195,10 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
OutputStream outputStream = connection.getDefaultOutputStream();
InputStream inputStream = connection.getDefaultInputStream();
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
NetworkPacket np = myDeviceInfo.toIdentityPacket();
np.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
byte[] message = np.serialize().getBytes(Charsets.UTF_8);
outputStream.write(message);
outputStream.flush();
@@ -223,9 +224,15 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
Log.i("BTLinkProvider/Server", "Received identity packet");
String certificateString = identityPacket.getString("certificate");
byte[] certificateBytes = Base64.decode(certificateString, 0);
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(identityPacket, certificate);
BluetoothLink link = new BluetoothLink(context, connection,
inputStream, outputStream, socket.getRemoteDevice(),
identityPacket.getString("deviceId"), BluetoothLinkProvider.this);
deviceInfo, BluetoothLinkProvider.this);
addLink(identityPacket, link);
} catch (Exception e) {
synchronized (sockets) {
@@ -360,7 +367,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
Log.i("BTLinkProvider/Client", "Received identity packet");
String myId = NetworkPacket.createIdentityPacket(context).getString("deviceId");
String myId = DeviceHelper.getDeviceId(context);
if (identityPacket.getString("deviceId").equals(myId)) {
// Probably won't happen, but just to be safe
connection.close();
@@ -373,10 +380,18 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
Log.i("BTLinkProvider/Client", "identity packet received, creating link");
final BluetoothLink link = new BluetoothLink(context, connection, inputStream, outputStream,
socket.getRemoteDevice(), identityPacket.getString("deviceId"), BluetoothLinkProvider.this);
String certificateString = identityPacket.getString("certificate");
byte[] certificateBytes = Base64.decode(certificateString, 0);
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(identityPacket, certificate);
final BluetoothLink link = new BluetoothLink(context, connection, inputStream, outputStream,
socket.getRemoteDevice(), deviceInfo, BluetoothLinkProvider.this);
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
NetworkPacket np2 = myDeviceInfo.toIdentityPacket();
np2.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
NetworkPacket np2 = NetworkPacket.createIdentityPacket(context);
link.sendPacket(np2, new Device.SendPacketStatusCallback() {
@Override
public void onSuccess() {

View File

@@ -16,6 +16,7 @@ import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.ThreadHelper;
import org.kde.kdeconnect.NetworkPacket;
@@ -42,6 +43,8 @@ public class LanLink extends BaseLink {
Locally, Remotely
}
private final DeviceInfo deviceInfo;
private volatile SSLSocket socket = null;
@Override
@@ -99,8 +102,9 @@ public class LanLink extends BaseLink {
return oldSocket;
}
public LanLink(Context context, String deviceId, BaseLinkProvider linkProvider, SSLSocket socket) throws IOException {
super(context, deviceId, linkProvider);
public LanLink(@NonNull Context context, @NonNull DeviceInfo deviceInfo, @NonNull BaseLinkProvider linkProvider, @NonNull SSLSocket socket) throws IOException {
super(context, linkProvider);
this.deviceInfo = deviceInfo;
reset(socket);
}
@@ -109,6 +113,11 @@ public class LanLink extends BaseLink {
return "LanLink";
}
@Override
public DeviceInfo getDeviceInfo() {
return deviceInfo;
}
@WorkerThread
@Override
public boolean sendPacket(@NonNull NetworkPacket np, @NonNull final Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) {

View File

@@ -17,6 +17,7 @@ import org.json.JSONException;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.ThreadHelper;
@@ -50,7 +51,7 @@ import kotlin.text.Charsets;
/**
* This LanLinkProvider creates {@link LanLink}s to other devices on the same
* WiFi network. The first packet sent over a socket must be an
* {@link NetworkPacket#createIdentityPacket(Context)}.
* {@link DeviceInfo#toIdentityPacket()}.
*
* @see #identityPacketReceived(NetworkPacket, Socket, LanLink.ConnectionStarted)
*/
@@ -128,13 +129,19 @@ public class LanLinkProvider extends BaseLinkProvider {
Log.i("KDE/LanLinkProvider", "Broadcast identity packet received from " + identityPacket.getString("deviceName"));
int tcpPort = identityPacket.getInt("tcpPort", MIN_PORT);
if (tcpPort < MIN_PORT || tcpPort > MAX_PORT) {
Log.e("LanLinkProvider", "TCP port outside of kdeconnect's range");
return;
}
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket(address, tcpPort);
configureSocket(socket);
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
NetworkPacket myIdentity = myDeviceInfo.toIdentityPacket();
OutputStream out = socket.getOutputStream();
NetworkPacket myIdentity = NetworkPacket.createIdentityPacket(context);
out.write(myIdentity.serialize().getBytes());
out.flush();
@@ -190,17 +197,19 @@ public class LanLinkProvider extends BaseLinkProvider {
identityPacketReceived(identityPacket, socket, connectionStarted);
}
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
String deviceName = identityPacket.getString("deviceName", "unknown");
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + deviceName + " trusted:" + isDeviceTrusted);
final SSLSocket sslSocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslSocket.addHandshakeCompletedListener(event -> {
String mode = clientMode ? "client" : "server";
try {
Certificate certificate = event.getPeerCertificates()[0];
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(deviceId, certificate, identityPacket, sslSocket);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(identityPacket, certificate);
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + deviceName + " secured with " + event.getCipherSuite());
addLink(sslSocket, deviceInfo);
} catch (IOException e) {
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + deviceName, e);
Device device = KdeConnect.getInstance().getDevice(deviceId);
if (device == null) {
return;
@@ -218,24 +227,26 @@ public class LanLinkProvider extends BaseLinkProvider {
/**
* Add or update a link in the {@link #visibleDevices} map.
*
* @param deviceId remote device id
* @param certificate remote device certificate
* @param identityPacket identity packet with the remote device's device name, type, protocol version, etc.
* @param socket a new Socket, which should be used to send and receive packets from the remote device
* @param deviceInfo remote device info
* @throws IOException if an exception is thrown by {@link LanLink#reset(SSLSocket)}
*/
private void addLink(String deviceId, Certificate certificate, final NetworkPacket identityPacket, SSLSocket socket) throws IOException {
LanLink currentLink = visibleDevices.get(deviceId);
if (currentLink != null) {
//Update old link
Log.i("KDE/LanLinkProvider", "Reusing same link for device " + deviceId);
final Socket oldSocket = currentLink.reset(socket);
private void addLink(SSLSocket socket, DeviceInfo deviceInfo) throws IOException {
LanLink link = visibleDevices.get(deviceInfo.id);
if (link != null) {
if (!link.getDeviceInfo().certificate.equals(deviceInfo.certificate)) {
Log.e("LanLinkProvider", "LanLink was asked to replace a socket but the certificate doesn't match, aborting");
return;
}
// Update existing link
Log.d("KDE/LanLinkProvider", "Reusing same link for device " + deviceInfo.id);
final Socket oldSocket = link.reset(socket);
} else {
Log.i("KDE/LanLinkProvider", "Creating a new link for device " + deviceId);
//Let's create the link
LanLink link = new LanLink(context, deviceId, this, socket);
visibleDevices.put(deviceId, link);
onConnectionReceived(deviceId, certificate, identityPacket, link);
// Create a new link
Log.d("KDE/LanLinkProvider", "Creating a new link for device " + deviceInfo.id);
link = new LanLink(context, deviceInfo, this, socket);
visibleDevices.put(deviceInfo.id, link);
onConnectionReceived(link);
}
}
@@ -368,7 +379,8 @@ public class LanLinkProvider extends BaseLinkProvider {
return;
}
NetworkPacket identity = NetworkPacket.createIdentityPacket(context);
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
NetworkPacket identity = myDeviceInfo.toIdentityPacket();
identity.set("tcpPort", tcpServer.getLocalPort());
byte[] bytes;

View File

@@ -14,12 +14,14 @@ import androidx.annotation.WorkerThread;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.NetworkPacket;
public class LoopbackLink extends BaseLink {
public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
super(context, "loopback", linkProvider);
super(context, linkProvider);
}
@Override
@@ -40,4 +42,9 @@ public class LoopbackLink extends BaseLink {
return true;
}
@Override
public DeviceInfo getDeviceInfo() {
return DeviceHelper.getDeviceInfo(context);
}
}

View File

@@ -9,9 +9,6 @@ package org.kde.kdeconnect.Backends.LoopbackBackend;
import android.content.Context;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.NetworkPacket;
public class LoopbackLinkProvider extends BaseLinkProvider {
@@ -32,9 +29,8 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
@Override
public void onNetworkChange() {
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
String deviceId = DeviceHelper.getDeviceId(context);
onConnectionReceived(deviceId, SslHelper.certificate, np, new LoopbackLink(context, this));
LoopbackLink link = new LoopbackLink(context, this);
onConnectionReceived(link);
}
@Override

View File

@@ -22,7 +22,6 @@ import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.IBinder;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -33,7 +32,6 @@ import androidx.lifecycle.MutableLiveData;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
@@ -82,10 +80,7 @@ public class BackgroundService extends Service {
private void registerLinkProviders() {
linkProviders.add(new LanLinkProvider(this));
String testLabSetting = Settings.System.getString(getContentResolver(), "firebase.test.lab");
if ("true".equals(testLabSetting)) {
linkProviders.add(new LoopbackLinkProvider(this));
}
// linkProviders.add(new LoopbackLinkProvider(this));
// linkProviders.add(new BluetoothLinkProvider(this));
}
@@ -206,6 +201,7 @@ public class BackgroundService extends Service {
.setContentIntent(pi)
.setPriority(NotificationCompat.PRIORITY_MIN) //MIN so it's not shown in the status bar before Oreo, on Oreo it will be bumped to LOW
.setShowWhen(false)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.setAutoCancel(false);
notification.setGroup("BackgroundService");

View File

@@ -14,7 +14,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.AnyThread;
@@ -26,7 +25,6 @@ import androidx.core.content.ContextCompat;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.lang3.StringUtils;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.NotificationHelper;
@@ -38,13 +36,9 @@ import org.kde.kdeconnect_tp.R;
import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -53,30 +47,26 @@ public class Device implements BaseLink.PacketReceiver {
private final Context context;
private final String deviceId;
private String name;
public Certificate certificate;
final DeviceInfo deviceInfo;
private int notificationId;
private int protocolVersion;
private DeviceType deviceType;
PairingHandler pairingHandler;
private final CopyOnWriteArrayList<PairingHandler.PairingCallback> pairingCallbacks = new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
private DevicePacketQueue packetQueue;
private List<String> supportedPlugins = new ArrayList<>();
private List<String> supportedPlugins;
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> pluginsWithoutPermissions = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> pluginsWithoutOptionalPermissions = new ConcurrentHashMap<>();
private MultiValuedMap<String, String> pluginsByIncomingInterface = new ArrayListValuedHashMap<>();
private final SharedPreferences settings;
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
private Set<String> incomingCapabilities = new HashSet<>();
public boolean supportsPacketType(String type) {
if (incomingCapabilities == null) {
if (deviceInfo.incomingCapabilities == null) {
return true;
} else {
return incomingCapabilities.contains(type);
return deviceInfo.incomingCapabilities.contains(type);
}
}
@@ -84,109 +74,52 @@ public class Device implements BaseLink.PacketReceiver {
void onPluginsChanged(@NonNull Device device);
}
public enum DeviceType {
Phone,
Tablet,
Computer,
Tv;
static public DeviceType FromString(String s) {
if ("tablet".equals(s)) return Tablet;
if ("phone".equals(s)) return Phone;
if ("tv".equals(s)) return Tv;
return Computer; //Default
}
@NonNull
public String toString() {
switch (this) {
case Tablet:
return "tablet";
case Phone:
return "phone";
case Tv:
return "tv";
default:
return "desktop";
}
}
public Drawable getIcon(Context context) {
int drawableId;
switch (this) {
case Phone:
drawableId = R.drawable.ic_device_phone_32dp;
break;
case Tablet:
drawableId = R.drawable.ic_device_tablet_32dp;
break;
case Tv:
drawableId = R.drawable.ic_device_tv_32dp;
break;
default:
drawableId = R.drawable.ic_device_laptop_32dp;
}
return ContextCompat.getDrawable(context, drawableId);
}
}
// Remembered trusted device, we need to wait for a incoming Link to communicate
/**
* Constructor for remembered, already-trusted devices.
* Given the deviceId, it will load the other properties from SharedPreferences.
*/
Device(@NonNull Context context, @NonNull String deviceId) throws CertificateException {
this.context = context;
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
this.deviceInfo = DeviceInfo.loadFromSettings(context, deviceId, settings);
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.Paired);
this.deviceId = deviceId;
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
this.protocolVersion = 0; //We don't know it yet
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
this.certificate = SslHelper.getDeviceCertificate(context, deviceId);
Log.i("Device","Loading trusted device: " + this.name);
//Assume every plugin is supported until addLink is called and we can get the actual list
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
//Do not load plugins yet, the device is not present
//reloadPluginsFromSettings();
this.supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); // Assume all are supported until we receive capabilities
Log.i("Device","Loading trusted device: " + deviceInfo.name);
}
// Device known via an incoming connection sent to us via a Link, we don't trust it yet
Device(@NonNull Context context, @NonNull String deviceId, @NonNull Certificate certificate, @NonNull NetworkPacket identityPacket, @NonNull BaseLink dl) {
Log.i("Device","Creating untrusted device");
/**
* Constructor for devices discovered but not trusted yet.
* Gets the DeviceInfo by calling link.getDeviceInfo() on the link passed.
* This constructor also calls addLink() with the link you pass to it, since it's not legal to have an unpaired Device with 0 links.
*/
Device(@NonNull Context context, @NonNull BaseLink link) {
this.context = context;
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
this.deviceInfo = link.getDeviceInfo();
this.settings = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE);
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.NotPaired);
this.deviceId = deviceId;
this.certificate = certificate;
// The following properties are read from the identityPacket in addLink since they can change in future identity packets
this.name = context.getString(R.string.unknown_device);
this.deviceType = DeviceType.Computer;
this.protocolVersion = 0;
addLink(identityPacket, dl);
this.supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); // Assume all are supported until we receive capabilities
Log.i("Device","Creating untrusted device: "+ deviceInfo.name);
addLink(link);
}
public String getName() {
return StringUtils.defaultString(name, context.getString(R.string.unknown_device));
return deviceInfo.name;
}
public Drawable getIcon() {
return deviceType.getIcon(context);
return deviceInfo.type.getIcon(context);
}
public DeviceType getDeviceType() {
return deviceType;
return deviceInfo.type;
}
public String getDeviceId() {
return deviceId;
return deviceInfo.id;
}
public Certificate getCertificate() {
return deviceInfo.certificate;
}
public Context getContext() {
@@ -195,7 +128,7 @@ public class Device implements BaseLink.PacketReceiver {
//Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
public int compareProtocolVersion() {
return protocolVersion - DeviceHelper.ProtocolVersion;
return deviceInfo.protocolVersion - DeviceHelper.ProtocolVersion;
}
@@ -254,28 +187,26 @@ public class Device implements BaseLink.PacketReceiver {
@Override
public void pairingSuccessful() {
Log.i("Device", "pairing successful, adding to trusted devices list");
hidePairingNotification();
// Store current device certificate so we can check it in the future (TOFU)
SharedPreferences.Editor editor = context.getSharedPreferences(getDeviceId(), Context.MODE_PRIVATE).edit();
try {
String encodedCertificate = Base64.encodeToString(certificate.getEncoded(), 0);
editor.putString("certificate", encodedCertificate);
} catch(CertificateEncodingException e) {
throw new RuntimeException(e);
}
editor.putString("deviceName", name);
editor.putString("deviceType", deviceType.toString());
editor.apply();
deviceInfo.saveInSettings(Device.this.settings);
// Store as trusted device
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().putBoolean(deviceId, true).apply();
preferences.edit().putBoolean(deviceInfo.id, true).apply();
reloadPluginsFromSettings();
try {
reloadPluginsFromSettings();
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
cb.pairingSuccessful();
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
cb.pairingSuccessful();
}
} catch (Exception e) {
Log.e("PairingHandler", "Exception in pairingSuccessful. Not unpairing because saving the trusted device succeeded");
e.printStackTrace();
}
}
@@ -289,10 +220,11 @@ public class Device implements BaseLink.PacketReceiver {
@Override
public void unpaired() {
Log.i("Device", "unpaired, removing from trusted devices list");
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().remove(deviceId).apply();
preferences.edit().remove(deviceInfo.id).apply();
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
SharedPreferences devicePreferences = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE);
devicePreferences.edit().clear().apply();
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
@@ -334,7 +266,7 @@ public class Device implements BaseLink.PacketReceiver {
final NotificationManager notificationManager = ContextCompat.getSystemService(getContext(), NotificationManager.class);
String verificationKeyShort = SslHelper.getVerificationKey(SslHelper.certificate, certificate).substring(8);
String verificationKeyShort = SslHelper.getVerificationKey(SslHelper.certificate, deviceInfo.certificate).substring(8);
Notification noti = new NotificationCompat.Builder(getContext(), NotificationHelper.Channels.DEFAULT)
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
@@ -365,7 +297,7 @@ public class Device implements BaseLink.PacketReceiver {
return !links.isEmpty();
}
public void addLink(NetworkPacket identityPacket, BaseLink link) {
public void addLink(BaseLink link) {
if (links.isEmpty()) {
packetQueue = new DevicePacketQueue(this);
}
@@ -373,33 +305,11 @@ public class Device implements BaseLink.PacketReceiver {
links.add(link);
link.addPacketReceiver(this);
this.protocolVersion = identityPacket.getInt("protocolVersion");
boolean hasChanges = updateDeviceInfo(link.getDeviceInfo());
if (identityPacket.has("deviceName")) {
this.name = identityPacket.getString("deviceName", this.name);
SharedPreferences.Editor editor = settings.edit();
editor.putString("deviceName", this.name);
editor.apply();
if (hasChanges || links.size() == 1) {
reloadPluginsFromSettings();
}
if (identityPacket.has("deviceType")) {
this.deviceType = DeviceType.FromString(identityPacket.getString("deviceType", "desktop"));
}
Log.i("KDE/Device", "addLink " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
Set<String> outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities", null);
Set<String> incomingCapabilities = identityPacket.getStringSet("incomingCapabilities", null);
if (incomingCapabilities != null && outgoingCapabilities != null) {
supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(incomingCapabilities, outgoingCapabilities));
} else {
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
}
this.incomingCapabilities = incomingCapabilities;
reloadPluginsFromSettings();
}
public void removeLink(BaseLink link) {
@@ -417,6 +327,30 @@ public class Device implements BaseLink.PacketReceiver {
}
}
public boolean updateDeviceInfo(@NonNull DeviceInfo newDeviceInfo) {
boolean hasChanges = false;
if (!deviceInfo.name.equals(newDeviceInfo.name) || deviceInfo.type != newDeviceInfo.type) {
hasChanges = true;
deviceInfo.name = newDeviceInfo.name;
deviceInfo.type = newDeviceInfo.type;
if (isPaired()) {
deviceInfo.saveInSettings(settings);
}
}
if (deviceInfo.outgoingCapabilities != newDeviceInfo.outgoingCapabilities ||
deviceInfo.incomingCapabilities != newDeviceInfo.incomingCapabilities) {
if (newDeviceInfo.outgoingCapabilities != null && newDeviceInfo.incomingCapabilities != null) {
hasChanges = true;
Log.i("updateDeviceInfo", "Updating supported plugins according to new capabilities");
supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(newDeviceInfo.incomingCapabilities, newDeviceInfo.outgoingCapabilities));
}
}
return hasChanges;
}
@Override
public void onPacketReceived(@NonNull NetworkPacket np) {
@@ -580,7 +514,7 @@ public class Device implements BaseLink.PacketReceiver {
}
if (!success) {
Log.e("KDE/sendPacket", "No device link (of " + links.size() + " available) could send the packet. Packet " + np.getType() + " to " + name + " lost!");
Log.e("KDE/sendPacket", "No device link (of " + links.size() + " available) could send the packet. Packet " + np.getType() + " to " + deviceInfo.name + " lost!");
}
return success;
@@ -641,8 +575,6 @@ public class Device implements BaseLink.PacketReceiver {
return false;
}
plugins.put(pluginKey, plugin);
if (!plugin.checkRequiredPermissions()) {
Log.d("KDE/addPlugin", "No permission " + pluginKey);
plugins.remove(pluginKey);
@@ -650,6 +582,7 @@ public class Device implements BaseLink.PacketReceiver {
return false;
} else {
Log.d("KDE/addPlugin", "Permissions OK " + pluginKey);
plugins.put(pluginKey, plugin);
pluginsWithoutPermissions.remove(pluginKey);
if (plugin.checkOptionalPermissions()) {
Log.d("KDE/addPlugin", "Optional Permissions OK " + pluginKey);
@@ -697,6 +630,7 @@ public class Device implements BaseLink.PacketReceiver {
}
public void reloadPluginsFromSettings() {
Log.i("Device", deviceInfo.name +": reloading plugins");
MultiValuedMap<String, String> newPluginsByIncomingInterface = new ArrayListValuedHashMap<>();
for (String pluginKey : supportedPlugins) {

View File

@@ -0,0 +1,135 @@
/*
* SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect
import android.content.Context
import android.content.SharedPreferences
import android.graphics.drawable.Drawable
import android.util.Base64
import androidx.core.content.ContextCompat
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
import org.kde.kdeconnect_tp.R
import java.security.cert.Certificate
import java.security.cert.CertificateEncodingException
import java.security.cert.CertificateException
/**
* DeviceInfo contains all the properties needed to instantiate a Device.
*/
class DeviceInfo(
@JvmField val id : String,
@JvmField val certificate : Certificate,
@JvmField var name : String,
@JvmField var type : DeviceType,
@JvmField var protocolVersion : Int = 0,
@JvmField var incomingCapabilities : Set<String>? = null,
@JvmField var outgoingCapabilities : Set<String>? = null,
) {
/**
* Saves the info in settings so it can be restored later using loadFromSettings().
* This is used to keep info from paired devices, even when they are not reachable.
* The capabilities and protocol version are not persisted.
*/
fun saveInSettings(settings: SharedPreferences) {
val editor = settings.edit()
try {
val encodedCertificate = Base64.encodeToString(certificate.encoded, 0)
editor.putString("certificate", encodedCertificate)
} catch (e: CertificateEncodingException) {
throw RuntimeException(e)
}
editor.putString("deviceName", name)
editor.putString("deviceType", type.toString())
editor.apply()
}
/**
* Serializes to a NetworkPacket, which LanLinkProvider uses to send this data over the network.
* The serialization doesn't include the certificate, since LanLink can query that from the socket.
* Can be deserialized using fromIdentityPacketAndCert(), given a certificate.
*/
fun toIdentityPacket(): NetworkPacket {
val np = NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY)
np.set("deviceId", id)
np.set("deviceName", name)
np.set("protocolVersion", protocolVersion)
np.set("deviceType", type.toString())
np.set("incomingCapabilities", incomingCapabilities)
np.set("outgoingCapabilities", outgoingCapabilities)
return np
}
companion object {
/**
* Recreates a DeviceInfo object that was persisted using saveInSettings()
*/
@JvmStatic
@Throws(CertificateException::class)
fun loadFromSettings(context : Context, deviceId: String, settings: SharedPreferences): DeviceInfo {
val deviceName = settings.getString("deviceName", "unknown")!!
val deviceType = DeviceType.fromString(settings.getString("deviceType", "desktop")!!)
val certificate = SslHelper.getDeviceCertificate(context, deviceId)
return DeviceInfo(id = deviceId, name = deviceName, type = deviceType, certificate = certificate)
}
/**
* Recreates a DeviceInfo object that was serialized using toIdentityPacket().
* Since toIdentityPacket() doesn't serialize the certificate, this needs to be passed separately.
*/
@JvmStatic
fun fromIdentityPacketAndCert(identityPacket : NetworkPacket, certificate : Certificate): DeviceInfo {
val deviceId = identityPacket.getString("deviceId")
val deviceName = identityPacket.getString("deviceName", "unknown")
val protocolVersion = identityPacket.getInt("protocolVersion")
val deviceType = DeviceType.fromString(identityPacket.getString("deviceType", "desktop"))
val incomingCapabilities = identityPacket.getStringSet("incomingCapabilities")
val outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities")
return DeviceInfo(id = deviceId, name = deviceName, type = deviceType, certificate = certificate,
protocolVersion = protocolVersion, incomingCapabilities = incomingCapabilities, outgoingCapabilities = outgoingCapabilities)
}
}
}
enum class DeviceType {
Phone, Tablet, Computer, Tv;
override fun toString(): String {
return when (this) {
Tablet -> "tablet"
Phone -> "phone"
Tv -> "tv"
else -> "desktop"
}
}
fun getIcon(context: Context): Drawable? {
val drawableId: Int = when (this) {
Phone -> R.drawable.ic_device_phone_32dp
Tablet -> R.drawable.ic_device_tablet_32dp
Tv -> R.drawable.ic_device_tv_32dp
else -> R.drawable.ic_device_laptop_32dp
}
return ContextCompat.getDrawable(context, drawableId)
}
companion object {
@JvmStatic
fun fromString(s: String): DeviceType {
return when (s) {
"phone" -> Phone
"tablet" -> Tablet
"tv" -> Tv
else -> Computer
}
}
}
}

View File

@@ -16,10 +16,14 @@ import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import com.univocity.parsers.common.TextParsingException;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.DeviceType;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Plugins.PluginFactory;
import java.io.BufferedReader;
import java.io.IOException;
@@ -53,13 +57,13 @@ public class DeviceHelper {
return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
}
public static Device.DeviceType getDeviceType(Context context) {
public static DeviceType getDeviceType(Context context) {
if (isTv(context)) {
return Device.DeviceType.Tv;
return DeviceType.Tv;
} else if (isTablet()) {
return Device.DeviceType.Tablet;
return DeviceType.Tablet;
} else {
return Device.DeviceType.Phone;
return DeviceType.Phone;
}
}
@@ -108,7 +112,7 @@ public class DeviceHelper {
Log.e("DeviceHelper", "Didn't find a device name for " + Build.MODEL);
}
}
} catch(IOException e) {
} catch(IOException | TextParsingException e) {
e.printStackTrace();
}
fetchingName = false;
@@ -146,4 +150,14 @@ public class DeviceHelper {
return preferences.getString(KEY_DEVICE_ID_PREFERENCE, null);
}
public static DeviceInfo getDeviceInfo(Context context) {
return new DeviceInfo(getDeviceId(context),
SslHelper.certificate,
getDeviceName(context),
DeviceHelper.getDeviceType(context),
ProtocolVersion,
PluginFactory.getIncomingCapabilities(),
PluginFactory.getOutgoingCapabilities());
}
}

View File

@@ -2,7 +2,7 @@
* SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
*/
package org.kde.kdeconnect;
@@ -24,7 +24,6 @@ import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.ThemeUtil;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -120,8 +119,9 @@ public class KdeConnect extends Application {
devices.put(deviceId, device);
device.addPairingCallback(devicePairingCallback);
} catch (CertificateException e) {
Log.e("KdeConnect", "Could not load trusted device, certificate not valid: " + deviceId);
Log.w("KdeConnect", "Couldn't load the certificate for a remembered device. Removing from trusted list.");
e.printStackTrace();
preferences.edit().remove(deviceId).apply();
}
}
}
@@ -151,18 +151,13 @@ public class KdeConnect extends Application {
private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() {
@Override
public void onConnectionReceived(@NonNull final String deviceId,
@NonNull final Certificate certificate,
@NonNull final NetworkPacket identityPacket,
@NonNull final BaseLink link) {
Device device = devices.get(deviceId);
public void onConnectionReceived(@NonNull final BaseLink link) {
Device device = devices.get(link.getDeviceId());
if (device != null) {
Log.i("KDE/Application", "addLink, known device: " + deviceId);
device.addLink(identityPacket, link);
device.addLink(link);
} else {
Log.i("KDE/Application", "addLink,unknown device: " + deviceId);
device = new Device(KdeConnect.this, deviceId, certificate, identityPacket, link);
devices.put(deviceId, device);
device = new Device(KdeConnect.this, link);
devices.put(link.getDeviceId(), device);
device.addPairingCallback(devicePairingCallback);
}
onDeviceListChanged();

View File

@@ -26,17 +26,14 @@ public class KdeConnectBroadcastReceiver extends BroadcastReceiver {
Log.i("KdeConnect", "MyUpdateReceiver");
BackgroundService.Start(context);
break;
case Intent.ACTION_PACKAGE_REPLACED:
Log.i("KdeConnect", "UpdateReceiver");
if (!intent.getData().getSchemeSpecificPart().equals(context.getPackageName())) {
Log.i("KdeConnect", "Ignoring, it's not me!");
return;
}
BackgroundService.Start(context);
break;
case Intent.ACTION_BOOT_COMPLETED:
Log.i("KdeConnect", "KdeConnectBroadcastReceiver");
BackgroundService.Start(context);
try {
BackgroundService.Start(context);
} catch (IllegalStateException e) { // To catch ForegroundServiceStartNotAllowedException
Log.w("BroadcastReceiver", "Couldn't start the foreground service.");
e.printStackTrace();
}
break;
case WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION:
case WifiManager.WIFI_STATE_CHANGED_ACTION:

View File

@@ -6,15 +6,10 @@
package org.kde.kdeconnect;
import android.content.Context;
import android.util.Log;
import org.apache.commons.io.IOUtils;
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.IOException;
@@ -263,26 +258,6 @@ public class NetworkPacket {
return np;
}
static public NetworkPacket createIdentityPacket(Context context) {
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY);
String deviceId = DeviceHelper.getDeviceId(context);
try {
np.mBody.put("deviceId", deviceId);
np.mBody.put("deviceName", DeviceHelper.getDeviceName(context));
np.mBody.put("protocolVersion", DeviceHelper.ProtocolVersion);
np.mBody.put("deviceType", DeviceHelper.getDeviceType(context).toString());
np.mBody.put("incomingCapabilities", new JSONArray(PluginFactory.getIncomingCapabilities()));
np.mBody.put("outgoingCapabilities", new JSONArray(PluginFactory.getOutgoingCapabilities()));
} catch (Exception e) {
Log.e("NetworkPacket", "Exception on createIdentityPacket", e);
}
return np;
}
public void setPayload(Payload payload) { mPayload = payload; }
public Payload getPayload() {

View File

@@ -24,7 +24,6 @@ import org.kde.kdeconnect_tp.R;
public class BatteryPlugin extends Plugin {
private final static String PACKET_TYPE_BATTERY = "kdeconnect.battery";
private final static String PACKET_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;
@@ -95,11 +94,6 @@ public class BatteryPlugin extends Plugin {
intentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
Intent currentState = context.registerReceiver(receiver, intentFilter);
receiver.onReceive(context, currentState);
// Request new battery info from the linked device
NetworkPacket np = new NetworkPacket(PACKET_TYPE_BATTERY_REQUEST);
np.set("request", true);
device.sendPacket(np);
return true;
}
@@ -111,16 +105,11 @@ public class BatteryPlugin extends Plugin {
@Override
public boolean onPacketReceived(@NonNull NetworkPacket np) {
if (np.getBoolean("request")) {
device.sendPacket(batteryInfo);
if (!PACKET_TYPE_BATTERY.equals(np.getType())) {
return false;
}
if (PACKET_TYPE_BATTERY.equals(np.getType())) {
remoteBatteryInfo = new DeviceBatteryInfo(np);
device.onPluginsChanged();
}
remoteBatteryInfo = new DeviceBatteryInfo(np);
device.onPluginsChanged();
return true;
}
@@ -140,12 +129,12 @@ public class BatteryPlugin extends Plugin {
@Override
public @NonNull String[] getSupportedPacketTypes() {
return new String[]{PACKET_TYPE_BATTERY_REQUEST, PACKET_TYPE_BATTERY};
return new String[]{PACKET_TYPE_BATTERY};
}
@Override
public @NonNull String[] getOutgoingPacketTypes() {
return new String[]{PACKET_TYPE_BATTERY_REQUEST, PACKET_TYPE_BATTERY};
return new String[]{PACKET_TYPE_BATTERY};
}
}

View File

@@ -19,7 +19,7 @@ import android.view.KeyEvent;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceType;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
@@ -33,7 +33,7 @@ public class BigscreenPlugin extends Plugin {
@Override
public boolean isCompatible() {
return device.getDeviceType().equals(Device.DeviceType.Tv) && super.isCompatible();
return device.getDeviceType().equals(DeviceType.Tv) && super.isCompatible();
}
@Override

View File

@@ -57,13 +57,6 @@ public class ConnectivityReportPlugin extends Plugin {
*/
private final static String PACKET_TYPE_CONNECTIVITY_REPORT = "kdeconnect.connectivity_report";
/**
* Packet sent to request the current connectivity state
* <p>
* The request packet shall contain no body
*/
private final static String PACKET_TYPE_CONNECTIVITY_REPORT_REQUEST = "kdeconnect.connectivity_report.request";
private final NetworkPacket connectivityInfo = new NetworkPacket(PACKET_TYPE_CONNECTIVITY_REPORT);
OnSubscriptionsChangedListener subListener = null;
@@ -219,29 +212,28 @@ public class ConnectivityReportPlugin extends Plugin {
runWithLooper(() -> {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
TelephonyHelper.cancelActiveSubscriptionIDsListener(context, subListener);
if (subListener != null) {
TelephonyHelper.cancelActiveSubscriptionIDsListener(context, subListener);
subListener = null;
}
}
for (Integer subID : listeners.keySet()) {
Log.i("ConnectivityReport", "Removed subscription ID " + subID);
tm.listen(listeners.get(subID), PhoneStateListener.LISTEN_NONE);
}
listeners.clear();
states.clear();
});
}
@Override
public boolean onPacketReceived(@NonNull NetworkPacket np) {
if (PACKET_TYPE_CONNECTIVITY_REPORT_REQUEST.equals(np.getType())) {
Log.i("ConnectivityReport", "Requested");
serializeSignalStrengths();
device.sendPacket(connectivityInfo);
}
return true;
return false;
}
@Override
public @NonNull String[] getSupportedPacketTypes() {
return new String[]{PACKET_TYPE_CONNECTIVITY_REPORT_REQUEST};
return new String[]{};
}
@Override

View File

@@ -17,12 +17,13 @@ import androidx.fragment.app.DialogFragment;
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.Plugins.RemoteKeyboardPlugin.RemoteKeyboardPlugin;
import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect.UserInterface.StartActivityAlertDialogFragment;
import org.kde.kdeconnect_tp.R;
//@PluginFactory.LoadablePlugin
@PluginFactory.LoadablePlugin
@RequiresApi(api = Build.VERSION_CODES.N)
public class MouseReceiverPlugin extends Plugin {
private final static String PACKET_TYPE_MOUSEPAD_REQUEST = "kdeconnect.mousepad.request";

View File

@@ -204,10 +204,12 @@ internal object AlbumArtCache {
}
//Only fetch an URL if we're not fetching it already
if (url in fetchUrlList || url in isFetchingList) {
return
synchronized(fetchUrlList) {
if (url in fetchUrlList || url in isFetchingList) {
return
}
fetchUrlList.add(url)
}
fetchUrlList.add(url)
initiateFetch()
}
@@ -215,12 +217,14 @@ internal object AlbumArtCache {
* Does the actual fetching and makes sure only not too many fetches are running at the same time
*/
private fun initiateFetch() {
if (numFetching >= 2 || fetchUrlList.isEmpty()) return
//Fetch the last-requested url first, it will probably be needed first
val url = fetchUrlList.last()
//Remove the url from the to-fetch list
fetchUrlList.remove(url)
var url : URL;
synchronized(fetchUrlList) {
if (numFetching >= 2 || fetchUrlList.isEmpty()) return
//Fetch the last-requested url first, it will probably be needed first
url = fetchUrlList.last()
//Remove the url from the to-fetch list
fetchUrlList.remove(url)
}
if ("file" == url.protocol) {
throw AssertionError("Not file urls should be possible here!")
}

View File

@@ -168,12 +168,13 @@ public class MprisMediaSession implements
* Prefers playing devices/mpris players, but tries to keep displaying the same
* player and device, while possible.
*/
private void updateCurrentPlayer() {
private MprisPlugin.MprisPlayer updateCurrentPlayer() {
Pair<Device, MprisPlugin.MprisPlayer> player = findPlayer();
//Update the last-displayed device and player
notificationDevice = player.first == null ? null : player.first.getDeviceId();
notificationPlayer = player.second;
return notificationPlayer;
}
private Pair<Device, MprisPlugin.MprisPlayer> findPlayer() {
@@ -215,7 +216,7 @@ public class MprisMediaSession implements
}
private MprisPlugin.MprisPlayer getPlayerFromDevice(Device device, MprisPlugin.MprisPlayer preferredPlayer) {
if (!mprisDevices.contains(device.getDeviceId()))
if (device == null || !mprisDevices.contains(device.getDeviceId()))
return null;
MprisPlugin plugin = device.getPlugin(MprisPlugin.class);
@@ -263,20 +264,17 @@ public class MprisMediaSession implements
return;
}
synchronized (instance) {
if (mediaSession == null) {
mediaSession = new MediaSessionCompat(context, MPRIS_MEDIA_SESSION_TAG);
mediaSession.setCallback(mediaSessionCallback, new Handler(context.getMainLooper()));
// Deprecated flags not required in Build.VERSION_CODES.O and later
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
}
//Make sure our information is up-to-date
MprisPlugin.MprisPlayer currentPlayer = updateCurrentPlayer();
Device device = KdeConnect.getInstance().getDevice(notificationDevice);
if (device == null) {
closeMediaNotification();
return;
}
//Make sure our information is up-to-date
updateCurrentPlayer();
//If the player disappeared (and no other playing one found), just remove the notification
if (notificationPlayer == null) {
if (currentPlayer == null) {
closeMediaNotification();
return;
}
@@ -285,38 +283,37 @@ public class MprisMediaSession implements
MediaMetadataCompat.Builder metadata = new MediaMetadataCompat.Builder();
metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, notificationPlayer.getTitle());
metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentPlayer.getTitle());
if (!notificationPlayer.getArtist().isEmpty()) {
metadata.putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, notificationPlayer.getArtist());
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, notificationPlayer.getArtist());
if (!currentPlayer.getArtist().isEmpty()) {
metadata.putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, currentPlayer.getArtist());
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentPlayer.getArtist());
}
if (!notificationPlayer.getAlbum().isEmpty()) {
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, notificationPlayer.getAlbum());
if (!currentPlayer.getAlbum().isEmpty()) {
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentPlayer.getAlbum());
}
if (notificationPlayer.getLength() > 0) {
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, notificationPlayer.getLength());
if (currentPlayer.getLength() > 0) {
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, currentPlayer.getLength());
}
Bitmap albumArt = notificationPlayer.getAlbumArt();
Bitmap albumArt = currentPlayer.getAlbumArt();
if (albumArt != null) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt);
}
mediaSession.setMetadata(metadata.build());
PlaybackStateCompat.Builder playbackState = new PlaybackStateCompat.Builder();
if (notificationPlayer.isPlaying()) {
playbackState.setState(PlaybackStateCompat.STATE_PLAYING, notificationPlayer.getPosition(), 1.0f);
if (currentPlayer.isPlaying()) {
playbackState.setState(PlaybackStateCompat.STATE_PLAYING, currentPlayer.getPosition(), 1.0f);
} else {
playbackState.setState(PlaybackStateCompat.STATE_PAUSED, notificationPlayer.getPosition(), 0.0f);
playbackState.setState(PlaybackStateCompat.STATE_PAUSED, currentPlayer.getPosition(), 0.0f);
}
//Create all actions (previous/play/pause/next)
Intent iPlay = new Intent(context, MprisMediaNotificationReceiver.class);
iPlay.setAction(MprisMediaNotificationReceiver.ACTION_PLAY);
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, currentPlayer.getPlayerName());
PendingIntent piPlay = PendingIntent.getBroadcast(context, 0, iPlay, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
NotificationCompat.Action.Builder aPlay = new NotificationCompat.Action.Builder(
R.drawable.ic_play_white, context.getString(R.string.mpris_play), piPlay);
@@ -324,7 +321,7 @@ public class MprisMediaSession implements
Intent iPause = new Intent(context, MprisMediaNotificationReceiver.class);
iPause.setAction(MprisMediaNotificationReceiver.ACTION_PAUSE);
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, currentPlayer.getPlayerName());
PendingIntent piPause = PendingIntent.getBroadcast(context, 0, iPause, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
NotificationCompat.Action.Builder aPause = new NotificationCompat.Action.Builder(
R.drawable.ic_pause_white, context.getString(R.string.mpris_pause), piPause);
@@ -332,7 +329,7 @@ public class MprisMediaSession implements
Intent iPrevious = new Intent(context, MprisMediaNotificationReceiver.class);
iPrevious.setAction(MprisMediaNotificationReceiver.ACTION_PREVIOUS);
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, currentPlayer.getPlayerName());
PendingIntent piPrevious = PendingIntent.getBroadcast(context, 0, iPrevious, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
NotificationCompat.Action.Builder aPrevious = new NotificationCompat.Action.Builder(
R.drawable.ic_previous_white, context.getString(R.string.mpris_previous), piPrevious);
@@ -340,14 +337,14 @@ public class MprisMediaSession implements
Intent iNext = new Intent(context, MprisMediaNotificationReceiver.class);
iNext.setAction(MprisMediaNotificationReceiver.ACTION_NEXT);
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, currentPlayer.getPlayerName());
PendingIntent piNext = PendingIntent.getBroadcast(context, 0, iNext, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
NotificationCompat.Action.Builder aNext = new NotificationCompat.Action.Builder(
R.drawable.ic_next_white, context.getString(R.string.mpris_next), piNext);
Intent iOpenActivity = new Intent(context, MprisActivity.class);
iOpenActivity.putExtra("deviceId", notificationDevice);
iOpenActivity.putExtra("player", notificationPlayer.getPlayerName());
iOpenActivity.putExtra("player", currentPlayer.getPlayerName());
PendingIntent piOpenActivity = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(iOpenActivity)
@@ -362,30 +359,30 @@ public class MprisMediaSession implements
.setShowWhen(false)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC)
.setSubText(KdeConnect.getInstance().getDevice(notificationDevice).getName());
.setSubText(device.getName());
notification.setContentTitle(notificationPlayer.getTitle());
notification.setContentTitle(currentPlayer.getTitle());
//Only set the notification body text if we have an author and/or album
if (!notificationPlayer.getArtist().isEmpty() && !notificationPlayer.getAlbum().isEmpty()) {
notification.setContentText(notificationPlayer.getArtist() + " - " + notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayerName() + ")");
} else if (!notificationPlayer.getArtist().isEmpty()) {
notification.setContentText(notificationPlayer.getArtist() + " (" + notificationPlayer.getPlayerName() + ")");
} else if (!notificationPlayer.getAlbum().isEmpty()) {
notification.setContentText(notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayerName() + ")");
if (!currentPlayer.getArtist().isEmpty() && !currentPlayer.getAlbum().isEmpty()) {
notification.setContentText(currentPlayer.getArtist() + " - " + currentPlayer.getAlbum() + " (" + currentPlayer.getPlayerName() + ")");
} else if (!currentPlayer.getArtist().isEmpty()) {
notification.setContentText(currentPlayer.getArtist() + " (" + currentPlayer.getPlayerName() + ")");
} else if (!currentPlayer.getAlbum().isEmpty()) {
notification.setContentText(currentPlayer.getAlbum() + " (" + currentPlayer.getPlayerName() + ")");
} else {
notification.setContentText(notificationPlayer.getPlayerName());
notification.setContentText(currentPlayer.getPlayerName());
}
if (albumArt != null) {
notification.setLargeIcon(albumArt);
}
if (!notificationPlayer.isPlaying()) {
if (!currentPlayer.isPlaying()) {
Intent iCloseNotification = new Intent(context, MprisMediaNotificationReceiver.class);
iCloseNotification.setAction(MprisMediaNotificationReceiver.ACTION_CLOSE_NOTIFICATION);
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, currentPlayer.getPlayerName());
PendingIntent piCloseNotification = PendingIntent.getBroadcast(context, 0, iCloseNotification, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
notification.setDeleteIntent(piCloseNotification);
}
@@ -393,37 +390,36 @@ public class MprisMediaSession implements
//Add media control actions
int numActions = 0;
long playbackActions = 0;
if (notificationPlayer.isGoPreviousAllowed()) {
if (currentPlayer.isGoPreviousAllowed()) {
notification.addAction(aPrevious.build());
playbackActions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
++numActions;
}
if (notificationPlayer.isPlaying() && notificationPlayer.isPauseAllowed()) {
if (currentPlayer.isPlaying() && currentPlayer.isPauseAllowed()) {
notification.addAction(aPause.build());
playbackActions |= PlaybackStateCompat.ACTION_PAUSE;
++numActions;
}
if (!notificationPlayer.isPlaying() && notificationPlayer.isPlayAllowed()) {
if (!currentPlayer.isPlaying() && currentPlayer.isPlayAllowed()) {
notification.addAction(aPlay.build());
playbackActions |= PlaybackStateCompat.ACTION_PLAY;
++numActions;
}
if (notificationPlayer.isGoNextAllowed()) {
if (currentPlayer.isGoNextAllowed()) {
notification.addAction(aNext.build());
playbackActions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
++numActions;
}
// Documentation says that this was added in Lollipop (21) but it seems to cause crashes on < Pie (28)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (notificationPlayer.isSeekAllowed()) {
if (currentPlayer.isSeekAllowed()) {
playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO;
}
}
playbackState.setActions(playbackActions);
mediaSession.setPlaybackState(playbackState.build());
//Only allow deletion if no music is notificationPlayer
notification.setOngoing(notificationPlayer.isPlaying());
//Only allow deletion if no music is currentPlayer
notification.setOngoing(currentPlayer.isPlaying());
//Use the MediaStyle notification, so it feels like other media players. That also allows adding actions
MediaStyle mediaStyle = new MediaStyle();
@@ -434,14 +430,24 @@ public class MprisMediaSession implements
} else if (numActions >= 3) {
mediaStyle.setShowActionsInCompactView(0, 1, 2);
}
mediaStyle.setMediaSession(mediaSession.getSessionToken());
notification.setStyle(mediaStyle);
notification.setGroup("MprisMediaSession");
//Display the notification
mediaSession.setActive(true);
final NotificationManager nm = ContextCompat.getSystemService(context, NotificationManager.class);
nm.notify(MPRIS_MEDIA_NOTIFICATION_ID, notification.build());
synchronized (instance) {
if (mediaSession == null) {
mediaSession = new MediaSessionCompat(context, MPRIS_MEDIA_SESSION_TAG);
mediaSession.setCallback(mediaSessionCallback, new Handler(context.getMainLooper()));
// Deprecated flags not required in Build.VERSION_CODES.O and later
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
}
mediaSession.setMetadata(metadata.build());
mediaSession.setPlaybackState(playbackState.build());
mediaStyle.setMediaSession(mediaSession.getSessionToken());
notification.setStyle(mediaStyle);
mediaSession.setActive(true);
final NotificationManager nm = ContextCompat.getSystemService(context, NotificationManager.class);
nm.notify(MPRIS_MEDIA_NOTIFICATION_ID, notification.build());
}
}
public void closeMediaNotification() {
@@ -495,11 +501,15 @@ public class MprisMediaSession implements
@Override
public void onListenerConnected(NotificationReceiver service) {
for (StatusBarNotification n : service.getActiveNotifications()) {
if ("com.spotify.music".equals(n.getPackageName())) {
spotifyRunning = true;
updateMediaNotification();
try {
for (StatusBarNotification n : service.getActiveNotifications()) {
if ("com.spotify.music".equals(n.getPackageName())) {
spotifyRunning = true;
updateMediaNotification();
}
}
} catch(SecurityException e) {
e.printStackTrace();
}
}

View File

@@ -245,7 +245,10 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
return; //Player hasn't actually changed
}
targetPlayer = plugin.getPlayerStatus(player);
targetPlayerName = targetPlayer.getPlayerName();
if (targetPlayer != null) {
targetPlayerName = targetPlayer.getPlayerName();
}
updatePlayerStatus(plugin);
if (targetPlayer != null && targetPlayer.isPlaying()) {

View File

@@ -24,6 +24,10 @@ class MprisReceiverPlayer {
this.name = name;
}
public MediaController getController() {
return controller;
}
boolean isPlaying() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;

View File

@@ -33,6 +33,8 @@ import org.kde.kdeconnect_tp.R;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@PluginFactory.LoadablePlugin
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
@@ -43,6 +45,7 @@ public class MprisReceiverPlugin extends Plugin {
private static final String TAG = "MprisReceiver";
private HashMap<String, MprisReceiverPlayer> players;
private HashMap<String, MprisReceiverCallback> playerCbs;
private MediaSessionChangeListener mediaSessionChangeListener;
@Override
@@ -52,6 +55,7 @@ public class MprisReceiverPlugin extends Plugin {
return false;
players = new HashMap<>();
playerCbs = new HashMap<>();
try {
MediaSessionManager manager = ContextCompat.getSystemService(context, MediaSessionManager.class);
if (null == manager)
@@ -175,7 +179,10 @@ public class MprisReceiverPlugin extends Plugin {
if (null == controllers) {
return;
}
for (MprisReceiverPlayer p : players.values()) {
p.getController().unregisterCallback(playerCbs.get(p.getName()));
}
playerCbs.clear();
players.clear();
createPlayers(controllers);
@@ -189,7 +196,9 @@ public class MprisReceiverPlugin extends Plugin {
if (controller.getPackageName().equals(context.getPackageName())) return;
MprisReceiverPlayer player = new MprisReceiverPlayer(controller, AppsHelper.appNameLookup(context, controller.getPackageName()));
controller.registerCallback(new MprisReceiverCallback(this, player), new Handler(Looper.getMainLooper()));
MprisReceiverCallback cb = new MprisReceiverCallback(this, player);
controller.registerCallback(cb, new Handler(Looper.getMainLooper()));
playerCbs.put(player.getName(), cb);
players.put(player.getName(), player);
}
@@ -209,6 +218,9 @@ public class MprisReceiverPlugin extends Plugin {
np.set("player", player.getName());
np.set("title", player.getTitle());
np.set("artist", player.getArtist());
String nowPlaying = Stream.of(player.getArtist(), player.getTitle())
.filter(StringUtils::isNotEmpty).collect(Collectors.joining(" - "));
np.set("nowPlaying", nowPlaying); // GSConnect 50 (so, Ubuntu 22.04) needs this
np.set("album", player.getAlbum());
np.set("isPlaying", player.isPlaying());
np.set("pos", player.getPosition());

View File

@@ -18,7 +18,7 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceType;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
@@ -41,7 +41,7 @@ public class PresenterPlugin extends Plugin {
@Override
public boolean isCompatible() {
return !device.getDeviceType().equals(Device.DeviceType.Phone) && super.isCompatible();
return !device.getDeviceType().equals(DeviceType.Phone) && super.isCompatible();
}
@Override

View File

@@ -92,7 +92,7 @@ class SimpleSftpServer {
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystem.Factory()));
keyAuth.deviceKey = device.certificate.getPublicKey();
keyAuth.deviceKey = device.getCertificate().getPublicKey();
sshd.setPublickeyAuthenticator(keyAuth);
sshd.setPasswordAuthenticator(passwordAuth);

View File

@@ -84,7 +84,7 @@ public class DeviceTest {
MockSharedPreference deviceSettings = new MockSharedPreference();
SharedPreferences.Editor editor = deviceSettings.edit();
editor.putString("deviceName", name);
editor.putString("deviceType", Device.DeviceType.Phone.toString());
editor.putString("deviceType", DeviceType.Phone.toString());
editor.putString("certificate", encodedCertificate);
editor.apply();
Mockito.when(context.getSharedPreferences(eq(deviceId), eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings);
@@ -110,12 +110,11 @@ public class DeviceTest {
@Test
public void testDeviceType() {
assertEquals(Device.DeviceType.Phone, Device.DeviceType.FromString(Device.DeviceType.Phone.toString()));
assertEquals(Device.DeviceType.Tablet, Device.DeviceType.FromString(Device.DeviceType.Tablet.toString()));
assertEquals(Device.DeviceType.Computer, Device.DeviceType.FromString(Device.DeviceType.Computer.toString()));
assertEquals(Device.DeviceType.Tv, Device.DeviceType.FromString(Device.DeviceType.Tv.toString()));
assertEquals(Device.DeviceType.Computer, Device.DeviceType.FromString(""));
assertEquals(Device.DeviceType.Computer, Device.DeviceType.FromString(null));
assertEquals(DeviceType.Phone, DeviceType.fromString(DeviceType.Phone.toString()));
assertEquals(DeviceType.Tablet, DeviceType.fromString(DeviceType.Tablet.toString()));
assertEquals(DeviceType.Computer, DeviceType.fromString(DeviceType.Computer.toString()));
assertEquals(DeviceType.Tv, DeviceType.fromString(DeviceType.Tv.toString()));
assertEquals(DeviceType.Computer, DeviceType.fromString("invalid"));
}
// Basic paired device testing
@@ -124,10 +123,10 @@ public class DeviceTest {
Device device = new Device(context, "testDevice");
assertEquals(device.getDeviceId(), "testDevice");
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
assertEquals(device.getDeviceType(), DeviceType.Phone);
assertEquals(device.getName(), "Test Device");
assertTrue(device.isPaired());
assertNotNull(device.certificate);
assertNotNull(device.deviceInfo.certificate);
}
public void testPairingDone() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, CertificateException {
@@ -137,7 +136,7 @@ public class DeviceTest {
fakeNetworkPacket.set("deviceId", deviceId);
fakeNetworkPacket.set("deviceName", "Unpaired Test Device");
fakeNetworkPacket.set("protocolVersion", DeviceHelper.ProtocolVersion);
fakeNetworkPacket.set("deviceType", Device.DeviceType.Phone.toString());
fakeNetworkPacket.set("deviceType", DeviceType.Phone.toString());
String certificateString =
"MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" +
"NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" +
@@ -157,18 +156,21 @@ public class DeviceTest {
"7n+KOQ==";
byte[] certificateBytes = Base64.decode(certificateString, 0);
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(fakeNetworkPacket, certificate);
LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class);
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
LanLink link = Mockito.mock(LanLink.class);
Mockito.when(link.getLinkProvider()).thenReturn(linkProvider);
Device device = new Device(context, deviceId, certificate, fakeNetworkPacket, link);
Mockito.when(link.getDeviceId()).thenReturn(deviceId);
Mockito.when(link.getDeviceInfo()).thenReturn(deviceInfo);
Device device = new Device(context, link);
assertNotNull(device);
assertEquals(device.getDeviceId(), deviceId);
assertEquals(device.getName(), "Unpaired Test Device");
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
assertNotNull(device.certificate);
assertEquals(device.getDeviceType(), DeviceType.Phone);
assertNotNull(device.deviceInfo.certificate);
Method method = PairingHandler.class.getDeclaredMethod("pairingDone");
method.setAccessible(true);

View File

@@ -10,10 +10,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import android.content.Context;
import android.util.Log;
import org.json.JSONException;
@@ -22,10 +19,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.mockito.Mockito;
import org.mockito.internal.util.collections.Sets;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.security.cert.Certificate;
@RunWith(PowerMockRunner.class)
@PrepareForTest({DeviceHelper.class, Log.class})
public class NetworkPacketTest {
@@ -34,7 +34,7 @@ public class NetworkPacketTest {
public void setUp() {
PowerMockito.mockStatic(DeviceHelper.class);
PowerMockito.when(DeviceHelper.getDeviceId(any())).thenReturn("123");
PowerMockito.when(DeviceHelper.getDeviceType(any())).thenReturn(Device.DeviceType.Phone);
PowerMockito.when(DeviceHelper.getDeviceType(any())).thenReturn(DeviceType.Phone);
PowerMockito.mockStatic(Log.class);
}
@@ -70,14 +70,23 @@ public class NetworkPacketTest {
@Test
public void testIdentity() {
Certificate cert = Mockito.mock(Certificate.class);
Context context = Mockito.mock(Context.class);
MockSharedPreference settings = new MockSharedPreference();
Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(settings);
DeviceInfo deviceInfo = new DeviceInfo("myid", cert, "myname", DeviceType.Tv, 12, Sets.newSet("ASDFG"), Sets.newSet("QWERTY"));
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
NetworkPacket np = deviceInfo.toIdentityPacket();
assertEquals(np.getInt("protocolVersion"), 12);
DeviceInfo parsed = DeviceInfo.fromIdentityPacketAndCert(np, cert);
assertEquals(parsed.name, deviceInfo.name);
assertEquals(parsed.id, deviceInfo.id);
assertEquals(parsed.type, deviceInfo.type);
assertEquals(parsed.protocolVersion, deviceInfo.protocolVersion);
assertEquals(parsed.incomingCapabilities, deviceInfo.incomingCapabilities);
assertEquals(parsed.outgoingCapabilities, deviceInfo.outgoingCapabilities);
assertEquals(np.getInt("protocolVersion"), DeviceHelper.ProtocolVersion);
}