2015-03-19 21:24:04 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2015 DanhDroid. 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.ui;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2015-03-28 19:33:48 +01:00
|
|
|
import android.content.res.Resources;
|
2015-03-19 21:24:04 +01:00
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Handler;
|
2015-03-27 21:32:02 +01:00
|
|
|
import android.os.Parcel;
|
|
|
|
import android.os.Parcelable;
|
2015-03-19 21:24:04 +01:00
|
|
|
import android.support.v4.app.Fragment;
|
2015-03-31 01:19:38 +02:00
|
|
|
import android.support.v4.widget.SwipeRefreshLayout;
|
2015-03-19 21:24:04 +01:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.AdapterView;
|
|
|
|
import android.widget.BaseAdapter;
|
|
|
|
import android.widget.GridView;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
import android.widget.ListAdapter;
|
|
|
|
import android.widget.PopupMenu;
|
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import org.xbmc.kore.R;
|
|
|
|
import org.xbmc.kore.host.HostManager;
|
|
|
|
import org.xbmc.kore.jsonrpc.ApiCallback;
|
2015-04-10 20:51:44 +02:00
|
|
|
import org.xbmc.kore.jsonrpc.HostConnection;
|
2015-03-19 21:24:04 +01:00
|
|
|
import org.xbmc.kore.jsonrpc.method.Files;
|
|
|
|
import org.xbmc.kore.jsonrpc.method.Player;
|
|
|
|
import org.xbmc.kore.jsonrpc.method.Playlist;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.ItemType;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.ListType;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
|
|
|
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
2015-03-28 19:33:48 +01:00
|
|
|
import org.xbmc.kore.utils.LogUtils;
|
2015-03-19 21:24:04 +01:00
|
|
|
import org.xbmc.kore.utils.UIUtils;
|
2015-03-28 19:33:48 +01:00
|
|
|
import org.xbmc.kore.utils.Utils;
|
2015-03-19 21:24:04 +01:00
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Queue;
|
|
|
|
|
|
|
|
import butterknife.ButterKnife;
|
|
|
|
import butterknife.InjectView;
|
|
|
|
|
|
|
|
/**
|
2015-03-22 16:55:18 +01:00
|
|
|
* Presents a list of files of different types (Video/Music)
|
2015-03-19 21:24:04 +01:00
|
|
|
*/
|
2015-03-31 00:42:03 +02:00
|
|
|
public class MediaFileListFragment extends Fragment {
|
2015-03-28 19:33:48 +01:00
|
|
|
private static final String TAG = LogUtils.makeLogTag(MediaFileListFragment.class);
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
public static final String MEDIA_TYPE = "mediaType";
|
2015-03-27 21:32:02 +01:00
|
|
|
public static final String PATH_CONTENTS = "pathContents";
|
|
|
|
public static final String ROOT_PATH_CONTENTS = "rootPathContents";
|
|
|
|
public static final String ROOT_VISITED = "rootVisited";
|
|
|
|
private static final String ADDON_SOURCE = "addons:";
|
2015-03-19 21:24:04 +01:00
|
|
|
|
|
|
|
private HostManager hostManager;
|
|
|
|
/**
|
|
|
|
* Handler on which to post RPC callbacks
|
|
|
|
*/
|
|
|
|
private Handler callbackHandler = new Handler();
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
String mediaType = Files.Media.MUSIC;
|
|
|
|
String parentDirectory = null;
|
2015-04-10 20:51:44 +02:00
|
|
|
int playlistId = PlaylistType.MUSIC_PLAYLISTID; // this is the ID of the music player
|
2015-03-19 21:24:04 +01:00
|
|
|
private MediaFileListAdapter adapter = null;
|
|
|
|
boolean browseRootAlready = false;
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
ArrayList<FileLocation> rootFileLocation = new ArrayList<FileLocation>();
|
|
|
|
Queue<FileLocation> mediaQueueFileLocation = new LinkedList<>();
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
@InjectView(R.id.list) GridView folderGridView;
|
2015-03-31 01:19:38 +02:00
|
|
|
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
2015-03-28 19:33:48 +01:00
|
|
|
@InjectView(android.R.id.empty) TextView emptyView;
|
2015-03-19 21:24:04 +01:00
|
|
|
|
|
|
|
public static MediaFileListFragment newInstance(final String media) {
|
|
|
|
MediaFileListFragment fragment = new MediaFileListFragment();
|
|
|
|
Bundle args = new Bundle();
|
|
|
|
args.putString(MEDIA_TYPE, media);
|
|
|
|
fragment.setArguments(args);
|
|
|
|
return fragment;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-03-27 21:32:02 +01:00
|
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
outState.putString(MEDIA_TYPE, mediaType);
|
|
|
|
try {
|
2015-04-23 20:26:12 +02:00
|
|
|
outState.putParcelableArrayList(PATH_CONTENTS, (ArrayList<FileLocation>)adapter.getFileItemList());
|
2015-03-27 21:32:02 +01:00
|
|
|
} catch (NullPointerException npe) {
|
|
|
|
// adapter is null probably nothing was save in bundle because the directory is empty
|
|
|
|
// ignore this so that the empty message would display later on
|
|
|
|
}
|
|
|
|
outState.putParcelableArrayList(ROOT_PATH_CONTENTS, rootFileLocation);
|
|
|
|
outState.putBoolean(ROOT_VISITED, browseRootAlready);
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
|
|
Bundle args = getArguments();
|
|
|
|
if (args != null) {
|
2015-03-22 16:55:18 +01:00
|
|
|
mediaType = args.getString(MEDIA_TYPE, Files.Media.MUSIC);
|
2015-03-19 21:24:04 +01:00
|
|
|
if (mediaType.equalsIgnoreCase(Files.Media.VIDEO)) {
|
2015-04-10 20:51:44 +02:00
|
|
|
playlistId = PlaylistType.VIDEO_PLAYLISTID;
|
2015-03-27 21:32:02 +01:00
|
|
|
} else if (mediaType.equalsIgnoreCase(Files.Media.PICTURES)) {
|
2015-04-10 20:51:44 +02:00
|
|
|
playlistId = PlaylistType.PICTURE_PLAYLISTID;
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
}
|
2015-03-28 19:33:48 +01:00
|
|
|
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
|
2015-03-19 21:24:04 +01:00
|
|
|
ButterKnife.inject(this, root);
|
|
|
|
|
|
|
|
hostManager = HostManager.getInstance(getActivity());
|
2015-03-31 01:19:38 +02:00
|
|
|
swipeRefreshLayout.setEnabled(false);
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
emptyView.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
2015-03-31 00:42:03 +02:00
|
|
|
browseSources();
|
2015-03-28 19:33:48 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
folderGridView.setEmptyView(emptyView);
|
2015-03-19 21:24:04 +01:00
|
|
|
folderGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
2015-03-31 00:42:03 +02:00
|
|
|
handleFileSelect(adapter.getItem(position));
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
if (adapter == null) {
|
|
|
|
adapter = new MediaFileListAdapter(getActivity(), R.layout.grid_item_file);
|
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
folderGridView.setAdapter(adapter);
|
2015-03-27 21:32:02 +01:00
|
|
|
if (savedInstanceState != null) {
|
|
|
|
mediaType = savedInstanceState.getString(MEDIA_TYPE);
|
|
|
|
//currentPath = savedInstanceState.getString(CURRENT_PATH);
|
|
|
|
if (mediaType.equalsIgnoreCase(Files.Media.VIDEO)) {
|
2015-04-10 20:51:44 +02:00
|
|
|
playlistId = PlaylistType.VIDEO_PLAYLISTID;
|
2015-03-27 21:32:02 +01:00
|
|
|
} else if (mediaType.equalsIgnoreCase(Files.Media.PICTURES)) {
|
2015-04-10 20:51:44 +02:00
|
|
|
playlistId = PlaylistType.PICTURE_PLAYLISTID;
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
|
|
|
ArrayList<FileLocation> list = savedInstanceState.getParcelableArrayList(PATH_CONTENTS);
|
|
|
|
rootFileLocation = savedInstanceState.getParcelableArrayList(ROOT_PATH_CONTENTS);
|
|
|
|
browseRootAlready = savedInstanceState.getBoolean(ROOT_VISITED);
|
|
|
|
adapter.setFilelistItems(list);
|
|
|
|
}
|
|
|
|
else {
|
2015-03-31 00:42:03 +02:00
|
|
|
browseSources();
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2015-03-31 00:42:03 +02:00
|
|
|
void handleFileSelect(FileLocation f) {
|
2015-03-27 21:32:02 +01:00
|
|
|
// if selection is a directory, browse the the level below
|
|
|
|
if (f.isDirectory) {
|
|
|
|
// a directory - store the path of this directory so that we can reverse travel if
|
|
|
|
// we want to
|
|
|
|
if (f.isRootDir()) {
|
|
|
|
if (browseRootAlready)
|
2015-03-31 00:42:03 +02:00
|
|
|
browseDirectory(f);
|
2015-03-27 21:32:02 +01:00
|
|
|
else {
|
2015-03-31 00:42:03 +02:00
|
|
|
browseSources();
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
|
|
|
} else {
|
2015-03-31 00:42:03 +02:00
|
|
|
browseDirectory(f);
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
2015-03-31 00:42:03 +02:00
|
|
|
} else {
|
|
|
|
playMediaFile(f.file);
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
|
2015-03-27 21:32:02 +01:00
|
|
|
public void onBackPressed() {
|
2015-03-31 00:42:03 +02:00
|
|
|
// Emulate a click on ..
|
|
|
|
handleFileSelect(adapter.getItem(0));
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
|
2015-03-27 21:32:02 +01:00
|
|
|
public boolean atRootDirectory() {
|
|
|
|
if (adapter.getCount() == 0)
|
|
|
|
return true;
|
|
|
|
FileLocation fl = adapter.getItem(0);
|
|
|
|
if (fl == null)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
// if we still see "..", it is not the real root directory
|
2015-03-28 19:33:48 +01:00
|
|
|
return fl.isRootDir() && (fl.title.contentEquals("..") == false);
|
|
|
|
}
|
|
|
|
|
2015-03-31 00:42:03 +02:00
|
|
|
/**
|
|
|
|
* Gets and presents the list of media sources
|
|
|
|
*/
|
|
|
|
private void browseSources() {
|
2015-03-19 21:24:04 +01:00
|
|
|
Files.GetSources action = new Files.GetSources(mediaType);
|
|
|
|
action.execute(hostManager.getConnection(), new ApiCallback<List<ItemType.Source>>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(List<ItemType.Source> result) {
|
2015-03-28 19:33:48 +01:00
|
|
|
if (!isAdded()) return;
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
// save this to compare when the user select a node
|
|
|
|
rootFileLocation.clear();
|
|
|
|
FileLocation fl;
|
|
|
|
for (ItemType.Source item : result) {
|
2015-03-27 21:32:02 +01:00
|
|
|
if (!item.file.contains(ADDON_SOURCE)) {
|
|
|
|
fl = new FileLocation(item.label, item.file, true);
|
|
|
|
fl.setRootDir(true);
|
|
|
|
rootFileLocation.add(fl);
|
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
browseRootAlready = true;
|
2015-03-28 19:33:48 +01:00
|
|
|
emptyView.setText(getString(R.string.source_empty));
|
2015-03-19 21:24:04 +01:00
|
|
|
adapter.setFilelistItems(rootFileLocation);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
2015-03-28 19:33:48 +01:00
|
|
|
if (!isAdded()) return;
|
2015-03-31 00:42:03 +02:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_getting_source_info), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
|
2015-03-31 00:42:03 +02:00
|
|
|
/**
|
|
|
|
* Gets and presents the files of the specified directory
|
|
|
|
* @param dir Directory to browse
|
|
|
|
*/
|
|
|
|
private void browseDirectory(final FileLocation dir) {
|
|
|
|
if (dir.isRootDir()) {
|
2015-03-19 21:24:04 +01:00
|
|
|
// this is a root directory
|
2015-03-31 00:42:03 +02:00
|
|
|
parentDirectory = dir.file;
|
|
|
|
} else {
|
2015-03-19 21:24:04 +01:00
|
|
|
// check to make sure that this is not our root path
|
|
|
|
String rootPath = null;
|
|
|
|
String path;
|
|
|
|
for (FileLocation fl : rootFileLocation) {
|
|
|
|
path = fl.file;
|
2015-03-31 00:42:03 +02:00
|
|
|
if (dir.file.contentEquals(path)) {
|
2015-03-19 21:24:04 +01:00
|
|
|
rootPath = fl.file;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rootPath != null) {
|
|
|
|
parentDirectory = rootPath;
|
2015-03-31 00:42:03 +02:00
|
|
|
dir.setRootDir(true);
|
2015-03-22 16:55:18 +01:00
|
|
|
} else {
|
2015-03-31 00:42:03 +02:00
|
|
|
parentDirectory = getParentDirectory(dir.file);
|
2015-03-22 16:55:18 +01:00
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
String[] properties = new String[] {
|
|
|
|
ListType.FieldsFiles.TITLE, ListType.FieldsFiles.ARTIST,
|
|
|
|
//ListType.FieldsFiles.ALBUMARTIST, ListType.FieldsFiles.GENRE,
|
|
|
|
//ListType.FieldsFiles.YEAR, ListType.FieldsFiles.RATING,
|
|
|
|
ListType.FieldsFiles.ALBUM, ListType.FieldsFiles.TRACK, ListType.FieldsFiles.DURATION,
|
|
|
|
//ListType.FieldsFiles.COMMENT,
|
|
|
|
//ListType.FieldsFiles.LYRICS, ListType.FieldsFiles.MUSICBRAINZTRACKID,
|
|
|
|
//ListType.FieldsFiles.MUSICBRAINZARTISTID, ListType.FieldsFiles.MUSICBRAINZALBUMID,
|
|
|
|
//ListType.FieldsFiles.MUSICBRAINZALBUMARTISTID, ListType.FieldsFiles.PLAYCOUNT,
|
|
|
|
//ListType.FieldsFiles.FANART,
|
|
|
|
//ListType.FieldsFiles.DIRECTOR, ListType.FieldsFiles.TRAILER,
|
|
|
|
ListType.FieldsFiles.TAGLINE,
|
|
|
|
//ListType.FieldsFiles.PLOT, ListType.FieldsFiles.PLOTOUTLINE, ListType.FieldsFiles.ORIGINALTITLE,
|
|
|
|
//ListType.FieldsFiles.LASTPLAYED, ListType.FieldsFiles.WRITER, ListType.FieldsFiles.STUDIO,
|
|
|
|
//ListType.FieldsFiles.MPAA, ListType.FieldsFiles.CAST, ListType.FieldsFiles.COUNTRY,
|
|
|
|
//ListType.FieldsFiles.IMDBNUMBER, ListType.FieldsFiles.PREMIERED,
|
|
|
|
//ListType.FieldsFiles.PRODUCTIONCODE,
|
|
|
|
ListType.FieldsFiles.RUNTIME,
|
|
|
|
//ListType.FieldsFiles.SET,
|
|
|
|
//ListType.FieldsFiles.SHOWLINK, ListType.FieldsFiles.STREAMDETAILS,
|
|
|
|
//ListType.FieldsFiles.TOP250, ListType.FieldsFiles.VOTES,
|
|
|
|
//ListType.FieldsFiles.FIRSTAIRED,
|
|
|
|
ListType.FieldsFiles.SEASON, ListType.FieldsFiles.EPISODE,
|
|
|
|
ListType.FieldsFiles.SHOWTITLE, ListType.FieldsFiles.THUMBNAIL, ListType.FieldsFiles.FILE,
|
|
|
|
//ListType.FieldsFiles.RESUME, ListType.FieldsFiles.ARTISTID, ListType.FieldsFiles.ALBUMID,
|
|
|
|
//ListType.FieldsFiles.TVSHOWID, ListType.FieldsFiles.SETID, ListType.FieldsFiles.WATCHEDEPISODES,
|
|
|
|
//ListType.FieldsFiles.DISC, ListType.FieldsFiles.TAG, ListType.FieldsFiles.ART,
|
|
|
|
//ListType.FieldsFiles.GENREID,
|
|
|
|
ListType.FieldsFiles.DISPLAYARTIST,
|
|
|
|
//ListType.FieldsFiles.ALBUMARTISTID, ListType.FieldsFiles.DESCRIPTION, ListType.FieldsFiles.THEME,
|
|
|
|
//ListType.FieldsFiles.MOOD, ListType.FieldsFiles.STYLE, ListType.FieldsFiles.ALBUMLABEL,
|
|
|
|
//ListType.FieldsFiles.SORTTITLE, ListType.FieldsFiles.EPISODEGUIDE,
|
|
|
|
//ListType.FieldsFiles.UNIQUEID, ListType.FieldsFiles.DATEADDED,
|
|
|
|
ListType.FieldsFiles.SIZE, ListType.FieldsFiles.LASTMODIFIED, ListType.FieldsFiles.MIMETYPE
|
|
|
|
};
|
|
|
|
|
2015-03-31 00:42:03 +02:00
|
|
|
Files.GetDirectory action = new Files.GetDirectory(dir.file,
|
2015-03-28 19:33:48 +01:00
|
|
|
mediaType,
|
|
|
|
new ListType.Sort(ListType.Sort.SORT_METHOD_LABEL, true, true),
|
|
|
|
properties);
|
2015-03-19 21:24:04 +01:00
|
|
|
action.execute(hostManager.getConnection(), new ApiCallback<List<ListType.ItemFile>>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(List<ListType.ItemFile> result) {
|
2015-03-22 16:55:18 +01:00
|
|
|
if (!isAdded()) return;
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
ArrayList<FileLocation> flList = new ArrayList<FileLocation>();
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
// insert the parent directory as the first item in the list
|
|
|
|
FileLocation fl = new FileLocation("..", parentDirectory, true);
|
2015-03-31 00:42:03 +02:00
|
|
|
fl.setRootDir(dir.isRootDir());
|
2015-03-19 21:24:04 +01:00
|
|
|
flList.add(0, fl);
|
2015-03-28 19:33:48 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
for (ListType.ItemFile i : result) {
|
2015-03-28 19:33:48 +01:00
|
|
|
flList.add(FileLocation.newInstanceFromItemFile(getActivity(), i));
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
adapter.setFilelistItems(flList);
|
|
|
|
browseRootAlready = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
2015-03-22 16:55:18 +01:00
|
|
|
if (!isAdded()) return;
|
2015-03-28 19:33:48 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_getting_source_info), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-04-10 20:51:44 +02:00
|
|
|
/**
|
|
|
|
* Starts playing the given media file
|
|
|
|
* @param filename Filename to start playing
|
|
|
|
*/
|
2015-03-19 21:24:04 +01:00
|
|
|
private void playMediaFile(final String filename) {
|
|
|
|
PlaylistType.Item item = new PlaylistType.Item();
|
|
|
|
item.file = filename;
|
|
|
|
Player.Open action = new Player.Open(item);
|
|
|
|
action.execute(hostManager.getConnection(), new ApiCallback<String>() {
|
|
|
|
@Override
|
2015-03-31 00:42:03 +02:00
|
|
|
public void onSuccess(String result) {
|
2015-03-19 21:24:04 +01:00
|
|
|
while (mediaQueueFileLocation.size() > 0) {
|
2015-04-10 20:51:44 +02:00
|
|
|
queueMediaFile(mediaQueueFileLocation.poll().file);
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
2015-03-22 16:55:18 +01:00
|
|
|
if (!isAdded()) return;
|
2015-03-19 21:24:04 +01:00
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_play_media_file), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
|
2015-04-10 20:51:44 +02:00
|
|
|
/**
|
|
|
|
* Queues the given media file on the active playlist, and starts it if nothing is playing
|
|
|
|
* @param filename File to queue
|
|
|
|
*/
|
|
|
|
private void queueMediaFile(final String filename) {
|
|
|
|
final HostConnection connection = hostManager.getConnection();
|
2015-03-19 21:24:04 +01:00
|
|
|
PlaylistType.Item item = new PlaylistType.Item();
|
2015-04-10 20:51:44 +02:00
|
|
|
item.file = filename;
|
2015-03-19 21:24:04 +01:00
|
|
|
Playlist.Add action = new Playlist.Add(playlistId, item);
|
2015-04-10 20:51:44 +02:00
|
|
|
action.execute(connection, new ApiCallback<String>() {
|
2015-03-19 21:24:04 +01:00
|
|
|
@Override
|
|
|
|
public void onSuccess(String result ) {
|
2015-04-10 20:51:44 +02:00
|
|
|
startPlaylistIfNoActivePlayers(connection, playlistId, callbackHandler);
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
2015-03-22 16:55:18 +01:00
|
|
|
if (!isAdded()) return;
|
2015-03-19 21:24:04 +01:00
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_queue_media_file), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
2015-04-10 20:51:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts a playlist if no active players are playing
|
|
|
|
* @param connection Host connection
|
|
|
|
* @param playlistId PlaylistId to start
|
|
|
|
* @param callbackHandler Handler on which to post method callbacks
|
|
|
|
*/
|
|
|
|
private void startPlaylistIfNoActivePlayers(final HostConnection connection,
|
|
|
|
final int playlistId,
|
|
|
|
final Handler callbackHandler) {
|
|
|
|
Player.GetActivePlayers action = new Player.GetActivePlayers();
|
|
|
|
action.execute(connection, new ApiCallback<ArrayList<PlayerType.GetActivePlayersReturnType>>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(ArrayList<PlayerType.GetActivePlayersReturnType> result ) {
|
|
|
|
// find out if any player is running. If it is not, start one
|
|
|
|
if (result.isEmpty()) {
|
2015-11-10 00:40:48 +01:00
|
|
|
Player.Open action = new Player.Open(Player.Open.TYPE_PLAYLIST, playlistId);
|
2015-04-10 20:51:44 +02:00
|
|
|
action.execute(connection, new ApiCallback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(String result) { }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
|
|
|
if (!isAdded()) return;
|
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_play_media_file), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(int errorCode, String description) {
|
|
|
|
if (!isAdded()) return;
|
|
|
|
Toast.makeText(getActivity(),
|
|
|
|
String.format(getString(R.string.error_get_active_player), description),
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}, callbackHandler);
|
2015-03-19 21:24:04 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return the path of the parent based on path
|
|
|
|
* @param path of the current media file
|
|
|
|
* @return path of the parent
|
|
|
|
*/
|
|
|
|
public static String getParentDirectory(final String path) {
|
|
|
|
String p = path;
|
|
|
|
String pathSymbol = "/"; // unix style
|
|
|
|
if (path.contains("\\")) {
|
|
|
|
pathSymbol = "\\"; // windows style
|
|
|
|
}
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
// if path ends with /, remove it before removing the directory name
|
|
|
|
if (path.endsWith(pathSymbol)) {
|
|
|
|
p = path.substring(0, path.length() - 1);
|
|
|
|
}
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-04-14 00:29:20 +02:00
|
|
|
if (p.lastIndexOf(pathSymbol) != -1) {
|
|
|
|
p = p.substring(0, p.lastIndexOf(pathSymbol));
|
|
|
|
}
|
2015-03-19 21:24:04 +01:00
|
|
|
p = p + pathSymbol; // add it back to make it look like path
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class MediaFileListAdapter extends BaseAdapter implements ListAdapter {
|
|
|
|
|
|
|
|
Context ctx;
|
|
|
|
int resource;
|
|
|
|
List<FileLocation> fileLocationItems = null;
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
int artWidth;
|
|
|
|
int artHeight;
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
private View.OnClickListener itemMenuClickListener = new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
final int position = (Integer)v.getTag();
|
|
|
|
if (fileLocationItems != null) {
|
|
|
|
final FileLocation loc = fileLocationItems.get(position);
|
|
|
|
if (!loc.isDirectory) {
|
|
|
|
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
|
|
|
|
popupMenu.getMenuInflater().inflate(R.menu.filelist_item, popupMenu.getMenu());
|
|
|
|
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onMenuItemClick(MenuItem item) {
|
|
|
|
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.action_queue_item:
|
2015-04-10 20:51:44 +02:00
|
|
|
queueMediaFile(loc.file);
|
2015-03-19 21:24:04 +01:00
|
|
|
return true;
|
|
|
|
case R.id.action_play_item:
|
|
|
|
playMediaFile(loc.file);
|
|
|
|
return true;
|
|
|
|
case R.id.action_play_from_this_item:
|
|
|
|
mediaQueueFileLocation.clear();
|
|
|
|
FileLocation fl;
|
|
|
|
for (int i = position + 1; i < fileLocationItems.size(); i++) {
|
|
|
|
fl = fileLocationItems.get(i);
|
|
|
|
if (!fl.isDirectory) {
|
|
|
|
mediaQueueFileLocation.add(fl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// start playing the selected one, then queue the rest make sure to queue
|
|
|
|
// the selected on last so the it does not lose its place in the queue
|
|
|
|
mediaQueueFileLocation.add(loc);
|
|
|
|
playMediaFile(loc.file);
|
2015-03-22 16:55:18 +01:00
|
|
|
return true;
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
popupMenu.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public MediaFileListAdapter(Context context, int resource) {
|
|
|
|
super();
|
|
|
|
this.ctx = context;
|
|
|
|
this.resource = resource;
|
|
|
|
this.fileLocationItems = null;
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
// Get the art dimensions
|
|
|
|
Resources resources = context.getResources();
|
|
|
|
artWidth = (int)(resources.getDimension(R.dimen.filelist_art_width) /
|
|
|
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
|
|
|
artHeight = (int)(resources.getDimension(R.dimen.filelist_art_heigth) /
|
|
|
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manually set the items on the adapter
|
|
|
|
* Calls notifyDataSetChanged()
|
|
|
|
*
|
|
|
|
* @param items list of files/directories
|
|
|
|
*/
|
|
|
|
public void setFilelistItems(List<FileLocation> items) {
|
|
|
|
this.fileLocationItems = items;
|
|
|
|
notifyDataSetChanged();
|
|
|
|
}
|
2015-03-22 16:55:18 +01:00
|
|
|
|
2015-03-27 21:32:02 +01:00
|
|
|
public List<FileLocation> getFileItemList() {
|
|
|
|
if (fileLocationItems == null)
|
|
|
|
return new ArrayList<FileLocation>();
|
|
|
|
return new ArrayList<FileLocation>(fileLocationItems);
|
|
|
|
}
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
@Override
|
|
|
|
public int getCount() {
|
|
|
|
if (fileLocationItems == null) {
|
|
|
|
return 0;
|
2015-03-22 16:55:18 +01:00
|
|
|
} else {
|
2015-03-19 21:24:04 +01:00
|
|
|
return fileLocationItems.size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FileLocation getItem(int position) {
|
|
|
|
if (fileLocationItems == null) {
|
|
|
|
return null;
|
2015-03-22 16:55:18 +01:00
|
|
|
} else {
|
2015-03-19 21:24:04 +01:00
|
|
|
return fileLocationItems.get(position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getItemId(int position) {
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getViewTypeCount () {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
@Override
|
|
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
|
|
ViewHolder viewHolder;
|
|
|
|
if (convertView == null) {
|
|
|
|
convertView = LayoutInflater.from(ctx)
|
|
|
|
.inflate(resource, parent, false);
|
|
|
|
|
|
|
|
// Setup View holder pattern
|
|
|
|
viewHolder = new ViewHolder();
|
|
|
|
viewHolder.art = (ImageView) convertView.findViewById(R.id.art);
|
|
|
|
viewHolder.title = (TextView) convertView.findViewById(R.id.title);
|
2015-03-28 19:33:48 +01:00
|
|
|
viewHolder.details = (TextView) convertView.findViewById(R.id.details);
|
2015-03-19 21:24:04 +01:00
|
|
|
viewHolder.contextMenu = (ImageView) convertView.findViewById(R.id.list_context_menu);
|
2015-03-28 19:33:48 +01:00
|
|
|
viewHolder.sizeDuration = (TextView) convertView.findViewById(R.id.size_duration);
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
convertView.setTag(viewHolder);
|
|
|
|
}
|
|
|
|
|
|
|
|
viewHolder = (ViewHolder) convertView.getTag();
|
|
|
|
FileLocation fileLocation = this.getItem(position);
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
// if (fileLocation.isDirectory) {
|
|
|
|
// viewHolder.title.setText(fileLocation.title);
|
|
|
|
// viewHolder.details.setText("");
|
|
|
|
// } else {
|
|
|
|
// viewHolder.title.setText("");
|
|
|
|
// viewHolder.details.setText(fileLocation.title);
|
|
|
|
// }
|
|
|
|
viewHolder.title.setText(fileLocation.title);
|
|
|
|
viewHolder.details.setText(fileLocation.details);
|
|
|
|
viewHolder.sizeDuration.setText(fileLocation.sizeDuration);
|
2015-03-19 21:24:04 +01:00
|
|
|
|
|
|
|
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
|
2015-03-28 19:33:48 +01:00
|
|
|
fileLocation.artUrl, fileLocation.title,
|
2015-03-19 21:24:04 +01:00
|
|
|
viewHolder.art, artWidth, artHeight);
|
|
|
|
// For the popup menu
|
|
|
|
if (fileLocation.isDirectory) {
|
|
|
|
viewHolder.contextMenu.setVisibility(View.GONE);
|
2015-03-22 16:55:18 +01:00
|
|
|
} else {
|
2015-03-19 21:24:04 +01:00
|
|
|
viewHolder.contextMenu.setVisibility(View.VISIBLE);
|
|
|
|
viewHolder.contextMenu.setTag(position);
|
|
|
|
viewHolder.contextMenu.setOnClickListener(itemMenuClickListener);
|
|
|
|
}
|
|
|
|
|
|
|
|
return convertView;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* View holder pattern
|
|
|
|
*/
|
2015-03-28 19:33:48 +01:00
|
|
|
private static class ViewHolder {
|
2015-03-19 21:24:04 +01:00
|
|
|
ImageView art;
|
|
|
|
TextView title;
|
2015-03-28 19:33:48 +01:00
|
|
|
TextView details;
|
|
|
|
TextView sizeDuration;
|
2015-03-19 21:24:04 +01:00
|
|
|
ImageView contextMenu;
|
|
|
|
}
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
public static class FileLocation implements Parcelable {
|
|
|
|
public final String title;
|
|
|
|
public final String details;
|
|
|
|
public final String sizeDuration;
|
|
|
|
public final String artUrl;
|
|
|
|
|
2015-03-19 21:24:04 +01:00
|
|
|
public final String file;
|
|
|
|
public final boolean isDirectory;
|
|
|
|
private boolean isRoot;
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isRootDir() { return this.isRoot; }
|
|
|
|
public void setRootDir(boolean root) { this.isRoot = root; }
|
|
|
|
|
2015-03-28 19:33:48 +01:00
|
|
|
public FileLocation(String title, String path, boolean isDir) {
|
|
|
|
this(title, path, isDir, null, null, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public FileLocation(String title, String path, boolean isDir, String details, String sizeDuration, String artUrl) {
|
|
|
|
this.title = title;
|
2015-03-19 21:24:04 +01:00
|
|
|
this.file = path;
|
|
|
|
this.isDirectory = isDir;
|
|
|
|
this.isRoot = false;
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
this.details = details;
|
|
|
|
this.sizeDuration = sizeDuration;
|
|
|
|
this.artUrl = artUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static FileLocation newInstanceFromItemFile(Context context, ListType.ItemFile itemFile) {
|
|
|
|
String title, details, sizeDuration, artUrl;
|
|
|
|
|
|
|
|
switch (itemFile.type) {
|
|
|
|
case ListType.ItemBase.TYPE_MOVIE:
|
|
|
|
title = itemFile.title;
|
|
|
|
details = itemFile.tagline;
|
|
|
|
sizeDuration = UIUtils.formatTime(itemFile.runtime);
|
|
|
|
artUrl = itemFile.thumbnail;
|
|
|
|
break;
|
|
|
|
case ListType.ItemBase.TYPE_EPISODE:
|
|
|
|
title = itemFile.title;
|
|
|
|
details = String.format(context.getString(R.string.season_episode), itemFile.season, itemFile.episode);
|
|
|
|
sizeDuration = UIUtils.formatTime(itemFile.runtime);
|
|
|
|
artUrl = itemFile.thumbnail;
|
|
|
|
break;
|
|
|
|
case ListType.ItemBase.TYPE_MUSIC_VIDEO:
|
|
|
|
title = itemFile.title;
|
|
|
|
details = Utils.listStringConcat(itemFile.artist, ", ") + " | " + itemFile.album;
|
|
|
|
sizeDuration = UIUtils.formatTime(itemFile.runtime);
|
|
|
|
artUrl = itemFile.thumbnail;
|
|
|
|
break;
|
|
|
|
case ListType.ItemBase.TYPE_ALBUM:
|
|
|
|
case ListType.ItemBase.TYPE_SONG:
|
|
|
|
title = itemFile.title;
|
|
|
|
details = itemFile.displayartist + " | " + itemFile.album;
|
|
|
|
artUrl = itemFile.thumbnail;
|
|
|
|
sizeDuration = UIUtils.formatTime(itemFile.duration);
|
|
|
|
break;
|
|
|
|
case ListType.ItemBase.TYPE_PICTURE:
|
|
|
|
default:
|
|
|
|
title = itemFile.label;
|
|
|
|
details = null;
|
|
|
|
artUrl = itemFile.thumbnail;
|
|
|
|
sizeDuration = UIUtils.formatFileSize(itemFile.size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new FileLocation(title, itemFile.file,
|
|
|
|
itemFile.filetype.equalsIgnoreCase(ListType.ItemFile.FILETYPE_DIRECTORY),
|
|
|
|
details, sizeDuration, artUrl);
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
2015-03-27 21:32:02 +01:00
|
|
|
|
|
|
|
private FileLocation(Parcel in) {
|
2015-03-28 19:33:48 +01:00
|
|
|
this.title = in.readString();
|
2015-03-27 21:32:02 +01:00
|
|
|
this.file = in.readString();
|
|
|
|
this.isDirectory = (in.readInt() != 0);
|
|
|
|
this.isRoot = (in.readInt() != 0);
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
this.details = in.readString();
|
|
|
|
this.sizeDuration = in.readString();
|
|
|
|
this.artUrl = in.readString();
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public int describeContents() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void writeToParcel(Parcel out, int flags) {
|
2015-03-28 19:33:48 +01:00
|
|
|
out.writeString(title);
|
2015-03-27 21:32:02 +01:00
|
|
|
out.writeString(file);
|
|
|
|
out.writeInt(isDirectory ? 1 : 0);
|
|
|
|
out.writeInt(isRoot ? 1 : 0);
|
2015-03-28 19:33:48 +01:00
|
|
|
|
|
|
|
out.writeString(details);
|
|
|
|
out.writeString(sizeDuration);
|
|
|
|
out.writeString(artUrl);
|
2015-03-27 21:32:02 +01:00
|
|
|
}
|
|
|
|
|
2015-04-14 00:21:35 +02:00
|
|
|
public static final Parcelable.Creator<FileLocation> CREATOR = new Parcelable.Creator<FileLocation>() {
|
2015-03-27 21:32:02 +01:00
|
|
|
public FileLocation createFromParcel(Parcel in) {
|
|
|
|
return new FileLocation(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
public FileLocation[] newArray(int size) {
|
|
|
|
return new FileLocation[size];
|
|
|
|
}
|
|
|
|
};
|
2015-03-19 21:24:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|