2015-01-14 12:12:47 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2016-12-20 21:47:24 +01:00
|
|
|
package org.xbmc.kore.ui.sections.remote;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
2015-06-23 19:43:57 +02:00
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.content.res.TypedArray;
|
2015-01-14 12:12:47 +01:00
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Handler;
|
|
|
|
import android.support.v4.app.Fragment;
|
2015-06-23 19:43:57 +02:00
|
|
|
import android.support.v7.widget.CardView;
|
2015-04-08 23:28:19 +02:00
|
|
|
import android.text.TextUtils;
|
2015-01-14 12:12:47 +01:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.AdapterView;
|
|
|
|
import android.widget.BaseAdapter;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
import android.widget.PopupMenu;
|
|
|
|
import android.widget.RelativeLayout;
|
|
|
|
import android.widget.TextView;
|
2016-01-11 10:32:07 +01:00
|
|
|
import android.widget.Toast;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
2015-03-09 22:35:18 +01:00
|
|
|
import org.xbmc.kore.R;
|
|
|
|
import org.xbmc.kore.host.HostConnectionObserver;
|
|
|
|
import org.xbmc.kore.host.HostConnectionObserver.PlayerEventsObserver;
|
|
|
|
import org.xbmc.kore.host.HostInfo;
|
|
|
|
import org.xbmc.kore.host.HostManager;
|
|
|
|
import org.xbmc.kore.jsonrpc.ApiCallback;
|
|
|
|
import org.xbmc.kore.jsonrpc.ApiMethod;
|
2016-01-11 10:32:07 +01:00
|
|
|
import org.xbmc.kore.jsonrpc.HostConnection;
|
2015-03-09 22:35:18 +01:00
|
|
|
import org.xbmc.kore.jsonrpc.method.Player;
|
|
|
|
import org.xbmc.kore.jsonrpc.method.Playlist;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.ListType;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
2016-01-11 10:32:07 +01:00
|
|
|
import org.xbmc.kore.ui.viewgroups.DynamicListView;
|
2015-03-09 22:35:18 +01:00
|
|
|
import org.xbmc.kore.utils.LogUtils;
|
|
|
|
import org.xbmc.kore.utils.UIUtils;
|
|
|
|
import org.xbmc.kore.utils.Utils;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import butterknife.ButterKnife;
|
2018-04-05 19:22:20 +02:00
|
|
|
import butterknife.BindView;
|
|
|
|
import butterknife.Unbinder;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Playlist view
|
|
|
|
*/
|
|
|
|
public class PlaylistFragment extends Fragment
|
|
|
|
implements PlayerEventsObserver {
|
|
|
|
private static final String TAG = LogUtils.makeLogTag(PlaylistFragment.class);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Host manager from which to get info about the current XBMC
|
|
|
|
*/
|
|
|
|
private HostManager hostManager;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Activity to communicate potential actions that change what's playing
|
|
|
|
*/
|
|
|
|
private HostConnectionObserver hostConnectionObserver;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler on which to post RPC callbacks
|
|
|
|
*/
|
|
|
|
private Handler callbackHandler = new Handler();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current active player id
|
|
|
|
*/
|
|
|
|
private int currentActivePlayerId = -1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current playlist
|
|
|
|
*/
|
|
|
|
private int currentPlaylistId = -1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Playlist adapter
|
|
|
|
*/
|
|
|
|
private PlayListAdapter playListAdapter;
|
|
|
|
|
2018-04-05 19:22:20 +02:00
|
|
|
private Unbinder unbinder;
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
/**
|
|
|
|
* Injectable views
|
|
|
|
*/
|
2018-04-05 19:22:20 +02:00
|
|
|
@BindView(R.id.info_panel) RelativeLayout infoPanel;
|
|
|
|
@BindView(R.id.playlist) DynamicListView playlistListView;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
2018-04-05 19:22:20 +02:00
|
|
|
@BindView(R.id.info_title) TextView infoTitle;
|
|
|
|
@BindView(R.id.info_message) TextView infoMessage;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
hostManager = HostManager.getInstance(getActivity());
|
|
|
|
hostConnectionObserver = hostManager.getHostConnectionObserver();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
|
|
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_playlist, container, false);
|
2018-04-05 19:22:20 +02:00
|
|
|
unbinder = ButterKnife.bind(this, root);
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
playListAdapter = new PlayListAdapter();
|
2016-01-11 10:32:07 +01:00
|
|
|
playlistListView.setAdapter(playListAdapter);
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
// When clicking on an item, play it
|
2016-01-11 10:32:07 +01:00
|
|
|
playlistListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
2015-01-14 12:12:47 +01:00
|
|
|
@Override
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
2015-11-10 00:40:48 +01:00
|
|
|
Player.Open action = new Player.Open(Player.Open.TYPE_PLAYLIST, currentPlaylistId, position);
|
2015-01-14 12:12:47 +01:00
|
|
|
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
|
|
super.onActivityCreated(savedInstanceState);
|
|
|
|
// We have options
|
|
|
|
setHasOptionsMenu(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
2015-02-15 20:11:32 +01:00
|
|
|
hostConnectionObserver.registerPlayerObserver(this, true);
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPause() {
|
|
|
|
super.onPause();
|
|
|
|
hostConnectionObserver.unregisterPlayerObserver(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
|
|
inflater.inflate(R.menu.playlist, menu);
|
|
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
|
|
}
|
|
|
|
|
2018-04-05 19:22:20 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroyView() {
|
|
|
|
super.onDestroyView();
|
|
|
|
unbinder.unbind();
|
|
|
|
}
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.action_clear_playlist:
|
|
|
|
Playlist.Clear action = new Playlist.Clear(currentPlaylistId);
|
|
|
|
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
|
|
|
// If we are playing something, refresh playlist
|
|
|
|
forceRefreshPlaylist();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
|
2015-04-10 20:51:44 +02:00
|
|
|
public void forceRefreshPlaylist() {
|
2015-01-14 12:12:47 +01:00
|
|
|
// If we are playing something, refresh playlist
|
|
|
|
if ((lastCallResult == PLAYER_IS_PLAYING) || (lastCallResult == PLAYER_IS_PAUSED)) {
|
2015-03-17 19:45:21 +01:00
|
|
|
setupPlaylistInfo(lastGetActivePlayerResult, lastGetPropertiesResult, lastGetItemResult);
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default callback for methods that don't return anything
|
|
|
|
*/
|
|
|
|
private ApiCallback<String> defaultStringActionCallback = ApiMethod.getDefaultActionCallback();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last call results
|
|
|
|
*/
|
|
|
|
private int lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
|
|
|
|
private ListType.ItemsAll lastGetItemResult = null;
|
|
|
|
private PlayerType.GetActivePlayersReturnType lastGetActivePlayerResult;
|
2015-03-17 19:45:21 +01:00
|
|
|
private PlayerType.PropertyValue lastGetPropertiesResult;
|
2015-01-14 12:12:47 +01:00
|
|
|
private List<ListType.ItemsAll> lastGetPlaylistItemsResult = null;
|
|
|
|
|
2017-07-13 20:10:49 +02:00
|
|
|
@Override
|
|
|
|
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
|
|
|
if (notificationsData.property.shuffled != null)
|
|
|
|
setupPlaylistInfo(lastGetActivePlayerResult, lastGetPropertiesResult, lastGetItemResult);
|
|
|
|
}
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
/**
|
|
|
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
|
|
|
*/
|
|
|
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
|
|
|
PlayerType.PropertyValue getPropertiesResult,
|
|
|
|
ListType.ItemsAll getItemResult) {
|
|
|
|
if ((lastGetPlaylistItemsResult == null) ||
|
2016-01-11 10:32:07 +01:00
|
|
|
(lastCallResult != PlayerEventsObserver.PLAYER_IS_PLAYING) ||
|
|
|
|
(currentActivePlayerId != getActivePlayerResult.playerid) ||
|
|
|
|
(lastGetItemResult.id != getItemResult.id)) {
|
2015-01-14 12:12:47 +01:00
|
|
|
// Check if something is different, and only if so, start the chain calls
|
2015-03-17 19:45:21 +01:00
|
|
|
setupPlaylistInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
2015-01-14 12:12:47 +01:00
|
|
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
|
|
|
} else {
|
|
|
|
// Hopefully nothing changed, so just use the last results
|
|
|
|
displayPlaylist(getItemResult, lastGetPlaylistItemsResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save results
|
|
|
|
lastCallResult = PLAYER_IS_PLAYING;
|
|
|
|
lastGetActivePlayerResult = getActivePlayerResult;
|
2015-03-17 19:45:21 +01:00
|
|
|
lastGetPropertiesResult = getPropertiesResult;
|
2015-01-14 12:12:47 +01:00
|
|
|
lastGetItemResult = getItemResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
|
|
|
PlayerType.PropertyValue getPropertiesResult,
|
|
|
|
ListType.ItemsAll getItemResult) {
|
|
|
|
if ((lastGetPlaylistItemsResult == null) ||
|
2016-01-11 10:32:07 +01:00
|
|
|
(lastCallResult != PlayerEventsObserver.PLAYER_IS_PLAYING) ||
|
|
|
|
(currentActivePlayerId != getActivePlayerResult.playerid) ||
|
|
|
|
(lastGetItemResult.id != getItemResult.id)) {
|
2015-03-17 19:45:21 +01:00
|
|
|
setupPlaylistInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
2015-01-14 12:12:47 +01:00
|
|
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
|
|
|
} else {
|
|
|
|
// Hopefully nothing changed, so just use the last results
|
|
|
|
displayPlaylist(getItemResult, lastGetPlaylistItemsResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
lastCallResult = PLAYER_IS_PAUSED;
|
|
|
|
lastGetActivePlayerResult = getActivePlayerResult;
|
2015-03-17 19:45:21 +01:00
|
|
|
lastGetPropertiesResult = getPropertiesResult;
|
2015-01-14 12:12:47 +01:00
|
|
|
lastGetItemResult = getItemResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void playerOnStop() {
|
|
|
|
HostInfo hostInfo = hostManager.getHostInfo();
|
|
|
|
switchToPanel(R.id.info_panel);
|
|
|
|
infoTitle.setText(R.string.nothing_playing);
|
|
|
|
infoMessage.setText(String.format(getString(R.string.connected_to), hostInfo.getName()));
|
|
|
|
|
|
|
|
lastCallResult = PLAYER_IS_STOPPED;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void playerOnConnectionError(int errorCode, String description) {
|
|
|
|
HostInfo hostInfo = hostManager.getHostInfo();
|
|
|
|
|
|
|
|
switchToPanel(R.id.info_panel);
|
|
|
|
if (hostInfo != null) {
|
|
|
|
infoTitle.setText(R.string.connecting);
|
|
|
|
// TODO: check error code
|
|
|
|
infoMessage.setText(String.format(getString(R.string.connecting_to), hostInfo.getName(), hostInfo.getAddress()));
|
|
|
|
} else {
|
|
|
|
infoTitle.setText(R.string.no_xbmc_configured);
|
|
|
|
infoMessage.setText(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
lastCallResult = PlayerEventsObserver.PLAYER_CONNECTION_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void playerNoResultsYet() {
|
|
|
|
// Initialize info panel
|
|
|
|
switchToPanel(R.id.info_panel);
|
|
|
|
HostInfo hostInfo = hostManager.getHostInfo();
|
|
|
|
if (hostInfo != null) {
|
|
|
|
infoTitle.setText(R.string.connecting);
|
|
|
|
} else {
|
|
|
|
infoTitle.setText(R.string.no_xbmc_configured);
|
|
|
|
}
|
|
|
|
infoMessage.setText(null);
|
|
|
|
lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void systemOnQuit() {
|
|
|
|
playerNoResultsYet();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore this
|
|
|
|
public void inputOnInputRequested(String title, String type, String value) {}
|
2015-02-15 20:11:32 +01:00
|
|
|
public void observerOnStopObserving() {}
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts the call chain to display the playlist
|
|
|
|
*/
|
|
|
|
private void setupPlaylistInfo(final PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
2015-03-17 19:45:21 +01:00
|
|
|
final PlayerType.PropertyValue getPropertiesResult,
|
2015-01-14 12:12:47 +01:00
|
|
|
final ListType.ItemsAll getItemResult) {
|
|
|
|
|
2015-03-17 19:45:21 +01:00
|
|
|
currentPlaylistId = getPropertiesResult.playlistid;
|
2015-01-14 12:12:47 +01:00
|
|
|
|
2015-03-17 19:45:21 +01:00
|
|
|
if (currentPlaylistId == -1) {
|
2015-01-14 12:12:47 +01:00
|
|
|
// Couldn't find a playlist of the same type, just report empty
|
|
|
|
displayEmptyPlaylistMessage();
|
|
|
|
} else {
|
|
|
|
// Call GetItems
|
|
|
|
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,
|
|
|
|
};
|
2015-03-17 19:45:21 +01:00
|
|
|
Playlist.GetItems getItems = new Playlist.GetItems(currentPlaylistId, propertiesToGet);
|
2015-01-14 12:12:47 +01:00
|
|
|
getItems.execute(hostManager.getConnection(), new ApiCallback<List<ListType.ItemsAll>>() {
|
|
|
|
@Override
|
2015-03-16 19:33:40 +01:00
|
|
|
public void onSuccess(List<ListType.ItemsAll> result) {
|
2015-01-22 23:19:28 +01:00
|
|
|
if (!isAdded()) return;
|
2015-01-14 12:12:47 +01:00
|
|
|
// Ok, we've got all the info, save and display playlist
|
|
|
|
lastGetPlaylistItemsResult = result;
|
|
|
|
displayPlaylist(getItemResult, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
2015-01-22 23:19:28 +01:00
|
|
|
if (!isAdded()) return;
|
2015-01-14 12:12:47 +01:00
|
|
|
// Oops
|
|
|
|
displayErrorGettingPlaylistMessage(description);
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void displayPlaylist(final ListType.ItemsAll getItemResult,
|
|
|
|
final List<ListType.ItemsAll> playlistItems) {
|
2016-06-29 12:04:24 +02:00
|
|
|
if (playlistItems.isEmpty()) {
|
2015-01-14 12:12:47 +01:00
|
|
|
displayEmptyPlaylistMessage();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switchToPanel(R.id.playlist);
|
|
|
|
|
2016-01-11 10:32:07 +01:00
|
|
|
//If a user is dragging a list item we must not modify the adapter to prevent
|
|
|
|
//the dragged item's adapter position from diverging from its listview position
|
|
|
|
if (!playlistListView.isItemBeingDragged()) {
|
|
|
|
// Set items, which call notifyDataSetChanged
|
|
|
|
playListAdapter.setPlaylistItems(playlistItems);
|
|
|
|
highlightItem(getItemResult, playlistItems);
|
|
|
|
} else {
|
|
|
|
highlightItem(getItemResult, playListAdapter.playlistItems);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void highlightItem(final ListType.ItemsAll item,
|
|
|
|
final List<ListType.ItemsAll> playlistItems) {
|
2015-01-14 12:12:47 +01:00
|
|
|
for (int i = 0; i < playlistItems.size(); i++) {
|
2016-01-11 10:32:07 +01:00
|
|
|
if ((playlistItems.get(i).id == item.id) &&
|
|
|
|
(playlistItems.get(i).type.equals(item.type))) {
|
|
|
|
|
|
|
|
//When user is dragging an item it is very annoying when we change the list position
|
|
|
|
if (!playlistListView.isItemBeingDragged()) {
|
|
|
|
playlistListView.setSelection(i);
|
|
|
|
}
|
|
|
|
playlistListView.setItemChecked(i, true);
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-11 10:32:07 +01:00
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
/**
|
|
|
|
* Switches the info panel shown (they are exclusive)
|
|
|
|
* @param panelResId The panel to show
|
|
|
|
*/
|
|
|
|
private void switchToPanel(int panelResId) {
|
|
|
|
switch (panelResId) {
|
|
|
|
case R.id.info_panel:
|
|
|
|
infoPanel.setVisibility(View.VISIBLE);
|
2016-01-11 10:32:07 +01:00
|
|
|
playlistListView.setVisibility(View.GONE);
|
2015-01-14 12:12:47 +01:00
|
|
|
break;
|
|
|
|
case R.id.playlist:
|
|
|
|
infoPanel.setVisibility(View.GONE);
|
2016-01-11 10:32:07 +01:00
|
|
|
playlistListView.setVisibility(View.VISIBLE);
|
2015-01-14 12:12:47 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays an error on the info panel
|
2015-03-17 19:45:21 +01:00
|
|
|
* @param details Details message
|
2015-01-14 12:12:47 +01:00
|
|
|
*/
|
|
|
|
private void displayErrorGettingPlaylistMessage(String details) {
|
|
|
|
switchToPanel(R.id.info_panel);
|
|
|
|
infoTitle.setText(R.string.error_getting_playlist);
|
|
|
|
infoMessage.setText(String.format(getString(R.string.error_message), details));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays empty playlist
|
|
|
|
*/
|
|
|
|
private void displayEmptyPlaylistMessage() {
|
|
|
|
switchToPanel(R.id.info_panel);
|
|
|
|
infoTitle.setText(R.string.playlist_empty);
|
|
|
|
infoMessage.setText(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adapter used to show the hosts in the ListView
|
|
|
|
*/
|
|
|
|
private class PlayListAdapter extends BaseAdapter
|
2016-01-11 10:32:07 +01:00
|
|
|
implements DynamicListView.DynamicListAdapter {
|
2015-01-14 12:12:47 +01:00
|
|
|
private View.OnClickListener playlistItemMenuClickListener = new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
final int position = (Integer)v.getTag();
|
|
|
|
|
|
|
|
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
|
|
|
|
popupMenu.getMenuInflater().inflate(R.menu.playlist_item, popupMenu.getMenu());
|
|
|
|
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onMenuItemClick(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.action_remove_playlist_item:
|
|
|
|
// Remove this item from the playlist
|
|
|
|
Playlist.Remove action = new Playlist.Remove(currentPlaylistId, position);
|
|
|
|
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
|
|
|
forceRefreshPlaylist();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
popupMenu.show();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The playlist items
|
|
|
|
*/
|
|
|
|
List<ListType.ItemsAll> playlistItems;
|
2015-03-28 19:08:07 +01:00
|
|
|
int artWidth = getResources().getDimensionPixelSize(R.dimen.playlist_art_width);
|
|
|
|
int artHeight = getResources().getDimensionPixelSize(R.dimen.playlist_art_heigth);
|
2015-01-14 12:12:47 +01:00
|
|
|
|
2015-06-23 19:43:57 +02:00
|
|
|
int cardBackgroundColor, selectedCardBackgroundColor;
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
public PlayListAdapter(List<ListType.ItemsAll> playlistItems) {
|
|
|
|
super();
|
|
|
|
this.playlistItems = playlistItems;
|
2015-06-23 19:43:57 +02:00
|
|
|
|
|
|
|
Resources.Theme theme = getActivity().getTheme();
|
|
|
|
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
|
|
|
|
R.attr.appCardBackgroundColor,
|
|
|
|
R.attr.appSelectedCardBackgroundColor});
|
2016-09-16 20:09:06 +02:00
|
|
|
Resources resources = getResources();
|
|
|
|
cardBackgroundColor =
|
|
|
|
styledAttributes.getColor(styledAttributes.getIndex(0), resources.getColor(R.color.dark_content_background));
|
|
|
|
selectedCardBackgroundColor =
|
|
|
|
styledAttributes.getColor(styledAttributes.getIndex(1), resources.getColor(R.color.dark_selected_content_background));
|
2015-06-23 19:43:57 +02:00
|
|
|
styledAttributes.recycle();
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public PlayListAdapter() {
|
2015-03-28 19:08:07 +01:00
|
|
|
this(null);
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manually set the items on the adapter
|
|
|
|
* Calls notifyDataSetChanged()
|
|
|
|
*
|
2015-03-17 19:45:21 +01:00
|
|
|
* @param playlistItems Items
|
2015-01-14 12:12:47 +01:00
|
|
|
*/
|
|
|
|
public void setPlaylistItems(List<ListType.ItemsAll> playlistItems) {
|
|
|
|
this.playlistItems = playlistItems;
|
|
|
|
notifyDataSetChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCount() {
|
|
|
|
if (playlistItems == null) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return playlistItems.size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ListType.ItemsAll getItem(int position) {
|
|
|
|
if (playlistItems == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return playlistItems.get(position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getItemId(int position) {
|
2016-01-11 10:32:07 +01:00
|
|
|
if (position < 0 || position >= playlistItems.size()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return playlistItems.get(position).id;
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getViewTypeCount () {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-01-11 10:32:07 +01:00
|
|
|
@Override
|
|
|
|
public void onSwapItems(int positionOne, int positionTwo) {
|
|
|
|
ListType.ItemsAll tmp = playlistItems.get(positionOne);
|
|
|
|
playlistItems.set(positionOne, playlistItems.get(positionTwo));
|
|
|
|
playlistItems.set(positionTwo, tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSwapFinished(final int originalPosition, final int finalPosition) {
|
|
|
|
final HostConnection hostConnection = hostManager.getConnection();
|
|
|
|
|
|
|
|
if (playlistItems.get(finalPosition).id == lastGetItemResult.id) {
|
|
|
|
Toast.makeText(getActivity(), R.string.cannot_move_playing_item, Toast.LENGTH_SHORT)
|
|
|
|
.show();
|
|
|
|
rollbackSwappedItems(originalPosition, finalPosition);
|
|
|
|
notifyDataSetChanged();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Playlist.Remove remove = new Playlist.Remove(currentPlaylistId, originalPosition);
|
|
|
|
remove.execute(hostConnection, new ApiCallback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(String result) {
|
|
|
|
Playlist.Insert insert = new Playlist.Insert(currentPlaylistId, finalPosition, createPlaylistTypeItem(playlistItems.get(finalPosition)));
|
|
|
|
insert.execute(hostConnection, new ApiCallback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(String result) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
|
|
|
//Remove succeeded but insert failed, so we need to remove item from playlist at final position
|
|
|
|
playlistItems.remove(finalPosition);
|
|
|
|
notifyDataSetChanged();
|
|
|
|
if (!isAdded()) return;
|
|
|
|
// Got an error, show toast
|
|
|
|
Toast.makeText(getActivity(), R.string.unable_to_move_item, Toast.LENGTH_SHORT)
|
|
|
|
.show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
|
|
|
rollbackSwappedItems(originalPosition, finalPosition);
|
|
|
|
notifyDataSetChanged();
|
|
|
|
if (!isAdded()) return;
|
|
|
|
// Got an error, show toast
|
|
|
|
Toast.makeText(getActivity(), R.string.unable_to_move_item, Toast.LENGTH_SHORT)
|
|
|
|
.show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
@Override
|
|
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
|
|
ViewHolder viewHolder;
|
|
|
|
|
|
|
|
if (convertView == null) {
|
|
|
|
convertView = LayoutInflater.from(getActivity())
|
2016-01-11 10:32:07 +01:00
|
|
|
.inflate(R.layout.grid_item_playlist, parent, false);
|
2015-01-14 12:12:47 +01:00
|
|
|
// ViewHolder pattern
|
|
|
|
viewHolder = new ViewHolder();
|
|
|
|
viewHolder.art = (ImageView)convertView.findViewById(R.id.art);
|
|
|
|
viewHolder.title = (TextView)convertView.findViewById(R.id.title);
|
|
|
|
viewHolder.details = (TextView)convertView.findViewById(R.id.details);
|
|
|
|
viewHolder.contextMenu = (ImageView)convertView.findViewById(R.id.list_context_menu);
|
|
|
|
viewHolder.duration = (TextView)convertView.findViewById(R.id.duration);
|
2015-06-23 19:43:57 +02:00
|
|
|
viewHolder.card = (CardView)convertView.findViewById(R.id.card);
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
convertView.setTag(viewHolder);
|
|
|
|
} else {
|
2015-06-23 19:43:57 +02:00
|
|
|
viewHolder = (ViewHolder) convertView.getTag();
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
final ListType.ItemsAll item = this.getItem(position);
|
|
|
|
|
|
|
|
// Differentiate between media
|
|
|
|
String title, details, artUrl;
|
|
|
|
int duration;
|
2015-02-16 12:36:21 +01:00
|
|
|
switch (item.type) {
|
|
|
|
case ListType.ItemsAll.TYPE_MOVIE:
|
2017-01-27 16:33:16 +01:00
|
|
|
title = TextUtils.isEmpty(item.title)? item.label : item.title;
|
2015-02-16 12:36:21 +01:00
|
|
|
details = item.tagline;
|
|
|
|
artUrl = item.thumbnail;
|
|
|
|
duration = item.runtime;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_EPISODE:
|
2017-01-27 16:33:16 +01:00
|
|
|
title = TextUtils.isEmpty(item.title)? item.label : item.title;
|
2015-02-16 12:36:21 +01:00
|
|
|
String season = String.format(getString(R.string.season_episode_abbrev), item.season, item.episode);
|
|
|
|
details = String.format("%s | %s", item.showtitle, season);
|
|
|
|
artUrl = item.art.poster;
|
|
|
|
duration = item.runtime;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_SONG:
|
2017-01-27 16:33:16 +01:00
|
|
|
title = TextUtils.isEmpty(item.title)? item.label : item.title;
|
2015-02-16 12:36:21 +01:00
|
|
|
details = item.displayartist + " | " + item.album;
|
|
|
|
artUrl = item.thumbnail;
|
|
|
|
duration = item.duration;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_MUSIC_VIDEO:
|
2017-01-27 16:33:16 +01:00
|
|
|
title = TextUtils.isEmpty(item.title)? item.label : item.title;
|
2015-02-16 12:36:21 +01:00
|
|
|
details = Utils.listStringConcat(item.artist, ", ") + " | " + item.album;
|
|
|
|
artUrl = item.thumbnail;
|
|
|
|
duration = item.runtime;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Don't yet recognize this type
|
2015-04-08 23:28:19 +02:00
|
|
|
title = TextUtils.isEmpty(item.label)? item.file : item.label;
|
2015-02-16 12:36:21 +01:00
|
|
|
details = item.type;
|
|
|
|
artUrl = item.thumbnail;
|
|
|
|
duration = item.runtime;
|
|
|
|
break;
|
2015-01-14 12:12:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
viewHolder.title.setText(title);
|
|
|
|
viewHolder.details.setText(details);
|
|
|
|
viewHolder.duration.setText((duration > 0) ? UIUtils.formatTime(duration) : "");
|
|
|
|
viewHolder.position = position;
|
|
|
|
|
2016-01-11 10:32:07 +01:00
|
|
|
int cardColor = (position == playlistListView.getCheckedItemPosition()) ?
|
|
|
|
selectedCardBackgroundColor: cardBackgroundColor;
|
2015-06-23 19:43:57 +02:00
|
|
|
viewHolder.card.setCardBackgroundColor(cardColor);
|
|
|
|
|
2015-02-16 12:36:21 +01:00
|
|
|
// If not video, change aspect ration of poster to a square
|
|
|
|
boolean isVideo = (item.type.equals(ListType.ItemsAll.TYPE_MOVIE)) ||
|
2016-01-11 10:32:07 +01:00
|
|
|
(item.type.equals(ListType.ItemsAll.TYPE_EPISODE));
|
2015-02-16 12:36:21 +01:00
|
|
|
if (!isVideo) {
|
|
|
|
ViewGroup.LayoutParams layoutParams = viewHolder.art.getLayoutParams();
|
|
|
|
layoutParams.width = layoutParams.height;
|
|
|
|
viewHolder.art.setLayoutParams(layoutParams);
|
|
|
|
artWidth = artHeight;
|
|
|
|
}
|
2015-01-24 12:14:40 +01:00
|
|
|
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
|
2015-06-23 19:43:57 +02:00
|
|
|
artUrl, title,
|
|
|
|
viewHolder.art, artWidth, artHeight);
|
2015-01-14 12:12:47 +01:00
|
|
|
|
|
|
|
// For the popupmenu
|
|
|
|
viewHolder.contextMenu.setTag(position);
|
|
|
|
viewHolder.contextMenu.setOnClickListener(playlistItemMenuClickListener);
|
|
|
|
|
|
|
|
return convertView;
|
|
|
|
}
|
|
|
|
|
2016-01-11 10:32:07 +01:00
|
|
|
private PlaylistType.Item createPlaylistTypeItem(ListType.ItemsAll item) {
|
|
|
|
PlaylistType.Item playlistItem = new PlaylistType.Item();
|
|
|
|
|
|
|
|
switch (item.type) {
|
|
|
|
case ListType.ItemsAll.TYPE_MOVIE:
|
|
|
|
playlistItem.movieid = item.id;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_EPISODE:
|
|
|
|
playlistItem.episodeid = item.id;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_SONG:
|
|
|
|
playlistItem.songid = item.id;
|
|
|
|
break;
|
|
|
|
case ListType.ItemsAll.TYPE_MUSIC_VIDEO:
|
|
|
|
playlistItem.musicvideoid = item.id;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LogUtils.LOGE(TAG, "createPlaylistTypeItem, failed to create item for "+item.type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return playlistItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void rollbackSwappedItems(int originalPosition, int newPosition) {
|
|
|
|
if (originalPosition > newPosition) {
|
|
|
|
for (int i = newPosition; i < originalPosition; i++) {
|
|
|
|
onSwapItems(i, i + 1);
|
|
|
|
}
|
|
|
|
} else if (originalPosition < newPosition) {
|
|
|
|
for (int i = newPosition; i > originalPosition; i--) {
|
|
|
|
onSwapItems(i, i - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-14 12:12:47 +01:00
|
|
|
private class ViewHolder {
|
|
|
|
ImageView art;
|
|
|
|
TextView title;
|
|
|
|
TextView details;
|
|
|
|
ImageView contextMenu;
|
|
|
|
TextView duration;
|
2015-06-23 19:43:57 +02:00
|
|
|
CardView card;
|
2015-01-14 12:12:47 +01:00
|
|
|
int position;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|