forked from Mirroring/Kore
Implemented showing playlists even when not playing (#618)
Implemented showing a music/video/picture playlist in PlaylistFragment even when playback has stopped but the playlist is still available on Kodi. The playlist is now only cleared if the Playlist.OnClear event is received from Kodi. As the pictures playlist does not support moving/removing items this has been disabled for the pictures playlist only. Added a new package org.xbmc.kore.host.actions to hold all Callable's that we use to handle complex interactions with Kodi that use multiple JSON RPC calls. Fixed issue in HostConnectionObserver in method notifySomethingIsPlaying where hostState.lastGetItemResult.label might not be set. Reduced calling (force)refreshPlaylist considerably as it should now be handled by the playlist observer.master
parent
46d4b5ffe1
commit
cdbdd98d6a
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2018 Martijn Brekhof. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.xbmc.kore.host.actions;
|
||||
|
||||
|
||||
import org.xbmc.kore.jsonrpc.ApiMethod;
|
||||
import org.xbmc.kore.jsonrpc.HostConnection;
|
||||
import org.xbmc.kore.jsonrpc.method.Playlist;
|
||||
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Retrieves the playlist items for the first non-empty playlist or null if no playlists are
|
||||
* available.
|
||||
*/
|
||||
public class GetPlaylist implements Callable<ArrayList<GetPlaylist.GetPlaylistResult>> {
|
||||
|
||||
private final static String[] propertiesToGet = new String[] {
|
||||
ListType.FieldsAll.ART,
|
||||
ListType.FieldsAll.ARTIST,
|
||||
ListType.FieldsAll.ALBUMARTIST,
|
||||
ListType.FieldsAll.ALBUM,
|
||||
ListType.FieldsAll.DISPLAYARTIST,
|
||||
ListType.FieldsAll.EPISODE,
|
||||
ListType.FieldsAll.FANART,
|
||||
ListType.FieldsAll.FILE,
|
||||
ListType.FieldsAll.SEASON,
|
||||
ListType.FieldsAll.SHOWTITLE,
|
||||
ListType.FieldsAll.STUDIO,
|
||||
ListType.FieldsAll.TAGLINE,
|
||||
ListType.FieldsAll.THUMBNAIL,
|
||||
ListType.FieldsAll.TITLE,
|
||||
ListType.FieldsAll.TRACK,
|
||||
ListType.FieldsAll.DURATION,
|
||||
ListType.FieldsAll.RUNTIME,
|
||||
};
|
||||
|
||||
static private HashMap<String, Integer> playlistsTypesAndIds;
|
||||
private String playlistType;
|
||||
private int playlistId = -1;
|
||||
private HostConnection hostConnection;
|
||||
|
||||
/**
|
||||
* Use this to get the first non-empty playlist
|
||||
* @param hostConnection
|
||||
*/
|
||||
public GetPlaylist(HostConnection hostConnection) {
|
||||
this.hostConnection = hostConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get a playlist for a specific playlist type
|
||||
* @param hostConnection
|
||||
* @param playlistType should be one of the types from {@link org.xbmc.kore.jsonrpc.type.PlaylistType.GetPlaylistsReturnType}.
|
||||
* If null the first non-empty playlist is returned.
|
||||
*/
|
||||
public GetPlaylist(HostConnection hostConnection, String playlistType) {
|
||||
this.hostConnection = hostConnection;
|
||||
this.playlistType = playlistType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get a playlist for a specific playlist id
|
||||
* @param hostConnection
|
||||
* @param playlistId
|
||||
*/
|
||||
public GetPlaylist(HostConnection hostConnection, int playlistId) {
|
||||
this.hostConnection = hostConnection;
|
||||
this.playlistId = playlistId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<GetPlaylistResult> call() throws ExecutionException, InterruptedException {
|
||||
if (playlistsTypesAndIds == null)
|
||||
playlistsTypesAndIds = getPlaylists(hostConnection);
|
||||
|
||||
if (playlistType != null) {
|
||||
GetPlaylistResult getPlaylistResult = retrievePlaylistItemsForType(playlistType);
|
||||
ArrayList<GetPlaylistResult> playlists = new ArrayList<>();
|
||||
playlists.add(getPlaylistResult);
|
||||
return playlists;
|
||||
} else if (playlistId > -1 ) {
|
||||
GetPlaylistResult getPlaylistResult = retrievePlaylistItemsForId(playlistId);
|
||||
ArrayList<GetPlaylistResult> playlists = new ArrayList<>();
|
||||
playlists.add(getPlaylistResult);
|
||||
return playlists;
|
||||
} else
|
||||
return retrieveNonEmptyPlaylists();
|
||||
}
|
||||
|
||||
private GetPlaylistResult retrievePlaylistItemsForId(int playlistId) throws InterruptedException,
|
||||
ExecutionException {
|
||||
List<ListType.ItemsAll> playlistItems = retrievePlaylistItems(hostConnection, playlistId);
|
||||
return new GetPlaylistResult(playlistId, getPlaylistType(playlistId), playlistItems);
|
||||
}
|
||||
|
||||
private GetPlaylistResult retrievePlaylistItemsForType(String type) throws InterruptedException,
|
||||
ExecutionException {
|
||||
List<ListType.ItemsAll> playlistItems = retrievePlaylistItems(hostConnection, playlistsTypesAndIds.get(type));
|
||||
return new GetPlaylistResult(playlistsTypesAndIds.get(type), type, playlistItems);
|
||||
}
|
||||
|
||||
private ArrayList<GetPlaylistResult> retrieveNonEmptyPlaylists() throws InterruptedException,
|
||||
ExecutionException {
|
||||
ArrayList<GetPlaylistResult> playlists = new ArrayList<>();
|
||||
|
||||
for (String type : playlistsTypesAndIds.keySet()) {
|
||||
List<ListType.ItemsAll> playlistItems = retrievePlaylistItems(hostConnection,
|
||||
playlistsTypesAndIds.get(type));
|
||||
if (!playlistItems.isEmpty())
|
||||
playlists.add(new GetPlaylistResult(playlistsTypesAndIds.get(type), type, playlistItems));
|
||||
}
|
||||
return playlists;
|
||||
}
|
||||
|
||||
private HashMap<String, Integer> getPlaylists(HostConnection hostConnection)
|
||||
throws ExecutionException, InterruptedException {
|
||||
HashMap<String, Integer> playlistsHashMap = new HashMap<>();
|
||||
ArrayList<PlaylistType.GetPlaylistsReturnType> playlistsReturnTypes = hostConnection.execute(new Playlist.GetPlaylists()).get();
|
||||
for (PlaylistType.GetPlaylistsReturnType type : playlistsReturnTypes) {
|
||||
playlistsHashMap.put(type.type, type.playlistid);
|
||||
}
|
||||
return playlistsHashMap;
|
||||
}
|
||||
|
||||
private List<ListType.ItemsAll> retrievePlaylistItems(HostConnection hostConnection,
|
||||
int playlistId)
|
||||
throws InterruptedException, ExecutionException {
|
||||
|
||||
ApiMethod<List<ListType.ItemsAll>> apiMethod = new Playlist.GetItems(playlistId,
|
||||
propertiesToGet);
|
||||
return hostConnection.execute(apiMethod).get();
|
||||
}
|
||||
|
||||
private String getPlaylistType(int playlistId) {
|
||||
for (String key : playlistsTypesAndIds.keySet()) {
|
||||
if (playlistsTypesAndIds.get(key) == playlistId)
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class GetPlaylistResult {
|
||||
final public String type;
|
||||
final public int id;
|
||||
final public List<ListType.ItemsAll> items;
|
||||
|
||||
private GetPlaylistResult(int playlistId, String type, List<ListType.ItemsAll> items) {
|
||||
this.id = playlistId;
|
||||
this.type = type;
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2015 Synced Synapse. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.xbmc.kore.jsonrpc.notification;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.xbmc.kore.jsonrpc.ApiNotification;
|
||||
import org.xbmc.kore.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* All Playlist.* notifications
|
||||
*/
|
||||
public class Playlist {
|
||||
|
||||
/**
|
||||
* Player.OnClear notification
|
||||
* Playlist has been cleared
|
||||
*/
|
||||
public static class OnClear extends ApiNotification {
|
||||
public static final String NOTIFICATION_NAME = "Playlist.OnClear";
|
||||
|
||||
public final int playlistId;
|
||||
|
||||
public OnClear(ObjectNode node) {
|
||||
super(node);
|
||||
ObjectNode dataNode = (ObjectNode)node.get("data");
|
||||
playlistId = JsonUtils.intFromJsonNode(dataNode, "playlistid");
|
||||
}
|
||||
|
||||
public String getNotificationName() { return NOTIFICATION_NAME; }
|
||||
}
|
||||
|
||||
public static class OnAdd extends ApiNotification {
|
||||
public static final String NOTIFICATION_NAME = "Playlist.OnAdd";
|
||||
|
||||
public final int playlistId;
|
||||
|
||||
public OnAdd(ObjectNode node) {
|
||||
super(node);
|
||||
ObjectNode dataNode = (ObjectNode)node.get("data");
|
||||
playlistId = JsonUtils.intFromJsonNode(dataNode, "playlistid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotificationName() {
|
||||
return NOTIFICATION_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnRemove extends ApiNotification {
|
||||
public static final String NOTIFICATION_NAME = "Playlist.OnRemove";
|
||||
|
||||
public final int playlistId;
|
||||
|
||||
public OnRemove(ObjectNode node) {
|
||||
super(node);
|
||||
ObjectNode dataNode = (ObjectNode)node.get("data");
|
||||
playlistId = JsonUtils.intFromJsonNode(dataNode, "playlistid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotificationName() {
|
||||
return NOTIFICATION_NAME;
|
||||
}
|
||||
}
|
||||
}
|