Added first version of PVR EPG

This commit is contained in:
Synced Synapse 2015-12-10 20:20:10 +00:00
parent 9b4c4d6315
commit 5ccd76b349
7 changed files with 375 additions and 19 deletions

View File

@ -18,6 +18,7 @@ package org.xbmc.kore.ui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
@ -188,23 +189,24 @@ public class PVRActivity extends BaseActivity
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();
// Replace list fragment
PVRChannelEPGListFragment pvrEPGFragment = PVRChannelEPGListFragment.newInstance(channelId);
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
// Set up transitions
if (Utils.isLollipopOrLater()) {
pvrEPGFragment.setEnterTransition(
TransitionInflater.from(this)
.inflateTransition(R.transition.media_details));
pvrEPGFragment.setReturnTransition(null);
} else {
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
R.anim.fragment_list_popenter, 0);
}
fragTrans.replace(R.id.fragment_container, pvrEPGFragment)
.addToBackStack(null)
.commit();
setupActionBar(selectedChannelTitle);
}
}

View File

@ -0,0 +1,268 @@
/*
* 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.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.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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.type.PVRType;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Fragment that presents the Guide for a channel
*/
public class PVRChannelEPGListFragment extends Fragment
implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = LogUtils.makeLogTag(PVRChannelEPGListFragment.class);
private HostManager hostManager;
private int channelId;
@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 BoadcastsAdapter boadcastsAdapter = null;
private static final String BUNDLE_KEY_CHANNELID = "bundle_key_channelid";
/**
* Create a new instance of this, initialized to show the current channel
*/
public static PVRChannelEPGListFragment newInstance(Integer channelId) {
PVRChannelEPGListFragment fragment = new PVRChannelEPGListFragment();
Bundle args = new Bundle();
args.putInt(BUNDLE_KEY_CHANNELID, channelId);
fragment.setArguments(args);
return fragment;
}
@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);
Bundle bundle = getArguments();
channelId = bundle.getInt(BUNDLE_KEY_CHANNELID, -1);
if (channelId == -1) {
// There's nothing to show
return null;
}
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);
browseEPG();
}
/**
* Swipe refresh layout callback
*/
/** {@inheritDoc} */
@Override
public void onRefresh () {
if (hostManager.getHostInfo() != null) {
browseEPG();
} else {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
.show();
}
}
/**
* Get the EPF for the channel and setup the gridview
*/
private void browseEPG() {
PVR.GetBroadcasts action = new PVR.GetBroadcasts(channelId, PVRType.FieldsBroadcast.allValues);
action.execute(hostManager.getConnection(), new ApiCallback<List<PVRType.DetailsBroadcast>>() {
@Override
public void onSuccess(List<PVRType.DetailsBroadcast> result) {
if (!isAdded()) return;
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(getString(R.string.no_broadcasts_found_refresh));
setupEPGGridview(result);
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(int errorCode, String description) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Error getting broadcasts: " + 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 Guide
*
* @param result Broadcasts obtained
*/
private void setupEPGGridview(List<PVRType.DetailsBroadcast> result) {
if (boadcastsAdapter == null) {
boadcastsAdapter = new BoadcastsAdapter(getActivity(), R.layout.grid_item_broadcast);
}
gridView.setAdapter(boadcastsAdapter);
boadcastsAdapter.clear();
boadcastsAdapter.addAll(result);
boadcastsAdapter.notifyDataSetChanged();
}
private class BoadcastsAdapter extends ArrayAdapter<PVRType.DetailsBroadcast> {
private HostManager hostManager;
private int artWidth, artHeight;
public BoadcastsAdapter(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_broadcast, parent, false);
// Setup View holder pattern
BroadcastViewHolder viewHolder = new BroadcastViewHolder();
viewHolder.titleView = (TextView)convertView.findViewById(R.id.title);
viewHolder.detailsView = (TextView)convertView.findViewById(R.id.details);
viewHolder.startTimeView = (TextView)convertView.findViewById(R.id.start_time);
viewHolder.endTimeView = (TextView)convertView.findViewById(R.id.end_time);
convertView.setTag(viewHolder);
}
final BroadcastViewHolder viewHolder = (BroadcastViewHolder)convertView.getTag();
PVRType.DetailsBroadcast broadcastDetails = this.getItem(position);
viewHolder.broadcastId = broadcastDetails.broadcastid;
viewHolder.title = broadcastDetails.title;
viewHolder.titleView.setText(broadcastDetails.title);
viewHolder.detailsView.setText(broadcastDetails.plot);
int runtime = broadcastDetails.runtime / 60;
String duration =
broadcastDetails.starttime + " | " +
String.format(this.getContext().getString(R.string.minutes_abbrev), String.valueOf(runtime));
// Parse dates
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
Date startTime, endTime;
try {
startTime = sdf.parse(broadcastDetails.starttime);
endTime = sdf.parse(broadcastDetails.endtime);
} catch (ParseException exc) {
startTime = new Date();
endTime = new Date();
}
int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_TIME;
viewHolder.startTimeView.setText(DateUtils.formatDateTime(getActivity(), startTime.getTime(), flags));
viewHolder.endTimeView.setText(DateUtils.formatDateTime(getActivity(), endTime.getTime(), flags));
// DateFormat dfLocal = DateFormat.getTimeInstance(DateFormat.SHORT);
// viewHolder.startTimeView.setText(dfLocal.format(startTime));
// viewHolder.endTimeView.setText(dfLocal.format(endTime));
//viewHolder.durationView.setText(duration);
return convertView;
}
}
/**
* View holder pattern
*/
private static class BroadcastViewHolder {
TextView titleView, detailsView,
startTimeView, endTimeView;
int broadcastId;
String title;
}
}

View File

@ -406,7 +406,9 @@ public class PVRChannelsListFragment extends Fragment
private View.OnClickListener channelItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final int channelId = (Integer)v.getTag();
final ChannelViewHolder viewHolder = (ChannelViewHolder)v.getTag();
final int channelId = viewHolder.channelId;
final String channelName = viewHolder.channelName;
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
popupMenu.getMenuInflater().inflate(R.menu.pvr_channel_list_item, popupMenu.getMenu());
@ -435,6 +437,9 @@ public class PVRChannelsListFragment extends Fragment
}
}, callbackHandler);
return true;
case R.id.action_epg_item:
listenerActivity.onChannelGuideSelected(channelId, channelName);
return true;
}
return false;
}
@ -486,7 +491,7 @@ public class PVRChannelsListFragment extends Fragment
viewHolder.artView, artWidth, artHeight);
// For the popupmenu
viewHolder.contextMenu.setTag(channelDetails.channelid);
viewHolder.contextMenu.setTag(viewHolder);
viewHolder.contextMenu.setOnClickListener(channelItemMenuClickListener);
return convertView;

View File

@ -0,0 +1,73 @@
<?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
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/start_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:minWidth="@dimen/epg_time_width"
android:gravity="end"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:paddingTop="@dimen/small_padding"/>
<TextView
android:id="@+id/end_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="@dimen/epg_time_width"
android:gravity="end"
android:layout_below="@id/start_time"
android:layout_alignRight="@id/start_time"
android:layout_alignEnd="@id/start_time"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?attr/appTextColorSecondary"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/start_time"
android:layout_toEndOf="@+id/start_time"
android:layout_alignTop="@id/start_time"
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"
android:layout_toRightOf="@id/end_time"
android:layout_toEndOf="@id/end_time"
style="@style/TextAppearance.Medialist.Details"
android:paddingBottom="@dimen/small_padding"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@ -16,6 +16,10 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_epg_item"
android:title="@string/pvr_epg"
android:orderInCategory="1"
app:showAsAction="never" />
<item android:id="@+id/action_record_item"
android:title="@string/record"
android:orderInCategory="1"

View File

@ -124,6 +124,8 @@
<dimen name="recordinglist_art_width">74dp</dimen>
<dimen name="recordinglist_art_heigth">74dp</dimen>
<dimen name="epg_time_width">56dp</dimen>
<!-- Notification -->
<dimen name="notification_height">64dp</dimen>
<dimen name="notification_expanded_height">128dp</dimen>

View File

@ -243,6 +243,7 @@
<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="no_broadcasts_found_refresh">No broadcasts 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>
@ -355,5 +356,6 @@
<string name="radio_channels">Radio Channels</string>
<string name="record">Record</string>
<string name="pvr_epg">Guide</string>
</resources>