Added PVR recordings list

This commit is contained in:
Synced Synapse 2015-11-24 22:55:56 +00:00
parent 77145d1c28
commit 610fa85671
10 changed files with 361 additions and 69 deletions

View File

@ -18,6 +18,7 @@ package org.xbmc.kore;
import android.text.format.DateUtils;
import org.xbmc.kore.jsonrpc.type.PVRType;
import org.xbmc.kore.ui.PVRListFragment;
import java.util.Set;
@ -102,6 +103,6 @@ public interface Settings {
public static final String KEY_PREF_NAV_DRAWER_ITEMS = "pref_nav_drawer_items";
public static final String KEY_PREF_PVR_LIST_CHANNEL_TYPE = "pref_pvr_list_channel_type";
public static final String DEFAULT_PREF_PVR_LIST_CHANNEL_TYPE = PVRType.ChannelType.TV;
public static final String KEY_PREF_PVR_LIST_TYPE = "pref_pvr_list_type";
public static final int DEFAULT_PREF_PVR_LIST_TYPE = PVRListFragment.LIST_TV_CHANNELS;
}

View File

@ -434,7 +434,8 @@ public class Player {
public final static String METHOD_NAME = "Player.Open";
public final static String TYPE_PLAYLIST = "playlist",
TYPE_CHANNEL = "channel";
TYPE_CHANNEL = "channel",
TYPE_RECORDING = "recording";
/**
* Start playback of either the playlist with the given ID, a slideshow with the pictures
@ -472,12 +473,15 @@ public class Player {
switch (itemType) {
case TYPE_PLAYLIST:
item.put("playlistid", itemId);
addParameterToRequest("item", item);
break;
case TYPE_CHANNEL:
item.put("channelid", itemId);
addParameterToRequest("item", item);
break;
case TYPE_RECORDING:
item.put("recordingid", itemId);
break;
}
addParameterToRequest("item", item);
}
@Override

View File

@ -55,7 +55,7 @@ public class PVRActivity extends BaseActivity
private int selectedChannelId = -1;
private String selectedChannelTitle = null;
private String selectedChannelType;
private int currentListType;
private NavigationDrawerFragment navigationDrawerFragment;
@ -98,9 +98,9 @@ public class PVRActivity extends BaseActivity
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
selectedChannelType = preferences.getString(Settings.KEY_PREF_PVR_LIST_CHANNEL_TYPE, Settings.DEFAULT_PREF_PVR_LIST_CHANNEL_TYPE);
currentListType = preferences.getInt(Settings.KEY_PREF_PVR_LIST_TYPE, Settings.DEFAULT_PREF_PVR_LIST_TYPE);
setupActionBar(selectedChannelType, selectedChannelGroupTitle);
setupActionBar(currentListType, selectedChannelGroupTitle);
}
@Override
@ -147,7 +147,7 @@ public class PVRActivity extends BaseActivity
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(selectedChannelType, null);
setupActionBar(currentListType, null);
getSupportFragmentManager().popBackStack();
return true;
} else if (returnToChannelGroupList && (selectedChannelGroupId != -1)) {
@ -165,25 +165,28 @@ public class PVRActivity extends BaseActivity
@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(selectedChannelType, null);
} else if ((selectedChannelGroupId != -1) && returnToChannelGroupList) {
selectedChannelGroupId = -1;
selectedChannelGroupTitle = null;
setupActionBar(selectedChannelType, null);
Fragment listFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if ((listFragment != null) &&
(listFragment instanceof PVRListFragment)) {
((PVRListFragment)listFragment).onBackPressed();
if ((currentListType == PVRListFragment.LIST_TV_CHANNELS) ||
(currentListType == PVRListFragment.LIST_RADIO_CHANNELS)) {
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(currentListType, null);
} else if ((selectedChannelGroupId != -1) && returnToChannelGroupList) {
selectedChannelGroupId = -1;
selectedChannelGroupTitle = null;
setupActionBar(currentListType, null);
Fragment listFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if ((listFragment != null) &&
(listFragment instanceof PVRListFragment)) {
((PVRListFragment) listFragment).onBackPressed();
}
return;
}
return;
}
super.onBackPressed();
}
private void setupActionBar(String channelType, String channelTitle) {
private void setupActionBar(int listType, String channelTitle) {
Toolbar toolbar = (Toolbar)findViewById(R.id.default_toolbar);
setSupportActionBar(toolbar);
@ -195,15 +198,16 @@ public class PVRActivity extends BaseActivity
boolean drawerIndicatorEnabled;
if (channelTitle != null) {
drawerIndicatorEnabled = !returnToChannelGroupList;
title = String.format("%s - %s", PVRListFragment.getChannelTypeTitle(this, channelType), channelTitle);
title = String.format("%s - %s", PVRListFragment.getPVRListTypeTitle(this, listType), channelTitle);
} else {
drawerIndicatorEnabled = true;
title = PVRListFragment.getChannelTypeTitle(this, channelType);
title = PVRListFragment.getPVRListTypeTitle(this, listType);
}
navigationDrawerFragment.setDrawerIndicatorEnabled(drawerIndicatorEnabled);
actionBar.setTitle(title);
}
/**
* Callback from list fragment when a channel group is selected.
* Setup action bar
@ -215,7 +219,7 @@ public class PVRActivity extends BaseActivity
selectedChannelGroupTitle = channelGroupTitle;
this.returnToChannelGroupList = canReturnToChannelGroupList;
setupActionBar(selectedChannelType, selectedChannelGroupTitle);
setupActionBar(currentListType, selectedChannelGroupTitle);
}
/**
@ -246,15 +250,15 @@ public class PVRActivity extends BaseActivity
// fragTrans.replace(R.id.fragment_container, pvrDetailsFragment)
// .addToBackStack(null)
// .commit();
setupActionBar(selectedChannelType, selectedChannelTitle);
setupActionBar(currentListType, selectedChannelTitle);
}
/**
* Callback from list fragment when the channel type is changed
* @param channelType Channel type selected
* Callback from list fragment when the list type is changed
* @param listType List type selected
*/
public void onChannelTypeSelected(String channelType) {
selectedChannelType = channelType;
setupActionBar(selectedChannelType, selectedChannelTitle);
public void onListTypeChanged(int listType) {
currentListType = listType;
setupActionBar(currentListType, selectedChannelTitle);
}
}

View File

@ -63,7 +63,7 @@ public class PVRListFragment extends Fragment
public static final String CHANNELGROUPID = "channel_group_id";
public interface OnPVRSelectedListener {
public void onChannelTypeSelected(String channelType);
public void onListTypeChanged(int listType);
public void onChannelGroupSelected(int channelGroupId, String channelGroupTitle, boolean canReturnToChannelGroupList);
public void onChannelGuideSelected(int channelId, String channelTitle);
}
@ -84,9 +84,14 @@ public class PVRListFragment extends Fragment
private ChannelGroupAdapter channelGroupAdapter = null;
private ChannelAdapter channelAdapter = null;
private RecordingsAdapter recordingsAdapter = null;
private int selectedChannelGroupId = -1;
private String selectedChannelType;
public static final int LIST_TV_CHANNELS = 0,
LIST_RADIO_CHANNELS = 1,
LIST_RECORDINGS = 2;
private int currentListType = LIST_TV_CHANNELS;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -103,7 +108,7 @@ public class PVRListFragment extends Fragment
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
selectedChannelType = preferences.getString(Settings.KEY_PREF_PVR_LIST_CHANNEL_TYPE, Settings.DEFAULT_PREF_PVR_LIST_CHANNEL_TYPE);
currentListType = preferences.getInt(Settings.KEY_PREF_PVR_LIST_TYPE, Settings.DEFAULT_PREF_PVR_LIST_TYPE);
hostManager = HostManager.getInstance(getActivity());
@ -125,12 +130,16 @@ public class PVRListFragment extends Fragment
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
if (selectedChannelGroupId == -1) {
if ((channelGroupAdapter == null) ||
(channelGroupAdapter.getCount() == 0))
browseChannelGroups();
if (currentListType == LIST_RECORDINGS) {
browseRecordings();
} else {
browseChannels(selectedChannelGroupId);
if (selectedChannelGroupId == -1) {
if ((channelGroupAdapter == null) ||
(channelGroupAdapter.getCount() == 0))
browseChannelGroups();
} else {
browseChannels(selectedChannelGroupId);
}
}
}
@ -166,23 +175,33 @@ public class PVRListFragment extends Fragment
outState.putInt(CHANNELGROUPID, selectedChannelGroupId);
}
public static String getChannelTypeTitle(Context context, String ChannelType) {
return (ChannelType.equals(PVRType.ChannelType.TV)) ?
context.getResources().getString(R.string.tv) :
context.getResources().getString(R.string.radio);
}
private void setupChannelTypeMenuItem(MenuItem item, String channelType) {
item.setTitle(getChannelTypeTitle(getActivity(), channelType));
public static String getPVRListTypeTitle(Context context, int listType) {
switch (listType) {
case LIST_TV_CHANNELS:
return context.getResources().getString(R.string.tv);
case LIST_RADIO_CHANNELS:
return context.getResources().getString(R.string.radio);
case LIST_RECORDINGS:
return context.getResources().getString(R.string.recordings);
}
return null;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.pvr_list, menu);
String switchChannelType = (selectedChannelType.equals(PVRType.ChannelType.TV)) ?
PVRType.ChannelType.RADIO : PVRType.ChannelType.TV;
setupChannelTypeMenuItem(menu.findItem(R.id.action_switch_channel_type), switchChannelType);
switch (currentListType) {
case LIST_TV_CHANNELS:
menu.findItem(R.id.action_pvr_list_tv_channels).setChecked(true);
break;
case LIST_RADIO_CHANNELS:
menu.findItem(R.id.action_pvr_list_radio_channels).setChecked(true);
break;
case LIST_RECORDINGS:
menu.findItem(R.id.action_pvr_list_recordings).setChecked(true);
break;
}
super.onCreateOptionsMenu(menu, inflater);
}
@ -190,19 +209,25 @@ public class PVRListFragment extends Fragment
public boolean onOptionsItemSelected(MenuItem item) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
switch (item.getItemId()) {
case R.id.action_switch_channel_type:
selectedChannelType = (selectedChannelType.equals(PVRType.ChannelType.TV)) ?
PVRType.ChannelType.RADIO : PVRType.ChannelType.TV;
preferences.edit()
.putString(Settings.KEY_PREF_PVR_LIST_CHANNEL_TYPE, selectedChannelType)
.apply();
setupChannelTypeMenuItem(item, selectedChannelType);
case R.id.action_pvr_list_tv_channels:
currentListType = LIST_TV_CHANNELS;
browseChannelGroups();
listenerActivity.onChannelTypeSelected(selectedChannelType);
break;
case R.id.action_pvr_list_radio_channels:
currentListType = LIST_RADIO_CHANNELS;
browseChannelGroups();
break;
case R.id.action_pvr_list_recordings:
currentListType = LIST_RECORDINGS;
browseRecordings();
break;
default:
break;
}
preferences.edit()
.putInt(Settings.KEY_PREF_PVR_LIST_TYPE, currentListType)
.apply();
listenerActivity.onListTypeChanged(currentListType);
return super.onOptionsItemSelected(item);
}
@ -214,10 +239,14 @@ public class PVRListFragment extends Fragment
@Override
public void onRefresh () {
if (hostManager.getHostInfo() != null) {
if (selectedChannelGroupId == -1) {
browseChannelGroups();
if (currentListType == LIST_RECORDINGS) {
browseRecordings();
} else {
browseChannels(selectedChannelGroupId);
if (selectedChannelGroupId == -1) {
browseChannelGroups();
} else {
browseChannels(selectedChannelGroupId);
}
}
} else {
swipeRefreshLayout.setRefreshing(false);
@ -239,7 +268,8 @@ public class PVRListFragment extends Fragment
*/
private void browseChannelGroups() {
LogUtils.LOGD(TAG, "Getting channel groups");
PVR.GetChannelGroups action = new PVR.GetChannelGroups(selectedChannelType);
String channelType = (currentListType == LIST_TV_CHANNELS)? PVRType.ChannelType.TV : PVRType.ChannelType.RADIO;
PVR.GetChannelGroups action = new PVR.GetChannelGroups(channelType);
action.execute(hostManager.getConnection(), new ApiCallback<List<PVRType.DetailsChannelGroup>>() {
@Override
public void onSuccess(List<PVRType.DetailsChannelGroup> result) {
@ -488,4 +518,151 @@ public class PVRListFragment extends Fragment
int channelId;
String channelName;
}
/**
* Get the recording list and setup the gridview
*/
private void browseRecordings() {
PVR.GetRecordings action = new PVR.GetRecordings(PVRType.FieldsRecording.allValues);
action.execute(hostManager.getConnection(), new ApiCallback<List<PVRType.DetailsRecording>>() {
@Override
public void onSuccess(List<PVRType.DetailsRecording> result) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Got recordings");
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(getString(R.string.no_recordings_found_refresh));
setupRecordingsGridview(result);
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error getting recordings: " + 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 recordings
*
* @param result Recordings obtained
*/
private void setupRecordingsGridview(List<PVRType.DetailsRecording> result) {
if (recordingsAdapter == null) {
recordingsAdapter = new RecordingsAdapter(getActivity(), R.layout.grid_item_recording);
}
gridView.setAdapter(recordingsAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Get the id from the tag
RecordingViewHolder tag = (RecordingViewHolder) view.getTag();
// Start the recording
Toast.makeText(getActivity(),
String.format(getString(R.string.starting_recording), tag.title),
Toast.LENGTH_SHORT).show();
Player.Open action = new Player.Open(Player.Open.TYPE_RECORDING, tag.recordingId);
action.execute(hostManager.getConnection(), new ApiCallback<String>() {
@Override
public void onSuccess(String result) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Started recording");
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error starting recording: " + description);
Toast.makeText(getActivity(),
String.format(getString(R.string.error_starting_recording), description),
Toast.LENGTH_SHORT).show();
}
}, callbackHandler);
}
});
recordingsAdapter.clear();
recordingsAdapter.addAll(result);
recordingsAdapter.notifyDataSetChanged();
}
private class RecordingsAdapter extends ArrayAdapter<PVRType.DetailsRecording> {
private HostManager hostManager;
private int artWidth, artHeight;
public RecordingsAdapter(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_recording, parent, false);
// Setup View holder pattern
RecordingViewHolder viewHolder = new RecordingViewHolder();
viewHolder.titleView = (TextView)convertView.findViewById(R.id.title);
viewHolder.detailsView = (TextView)convertView.findViewById(R.id.details);
viewHolder.artView = (ImageView)convertView.findViewById(R.id.art);
viewHolder.durationView = (TextView)convertView.findViewById(R.id.duration);
convertView.setTag(viewHolder);
}
final RecordingViewHolder viewHolder = (RecordingViewHolder)convertView.getTag();
PVRType.DetailsRecording recordingDetails = this.getItem(position);
viewHolder.recordingId = recordingDetails.recordingid;
viewHolder.title = recordingDetails.title;
viewHolder.titleView.setText(recordingDetails.title);
viewHolder.detailsView.setText(recordingDetails.channel);
UIUtils.loadImageWithCharacterAvatar(getContext(), hostManager,
(recordingDetails.art != null) ?
recordingDetails.art.poster : recordingDetails.icon,
recordingDetails.channel,
viewHolder.artView, artWidth, artHeight);
int runtime = recordingDetails.runtime / 60;
String duration =
recordingDetails.starttime + " | " +
String.format(this.getContext().getString(R.string.minutes_abbrev), String.valueOf(runtime));
viewHolder.durationView.setText(duration);
return convertView;
}
}
/**
* View holder pattern
*/
private static class RecordingViewHolder {
ImageView artView;
TextView titleView, detailsView, durationView;
int recordingId;
String title;
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4,14h4v-4H4v4zm0,5h4v-4H4v4zM4,9h4V5H4v4zm5,5h12v-4H9v4zm0,5h12v-4H9v4zM9,5v4h12V5H9z"/>
</vector>

View File

@ -0,0 +1,66 @@
<?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">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/art"
android:layout_width="@dimen/recordinglist_art_width"
android:layout_height="@dimen/recordinglist_art_heigth"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:contentDescription="@string/poster"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/art"
android:layout_toEndOf="@id/art"
android:layout_alignTop="@id/art"
style="@style/TextAppearance.Medialist.Title"/>
<TextView
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_alignStart="@id/title"
android:layout_below="@id/title"
style="@style/TextAppearance.Medialist.Details"/>
<TextView
android:id="@+id/duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_alignStart="@id/title"
android:layout_below="@id/details"
android:layout_alignParentBottom="true"
style="@style/TextAppearance.Medialist.OtherInfo"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@ -15,9 +15,28 @@
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_switch_channel_type"
android:title="@string/tv"
android:orderInCategory="1"
app:showAsAction="never" />
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/tv_radio"
android:orderInCategory="1"
android:icon="@drawable/ic_dvr_24dp"
app:showAsAction="ifRoom">
<menu>
<group android:checkableBehavior="single"
android:orderInCategory="1">
<item android:id="@+id/action_pvr_list_tv_channels"
android:title="@string/tv_channels"
android:orderInCategory="1"
app:showAsAction="ifRoom" />
<item android:id="@+id/action_pvr_list_radio_channels"
android:title="@string/radio_channels"
android:orderInCategory="2"
app:showAsAction="ifRoom" />
<item android:id="@+id/action_pvr_list_recordings"
android:title="@string/recordings"
android:orderInCategory="3"
app:showAsAction="ifRoom" />
</group>
</menu>
</item>
</menu>

View File

@ -74,4 +74,7 @@
<dimen name="channellist_art_width">88dp</dimen>
<dimen name="channellist_art_heigth">88dp</dimen>
<dimen name="recordinglist_art_width">88dp</dimen>
<dimen name="recordinglist_art_heigth">88dp</dimen>
</resources>

View File

@ -121,6 +121,9 @@
<dimen name="channellist_art_width">74dp</dimen>
<dimen name="channellist_art_heigth">74dp</dimen>
<dimen name="recordinglist_art_width">74dp</dimen>
<dimen name="recordinglist_art_heigth">74dp</dimen>
<!-- Notification -->
<dimen name="notification_height">64dp</dimen>
<dimen name="notification_expanded_height">128dp</dimen>

View File

@ -242,6 +242,7 @@
<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.\n\nSwipe down to refresh</string>
<string name="no_channels_found_refresh">No channels found.\n\nSwipe down to refresh</string>
<string name="no_recordings_found_refresh">No recordings found.\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>
@ -338,13 +339,18 @@
<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_getting_pvr_info">An error occurred while getting pvr info: %1$s</string>
<string name="might_not_have_pvr">An error occurred while getting channels info, probably because your media center doesn\'t have a tuner or it isn\'t configured.\n\nIf that\'s the case and you\'d like to remove this entry from the side menu, you can do it in the Settings.</string>
<string name="error_starting_channel">An error occurred starting channel playback: %1$s</string>
<string name="error_starting_recording">An error occurred starting a recording: %1$s</string>
<string name="channel_switching">Switching to channel %1$s</string>
<string name="starting_recording">Starting recording %1$s</string>
<string name="refresh">refresh</string>
<string name="tv">TV</string>
<string name="radio">Radio</string>
<string name="recordings">Recordings</string>
<string name="tv_channels">TV Channels</string>
<string name="radio_channels">Radio Channels</string>
</resources>