Switch PVR fragments to using tabs instead of a menu for navigation

This commit is contained in:
Synced Synapse 2015-11-28 18:19:44 +00:00
parent fa43d43066
commit 1cf701a422
12 changed files with 815 additions and 766 deletions

View File

@ -102,7 +102,4 @@ public interface Settings {
public static final boolean DEFAULT_PREF_CHECKED_EVENT_SERVER_CONNECTION = false;
public static final String KEY_PREF_NAV_DRAWER_ITEMS = "pref_nav_drawer_items";
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

@ -30,17 +30,12 @@ import org.xbmc.kore.R;
import org.xbmc.kore.utils.Utils;
/**
* Created by danhdroid on 3/18/15.
* Handles listing of files fragments
*/
public class FileActivity extends BaseActivity {
private NavigationDrawerFragment navigationDrawerFragment;
public interface OnBackPressedListener {
void onBackPressed();
boolean currentPageAtRootDirectory();
}
OnBackPressedListener fragmentBackListener;
public void setBackPressedListener(OnBackPressedListener listener) {
@ -106,10 +101,8 @@ public class FileActivity extends BaseActivity {
public void onBackPressed() {
// tell fragment to move up one directory
if (fragmentBackListener != null) {
if (!fragmentBackListener.currentPageAtRootDirectory())
fragmentBackListener.onBackPressed();
else
// already at the root level of the current page
boolean handled = fragmentBackListener.onBackPressed();
if (!handled)
super.onBackPressed();
}

View File

@ -31,10 +31,10 @@ import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Created by danhdroid on 3/18/15.
* Manages the viewpager of files
*/
public class FileListFragment extends Fragment
implements FileActivity.OnBackPressedListener {
implements OnBackPressedListener {
@InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip;
@InjectView(R.id.pager) ViewPager viewPager;
@ -83,23 +83,15 @@ public class FileListFragment extends Fragment
}
@Override
public void onBackPressed() {
// tell current fragment to move up one directory
public boolean onBackPressed() {
// Tell current fragment to move up one directory, if possible
MediaFileListFragment curPage = findFragmentByPosition(viewPager.getCurrentItem() + 1);
if (curPage != null) {
// based on the current position cast the page to the correct
// class and call the method
if ((curPage != null) && !curPage.atRootDirectory()) {
curPage.onBackPressed();
return true;
}
}
@Override
public boolean currentPageAtRootDirectory() {
MediaFileListFragment curPage = findFragmentByPosition(viewPager.getCurrentItem() + 1);
if (curPage != null) {
return curPage.atRootDirectory();
}
return true;
// Not handled, let the activity handle it
return false;
}
}

View File

@ -166,7 +166,7 @@ public class NavigationDrawerFragment extends Fragment {
styledAttributes.getResourceId(ACTIVITY_MUSIC, 0)));
if (shownItems.contains(String.valueOf(ACTIVITY_PVR)))
items.add(new DrawerItem(DrawerItem.TYPE_NORMAL_ITEM, ACTIVITY_PVR,
getString(R.string.tv_radio),
getString(R.string.pvr),
styledAttributes.getResourceId(ACTIVITY_PVR, 0)));
if (shownItems.contains(String.valueOf(ACTIVITY_FILES)))
items.add(new DrawerItem(DrawerItem.TYPE_NORMAL_ITEM, ACTIVITY_FILES,

View File

@ -0,0 +1,13 @@
package org.xbmc.kore.ui;
/**
* Interface to pass OnBack events from activities to fragments
*/
public interface OnBackPressedListener {
/**
* Implement this method to handle onBackPressed events received by the activity
*
* @return True if the event was handled and consumed, False if the activity should handle it (pop back stack)
*/
boolean onBackPressed();
}

View File

@ -17,10 +17,7 @@ package org.xbmc.kore.ui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
@ -30,32 +27,24 @@ import android.view.MenuItem;
import android.view.Window;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.Utils;
/**
* Controls the presentation of Live TV/Radio information (list, details)
* Controls the presentation of Live TV/Radio and recordings information (list, details)
* All the information is presented by specific fragments
*/
public class PVRActivity extends BaseActivity
implements PVRListFragment.OnPVRSelectedListener {
implements PVRChannelsListFragment.OnPVRChannelSelectedListener {
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 RETURNTOCHANNELGROUP = "return_to_channel_group";
public static final String CHANNELID = "channel_id";
public static final String CHANNELTITLE = "channel_title";
private int selectedChannelGroupId = -1;
private String selectedChannelGroupTitle = null;
private boolean returnToChannelGroupList = true;
private static final String LISTFRAGMENTTAG = "listfragmenttag";
private int selectedChannelId = -1;
private String selectedChannelTitle = null;
private int currentListType;
private NavigationDrawerFragment navigationDrawerFragment;
@ -86,21 +75,14 @@ public class PVRActivity extends BaseActivity
}
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, pvrListFragment)
.add(R.id.fragment_container, pvrListFragment, LISTFRAGMENTTAG)
.commit();
} else {
selectedChannelGroupId = savedInstanceState.getInt(CHANNELGROUPID, -1);
selectedChannelGroupTitle = savedInstanceState.getString(CHANNELGROUPTITLE, null);
returnToChannelGroupList = savedInstanceState.getBoolean(RETURNTOCHANNELGROUP, true);
selectedChannelId = savedInstanceState.getInt(CHANNELID, -1);
selectedChannelTitle = savedInstanceState.getString(CHANNELTITLE, null);
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
currentListType = preferences.getInt(Settings.KEY_PREF_PVR_LIST_TYPE, Settings.DEFAULT_PREF_PVR_LIST_TYPE);
setupActionBar(currentListType, selectedChannelGroupTitle);
setupActionBar(selectedChannelTitle);
}
@Override
@ -114,21 +96,14 @@ public class PVRActivity extends BaseActivity
}
@Override
protected void onSaveInstanceState (Bundle outState) {
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CHANNELGROUPID, selectedChannelGroupId);
outState.putString(CHANNELGROUPTITLE, selectedChannelGroupTitle);
outState.putBoolean(RETURNTOCHANNELGROUP, returnToChannelGroupList);
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);
}
@ -147,12 +122,9 @@ public class PVRActivity extends BaseActivity
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(currentListType, null);
setupActionBar(null);
getSupportFragmentManager().popBackStack();
return true;
} else if (returnToChannelGroupList && (selectedChannelGroupId != -1)) {
onBackPressed();
return true;
}
break;
default:
@ -164,29 +136,23 @@ public class PVRActivity extends BaseActivity
@Override
public void onBackPressed() {
boolean handled = false;
// If we are showing details in portrait, clear selected and show action bar
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;
if (selectedChannelId != -1) {
selectedChannelId = -1;
selectedChannelTitle = null;
setupActionBar(null);
} else {
PVRListFragment fragment = (PVRListFragment)getSupportFragmentManager().findFragmentByTag(LISTFRAGMENTTAG);
if (fragment != null) {
handled = fragment.onBackPressed();
}
}
super.onBackPressed();
if (!handled)
super.onBackPressed();
}
private void setupActionBar(int listType, String channelTitle) {
private void setupActionBar(String channelTitle) {
Toolbar toolbar = (Toolbar)findViewById(R.id.default_toolbar);
setSupportActionBar(toolbar);
@ -194,34 +160,16 @@ public class PVRActivity extends BaseActivity
if (actionBar == null) return;
actionBar.setDisplayHomeAsUpEnabled(true);
String title;
boolean drawerIndicatorEnabled;
if (channelTitle != null) {
drawerIndicatorEnabled = !returnToChannelGroupList;
title = String.format("%s - %s", PVRListFragment.getPVRListTypeTitle(this, listType), channelTitle);
navigationDrawerFragment.setDrawerIndicatorEnabled(false);
actionBar.setTitle(channelTitle);
} else {
drawerIndicatorEnabled = true;
title = PVRListFragment.getPVRListTypeTitle(this, listType);
navigationDrawerFragment.setDrawerIndicatorEnabled(true);
actionBar.setTitle(R.string.pvr);
}
navigationDrawerFragment.setDrawerIndicatorEnabled(drawerIndicatorEnabled);
actionBar.setTitle(title);
}
/**
* 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, boolean canReturnToChannelGroupList) {
selectedChannelGroupId = channelGroupId;
selectedChannelGroupTitle = channelGroupTitle;
this.returnToChannelGroupList = canReturnToChannelGroupList;
setupActionBar(currentListType, selectedChannelGroupTitle);
}
/**
* Callback from list fragment when the channel guide should be displayed.
* Setup action bar and repolace list fragment
@ -250,15 +198,6 @@ public class PVRActivity extends BaseActivity
// fragTrans.replace(R.id.fragment_container, pvrDetailsFragment)
// .addToBackStack(null)
// .commit();
setupActionBar(currentListType, selectedChannelTitle);
}
/**
* Callback from list fragment when the list type is changed
* @param listType List type selected
*/
public void onListTypeChanged(int listType) {
currentListType = listType;
setupActionBar(currentListType, selectedChannelTitle);
setupActionBar(selectedChannelTitle);
}
}

View File

@ -0,0 +1,458 @@
/*
* 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.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.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.Settings;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.ApiException;
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.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 PVRChannelsListFragment extends Fragment
implements SwipeRefreshLayout.OnRefreshListener, OnBackPressedListener {
private static final String TAG = LogUtils.makeLogTag(PVRChannelsListFragment.class);
public static final String CHANNELGROUPID = "channelgroupid";
public static final String SINGLECHANNELGROUP = "singlechannelgroup";
public interface OnPVRChannelSelectedListener {
public void onChannelGuideSelected(int channelId, String channelTitle);
}
// Activity listener
private OnPVRChannelSelectedListener 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;
private int currentListType;
private boolean singleChannelGroup = false;
@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);
if (savedInstanceState != null) {
selectedChannelGroupId = savedInstanceState.getInt(CHANNELGROUPID);
singleChannelGroup = savedInstanceState.getBoolean(SINGLECHANNELGROUP);
}
hostManager = HostManager.getInstance(getActivity());
currentListType = getArguments().getInt(PVRListFragment.PVR_LIST_TYPE_KEY, PVRListFragment.LIST_TV_CHANNELS);
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 (selectedChannelGroupId == -1) {
if ((channelGroupAdapter == null) ||
(channelGroupAdapter.getCount() == 0))
browseChannelGroups();
} else {
browseChannels(selectedChannelGroupId);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listenerActivity = (OnPVRChannelSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnPVRChannelSelectedListener");
}
}
@Override
public void onDetach() {
super.onDetach();
listenerActivity = null;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CHANNELGROUPID, selectedChannelGroupId);
outState.putBoolean(SINGLECHANNELGROUP, singleChannelGroup);
}
/**
* 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 viewpager fragment
*
* @return True if back eas handled, false if it wasn't
*/
public boolean onBackPressed() {
if (!singleChannelGroup && (selectedChannelGroupId != -1)) {
selectedChannelGroupId = -1;
browseChannelGroups();
return true;
}
return false;
}
/**
* Get the channel groups list and setup the gridview
*/
private void browseChannelGroups() {
LogUtils.LOGD(TAG, "Getting channel groups");
String channelType = (currentListType == PVRListFragment.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) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Got channel groups");
if (result.size() == 1) {
// Single channel group, go directly to channel list
singleChannelGroup = true;
selectedChannelGroupId = result.get(0).channelgroupid;
browseChannels(selectedChannelGroupId);
} else {
// 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);
if (errorCode == ApiException.API_ERROR) {
emptyView.setText(String.format(getString(R.string.might_not_have_pvr), description));
} else {
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
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

@ -15,112 +15,59 @@
*/
package org.xbmc.kore.ui;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.astuetz.PagerSlidingTabStrip;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.ApiException;
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.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.List;
import org.xbmc.kore.utils.TabsAdapter;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Fragment that presents the movie list
* Container for the various PVR lists
*/
public class PVRListFragment extends Fragment
implements SwipeRefreshLayout.OnRefreshListener {
implements OnBackPressedListener {
private static final String TAG = LogUtils.makeLogTag(PVRListFragment.class);
public static final String CHANNELGROUPID = "channel_group_id";
private TabsAdapter tabsAdapter;
public interface OnPVRSelectedListener {
public void onListTypeChanged(int listType);
public void onChannelGroupSelected(int channelGroupId, String channelGroupTitle, boolean canReturnToChannelGroupList);
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 RecordingsAdapter recordingsAdapter = null;
private int selectedChannelGroupId = -1;
@InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip;
@InjectView(R.id.pager) ViewPager viewPager;
public static final String PVR_LIST_TYPE_KEY = "pvr_list_type_key";
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) {
super.onCreate(savedInstanceState);
}
LIST_RADIO_CHANNELS = 1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_default_view_pager, container, false);
ButterKnife.inject(this, root);
if (savedInstanceState != null) {
selectedChannelGroupId = savedInstanceState.getInt(CHANNELGROUPID);
Bundle tvArgs = new Bundle(), radioArgs = new Bundle();
if (getArguments() != null) {
tvArgs.putAll(getArguments());
radioArgs.putAll(getArguments());
}
tvArgs.putInt(PVR_LIST_TYPE_KEY, LIST_TV_CHANNELS);
radioArgs.putInt(PVR_LIST_TYPE_KEY, LIST_RADIO_CHANNELS);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
currentListType = preferences.getInt(Settings.KEY_PREF_PVR_LIST_TYPE, Settings.DEFAULT_PREF_PVR_LIST_TYPE);
tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager())
.addTab(PVRChannelsListFragment.class, tvArgs, R.string.tv_channels, 1)
.addTab(PVRChannelsListFragment.class, radioArgs, R.string.radio_channels, 2)
.addTab(PVRRecordingsListFragment.class, getArguments(), R.string.recordings, 3);
hostManager = HostManager.getInstance(getActivity());
swipeRefreshLayout.setOnRefreshListener(this);
emptyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onRefresh();
}
});
gridView.setEmptyView(emptyView);
viewPager.setAdapter(tabsAdapter);
pagerTabStrip.setViewPager(viewPager);
return root;
}
@ -128,541 +75,24 @@ public class PVRListFragment extends Fragment
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
setHasOptionsMenu(false);
}
if (currentListType == LIST_RECORDINGS) {
browseRecordings();
} else {
if (selectedChannelGroupId == -1) {
if ((channelGroupAdapter == null) ||
(channelGroupAdapter.getCount() == 0))
browseChannelGroups();
} else {
browseChannels(selectedChannelGroupId);
}
}
Fragment findFragmentByPosition(int position) {
String tag = "android:switcher:" + viewPager.getId() + ":" + position;
return getChildFragmentManager().findFragmentByTag(tag);
}
@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();
}
@Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CHANNELGROUPID, selectedChannelGroupId);
}
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);
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);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
switch (item.getItemId()) {
case R.id.action_pvr_list_tv_channels:
currentListType = LIST_TV_CHANNELS;
browseChannelGroups();
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);
}
/**
* Swipe refresh layout callback
*/
/** {@inheritDoc} */
@Override
public void onRefresh () {
if (hostManager.getHostInfo() != null) {
if (currentListType == LIST_RECORDINGS) {
browseRecordings();
} else {
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() {
LogUtils.LOGD(TAG, "Getting channel groups");
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) {
if (!isAdded()) return;
LogUtils.LOGD(TAG, "Got channel groups");
if (result.size() == 1) {
// Single channel group, go directly to channel list
selectedChannelGroupId = result.get(0).channelgroupid;
listenerActivity.onChannelGroupSelected(selectedChannelGroupId, result.get(0).label, false);
browseChannels(selectedChannelGroupId);
} else {
// 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);
if (errorCode == ApiException.API_ERROR) {
emptyView.setText(String.format(getString(R.string.might_not_have_pvr), description));
} else {
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, true);
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);
public boolean onBackPressed() {
// Tell current fragment to move up one directory, if possible
Fragment visibleFragment = findFragmentByPosition(viewPager.getCurrentItem() + 1);
if (visibleFragment instanceof OnBackPressedListener) {
return ((OnBackPressedListener) visibleFragment).onBackPressed();
}
/** {@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;
}
/**
* 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;
// Not handled, let the activity handle it
return false;
}
}

View File

@ -0,0 +1,269 @@
/*
* 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.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.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.Settings;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.ApiException;
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.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Fragment that presents the PVR recordings list
*/
public class PVRRecordingsListFragment extends Fragment
implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = LogUtils.makeLogTag(PVRRecordingsListFragment.class);
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 RecordingsAdapter recordingsAdapter = null;
@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);
browseRecordings();
}
/**
* Swipe refresh layout callback
*/
/** {@inheritDoc} */
@Override
public void onRefresh () {
if (hostManager.getHostInfo() != null) {
browseRecordings();
} else {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.no_xbmc_configured, Toast.LENGTH_SHORT)
.show();
}
}
/**
* 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

@ -1,42 +0,0 @@
<?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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
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

@ -46,7 +46,7 @@
<item>@string/movies</item>
<item>@string/tv_shows</item>
<item>@string/music</item>
<item>@string/tv_radio</item>
<item>@string/pvr</item>
<item>@string/files</item>
<item>@string/addons</item>
</string-array>

View File

@ -36,7 +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="pvr">PVR</string>
<string name="no_xbmc_configured">No media center configured</string>
<string name="add_xbmc">Add Media Center</string>