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

Compare commits

...

19 Commits
v1.8 ... v1.8.2

Author SHA1 Message Date
Albert Vaca
64fd08f3ac Bump version to release 2018-03-27 20:34:56 +02:00
Albert Vaca
e67e43efa1 Revert "Synchronize everything to see if this fixes a crash in 8.1"
This reverts commit cf808c03ba.
and commit 3d4bf643d4.
2018-03-27 20:34:56 +02:00
Philip Cohn-Cort
fda08929af Add method documentation to LanLinkProvider.java
Summary:
This adds some simple explanation to the `identityPacketReceived` and
`addLink` methods.

Test Plan: N/A

Reviewers: #kde_connect, mtijink

Reviewed By: #kde_connect, mtijink

Differential Revision: https://phabricator.kde.org/D11672
2018-03-26 13:54:04 +02:00
Albert Vaca
2cb025e368 Bump version to release 2018-03-25 22:01:06 +02:00
Albert Vaca
cf808c03ba Add timeout so handshake doesn't block forever 2018-03-25 22:00:39 +02:00
Albert Vaca
2b52cd1547 Bumped version to release 2018-03-25 20:42:52 +02:00
Albert Vaca
3d4bf643d4 Synchronize everything to see if this fixes a crash in 8.1 2018-03-25 20:33:19 +02:00
Matthijs Tijink
00b6677aa4 When receiving a file Android, show the image in the notification
Summary: The image is shown, similar to when you take a screenshot.

Test Plan: Works great! Doesn't crash for non-images.

Reviewers: #kde_connect, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: #kde_connect

Differential Revision: https://phabricator.kde.org/D11679
2018-03-25 14:51:12 +02:00
Matthijs Tijink
bf0cab9ef2 Support file url album art - Android
Summary: If the album art is a file url, the code will ask the remote device to transfer that file. It's then stored in the album art cache.

Test Plan: Works fine for me, even when quickly skipping songs.

Reviewers: #kde_connect, nicolasfella, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: albertvaka, nicolasfella, #kde_connect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D11018
2018-03-25 13:41:40 +02:00
Matthijs Tijink
1c3e6f84a7 Add album art to media notification and lock screen
Summary:
Adds the album art to the media notification and lock screen.

How it looks on my phone (disregard the somewhat weird look, that's just the weird UI my phone vendor uses):
{F5728066}

Google images shows how it'll look on other phones (this one includes the lock screen image):
{F5728069}

And Android Oreo automatically colors the notification based on the album art color (untested, but should work):
{F5728070}

Test Plan: Changes are minor and it works fine for me.

Reviewers: #kde_connect, nicolasfella, albertvaka

Reviewed By: #kde_connect, nicolasfella, albertvaka

Subscribers: nicolasfella

Differential Revision: https://phabricator.kde.org/D10798
2018-03-25 13:39:17 +02:00
Matthijs Tijink
c8dbbb1fe8 Show connected devices on top
Summary: After that, available devices and then remembered devices.

Reviewers: #kde_connect, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: nicolasfella, #kde_connect

Differential Revision: https://phabricator.kde.org/D11641
2018-03-24 21:55:53 +01:00
Philip Cohn-Cort
a17b75264d Use simpler syntax for Google Maven repository
Summary: This follows the guidelines described at https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#apply_plugin

Test Plan: Try to build with this change (`./gradlew assembleRelease` or similar)

Reviewers: #kde_connect, mtijink

Reviewed By: #kde_connect, mtijink

Differential Revision: https://phabricator.kde.org/D11645
2018-03-24 20:23:11 +01:00
Philip Cohn-Cort
7276e60aa4 Only make one request for list of MPRIS players during plugin creation
Summary:
MPrisPlugin previously made two calls to `requestPlayerList()` in its
`onCreate()` method. This change deletes the first of those, so that
only one network packet is sent and received.

Test Plan:
Pair a new device, confirm that only one request for media players is
received by remote client.

Reviewers: #kde_connect, mtijink

Reviewed By: #kde_connect, mtijink

Differential Revision: https://phabricator.kde.org/D11648
2018-03-24 18:38:23 +01:00
Matthijs Tijink
7877d2803c Fix blurry rasterized icons
Summary:
The vector icons were rastizered for older Android versions, but these were on the small side. This fixes the size.

BUG: 392061

Test Plan: The icons aren't blurry anymore on Android 4.2.

Reviewers: #kde_connect, nicolasfella

Reviewed By: #kde_connect, nicolasfella

Subscribers: #kde_connect

Differential Revision: https://phabricator.kde.org/D11644
2018-03-24 18:35:07 +01:00
Matthijs Tijink
b1c4b6e1e9 Show the same mpris player in the notification as in the app
Summary:
So switching (currently playing) players in the app will also switch the
notification.

Test Plan: Tested with two players playing at the same time, and switching between them.

Reviewers: #kde_connect, nicolasfella

Reviewed By: #kde_connect, nicolasfella

Subscribers: #kde_connect

Differential Revision: https://phabricator.kde.org/D11647
2018-03-24 18:34:35 +01:00
Matthijs Tijink
eb801fa535 Don't show flash of pairing buttons when opening a device
Summary:
The buttons are now hidden, and only showed once we know that the device
is unpaired.

Test Plan: Pairing, pairing request and opening a device still work.

Reviewers: #kde_connect, nicolasfella

Reviewed By: #kde_connect, nicolasfella

Subscribers: #kde_connect

Differential Revision: https://phabricator.kde.org/D11627
2018-03-24 11:07:46 +01:00
Nicolas Fella
ac4aaf1b39 Make MPRIS Activity singletop 2018-03-23 22:20:13 +01:00
Matthijs Tijink
9840a39992 Always show/hide both next and previous buttons
Summary: As @kossebau mentioned, a disappearing "previous" button is not really great, since you'll likely press "next". This diff makes sure that when you can press previous and/or next, both buttons are shown (potentially disabled).

Test Plan: Works in all combinations of enabled/disabled for both buttons

Reviewers: #kde_connect, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: kossebau, #kde_connect

Differential Revision: https://phabricator.kde.org/D11437
2018-03-23 19:18:41 +01:00
l10n daemon script
e73c18d2e3 GIT_SILENT made messages (after extraction) 2018-03-17 03:13:39 +01:00
13 changed files with 214 additions and 62 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp"
android:versionCode="1800"
android:versionName="1.8.0">
android:versionCode="1820"
android:versionName="1.8.2">
<supports-screens
android:anyDensity="true"
@@ -146,7 +146,8 @@
<activity
android:name="org.kde.kdeconnect.Plugins.MprisPlugin.MprisActivity"
android:label="@string/remote_control"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity">
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
android:launchMode="singleTop">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />

View File

@@ -1,10 +1,7 @@
buildscript {
repositories {
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
@@ -73,10 +70,7 @@ dependencies {
repositories {
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
implementation 'com.android.support:support-v4:25.4.0'

View File

@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="192dp"
android:height="192dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path

View File

@@ -11,6 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
android:orientation="vertical">
<ProgressBar

View File

@@ -82,8 +82,6 @@
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Шаљем фајл на %1s</string>
<string name="outgoing_files_title">Шаљем фајлове на %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Послато %1$d од %2$d фајлова</string>
<string name="received_file_title">Примљен фајл са %1s</string>
<string name="received_file_fail_title">Неуспео пријем фајла са %1s</string>
<string name="received_file_text">Тапните да отворите „%1s“</string>

View File

@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Backends.LanBackend;
@@ -27,6 +27,7 @@ import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
@@ -59,6 +60,13 @@ import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
/**
* This BaseLinkProvider 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)}.
*
* @see #identityPacketReceived(NetworkPacket, Socket, LanLink.ConnectionStarted)
*/
public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDisconnectedCallback {
public static final int MIN_VERSION_WITH_SSL_SUPPORT = 6;
@@ -157,14 +165,14 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
Log.e("KDE/LanLinkProvider", "Cannot connect to " + address);
e.printStackTrace();
if (!reverseConnectionBlackList.contains(address)) {
Log.w("KDE/LanLinkProvider","Blacklisting "+address);
Log.w("KDE/LanLinkProvider", "Blacklisting " + address);
reverseConnectionBlackList.add(address);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
reverseConnectionBlackList.remove(address);
}
}, 5*1000);
}, 5 * 1000);
// Try to cause a reverse connection
onNetworkChange();
@@ -180,6 +188,18 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
}
/**
* Called when a new 'identity' packet is received. Those are passed here by
* {@link #tcpPacketReceived(Socket)} and {@link #udpPacketReceived(DatagramPacket)}.
* <p>
* If the remote device should be connected, this calls {@link #addLink}.
* Otherwise, if there was an Exception, we unpair from that device.
* </p>
*
* @param identityPacket identity of a remote device
* @param socket a new Socket, which should be used to receive packets from the remote device
* @param connectionStarted which side started this connection
*/
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted) {
String myId = DeviceHelper.getDeviceId(context);
@@ -213,20 +233,20 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
});
}
Log.i("KDE/LanLinkProvider","Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:"+isDeviceTrusted);
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
final SSLSocket sslsocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslsocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
String mode = clientMode? "client" : "server";
String mode = clientMode ? "client" : "server";
try {
Certificate certificate = event.getPeerCertificates()[0];
identityPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
Log.i("KDE/LanLinkProvider","Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(identityPacket, sslsocket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"));
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"));
e.printStackTrace();
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
@@ -246,7 +266,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
try {
sslsocket.startHandshake();
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Handshake failed with " + identityPacket.getString("deviceName"));
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPacket.getString("deviceName"));
e.printStackTrace();
//String[] ciphers = sslsocket.getSupportedCipherSuites();
@@ -265,6 +285,19 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
/**
* Add or update a link in the {@link #visibleComputers} map. This method is synchronized, which ensures that only one
* link is operated on at a time.
* <p>
* Without synchronization, the call to {@link SslHelper#parseCertificate(byte[])} in
* {@link Device#addLink(NetworkPacket, BaseLink)} crashes on some devices running Oreo 8.1 (SDK level 27).
* </p>
*
* @param identityPacket representation of remote device
* @param socket a new Socket, which should be used to receive packets from the remote device
* @param connectionOrigin which side started this connection
* @throws IOException if an exception is thrown by {@link LanLink#reset(Socket, LanLink.ConnectionStarted)}
*/
private synchronized void addLink(final NetworkPacket identityPacket, Socket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
String deviceId = identityPacket.getString("deviceId");
@@ -313,7 +346,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
Log.e("LanLinkProvider", "UdpReceive exception");
}
}
Log.w("UdpListener","Stopping UDP listener");
Log.w("UdpListener", "Stopping UDP listener");
}
}).start();
return server;
@@ -347,13 +380,13 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
static ServerSocket openServerSocketOnFreePort(int minPort) throws IOException {
int tcpPort = minPort;
while(tcpPort < MAX_PORT) {
while (tcpPort < MAX_PORT) {
try {
ServerSocket candidateServer = new ServerSocket();
candidateServer.bind(new InetSocketAddress(tcpPort));
Log.i("KDE/LanLink", "Using port "+tcpPort);
Log.i("KDE/LanLink", "Using port " + tcpPort);
return candidateServer;
} catch(IOException e) {
} catch (IOException e) {
tcpPort++;
}
}
@@ -390,7 +423,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
bytes = identity.serialize().getBytes(StringsHelper.UTF8);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider","Failed to create DatagramSocket");
Log.e("KDE/LanLinkProvider", "Failed to create DatagramSocket");
}
if (bytes != null) {
@@ -415,6 +448,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
}).start();
}
@Override
public void onStart() {
//Log.i("KDE/LanLinkProvider", "onStart");
@@ -431,7 +465,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
// and newer android versions. Although devices with android version less than ICS cannot connect to other devices who also have android version less
// than ICS because server is disabled on both
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.w("KDE/LanLinkProvider","Not starting a TCP server because it's not supported on Android < 14. Operating only as client.");
Log.w("KDE/LanLinkProvider", "Not starting a TCP server because it's not supported on Android < 14. Operating only as client.");
} else {
setupTcpListener();
}
@@ -451,17 +485,17 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
listening = false;
try {
tcpServer.close();
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
}
try {
udpServer.close();
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
}
try {
udpServerOldPort.close();
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
}
}

View File

@@ -66,6 +66,10 @@ public final class AlbumArtCache {
* A list of urls yet to be fetched.
*/
private static final ArrayList<URL> fetchUrlList = new ArrayList<>();
/**
* A list of urls currently being fetched
*/
private static final ArrayList<URL> isFetchingList = new ArrayList<>();
/**
* A integer indicating how many fetches are in progress.
*/
@@ -123,7 +127,7 @@ public final class AlbumArtCache {
* @param albumUrl The album art url
* @return A bitmap for the album art. Can be null if not (yet) found
*/
public static Bitmap getAlbumArt(String albumUrl) {
public static Bitmap getAlbumArt(String albumUrl, MprisPlugin plugin, String player) {
//If the url is invalid, return "no album art"
if (albumUrl == null || albumUrl.isEmpty()) {
return null;
@@ -138,8 +142,8 @@ public final class AlbumArtCache {
return null;
}
//We currently only support http(s) urls
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https")) {
//We currently only support http(s) and file urls
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https") && !url.getProtocol().equals("file")) {
return null;
}
@@ -163,11 +167,7 @@ public final class AlbumArtCache {
try {
DiskLruCache.Snapshot item = diskCache.get(urlToDiskCacheKey(albumUrl));
if (item != null) {
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
decodeOptions.inScaled = false;
decodeOptions.inDensity = 1;
decodeOptions.inTargetDensity = 1;
Bitmap result = BitmapFactory.decodeStream(item.getInputStream(0), null, decodeOptions);
Bitmap result = BitmapFactory.decodeStream(item.getInputStream(0));
item.close();
MemoryCacheItem memItem = new MemoryCacheItem();
if (result != null) {
@@ -189,7 +189,20 @@ public final class AlbumArtCache {
/* If not found, we have not tried fetching it (recently), or a fetch is in-progress.
Either way, just add it to the fetch queue and starting fetching it if no fetch is running. */
fetchUrl(url);
if ("file".equals(url.getProtocol())) {
//Special-case file, since we need to fetch it from the remote
if (isFetchingList.contains(url)) return null;
if (!plugin.askTransferAlbumArt(albumUrl, player)) {
//It doesn't support transferring the art, so mark it as failed in the memory cache
MemoryCacheItem cacheItem = new MemoryCacheItem();
cacheItem.failedFetch = true;
cacheItem.albumArt = null;
memoryCache.put(url.toString(), cacheItem);
}
} else {
fetchUrl(url);
}
return null;
}
@@ -206,7 +219,7 @@ public final class AlbumArtCache {
}
//Only fetch an URL if we're not fetching it already
if (fetchUrlList.contains(url)) {
if (fetchUrlList.contains(url) || isFetchingList.contains(url)) {
return;
}
@@ -323,8 +336,8 @@ public final class AlbumArtCache {
memoryCache.put(url.toString(), cacheItem);
}
//Remove the url from the to-fetch list
fetchUrlList.remove(url);
//Remove the url from the fetching list
isFetchingList.remove(url);
//Fetch the next url (if any)
--numFetching;
initiateFetch();
@@ -338,10 +351,19 @@ public final class AlbumArtCache {
if (numFetching >= 2) return;
if (fetchUrlList.isEmpty()) return;
++numFetching;
//Fetch the last-requested url first, it will probably be needed first
URL url = fetchUrlList.get(fetchUrlList.size() - 1);
//Remove the url from the to-fetch list
fetchUrlList.remove(url);
if ("file".equals(url.getProtocol())) {
throw new AssertionError("Not file urls should be possible here!");
}
//Download the album art ourselves
++numFetching;
//Add the url to the currently-fetching list
isFetchingList.add(url);
try {
DiskLruCache.Editor cacheItem = diskCache.edit(urlToDiskCacheKey(url.toString()));
if (cacheItem == null) {
@@ -392,6 +414,9 @@ public final class AlbumArtCache {
//We need the disk cache for this
if (diskCache == null) {
Log.e("KDE/Mpris/AlbumArtCache", "The disk cache is not intialized!");
try {
payload.close();
} catch (IOException ignored) {}
return;
}
@@ -400,20 +425,46 @@ public final class AlbumArtCache {
url = new URL(albumUrl);
} catch (MalformedURLException e) {
//Shouldn't happen (checked on receival of the url), but just to be sure
try {
payload.close();
} catch (IOException ignored) {}
return;
}
if (!"file".equals(url.getProtocol())) {
//Shouldn't happen (otherwise we wouldn't have asked for the payload), but just to be sure
try {
payload.close();
} catch (IOException ignored) {}
return;
}
//Only fetch the URL if we're not fetching it already
if (fetchUrlList.contains(url)) {
if (isFetchingList.contains(url)) {
try {
payload.close();
} catch (IOException ignored) {}
return;
}
fetchUrlList.add(url);
//Check if we already have this art
try {
if (memoryCache.get(albumUrl) != null || diskCache.get(urlToDiskCacheKey(albumUrl)) != null) {
try {
payload.close();
} catch (IOException ignored) {}
return;
}
} catch (IOException e) {
Log.e("KDE/Mpris/AlbumArtCache", "Disk cache problem!", e);
try {
payload.close();
} catch (IOException ignored) {}
return;
}
//Add it to the currently-fetching list
isFetchingList.add(url);
++numFetching;
try {
@@ -422,6 +473,9 @@ public final class AlbumArtCache {
Log.e("KDE/Mpris/AlbumArtCache",
"Two disk cache edits happened at the same time, should be impossible!");
--numFetching;
try {
payload.close();
} catch (IOException ignored) {}
return;
}

View File

@@ -139,6 +139,10 @@ public class MprisActivity extends AppCompatActivity {
}
targetPlayer = mpris.getPlayerStatus(player);
updatePlayerStatus(mpris);
if (targetPlayer.isPlaying()) {
MprisMediaSession.getInstance().playerSelected(targetPlayer);
}
}
@Override
@@ -246,8 +250,17 @@ public class MprisActivity extends AppCompatActivity {
findViewById(R.id.volume_layout).setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.rew_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.ff_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.next_button).setVisibility(playerStatus.isGoNextAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.prev_button).setVisibility(playerStatus.isGoPreviousAllowed() ? View.VISIBLE : View.GONE);
//Show and hide previous/next buttons simultaneously
if (playerStatus.isGoPreviousAllowed() || playerStatus.isGoNextAllowed()) {
findViewById(R.id.prev_button).setVisibility(View.VISIBLE);
findViewById(R.id.prev_button).setEnabled(playerStatus.isGoPreviousAllowed());
findViewById(R.id.next_button).setVisibility(View.VISIBLE);
findViewById(R.id.next_button).setEnabled(playerStatus.isGoNextAllowed());
} else {
findViewById(R.id.prev_button).setVisibility(View.GONE);
findViewById(R.id.next_button).setVisibility(View.GONE);
}
}
/**

View File

@@ -25,6 +25,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -253,6 +254,11 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, notificationPlayer.getLength());
}
Bitmap albumArt = notificationPlayer.getAlbumArt();
if (albumArt != null) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt);
}
mediaSession.setMetadata(metadata.build());
PlaybackStateCompat.Builder playbackState = new PlaybackStateCompat.Builder();
@@ -326,6 +332,10 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
notification.setContentText(notificationPlayer.getPlayer());
}
if (albumArt != null) {
notification.setLargeIcon(albumArt);
}
if (!notificationPlayer.isPlaying()) {
Intent iCloseNotification = new Intent(service, MprisMediaNotificationReceiver.class);
iCloseNotification.setAction(MprisMediaNotificationReceiver.ACTION_CLOSE_NOTIFICATION);
@@ -405,4 +415,9 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
updateMediaNotification();
}
public void playerSelected(MprisPlugin.MprisPlayer player) {
notificationPlayer = player;
updateMediaNotification();
}
}

View File

@@ -130,7 +130,7 @@ public class MprisPlugin extends Plugin {
* @return The album art, or null if not available
*/
public Bitmap getAlbumArt() {
return AlbumArtCache.getAlbumArt(albumArtUrl);
return AlbumArtCache.getAlbumArt(albumArtUrl, MprisPlugin.this, player);
}
public boolean isSetVolumeAllowed() {
@@ -205,6 +205,7 @@ public class MprisPlugin extends Plugin {
public final static String PACKET_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request";
private HashMap<String, MprisPlayer> players = new HashMap<>();
private boolean supportAlbumArtPayload = false;
private HashMap<String, Handler> playerStatusUpdated = new HashMap<>();
private HashMap<String, Handler> playerListUpdated = new HashMap<>();
@@ -231,7 +232,6 @@ public class MprisPlugin extends Plugin {
@Override
public boolean onCreate() {
requestPlayerList();
MprisMediaSession.getInstance().onCreate(context.getApplicationContext(), this, device.getDeviceId());
//Always request the player list so the data is up-to-date
@@ -266,6 +266,11 @@ public class MprisPlugin extends Plugin {
@Override
public boolean onPacketReceived(NetworkPacket np) {
if (np.getBoolean("transferringAlbumArt", false)) {
AlbumArtCache.payloadToDiskCache(np.getString("albumArtUrl"), np.getPayload());
return true;
}
if (np.has("player")) {
MprisPlayer playerStatus = players.get(np.getString("player"));
if (playerStatus != null) {
@@ -306,6 +311,9 @@ public class MprisPlugin extends Plugin {
}
}
//Remember if the connected device support album art payloads
supportAlbumArtPayload = np.getBoolean("supportAlbumArtPayload", supportAlbumArtPayload);
List<String> newPlayerList = np.getStringList("playerList");
if (newPlayerList != null) {
boolean equals = true;
@@ -463,4 +471,22 @@ public class MprisPlugin extends Plugin {
}
}
}
public boolean askTransferAlbumArt(String url, String playerName) {
//First check if the remote supports transferring album art
if (!supportAlbumArtPayload) return false;
if (url.isEmpty()) return false;
MprisPlayer player = getPlayerStatus(playerName);
if (player == null) return false;
if (player.albumArtUrl.equals(url)) {
NetworkPacket np = new NetworkPacket(PACKET_TYPE_MPRIS_REQUEST);
np.set("player", player.getPlayer());
np.set("albumArtUrl", url);
device.sendPacket(np);
return true;
}
return false;
}
}

View File

@@ -26,6 +26,8 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
@@ -38,6 +40,7 @@ import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.io.FileNotFoundException;
public class ShareNotification {
@@ -99,6 +102,18 @@ public class ShareNotification {
* - Proxy to real files (in case of the default download folder)
* - Proxy to the underlying content uri (in case of a custom download folder)
*/
//If it's an image, try to show it in the notification
if (mimeType.startsWith("image/")) {
try {
Bitmap image = BitmapFactory.decodeStream(device.getContext().getContentResolver().openInputStream(destinationUri));
if (image != null) {
builder.setLargeIcon(image);
builder.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(image));
}
} catch (FileNotFoundException ignored) {}
}
if (!"file".equals(destinationUri.getScheme())) {
return;
}

View File

@@ -323,6 +323,7 @@ public class DeviceFragment extends Fragment {
if (device.isPairRequestedByPeer()) {
((TextView) rootView.findViewById(R.id.pair_message)).setText(R.string.pair_requested);
rootView.findViewById(R.id.pairing_buttons).setVisibility(View.VISIBLE);
rootView.findViewById(R.id.pair_progress).setVisibility(View.GONE);
rootView.findViewById(R.id.pair_button).setVisibility(View.GONE);
rootView.findViewById(R.id.pair_request).setVisibility(View.VISIBLE);

View File

@@ -159,16 +159,6 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
SectionItem section;
Resources res = getResources();
section = new SectionItem(res.getString(R.string.category_not_paired_devices));
section.isSectionEmpty = true;
items.add(section);
for (Device device : devices) {
if (device.isReachable() && !device.isPaired()) {
items.add(new PairingDeviceItem(device, PairingFragment.this));
section.isSectionEmpty = false;
}
}
section = new SectionItem(res.getString(R.string.category_connected_devices));
section.isSectionEmpty = true;
items.add(section);
@@ -182,6 +172,16 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
items.remove(items.size() - 1); //Remove connected devices section if empty
}
section = new SectionItem(res.getString(R.string.category_not_paired_devices));
section.isSectionEmpty = true;
items.add(section);
for (Device device : devices) {
if (device.isReachable() && !device.isPaired()) {
items.add(new PairingDeviceItem(device, PairingFragment.this));
section.isSectionEmpty = false;
}
}
section = new SectionItem(res.getString(R.string.category_remembered_devices));
section.isSectionEmpty = true;
items.add(section);