Add support for PVR - first version.

This commit is contained in:
Synced Synapse 2015-11-09 23:40:48 +00:00
parent 19611feb2a
commit cc3ecdd5f5
20 changed files with 839 additions and 29 deletions

View File

@ -53,6 +53,7 @@
<activity android:name="org.xbmc.kore.ui.AddonsActivity"/>
<activity android:name="org.xbmc.kore.ui.SettingsActivity"/>
<activity android:name="org.xbmc.kore.ui.FileActivity"/>
<activity android:name="org.xbmc.kore.ui.PVRActivity"/>
<activity android:name="org.xbmc.kore.ui.AllCastActivity"/>
<!-- Providers -->

View File

@ -433,13 +433,17 @@ public class Player {
public static final class Open extends ApiMethod<String> {
public final static String METHOD_NAME = "Player.Open";
public final static String TYPE_PLAYLIST = "playlist",
TYPE_CHANNEL = "channel";
/**
* Start playback of either the playlist with the given ID, a slideshow with the pictures
* from the given directory or a single file or an item from the database.
* @param playlistId
* @param position
* @param itemType This should always be TYPE_PLAYLIST
* @param playlistId Id
* @param position Position to start
*/
public Open(int playlistId, int position) {
public Open(String itemType, int playlistId, int position) {
super();
final ObjectNode item = objectMapper.createObjectNode();
item.put("playlistid", playlistId);
@ -458,14 +462,22 @@ public class Player {
}
/**
* Select the active player
* @param playlistId playlist ID to select
* Starts playing a playlist or channel
* @param itemType TYPE_PLAYLIST or TYPE_CHANNEL
* @param itemId Corresponding ID to open
*/
public Open(int playlistId) {
public Open(String itemType, int itemId) {
super();
final ObjectNode item = objectMapper.createObjectNode();
item.put("playlistid", playlistId);
addParameterToRequest("item", item);
switch (itemType) {
case TYPE_PLAYLIST:
item.put("playlistid", itemId);
addParameterToRequest("item", item);
break;
case TYPE_CHANNEL:
item.put("channelid", itemId);
addParameterToRequest("item", item);
}
}
@Override

View File

@ -402,7 +402,7 @@ public class MediaFileListFragment extends Fragment {
public void onSuccess(ArrayList<PlayerType.GetActivePlayersReturnType> result ) {
// find out if any player is running. If it is not, start one
if (result.isEmpty()) {
Player.Open action = new Player.Open(playlistId);
Player.Open action = new Player.Open(Player.Open.TYPE_PLAYLIST, playlistId);
action.execute(connection, new ApiCallback<String>() {
@Override
public void onSuccess(String result) { }

View File

@ -29,7 +29,6 @@ import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@ -65,9 +64,10 @@ public class NavigationDrawerFragment extends Fragment {
ACTIVITY_MOVIES = 2,
ACTIVITY_TVSHOWS = 3,
ACTIVITY_MUSIC = 4,
ACTIVITY_FILES = 5,
ACTIVITY_ADDONS = 6,
ACTIVITY_SETTINGS = 7;
ACTIVITY_PVR = 5,
ACTIVITY_FILES = 6,
ACTIVITY_ADDONS = 7,
ACTIVITY_SETTINGS = 8;
// The current selected item id (based on the activity)
private static int selectedItemId = -1;
@ -126,6 +126,7 @@ public class NavigationDrawerFragment extends Fragment {
R.attr.iconMovies,
R.attr.iconTvShows,
R.attr.iconMusic,
R.attr.iconPVR,
R.attr.iconFiles,
R.attr.iconAddons,
R.attr.iconSettings,
@ -149,6 +150,9 @@ public class NavigationDrawerFragment extends Fragment {
new DrawerItem(DrawerItem.TYPE_NORMAL_ITEM, ACTIVITY_MUSIC,
getString(R.string.music),
styledAttributes.getResourceId(ACTIVITY_MUSIC, 0)),
new DrawerItem(DrawerItem.TYPE_NORMAL_ITEM, ACTIVITY_PVR,
getString(R.string.tv_radio),
styledAttributes.getResourceId(ACTIVITY_PVR, 0)),
new DrawerItem(DrawerItem.TYPE_NORMAL_ITEM, ACTIVITY_FILES,
getString(R.string.files),
styledAttributes.getResourceId(ACTIVITY_FILES, 0)),
@ -274,6 +278,8 @@ public class NavigationDrawerFragment extends Fragment {
return ACTIVITY_TVSHOWS;
else if (activity instanceof MusicActivity)
return ACTIVITY_MUSIC;
else if (activity instanceof PVRActivity)
return ACTIVITY_PVR;
else if (activity instanceof FileActivity)
return ACTIVITY_FILES;
else if (activity instanceof AddonsActivity)
@ -289,13 +295,14 @@ public class NavigationDrawerFragment extends Fragment {
*/
private static final SparseArray<Class> activityItemIdMap;
static {
activityItemIdMap = new SparseArray<Class>(10);
activityItemIdMap = new SparseArray<>(10);
activityItemIdMap.put(ACTIVITY_HOSTS, HostManagerActivity.class);
activityItemIdMap.put(ACTIVITY_REMOTE, RemoteActivity.class);
activityItemIdMap.put(ACTIVITY_MOVIES, MoviesActivity.class);
activityItemIdMap.put(ACTIVITY_MUSIC, MusicActivity.class);
activityItemIdMap.put(ACTIVITY_FILES, FileActivity.class);
activityItemIdMap.put(ACTIVITY_TVSHOWS, TVShowsActivity.class);
activityItemIdMap.put(ACTIVITY_PVR, PVRActivity.class);
activityItemIdMap.put(ACTIVITY_ADDONS, AddonsActivity.class);
activityItemIdMap.put(ACTIVITY_SETTINGS, SettingsActivity.class);
@ -339,11 +346,8 @@ public class NavigationDrawerFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
return super.onOptionsItemSelected(item);
}
/**
@ -396,10 +400,8 @@ public class NavigationDrawerFragment extends Fragment {
R.attr.textColorOverPrimary
});
Resources resources = context.getResources();
selectedItemColor = styledAttributes.getColor(0,
resources.getColor(R.color.accent_default));
hostItemColor = styledAttributes.getColor(1,
resources.getColor(R.color.white));
selectedItemColor = styledAttributes.getColor(0, resources.getColor(R.color.accent_default));
hostItemColor = styledAttributes.getColor(1, resources.getColor(R.color.white));
styledAttributes.recycle();
}

View File

@ -708,6 +708,21 @@ public class NowPlayingFragment extends Fragment
maxRating = null;
votes = null;
break;
case ListType.ItemsAll.TYPE_CHANNEL:
switchToPanel(R.id.media_panel);
title = getItemResult.label;
underTitle = getItemResult.title;
art = getItemResult.fanart;
poster = getItemResult.thumbnail;
genreSeason = Utils.listStringConcat(getItemResult.genre, ", ");
year = getItemResult.premiered;
descriptionPlot = getItemResult.plot;
rating = getItemResult.rating;
maxRating = null;
votes = null;
break;
default:
// Other type, just present basic info
switchToPanel(R.id.media_panel);
@ -729,7 +744,7 @@ public class NowPlayingFragment extends Fragment
mediaTitle.setText(title);
mediaUndertitle.setText(underTitle);
setDurationInfo(getPropertiesResult.time, getPropertiesResult.totaltime, getPropertiesResult.speed);
setDurationInfo(getItemResult.type, getPropertiesResult.time, getPropertiesResult.totaltime, getPropertiesResult.speed);
mediaSeekbar.setOnSeekBarChangeListener(seekbarChangeListener);
if (!TextUtils.isEmpty(year) || !TextUtils.isEmpty(genreSeason)) {
@ -822,7 +837,7 @@ public class NowPlayingFragment extends Fragment
} else {
// No fanart, just present the poster
mediaPoster.setVisibility(View.GONE);
UIUtils.loadImageIntoImageview(hostManager, poster, mediaArt, artWidth, artHeight);
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager, poster, title, mediaArt, artWidth, artHeight);
// Reset padding
int paddingLeft = mediaTitle.getPaddingRight(),
paddingRight = mediaTitle.getPaddingRight(),
@ -932,11 +947,12 @@ public class NowPlayingFragment extends Fragment
/**
* Sets the information about current media duration and sets seekbar
* @param type What is playing
* @param time Current time
* @param totalTime Total time
* @param speed Media speed
*/
private void setDurationInfo(GlobalType.Time time, GlobalType.Time totalTime, int speed) {
private void setDurationInfo(String type, GlobalType.Time time, GlobalType.Time totalTime, int speed) {
mediaTotalTime = totalTime.hours * 3600 +
totalTime.minutes * 60 +
totalTime.seconds;
@ -951,7 +967,7 @@ public class NowPlayingFragment extends Fragment
// Only update when its playing
mediaSeekbar.removeCallbacks(seekBarUpdater);
if (speed == 1) {
if ((speed == 1) || (type.equals(ListType.ItemsAll.TYPE_CHANNEL))) {
mediaSeekbar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
}
}

View File

@ -0,0 +1,235 @@
/*
* 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.ui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.transition.TransitionInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import org.xbmc.kore.R;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.Utils;
/**
* Controls the presentation of Live TV/Radio information (list, details)
* All the information is presented by specific fragments
*/
public class PVRActivity extends BaseActivity
implements PVRListFragment.OnPVRSelectedListener {
private static final String TAG = LogUtils.makeLogTag(PVRActivity.class);
public static final String CHANNELGROUPID = "channel_group_id";
public static final String CHANNELGROUPTITLE = "channel_group_title";
public static final String CHANNELID = "channel_id";
public static final String CHANNELTITLE = "channel_title";
private int selectedChannelGroupId = -1;
private String selectedChannelGroupTitle = null;
private int selectedChannelId = -1;
private String selectedChannelTitle = null;
private NavigationDrawerFragment navigationDrawerFragment;
@TargetApi(21)
@Override
protected void onCreate(Bundle savedInstanceState) {
// Request transitions on lollipop
if (Utils.isLollipopOrLater()) {
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_generic_media);
// Set up the drawer.
navigationDrawerFragment = (NavigationDrawerFragment)getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
navigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));
if (savedInstanceState == null) {
PVRListFragment pvrListFragment = new PVRListFragment();
// Setup animations
if (Utils.isLollipopOrLater()) {
pvrListFragment.setExitTransition(null);
pvrListFragment.setReenterTransition(TransitionInflater
.from(this)
.inflateTransition(android.R.transition.fade));
}
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, pvrListFragment)
.commit();
} else {
selectedChannelGroupId = savedInstanceState.getInt(CHANNELGROUPID, -1);
selectedChannelGroupTitle = savedInstanceState.getString(CHANNELGROUPTITLE, null);
selectedChannelId = savedInstanceState.getInt(CHANNELID, -1);
selectedChannelTitle = savedInstanceState.getString(CHANNELTITLE, null);
}
setupActionBar(selectedChannelGroupTitle);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CHANNELGROUPID, selectedChannelGroupId);
outState.putString(CHANNELGROUPTITLE, selectedChannelGroupTitle);
outState.putInt(CHANNELID, selectedChannelId);
outState.putString(CHANNELTITLE, selectedChannelTitle);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// if (!navigationDrawerFragment.isDrawerOpen()) {
// getMenuInflater().inflate(R.menu.media_info, menu);
// }
getMenuInflater().inflate(R.menu.media_info, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_remote:
// Starts remote
Intent launchIntent = new Intent(this, RemoteActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(launchIntent);
return true;
case android.R.id.home:
// If showing detail view, back up to list
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(null);
getSupportFragmentManager().popBackStack();
return true;
} else if (selectedChannelGroupId != -1) {
onBackPressed();
return true;
}
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
// If we are showing details in portrait, clear selected and show action bar
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(null);
} else if (selectedChannelGroupId != -1) {
selectedChannelGroupId = -1;
selectedChannelGroupTitle = null;
setupActionBar(null);
Fragment listFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if ((listFragment != null) &&
(listFragment instanceof PVRListFragment)) {
((PVRListFragment)listFragment).onBackPressed();
}
return;
}
super.onBackPressed();
}
private void setupActionBar(String channelTitle) {
Toolbar toolbar = (Toolbar)findViewById(R.id.default_toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar == null) return;
actionBar.setDisplayHomeAsUpEnabled(true);
if (channelTitle != null) {
navigationDrawerFragment.setDrawerIndicatorEnabled(false);
actionBar.setTitle(channelTitle);
} else {
navigationDrawerFragment.setDrawerIndicatorEnabled(true);
actionBar.setTitle(R.string.tv_radio);
}
}
/**
* Callback from list fragment when a channel group is selected.
* Setup action bar
* @param channelGroupId Channel group selected
* @param channelGroupTitle Title
*/
public void onChannelGroupSelected(int channelGroupId, String channelGroupTitle) {
selectedChannelGroupId = channelGroupId;
selectedChannelGroupTitle = channelGroupTitle;
setupActionBar(selectedChannelGroupTitle);
}
/**
* Callback from list fragment when the channel guide should be displayed.
* Setup action bar and repolace list fragment
* @param channelId Channel selected
* @param channelTitle Title
*/
@TargetApi(21)
public void onChannelGuideSelected(int channelId, String channelTitle) {
selectedChannelId = channelId;
selectedChannelTitle = channelTitle;
// // Replace list fragment
// PVRDetailsFragment pvrDetailsFragment = PVRDetailsFragment.newInstance(channelId);
// FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
//
// // Set up transitions
// if (Utils.isLollipopOrLater()) {
// pvrDetailsFragment.setEnterTransition(TransitionInflater.from(this)
// .inflateTransition(R.transition.media_details));
// pvrDetailsFragment.setReturnTransition(null);
// } else {
// fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
// R.anim.fragment_list_popenter, 0);
// }
//
// fragTrans.replace(R.id.fragment_container, pvrDetailsFragment)
// .addToBackStack(null)
// .commit();
setupActionBar(selectedChannelTitle);
}
}

View File

@ -0,0 +1,415 @@
/*
* 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.ui;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
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;
import org.xbmc.kore.jsonrpc.method.PVR;
import org.xbmc.kore.jsonrpc.method.Player;
import org.xbmc.kore.jsonrpc.type.PVRType;
import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Fragment that presents the movie list
*/
public class PVRListFragment extends Fragment
implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = LogUtils.makeLogTag(PVRListFragment.class);
public interface OnPVRSelectedListener {
public void onChannelGroupSelected(int channelGroupId, String channelGroupTitle);
public void onChannelGuideSelected(int channelId, String channelTitle);
}
// Activity listener
private OnPVRSelectedListener listenerActivity;
private HostManager hostManager;
@InjectView(R.id.list) GridView gridView;
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
@InjectView(android.R.id.empty) TextView emptyView;
/**
* Handler on which to post RPC callbacks
*/
private Handler callbackHandler = new Handler();
private ChannelGroupAdapter channelGroupAdapter = null;
private ChannelAdapter channelAdapter = null;
private int selectedChannelGroupId = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
ButterKnife.inject(this, root);
hostManager = HostManager.getInstance(getActivity());
swipeRefreshLayout.setOnRefreshListener(this);
emptyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onRefresh();
}
});
gridView.setEmptyView(emptyView);
return root;
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(false);
if ((channelGroupAdapter == null) ||
(channelGroupAdapter.getCount() == 0))
browseChannelGroups();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listenerActivity = (OnPVRSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnPVRSelectedListener");
}
}
@Override
public void onDetach() {
super.onDetach();
listenerActivity = null;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
/**
* Swipe refresh layout callback
*/
/** {@inheritDoc} */
@Override
public void onRefresh () {
if (hostManager.getHostInfo() != null) {
if (selectedChannelGroupId == -1) {
browseChannelGroups();
} else {
browseChannels(selectedChannelGroupId);
}
} else {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
.show();
}
}
/**
* Called by the enclosing activity
*/
public void onBackPressed() {
selectedChannelGroupId = -1;
browseChannelGroups();
}
/**
* Get the channel groups list and setup the gridview
*/
private void browseChannelGroups() {
// TODO: Make the channel type selectable
LogUtils.LOGD(TAG, "Getting channel groups");
PVR.GetChannelGroups action = new PVR.GetChannelGroups(PVRType.ChannelType.TV);
action.execute(hostManager.getConnection(), new ApiCallback<List<PVRType.DetailsChannelGroup>>() {
@Override
public void onSuccess(List<PVRType.DetailsChannelGroup> result) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Got channel groups");
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(getString(R.string.no_channel_groups_found_refresh));
setupChannelGroupsGridview(result);
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error getting channel groups: " + description);
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(String.format(getString(R.string.error_getting_pvr_info), description));
Toast.makeText(getActivity(),
String.format(getString(R.string.error_getting_pvr_info), description),
Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
}, callbackHandler);
}
/**
* Called when we get the channel groups
*
* @param result ChannelGroups obtained
*/
private void setupChannelGroupsGridview(List<PVRType.DetailsChannelGroup> result) {
if (channelGroupAdapter == null) {
channelGroupAdapter = new ChannelGroupAdapter(getActivity(), R.layout.grid_item_channel_group);
}
gridView.setAdapter(channelGroupAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Get the id from the tag
ChannelGroupViewHolder tag = (ChannelGroupViewHolder) view.getTag();
selectedChannelGroupId = tag.channelGroupId;
// Notify the activity and show the channels
listenerActivity.onChannelGroupSelected(tag.channelGroupId, tag.channelGroupName);
browseChannels(tag.channelGroupId);
}
});
channelGroupAdapter.clear();
channelGroupAdapter.addAll(result);
channelGroupAdapter.notifyDataSetChanged();
}
/**
* Gets and displays the channels of a channelgroup
* @param channelGroupId id
*/
private void browseChannels(int channelGroupId) {
String[] properties = PVRType.FieldsChannel.allValues;
LogUtils.LOGD(TAG, "Getting channels");
PVR.GetChannels action = new PVR.GetChannels(channelGroupId, properties);
action.execute(hostManager.getConnection(), new ApiCallback<List<PVRType.DetailsChannel>>() {
@Override
public void onSuccess(List<PVRType.DetailsChannel> result) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Got channels");
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(getString(R.string.no_channels_found_refresh));
setupChannelsGridview(result);
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error getting channels: " + description);
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(String.format(getString(R.string.error_getting_pvr_info), description));
Toast.makeText(getActivity(),
String.format(getString(R.string.error_getting_pvr_info), description),
Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
}, callbackHandler);
}
/**
* Called when we get the channels
*
* @param result Channels obtained
*/
private void setupChannelsGridview(List<PVRType.DetailsChannel> result) {
if (channelAdapter == null) {
channelAdapter = new ChannelAdapter(getActivity(), R.layout.grid_item_channel);
}
gridView.setAdapter(channelAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Get the id from the tag
ChannelViewHolder tag = (ChannelViewHolder) view.getTag();
// Start the channel
Toast.makeText(getActivity(),
String.format(getString(R.string.channel_switching), tag.channelName),
Toast.LENGTH_SHORT).show();
Player.Open action = new Player.Open(Player.Open.TYPE_CHANNEL, tag.channelId);
action.execute(hostManager.getConnection(), new ApiCallback<String>() {
@Override
public void onSuccess(String result) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Started channel");
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error starting channel: " + description);
Toast.makeText(getActivity(),
String.format(getString(R.string.error_starting_channel), description),
Toast.LENGTH_SHORT).show();
}
}, callbackHandler);
}
});
channelAdapter.clear();
channelAdapter.addAll(result);
channelAdapter.notifyDataSetChanged();
}
private class ChannelGroupAdapter extends ArrayAdapter<PVRType.DetailsChannelGroup> {
public ChannelGroupAdapter(Context context, int resource) {
super(context, resource);
}
/** {@inheritDoc} */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity())
.inflate(R.layout.grid_item_channel_group, parent, false);
// Setup View holder pattern
ChannelGroupViewHolder viewHolder = new ChannelGroupViewHolder();
viewHolder.titleView = (TextView)convertView.findViewById(R.id.title);
convertView.setTag(viewHolder);
}
final ChannelGroupViewHolder viewHolder = (ChannelGroupViewHolder)convertView.getTag();
PVRType.DetailsChannelGroup channelGroupDetails = this.getItem(position);
viewHolder.channelGroupId = channelGroupDetails.channelgroupid;
viewHolder.channelGroupName = channelGroupDetails.label;
viewHolder.titleView.setText(viewHolder.channelGroupName);
return convertView;
}
}
/**
* View holder pattern
*/
private static class ChannelGroupViewHolder {
TextView titleView;
int channelGroupId;
String channelGroupName;
}
private class ChannelAdapter extends ArrayAdapter<PVRType.DetailsChannel> {
private HostManager hostManager;
private int artWidth, artHeight;
public ChannelAdapter(Context context, int resource) {
super(context, resource);
this.hostManager = HostManager.getInstance(context);
Resources resources = context.getResources();
artWidth = (int)(resources.getDimension(R.dimen.channellist_art_width) /
UIUtils.IMAGE_RESIZE_FACTOR);
artHeight = (int)(resources.getDimension(R.dimen.channellist_art_heigth) /
UIUtils.IMAGE_RESIZE_FACTOR);
}
/** {@inheritDoc} */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater
.from(getActivity())
.inflate(R.layout.grid_item_channel, parent, false);
// Setup View holder pattern
ChannelViewHolder viewHolder = new ChannelViewHolder();
viewHolder.titleView = (TextView)convertView.findViewById(R.id.title);
viewHolder.detailsView = (TextView)convertView.findViewById(R.id.details);
viewHolder.artView = (ImageView)convertView.findViewById(R.id.art);
convertView.setTag(viewHolder);
}
final ChannelViewHolder viewHolder = (ChannelViewHolder)convertView.getTag();
PVRType.DetailsChannel channelDetails = this.getItem(position);
viewHolder.channelId = channelDetails.channelid;
viewHolder.channelName = channelDetails.channel;
viewHolder.titleView.setText(channelDetails.channel);
String details = (channelDetails.broadcastnow != null)?
channelDetails.broadcastnow.title : null;
viewHolder.detailsView.setText(details);
UIUtils.loadImageWithCharacterAvatar(getContext(), hostManager,
channelDetails.thumbnail, channelDetails.channel,
viewHolder.artView, artWidth, artHeight);
return convertView;
}
}
/**
* View holder pattern
*/
private static class ChannelViewHolder {
TextView titleView, detailsView;
ImageView artView;
int channelId;
String channelName;
}
}

View File

@ -131,7 +131,7 @@ public class PlaylistFragment extends Fragment
playlistGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Player.Open action = new Player.Open(currentPlaylistId, position);
Player.Open action = new Player.Open(Player.Open.TYPE_PLAYLIST, currentPlaylistId, position);
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
}
});

View File

@ -433,7 +433,7 @@ public class RemoteActivity extends BaseActivity
@Override
public void onSuccess(String result ) {
if (startPlaylist) {
Player.Open action = new Player.Open(PlaylistType.VIDEO_PLAYLISTID);
Player.Open action = new Player.Open(Player.Open.TYPE_PLAYLIST, PlaylistType.VIDEO_PLAYLISTID);
action.execute(connection, new ApiCallback<String>() {
@Override
public void onSuccess(String result) {

View File

@ -588,6 +588,15 @@ public class RemoteFragment extends Fragment
currentFastForwardIcon = fastForwardIcon;
currentRewindIcon = rewindIcon;
break;
case ListType.ItemsAll.TYPE_CHANNEL:
switchToPanel(R.id.media_panel, true);
title = nowPlaying.label;
underTitle = nowPlaying.title;
thumbnailUrl = nowPlaying.thumbnail;
currentFastForwardIcon = fastForwardIcon;
currentRewindIcon = rewindIcon;
break;
default:
switchToPanel(R.id.media_panel, true);
title = nowPlaying.label;

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector android:alpha="1.00" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF" android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zm0,14H3V5h18v12zm-2,-9H8v2h11V8zm0,4H8v2h11v-2zM7,8H5v2h2V8zm0,4H5v2h2v-2z"/>
</vector>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/art"
android:layout_width="@dimen/channellist_art_width"
android:layout_height="@dimen/channellist_art_heigth"
android:contentDescription="@string/poster"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.Medialist.Title"
android:paddingTop="0dp"/>
<TextView
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.Medialist.Details"
android:paddingBottom="0dp"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.Medialist.Title"
android:padding="@dimen/default_padding"/>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -27,6 +27,7 @@
android:layout_marginLeft="@dimen/default_padding"
android:layout_marginRight="@dimen/default_padding"
android:layout_centerVertical="true"
android:tint="?attr/defaultButtonColorFilter"
android:src="@drawable/ic_launcher"/>
<TextView

View File

@ -71,4 +71,7 @@
<dimen name="addondetail_poster_width">146dp</dimen>
<dimen name="addondetail_poster_heigth">146dp</dimen>
<dimen name="channellist_art_width">88dp</dimen>
<dimen name="channellist_art_heigth">88dp</dimen>
</resources>

View File

@ -35,6 +35,7 @@
<attr name="drawerBackgroundColor" format="reference|color" />
<attr name="remoteButtonColorFilter" format="reference|color" />
<attr name="defaultButtonColorFilter" format="reference|color" />
<!--<attr name="remoteBackgroundColorFilter" format="reference|color" />-->
<!-- Icons -->
@ -48,6 +49,7 @@
<attr name="iconHome" format="reference" />
<attr name="iconAddons" format="reference" />
<attr name="iconFiles" format="reference" />
<attr name="iconPVR" format="reference" />
<attr name="iconNew" format="reference" />
<attr name="iconNewToolbar" format="reference" />

View File

@ -118,6 +118,9 @@
<dimen name="filelist_art_width">52dp</dimen>
<dimen name="filelist_art_heigth">76dp</dimen>
<dimen name="channellist_art_width">74dp</dimen>
<dimen name="channellist_art_heigth">74dp</dimen>
<!-- Notification -->
<dimen name="notification_height">64dp</dimen>
<dimen name="notification_expanded_height">128dp</dimen>

View File

@ -36,6 +36,7 @@
<string name="files">Files</string>
<string name="video">Video</string>
<string name="file_browser">File Browser</string>
<string name="tv_radio">TV/Radio</string>
<string name="no_xbmc_configured">No media center configured</string>
<string name="add_xbmc">Add Media Center</string>
@ -239,6 +240,8 @@
<string name="no_genres_found_refresh">No genres found\n\nSwipe down to refresh</string>
<string name="no_addons_found_refresh">No addons found or not connected\n\nSwipe down to refresh</string>
<string name="no_music_videos_found_refresh">No videos found\n\nSwipe down to refresh</string>
<string name="no_channel_groups_found_refresh">No channel groups found or not connected\n\nSwipe down to refresh</string>
<string name="no_channels_found_refresh">No channel groups found or not connected\n\nSwipe down to refresh</string>
<string name="pull_to_refresh">Pull to refresh</string>
<string name="no_cast_info">No cast info to display</string>
@ -334,4 +337,8 @@
<string name="play_on_kodi">Play on Kodi</string>
<string name="error_getting_pvr_info">An error occurred while getting channels info: %1$s</string>
<string name="error_starting_channel">An error occurred starting channel playback: %1$s</string>
<string name="channel_switching">Switching to channel %1$s</string>
</resources>

View File

@ -69,6 +69,7 @@
<item name="separatorColor">@color/dark_separator</item>
<item name="remoteButtonColorFilter">@color/white</item>
<item name="defaultButtonColorFilter">@color/white</item>
<!--<item name="remoteBackgroundColorFilter">@color/dark_content_background</item>-->
<!-- Icons -->
@ -93,6 +94,7 @@
<item name="iconHome">@drawable/ic_home_white_24dp</item>
<item name="iconAddons">@drawable/ic_extension_white_24dp</item>
<item name="iconFiles">@drawable/ic_folder_white_24dp</item>
<item name="iconPVR">@drawable/ic_dvr_24dp</item>
<item name="iconNew">@drawable/ic_add_box_white_24dp</item>
<item name="iconNewToolbar">@drawable/ic_add_box_white_24dp</item>
@ -180,6 +182,7 @@
<item name="separatorColor">@color/light_separator</item>
<item name="remoteButtonColorFilter">@color/black_dim_54pct</item>
<item name="defaultButtonColorFilter">@color/black_dim_54pct</item>
<!--<item name="remoteBackgroundColorFilter">@color/light_content_background</item>-->
<!-- Icons, same for all themes, will be colored dynamically -->
@ -214,6 +217,7 @@
<item name="iconHome">@drawable/ic_home_black_24dp</item>
<item name="iconAddons">@drawable/ic_extension_black_24dp</item>
<item name="iconFiles">@drawable/ic_folder_black_24dp</item>
<item name="iconPVR">@drawable/ic_dvr_24dp</item>
<item name="iconNew">@drawable/ic_add_box_black_24dp</item>
<item name="iconNewToolbar">@drawable/ic_add_box_white_24dp</item>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.android.tools.build:gradle:1.4.+'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files