forked from Mirroring/Kore
Allow sorting of PVR Recordings and optionally hide watched items. (#694)
* Added menu options to PVR recordings view fragment: Sorting and hide_watched option. Default to unsorted (showing the order delivered by Kodi). Default to not hide watched recordings. (#385) * Implemented sorting and filtering of PVR recordings as per user's selection. (#385)master
parent
37ef130a7c
commit
a3ed983fee
|
@ -49,7 +49,8 @@ public class Settings {
|
|||
SORT_BY_ALBUM = 5,
|
||||
SORT_BY_ARTIST = 6,
|
||||
SORT_BY_ARTIST_YEAR = 7,
|
||||
SORT_BY_LAST_PLAYED = 8;
|
||||
SORT_BY_LAST_PLAYED = 8,
|
||||
UNSORTED = 9;
|
||||
|
||||
/**
|
||||
* Preferences keys.
|
||||
|
@ -136,6 +137,14 @@ public class Settings {
|
|||
public static final String KEY_PREF_TVSHOWS_SHOW_WATCHED_STATUS = "tvshows_show_watched_status";
|
||||
public static final boolean DEFAULT_PREF_TVSHOWS_SHOW_WATCHED_STATUS = true;
|
||||
|
||||
// Filter watched pvr recordings on movie list
|
||||
public static final String KEY_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED = "pvr_recordings_filter_hide_watched";
|
||||
public static final boolean DEFAULT_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED = false;
|
||||
|
||||
// Sort order on pvr recordings
|
||||
public static final String KEY_PREF_PVR_RECORDINGS_SORT_ORDER = "pvr_recordings_sort_order";
|
||||
public static final int DEFAULT_PREF_PVR_RECORDINGS_SORT_ORDER = UNSORTED;
|
||||
|
||||
// Filter disabled addons on addons list
|
||||
public static final String KEY_PREF_ADDONS_FILTER_HIDE_DISABLED = "addons_filter_hide_disabled";
|
||||
public static final boolean DEFAULT_PREF_ADDONS_FILTER_HIDE_DISABLED = false;
|
||||
|
|
|
@ -16,12 +16,17 @@
|
|||
package org.xbmc.kore.ui.sections.video;
|
||||
|
||||
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;
|
||||
|
@ -32,6 +37,7 @@ 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.method.PVR;
|
||||
|
@ -40,6 +46,9 @@ import org.xbmc.kore.jsonrpc.type.PVRType;
|
|||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
@ -96,7 +105,7 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
setHasOptionsMenu(true);
|
||||
browseRecordings();
|
||||
}
|
||||
|
||||
|
@ -106,6 +115,91 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
unbinder.unbind();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (!isAdded()) {
|
||||
// HACK: Fix crash reported on Play Store. Why does this is necessary is beyond me
|
||||
// copied from MovieListFragment#onCreateOptionsMenu
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
return;
|
||||
}
|
||||
|
||||
inflater.inflate(R.menu.pvr_recording_list, menu);
|
||||
|
||||
// Setup filters
|
||||
MenuItem hideWatched = menu.findItem(R.id.action_hide_watched),
|
||||
sortByNameAndDate = menu.findItem(R.id.action_sort_by_name_and_date_added),
|
||||
sortByDateAdded = menu.findItem(R.id.action_sort_by_date_added),
|
||||
unsorted = menu.findItem(R.id.action_unsorted);
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
hideWatched.setChecked(preferences.getBoolean(Settings.KEY_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED));
|
||||
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_PVR_RECORDINGS_SORT_ORDER, Settings.DEFAULT_PREF_PVR_RECORDINGS_SORT_ORDER);
|
||||
switch (sortOrder) {
|
||||
case Settings.SORT_BY_DATE_ADDED:
|
||||
sortByDateAdded.setChecked(true);
|
||||
break;
|
||||
case Settings.SORT_BY_NAME:
|
||||
sortByNameAndDate.setChecked(true);
|
||||
break;
|
||||
default:
|
||||
unsorted.setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this to reload the items in the list
|
||||
*/
|
||||
public void refreshList() {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_hide_watched:
|
||||
item.setChecked(!item.isChecked());
|
||||
preferences.edit()
|
||||
.putBoolean(Settings.KEY_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED, item.isChecked())
|
||||
.apply();
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_name_and_date_added:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_PVR_RECORDINGS_SORT_ORDER, Settings.SORT_BY_NAME)
|
||||
.apply();
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_sort_by_date_added:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_PVR_RECORDINGS_SORT_ORDER, Settings.SORT_BY_DATE_ADDED)
|
||||
.apply();
|
||||
refreshList();
|
||||
break;
|
||||
case R.id.action_unsorted:
|
||||
item.setChecked(true);
|
||||
preferences.edit()
|
||||
.putInt(Settings.KEY_PREF_PVR_RECORDINGS_SORT_ORDER, Settings.UNSORTED)
|
||||
.apply();
|
||||
refreshList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
|
@ -134,10 +228,90 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
|
||||
// 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);
|
||||
|
||||
// As the JSON RPC API does not support sorting or filter parameters for PVR.GetRecordings
|
||||
// we apply the sorting and filtering right here.
|
||||
// See https://kodi.wiki/view/JSON-RPC_API/v9#PVR.GetRecordings
|
||||
List<PVRType.DetailsRecording> finalResult = filter(result);
|
||||
sort(finalResult);
|
||||
|
||||
setupRecordingsGridview(finalResult);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
private List<PVRType.DetailsRecording> filter(List<PVRType.DetailsRecording> itemList) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
boolean hideWatched = preferences.getBoolean(Settings.KEY_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED);
|
||||
|
||||
if (!hideWatched) {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
List<PVRType.DetailsRecording> result = new ArrayList<>(itemList.size());
|
||||
for (PVRType.DetailsRecording item:itemList) {
|
||||
if (hideWatched) {
|
||||
if (item.playcount > 0) {
|
||||
continue; // Skip this item as it is played.
|
||||
} else {
|
||||
// Heuristic: Try to guess if it's play from resume timestamp.
|
||||
double resumePosition = item.resume.position;
|
||||
int runtime = item.runtime;
|
||||
if (runtime < resumePosition) {
|
||||
// Tv show duration is smaller than resume position.
|
||||
// The tv show likely has been watched.
|
||||
// It's still possible some minutes have not yet been watched
|
||||
// at the end of the show as some minutes at the
|
||||
// recording start do not belong to the show.
|
||||
// Never the less skip this item.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// more conditions may be added here
|
||||
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sort(List<PVRType.DetailsRecording> itemList) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
|
||||
int sortOrder = preferences.getInt(Settings.KEY_PREF_PVR_RECORDINGS_SORT_ORDER, Settings.DEFAULT_PREF_PVR_RECORDINGS_SORT_ORDER);
|
||||
|
||||
Comparator<PVRType.DetailsRecording> comparator;
|
||||
switch (sortOrder) {
|
||||
case Settings.SORT_BY_DATE_ADDED:
|
||||
// sort by recording start time descending (most current first)
|
||||
// luckily the starttime is in sortable format yyyy-MM-dd hh:mm:ss
|
||||
comparator = new Comparator<PVRType.DetailsRecording>() {
|
||||
@Override
|
||||
public int compare(PVRType.DetailsRecording a, PVRType.DetailsRecording b) {
|
||||
return b.starttime.compareTo(a.starttime);
|
||||
}
|
||||
};
|
||||
Collections.sort(itemList, comparator);
|
||||
break;
|
||||
case Settings.SORT_BY_NAME:
|
||||
// sort by recording title and start time
|
||||
comparator = new Comparator<PVRType.DetailsRecording>() {
|
||||
@Override
|
||||
public int compare(PVRType.DetailsRecording a, PVRType.DetailsRecording b) {
|
||||
int result = a.title.compareToIgnoreCase(b.title);
|
||||
if (0 == result) { // note the yoda condition ;)
|
||||
// sort by starttime descending (most current first)
|
||||
result = b.starttime.compareTo(a.starttime);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Collections.sort(itemList, comparator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
if (!isAdded()) return;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?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/sort_order"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<group
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/action_sort_by_name_and_date_added"
|
||||
android:title="@string/sort_by_name"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_sort_by_date_added"
|
||||
android:title="@string/sort_by_date_added"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_unsorted"
|
||||
android:title="@string/unsorted"
|
||||
app:showAsAction="never"/>
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<group
|
||||
android:checkableBehavior="all">
|
||||
<item
|
||||
android:id="@+id/action_hide_watched"
|
||||
android:title="@string/hide_watched"
|
||||
app:showAsAction="never"/>
|
||||
</group>
|
||||
|
||||
</menu>
|
|
@ -325,6 +325,7 @@
|
|||
<string name="sort_by_length">Nach Länge</string>
|
||||
<string name="sort_by_date_added">Nach Hinzufügungsdatum</string>
|
||||
<string name="sort_by_last_played">Zuletzt gespielt</string>
|
||||
<string name="unsorted">Nicht sortiert</string>
|
||||
<string name="ignore_prefixes">Präfixe ignorieren</string>
|
||||
|
||||
<!-- Preferences strings -->
|
||||
|
|
|
@ -340,6 +340,7 @@
|
|||
<string name="sort_by_length">By length</string>
|
||||
<string name="sort_by_date_added">By date added</string>
|
||||
<string name="sort_by_last_played">By last played</string>
|
||||
<string name="unsorted">Unsorted</string>
|
||||
<string name="ignore_prefixes">Ignore prefixes</string>
|
||||
<string name="show_watched_status">Show watched status</string>
|
||||
<string name="show_rating">Show rating</string>
|
||||
|
|
Loading…
Reference in New Issue