From bc889989e583d3557005df30bb903564e7dc7c51 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 10 Apr 2013 16:35:41 +0200 Subject: [PATCH 01/45] added epg list activity --- res/layout/epgnow_list_title.xml | 26 +++ res/layout/epgnow_list_widget.xml | 7 + .../tvhguide/EPGNowListActivity.java | 208 ++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 res/layout/epgnow_list_title.xml create mode 100644 res/layout/epgnow_list_widget.xml create mode 100644 src/org/tvheadend/tvhguide/EPGNowListActivity.java diff --git a/res/layout/epgnow_list_title.xml b/res/layout/epgnow_list_title.xml new file mode 100644 index 0000000..98ec8f1 --- /dev/null +++ b/res/layout/epgnow_list_title.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml new file mode 100644 index 0000000..4341563 --- /dev/null +++ b/res/layout/epgnow_list_widget.xml @@ -0,0 +1,7 @@ + diff --git a/src/org/tvheadend/tvhguide/EPGNowListActivity.java b/src/org/tvheadend/tvhguide/EPGNowListActivity.java new file mode 100644 index 0000000..1b89625 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGNowListActivity.java @@ -0,0 +1,208 @@ +package org.tvheadend.tvhguide; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.tvheadend.tvhguide.model.Programme; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.FragmentTransaction; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.ListFragment; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class EPGNowListActivity extends FragmentActivity implements + ActionBar.TabListener { + + /** + * The serialization (saved instance state) Bundle key representing the + * current tab position. + */ + private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(savedInstanceState); + setContentView(R.layout.epgnow_list_widget); + + // Set up the action bar to show tabs. + final ActionBar actionBar = getActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + + // For each of the sections in the app, add a tab to the action bar. + actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_now) + .setTabListener(this)); + actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_next) + .setTabListener(this)); + actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_2000) + .setTabListener(this)); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.epgnow_list_title); + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + // Restore the previously serialized current tab position. + if (savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM)) { + getActionBar().setSelectedNavigationItem( + savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM)); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + // Serialize the current tab position. + outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActionBar() + .getSelectedNavigationIndex()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.epgnow_list_widget, menu); + return true; + } + + @Override + public void onTabSelected(ActionBar.Tab tab, + FragmentTransaction fragmentTransaction) { + // When the given tab is selected, show the tab contents in the + // container view. + EPGListFragment fragment = new EPGListFragment(); + Bundle args = new Bundle(); + args.putInt(EPGListFragment.ARG_SECTION_NUMBER, tab.getPosition() + 1); + fragment.setArguments(args); + getSupportFragmentManager().beginTransaction() + .replace(R.id.container, fragment).commit(); + } + + @Override + public void onTabUnselected(ActionBar.Tab tab, + FragmentTransaction fragmentTransaction) { + } + + @Override + public void onTabReselected(ActionBar.Tab tab, + FragmentTransaction fragmentTransaction) { + } + + /** + * A dummy fragment representing a section of the app, but that simply + * displays dummy text. + */ + public static class EPGListFragment extends ListFragment { + /** + * The fragment argument representing the section number for this + * fragment. + */ + public static final String ARG_SECTION_NUMBER = "section_number"; + + private ProgrammeListAdapter prAdapter; + + public EPGListFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + List prList = new ArrayList(); + prAdapter = new ProgrammeListAdapter(getActivity(), prList); + prAdapter.sort(); + setListAdapter(prAdapter); + } + + // @Override + // public View onCreateView(LayoutInflater inflater, + // ViewGroup convertView, Bundle savedInstanceState) { + // getArguments().getInt(ARG_SECTION_NUMBER); + // + // // generate own view + // View rowview = convertView; + // if (null == rowview) { + // rowview = inflater.inflate(R.layout.epgnow_list_widget, null); + // } + // + // return rowview; + // } + } + + static class ProgrammeListAdapter extends ArrayAdapter { + + Activity context; + List list; + + ProgrammeListAdapter(Activity context, List list) { + super(context, R.layout.epgnow_list_widget, list); + this.context = context; + this.list = list; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Programme x, Programme y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Programme programme) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Programme pr = (Programme) listView.getItemAtPosition(pos); + + if (view.getTag() == null || pr == null) { + continue; + } + + if (programme.id != pr.id) { + continue; + } + + // ViewWarpper wrapper = (ViewWarpper) view.getTag(); + // wrapper.repaint(programme); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + // ViewWarpper wrapper = null; + + if (row == null) { + LayoutInflater inflater = context.getLayoutInflater(); + row = inflater + .inflate(R.layout.epgnow_list_widget, null, false); + + // wrapper = new ViewWarpper(row); + // row.setTag(wrapper); + + } else { + // wrapper = (ViewWarpper) row.getTag(); + } + + Programme p = getItem(position); + // wrapper.repaint(p); + return row; + } + } +} From b9e7c1115d534b0908c83cfdff2dc75c5c807a3b Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 10 Apr 2013 17:31:12 +0200 Subject: [PATCH 02/45] added missing files --- AndroidManifest.xml | 75 ++++++++++++++----- lint.xml | 3 + project.properties | 2 +- res/values/strings.xml | 46 +++--------- .../tvhguide/EPGNowListActivity.java | 2 +- 5 files changed, 70 insertions(+), 58 deletions(-) create mode 100644 lint.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f1cb601..f3fa3e3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,32 +2,69 @@ + android:versionName="1.6.5" > + - - - - + + + + + + + - - + + + - - + + + - + + - - - - - - - + + + + + + + + - + + \ No newline at end of file diff --git a/lint.xml b/lint.xml new file mode 100644 index 0000000..ee0eead --- /dev/null +++ b/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/project.properties b/project.properties index 5a70945..c4f09d2 100644 --- a/project.properties +++ b/project.properties @@ -8,4 +8,4 @@ # project structure. # Project target. -target=android-7 +target=android-17 diff --git a/res/values/strings.xml b/res/values/strings.xml index ec275ac..5393398 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,71 +1,51 @@ - TVHGuide + TVHGuide Help Settings Refresh Tags Recordings - Record Cancel recording Remove recording - Hostname Enter Server Hostname - Port Enter Server Port - Username Enter Username - Password Enter Password - User interface - Channel icons Show channel icons? - Light theme Use a light theme - Resolution Maximum resolution for the transcoder - Audio codec Audio codec to use with the transcoder - Video codec Video codec to use with the transcoder - Subtitle codec Subtitle codec to use with the transcoder - Media container Prefered media container - Playback - HTTP port Enter Server HTTP Port - Transcode stream Request a transcoded stream during live playback - Prefer external player Use an external player for live streams (BSPlayer seems OK) - Access denied Invalid response from server Can\'t connect to server Lost connection to server - Loading… Please wait a few seconds… - All channels Get more Season @@ -75,7 +55,7 @@ Rating Type Part - + Movie/Drama News/Current affairs @@ -89,7 +69,6 @@ Leisure hobbies Misc - Movie/Drama Detective/Thriller @@ -101,7 +80,6 @@ Serious/ClassicalReligion/Historical Adult Movie/Drama - News/Current affairs News/Weather Report @@ -109,14 +87,12 @@ Documentary Discussion/Interview/Debate - Show/Game show Game show/Quiz/Contest Variety Talk - Sports Special Event @@ -131,7 +107,6 @@ Equestrian Martial sports - Children\'s / Youth Pre-school @@ -140,7 +115,6 @@ Informational/Educational/Schools Cartoons/Puppets - Music/Ballet/Dance Rock/Pop @@ -150,7 +124,6 @@ Musical/Opera Ballet - Arts/Culture Performing Arts @@ -165,17 +138,14 @@ Magazine Fashion - Social/Political issues/Economics Magazine/Report/Domentary Economics/Social Advisory Remarkable People - - - Education/Science/Factual + Education/Science/Factual Nature/Animals/Environment Technology/Natural sciences Medicine/Physiology/Psychology @@ -184,7 +154,6 @@ Further Education Languages - Leisure hobbies Tourism/Travel @@ -195,7 +164,6 @@ Advertisement/Shopping Gardening - Misc Black and White @@ -208,10 +176,14 @@ Completed Missed Invalid - Play No transmission - Search the EPG today + Hello world! + TestActivity + + Now + Next + 20:15 \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGNowListActivity.java b/src/org/tvheadend/tvhguide/EPGNowListActivity.java index 1b89625..cbf675d 100644 --- a/src/org/tvheadend/tvhguide/EPGNowListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGNowListActivity.java @@ -76,7 +76,7 @@ public void onSaveInstanceState(Bundle outState) { @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.epgnow_list_widget, menu); + getMenuInflater().inflate(R.menu.main_menu, menu); return true; } From 3a0c69abc6f0d5b528b2994b78b363a3d37437a2 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 10 Apr 2013 22:44:34 +0200 Subject: [PATCH 03/45] reimplemented timeslot based epg --- AndroidManifest.xml | 3 + res/layout/epgnow_list_activity.xml | 8 + res/layout/epgnow_list_widget.xml | 78 +- res/menu/main_menu.xml | 3 + res/values/strings.xml | 2 + .../tvhguide/ChannelListActivity.java | 741 ++++++++-------- ...Activity.java => EPGTimeListActivity.java} | 102 ++- .../tvhguide/EPGTimeListViewWrapper.java | 65 ++ .../tvheadend/tvhguide/ProgrammeActivity.java | 417 ++++----- .../tvhguide/ProgrammeListActivity.java | 769 ++++++++--------- .../tvhguide/ProgrammeListViewWrapper.java | 147 ++++ .../tvhguide/SearchResultActivity.java | 791 +++++++++--------- .../tvheadend/tvhguide/SettingsActivity.java | 103 +-- .../tvhguide/TVHGuideApplication.java | 745 +++++++++-------- .../tvhguide/htsp/HTSConnection.java | 563 ++++++------- 15 files changed, 2428 insertions(+), 2109 deletions(-) create mode 100644 res/layout/epgnow_list_activity.xml rename src/org/tvheadend/tvhguide/{EPGNowListActivity.java => EPGTimeListActivity.java} (63%) create mode 100644 src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java create mode 100644 src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f3fa3e3..fc285e9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -50,6 +50,9 @@ + diff --git a/res/layout/epgnow_list_activity.xml b/res/layout/epgnow_list_activity.xml new file mode 100644 index 0000000..c671c39 --- /dev/null +++ b/res/layout/epgnow_list_activity.xml @@ -0,0 +1,8 @@ + + diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml index 4341563..0b24cdd 100644 --- a/res/layout/epgnow_list_widget.xml +++ b/res/layout/epgnow_list_widget.xml @@ -1,7 +1,71 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml index e9a7fd0..3989727 100644 --- a/res/menu/main_menu.xml +++ b/res/menu/main_menu.xml @@ -11,6 +11,9 @@ + + Help Settings Refresh + EPG List Tags Recordings Record @@ -186,4 +187,5 @@ Now Next 20:15 + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/ChannelListActivity.java b/src/org/tvheadend/tvhguide/ChannelListActivity.java index 596be15..91fd421 100644 --- a/src/org/tvheadend/tvhguide/ChannelListActivity.java +++ b/src/org/tvheadend/tvhguide/ChannelListActivity.java @@ -18,6 +18,15 @@ */ package org.tvheadend.tvhguide; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.ChannelTag; + import android.app.Activity; import android.app.AlertDialog; import android.app.ListActivity; @@ -26,366 +35,388 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; +import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.*; -import android.widget.*; -import java.util.*; -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.htsp.HTSListener; -import org.tvheadend.tvhguide.htsp.HTSService; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.ChannelTag; +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.view.Window; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; /** - * + * * @author john-tornblom */ public class ChannelListActivity extends ListActivity implements HTSListener { - private ChannelListAdapter chAdapter; - ArrayAdapter tagAdapter; - private AlertDialog tagDialog; - private TextView tagTextView; - private ImageView tagImageView; - private View tagBtn; - private ProgressBar pb; - private ChannelTag currentTag; - - @Override - public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - - super.onCreate(icicle); - - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - chAdapter = new ChannelListAdapter(this, new ArrayList()); - setListAdapter(chAdapter); - - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.channel_list_title); - tagTextView = (TextView) findViewById(R.id.ct_title); - tagImageView = (ImageView) findViewById(R.id.ct_logo); - - pb = (ProgressBar) findViewById(R.id.ct_loading); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.menu_tags); - - tagAdapter = new ArrayAdapter( - this, - android.R.layout.simple_dropdown_item_1line, - new ArrayList()); - - builder.setAdapter(tagAdapter, new android.content.DialogInterface.OnClickListener() { - - public void onClick(DialogInterface arg0, int pos) { - setCurrentTag(tagAdapter.getItem(pos)); - populateList(); - } - }); - - tagDialog = builder.create(); - tagBtn = findViewById(R.id.ct_btn); - tagBtn.setOnClickListener(new android.view.View.OnClickListener() { - - public void onClick(View arg0) { - tagDialog.show(); - } - }); - - registerForContextMenu(getListView()); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main_menu, menu); - return true; - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.string.ch_play: { - startActivity(item.getIntent()); - return true; - } - case R.string.search_hint: { - startSearch(null, false, item.getIntent().getExtras(), false); - return true; - } - default: { - return false; - } - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - MenuItem item = menu.add(ContextMenu.NONE, R.string.ch_play, ContextMenu.NONE, R.string.ch_play); - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Channel ch = chAdapter.getItem(info.position); - - menu.setHeaderTitle(ch.name); - Intent intent = new Intent(this, PlaybackActivity.class); - intent.putExtra("channelId", ch.id); - item.setIntent(intent); - - item = menu.add(ContextMenu.NONE, R.string.search_hint, ContextMenu.NONE, R.string.search_hint); - intent = new Intent(); - intent.putExtra("channelId", ch.id); - item.setIntent(intent); - } - - void connect(boolean force) { - if (force) { - chAdapter.clear(); - } - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - String hostname = prefs.getString("serverHostPref", "localhost"); - int port = Integer.parseInt(prefs.getString("serverPortPref", "9982")); - String username = prefs.getString("usernamePref", ""); - String password = prefs.getString("passwordPref", ""); - - Intent intent = new Intent(ChannelListActivity.this, HTSService.class); - intent.setAction(HTSService.ACTION_CONNECT); - intent.putExtra("hostname", hostname); - intent.putExtra("port", port); - intent.putExtra("username", username); - intent.putExtra("password", password); - intent.putExtra("force", force); - - startService(intent); - } - - private void setCurrentTag(ChannelTag t) { - currentTag = t; - - if (t == null) { - tagTextView.setText(R.string.pr_all_channels); - tagImageView.setImageResource(R.drawable.logo_72); - } else { - tagTextView.setText(currentTag.name); - if (currentTag.iconBitmap != null) { - tagImageView.setImageBitmap(currentTag.iconBitmap); - } else { - tagImageView.setImageResource(R.drawable.logo_72); - } - } - } - - private void populateList() { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - - chAdapter.clear(); - - for (Channel ch : app.getChannels()) { - if (currentTag == null || ch.hasTag(currentTag.id)) { - chAdapter.add(ch); - } - } - - chAdapter.sort(); - chAdapter.notifyDataSetChanged(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.mi_settings: { - Intent intent = new Intent(getBaseContext(), SettingsActivity.class); - startActivityForResult(intent, R.id.mi_settings); - return true; - } - case R.id.mi_refresh: { - connect(true); - return true; - } - case R.id.mi_recordings: { - Intent intent = new Intent(getBaseContext(), RecordingListActivity.class); - startActivity(intent); - return true; - } - case R.id.mi_search: { - onSearchRequested(); - return true; - } - case R.id.mi_tags: { - tagDialog.show(); - return true; - } - default: { - return super.onOptionsItemSelected(item); - } - } - } - - @Override - protected void onResume() { - super.onResume(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addListener(this); - - connect(false); - setLoading(app.isLoading()); - } - - @Override - protected void onPause() { - super.onPause(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeListener(this); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Channel ch = (Channel) chAdapter.getItem(position); - - if (ch.epg.isEmpty()) { - return; - } - - Intent intent = new Intent(getBaseContext(), ProgrammeListActivity.class); - intent.putExtra("channelId", ch.id); - startActivity(intent); - } - - private void setLoading(boolean loading) { - tagBtn.setEnabled(!loading); - if (loading) { - pb.setVisibility(ProgressBar.VISIBLE); - tagTextView.setText(R.string.inf_load); - tagImageView.setVisibility(ImageView.INVISIBLE); - } else { - pb.setVisibility(ProgressBar.GONE); - tagImageView.setVisibility(ImageView.VISIBLE); - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - tagAdapter.clear(); - for (ChannelTag t : app.getChannelTags()) { - tagAdapter.add(t); - } - - populateList(); - setCurrentTag(currentTag); - } - } - - public void onMessage(String action, final Object obj) { - if (action.equals(TVHGuideApplication.ACTION_LOADING)) { - - runOnUiThread(new Runnable() { - - public void run() { - boolean loading = (Boolean) obj; - setLoading(loading); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { - runOnUiThread(new Runnable() { - - public void run() { - chAdapter.add((Channel) obj); - chAdapter.notifyDataSetChanged(); - chAdapter.sort(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_DELETE)) { - runOnUiThread(new Runnable() { - - public void run() { - chAdapter.remove((Channel) obj); - chAdapter.notifyDataSetChanged(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Channel channel = (Channel) obj; - chAdapter.updateView(getListView(), channel); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_TAG_ADD)) { - runOnUiThread(new Runnable() { - - public void run() { - ChannelTag tag = (ChannelTag) obj; - tagAdapter.add(tag); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_TAG_DELETE)) { - runOnUiThread(new Runnable() { - - public void run() { - ChannelTag tag = (ChannelTag) obj; - tagAdapter.remove(tag); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_TAG_UPDATE)) { - //NOP - } - } - - class ChannelListAdapter extends ArrayAdapter { - - ChannelListAdapter(Activity context, List list) { - super(context, R.layout.channel_list_widget, list); - } - - public void sort() { - sort(new Comparator() { - - public int compare(Channel x, Channel y) { - return x.compareTo(y); - } - }); - } - - public void updateView(ListView listView, Channel channel) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Channel ch = (Channel) listView.getItemAtPosition(pos); - - if (view.getTag() == null || ch == null) { - continue; - } - - if (channel.id != ch.id) { - continue; - } - - ChannelListViewWrapper wrapper = (ChannelListViewWrapper) view.getTag(); - wrapper.repaint(channel); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - ChannelListViewWrapper wrapper; - - Channel ch = getItem(position); - Activity activity = (Activity) getContext(); - - if (row == null) { - LayoutInflater inflater = activity.getLayoutInflater(); - row = inflater.inflate(R.layout.channel_list_widget, null, false); - row.requestLayout(); - wrapper = new ChannelListViewWrapper(row); - row.setTag(wrapper); - - } else { - wrapper = (ChannelListViewWrapper) row.getTag(); - } - - wrapper.repaint(ch); - return row; - } - } + private ChannelListAdapter chAdapter; + ArrayAdapter tagAdapter; + private AlertDialog tagDialog; + private TextView tagTextView; + private ImageView tagImageView; + private View tagBtn; + private ProgressBar pb; + private ChannelTag currentTag; + + @Override + public void onCreate(Bundle icicle) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(icicle); + + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + chAdapter = new ChannelListAdapter(this, new ArrayList()); + setListAdapter(chAdapter); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.channel_list_title); + tagTextView = (TextView) findViewById(R.id.ct_title); + tagImageView = (ImageView) findViewById(R.id.ct_logo); + + pb = (ProgressBar) findViewById(R.id.ct_loading); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.menu_tags); + + tagAdapter = new ArrayAdapter(this, + android.R.layout.simple_dropdown_item_1line, + new ArrayList()); + + builder.setAdapter(tagAdapter, + new android.content.DialogInterface.OnClickListener() { + + public void onClick(DialogInterface arg0, int pos) { + setCurrentTag(tagAdapter.getItem(pos)); + populateList(); + } + }); + + tagDialog = builder.create(); + tagBtn = findViewById(R.id.ct_btn); + tagBtn.setOnClickListener(new android.view.View.OnClickListener() { + + public void onClick(View arg0) { + tagDialog.show(); + } + }); + + registerForContextMenu(getListView()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_menu, menu); + return true; + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.ch_play: { + startActivity(item.getIntent()); + return true; + } + case R.string.search_hint: { + startSearch(null, false, item.getIntent().getExtras(), false); + return true; + } + default: { + return false; + } + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + MenuItem item = menu.add(ContextMenu.NONE, R.string.ch_play, + ContextMenu.NONE, R.string.ch_play); + + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Channel ch = chAdapter.getItem(info.position); + + menu.setHeaderTitle(ch.name); + Intent intent = new Intent(this, PlaybackActivity.class); + intent.putExtra("channelId", ch.id); + item.setIntent(intent); + + item = menu.add(ContextMenu.NONE, R.string.search_hint, + ContextMenu.NONE, R.string.search_hint); + intent = new Intent(); + intent.putExtra("channelId", ch.id); + item.setIntent(intent); + } + + void connect(boolean force) { + if (force) { + chAdapter.clear(); + } + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + String hostname = prefs.getString("serverHostPref", "mediaserver"); + int port = Integer.parseInt(prefs.getString("serverPortPref", "9982")); + String username = prefs.getString("usernamePref", ""); + String password = prefs.getString("passwordPref", ""); + + Intent intent = new Intent(ChannelListActivity.this, HTSService.class); + intent.setAction(HTSService.ACTION_CONNECT); + intent.putExtra("hostname", hostname); + intent.putExtra("port", port); + intent.putExtra("username", username); + intent.putExtra("password", password); + intent.putExtra("force", force); + + startService(intent); + } + + private void setCurrentTag(ChannelTag t) { + currentTag = t; + + if (t == null) { + tagTextView.setText(R.string.pr_all_channels); + tagImageView.setImageResource(R.drawable.logo_72); + } else { + tagTextView.setText(currentTag.name); + if (currentTag.iconBitmap != null) { + tagImageView.setImageBitmap(currentTag.iconBitmap); + } else { + tagImageView.setImageResource(R.drawable.logo_72); + } + } + } + + private void populateList() { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + + chAdapter.clear(); + + for (Channel ch : app.getChannels()) { + if (currentTag == null || ch.hasTag(currentTag.id)) { + chAdapter.add(ch); + } + } + + chAdapter.sort(); + chAdapter.notifyDataSetChanged(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mi_settings: { + Intent intent = new Intent(getBaseContext(), SettingsActivity.class); + startActivityForResult(intent, R.id.mi_settings); + return true; + } + case R.id.mi_refresh: { + connect(true); + return true; + } + case R.id.mi_recordings: { + Intent intent = new Intent(getBaseContext(), + RecordingListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_epg_list: { + Intent intent = new Intent(getBaseContext(), + EPGTimeListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_search: { + onSearchRequested(); + return true; + } + case R.id.mi_tags: { + tagDialog.show(); + return true; + } + default: { + return super.onOptionsItemSelected(item); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addListener(this); + + connect(false); + setLoading(app.isLoading()); + } + + @Override + protected void onPause() { + super.onPause(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeListener(this); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Channel ch = (Channel) chAdapter.getItem(position); + + if (ch.epg.isEmpty()) { + return; + } + + Intent intent = new Intent(getBaseContext(), + ProgrammeListActivity.class); + intent.putExtra("channelId", ch.id); + startActivity(intent); + } + + private void setLoading(boolean loading) { + tagBtn.setEnabled(!loading); + if (loading) { + pb.setVisibility(ProgressBar.VISIBLE); + tagTextView.setText(R.string.inf_load); + tagImageView.setVisibility(ImageView.INVISIBLE); + } else { + pb.setVisibility(ProgressBar.GONE); + tagImageView.setVisibility(ImageView.VISIBLE); + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + tagAdapter.clear(); + for (ChannelTag t : app.getChannelTags()) { + tagAdapter.add(t); + } + + populateList(); + setCurrentTag(currentTag); + } + } + + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_LOADING)) { + + runOnUiThread(new Runnable() { + + public void run() { + boolean loading = (Boolean) obj; + setLoading(loading); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + chAdapter.add((Channel) obj); + chAdapter.notifyDataSetChanged(); + chAdapter.sort(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + chAdapter.remove((Channel) obj); + chAdapter.notifyDataSetChanged(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Channel channel = (Channel) obj; + chAdapter.updateView(getListView(), channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + ChannelTag tag = (ChannelTag) obj; + tagAdapter.add(tag); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + ChannelTag tag = (ChannelTag) obj; + tagAdapter.remove(tag); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_UPDATE)) { + // NOP + } + } + + class ChannelListAdapter extends ArrayAdapter { + + ChannelListAdapter(Activity context, List list) { + super(context, R.layout.channel_list_widget, list); + } + + public void sort() { + sort(new Comparator() { + + public int compare(Channel x, Channel y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Channel channel) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Channel ch = (Channel) listView.getItemAtPosition(pos); + + if (view.getTag() == null || ch == null) { + continue; + } + + if (channel.id != ch.id) { + continue; + } + + ChannelListViewWrapper wrapper = (ChannelListViewWrapper) view + .getTag(); + wrapper.repaint(channel); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + ChannelListViewWrapper wrapper; + + Channel ch = getItem(position); + Activity activity = (Activity) getContext(); + + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate(R.layout.channel_list_widget, null, + false); + row.requestLayout(); + wrapper = new ChannelListViewWrapper(row); + row.setTag(wrapper); + + } else { + wrapper = (ChannelListViewWrapper) row.getTag(); + } + + wrapper.repaint(ch); + return row; + } + } } diff --git a/src/org/tvheadend/tvhguide/EPGNowListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java similarity index 63% rename from src/org/tvheadend/tvhguide/EPGNowListActivity.java rename to src/org/tvheadend/tvhguide/EPGTimeListActivity.java index cbf675d..9b9ca7d 100644 --- a/src/org/tvheadend/tvhguide/EPGNowListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -1,10 +1,14 @@ package org.tvheadend.tvhguide; -import java.util.ArrayList; +import java.sql.Time; +import java.text.ParseException; import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Set; -import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.Channel; import android.app.ActionBar; import android.app.Activity; @@ -14,6 +18,7 @@ import android.preference.PreferenceManager; import android.support.v4.app.FragmentActivity; import android.support.v4.app.ListFragment; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; @@ -22,7 +27,13 @@ import android.widget.ArrayAdapter; import android.widget.ListView; -public class EPGNowListActivity extends FragmentActivity implements +/** + * + * @author mike.toggweiler time based epg list. Show next starting programm by + * channel + * + */ +public class EPGTimeListActivity extends FragmentActivity implements ActionBar.TabListener { /** @@ -31,6 +42,7 @@ public class EPGNowListActivity extends FragmentActivity implements */ private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item"; + @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager @@ -39,19 +51,23 @@ protected void onCreate(Bundle savedInstanceState) { setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); super.onCreate(savedInstanceState); - setContentView(R.layout.epgnow_list_widget); + setContentView(R.layout.epgnow_list_activity); // Set up the action bar to show tabs. final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - // For each of the sections in the app, add a tab to the action bar. - actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_now) - .setTabListener(this)); - actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_next) - .setTabListener(this)); - actionBar.addTab(actionBar.newTab().setText(R.string.title_tab_2000) - .setTabListener(this)); + // For each timeslot create tab + HashSet defaults = new HashSet(); + java.text.DateFormat format = DateFormat.getTimeFormat(this); + defaults.add(format.format(new Time(12, 0, 0))); + defaults.add(format.format(new Time(16, 0, 0))); + defaults.add(format.format(new Time(20, 0, 0))); + Set timeslots = prefs.getStringSet("epg.timeslots", defaults); + for (String timeslot : timeslots) { + actionBar.addTab(actionBar.newTab().setText(timeslot) + .setTabListener(this)); + } getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.epgnow_list_title); @@ -87,7 +103,7 @@ public void onTabSelected(ActionBar.Tab tab, // container view. EPGListFragment fragment = new EPGListFragment(); Bundle args = new Bundle(); - args.putInt(EPGListFragment.ARG_SECTION_NUMBER, tab.getPosition() + 1); + args.putString(EPGListFragment.ARG_TIME_SLOT, tab.getText().toString()); fragment.setArguments(args); getSupportFragmentManager().beginTransaction() .replace(R.id.container, fragment).commit(); @@ -112,9 +128,9 @@ public static class EPGListFragment extends ListFragment { * The fragment argument representing the section number for this * fragment. */ - public static final String ARG_SECTION_NUMBER = "section_number"; + public static final String ARG_TIME_SLOT = "time_slot"; - private ProgrammeListAdapter prAdapter; + private EPGTimeListAdapter prAdapter; public EPGListFragment() { } @@ -123,10 +139,23 @@ public EPGListFragment() { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - List prList = new ArrayList(); - prAdapter = new ProgrammeListAdapter(getActivity(), prList); - prAdapter.sort(); - setListAdapter(prAdapter); + String timeSlot = getArguments().getString(ARG_TIME_SLOT); + java.text.DateFormat format = DateFormat + .getTimeFormat(getActivity()); + Date date; + try { + date = format.parse(timeSlot); + + TVHGuideApplication thv = (TVHGuideApplication) getActivity() + .getApplication(); + List list = thv.getChannels(); + prAdapter = new EPGTimeListAdapter(getActivity(), list, date); + prAdapter.sort(); + setListAdapter(prAdapter); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } // @Override @@ -144,65 +173,70 @@ public void onCreate(Bundle savedInstanceState) { // } } - static class ProgrammeListAdapter extends ArrayAdapter { + static class EPGTimeListAdapter extends ArrayAdapter { Activity context; - List list; + List list; + Date timeSlot; - ProgrammeListAdapter(Activity context, List list) { + EPGTimeListAdapter(Activity context, List list, Date timeSlot) { super(context, R.layout.epgnow_list_widget, list); this.context = context; this.list = list; + this.timeSlot = timeSlot; } public void sort() { - sort(new Comparator() { + sort(new Comparator() { - public int compare(Programme x, Programme y) { + public int compare(Channel x, Channel y) { return x.compareTo(y); } }); } - public void updateView(ListView listView, Programme programme) { + public void updateView(ListView listView, Channel channel) { for (int i = 0; i < listView.getChildCount(); i++) { View view = listView.getChildAt(i); int pos = listView.getPositionForView(view); - Programme pr = (Programme) listView.getItemAtPosition(pos); + Channel pr = (Channel) listView.getItemAtPosition(pos); if (view.getTag() == null || pr == null) { continue; } - if (programme.id != pr.id) { + if (channel.id != pr.id) { continue; } - // ViewWarpper wrapper = (ViewWarpper) view.getTag(); - // wrapper.repaint(programme); + EPGTimeListViewWrapper wrapper = (EPGTimeListViewWrapper) view + .getTag(); + wrapper.repaint(channel); } } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; - // ViewWarpper wrapper = null; + EPGTimeListViewWrapper wrapper = null; if (row == null) { LayoutInflater inflater = context.getLayoutInflater(); row = inflater .inflate(R.layout.epgnow_list_widget, null, false); - // wrapper = new ViewWarpper(row); - // row.setTag(wrapper); + wrapper = new EPGTimeListViewWrapper(row, timeSlot); + row.setTag(wrapper); } else { - // wrapper = (ViewWarpper) row.getTag(); + wrapper = (EPGTimeListViewWrapper) row.getTag(); } - Programme p = getItem(position); - // wrapper.repaint(p); + Channel channel = getItem(position); + wrapper.repaint(channel); return row; } + } + } diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java new file mode 100644 index 0000000..e5f6f16 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -0,0 +1,65 @@ +package org.tvheadend.tvhguide; + +import java.util.Date; +import java.util.Iterator; + +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; + +import android.content.SharedPreferences; +import android.graphics.drawable.BitmapDrawable; +import android.preference.PreferenceManager; +import android.view.View; +import android.widget.ImageView; + +public class EPGTimeListViewWrapper extends ProgrammeListViewWrapper { + + private final Date timeSlot; + private ImageView icon; + + public EPGTimeListViewWrapper(View base, Date timeSlot) { + super(base); + + icon = (ImageView) base.findViewById(R.id.ch_icon); + + this.timeSlot = timeSlot; + } + + @SuppressWarnings("deprecation") + public void repaint(Channel channel) { + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(icon.getContext()); + Boolean showIcons = prefs.getBoolean("showIconPref", false); + icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); + icon.invalidate(); + + Programme pr = getNextProgramme(channel); + if (pr == null) { + title.setText(R.string.ch_no_transmission); + } else { + super.repaint(pr); + } + } + + /** + * get next program based on channel and timeslot + * + * @return + */ + protected Programme getNextProgramme(Channel channel) { + Iterator it = channel.epg.iterator(); + if (!channel.isTransmitting) { + return null; + } + // find first programm after timeslot + while (it.hasNext()) { + Programme pr = it.next(); + if (pr.start.after(timeSlot)) { + return pr; + } + } + return null; + } +} diff --git a/src/org/tvheadend/tvhguide/ProgrammeActivity.java b/src/org/tvheadend/tvhguide/ProgrammeActivity.java index 90a983d..3e1f4f2 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeActivity.java @@ -18,6 +18,14 @@ */ package org.tvheadend.tvhguide; +import org.tvheadend.tvhguide.R.string; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.intent.SearchEPGIntent; +import org.tvheadend.tvhguide.intent.SearchIMDbIntent; +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.SeriesInfo; + import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; @@ -33,135 +41,126 @@ import android.widget.RatingBar; import android.widget.TextView; -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.R.string; -import org.tvheadend.tvhguide.htsp.HTSService; -import org.tvheadend.tvhguide.intent.SearchEPGIntent; -import org.tvheadend.tvhguide.intent.SearchIMDbIntent; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.Programme; -import org.tvheadend.tvhguide.model.SeriesInfo; - /** - * + * * @author john-tornblom */ public class ProgrammeActivity extends Activity { - private Programme programme; - - @Override - public void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - - super.onCreate(savedInstanceState); - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel channel = app.getChannel(getIntent().getLongExtra("channelId", 0)); - if (channel == null) { - finish(); - return; - } - - long eventId = getIntent().getLongExtra("eventId", 0); - for (Programme p : channel.epg) { - if (p.id == eventId) { - programme = p; - break; - } - } - - if (programme == null) { - finish(); - return; - } - - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - setContentView(R.layout.programme_layout); - - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.programme_title); - TextView t = (TextView) findViewById(R.id.ct_title); - t.setText(channel.name); - - if (channel.iconBitmap != null) { - ImageView iv = (ImageView) findViewById(R.id.ct_logo); - iv.setImageBitmap(channel.iconBitmap); - } - - - TextView text = (TextView) findViewById(R.id.pr_title); - text.setText(programme.title); - - text = (TextView) findViewById(R.id.pr_channel); - text.setText(channel.name); - - text = (TextView) findViewById(R.id.pr_airing); - text.setText( - DateFormat.getLongDateFormat(text.getContext()).format(programme.start) - + " " - + DateFormat.getTimeFormat(text.getContext()).format(programme.start) - + " - " - + DateFormat.getTimeFormat(text.getContext()).format(programme.stop)); - - - if(programme.summary.length() == 0 && programme.description.length() == 0) { - View v = findViewById(R.id.pr_summay_and_desc_layout); - v.setVisibility(View.GONE); - } else { - text = (TextView) findViewById(R.id.pr_summary); - text.setText(programme.summary); - if(programme.summary.length() == 0) - text.setVisibility(View.GONE); - - text = (TextView) findViewById(R.id.pr_desc); - text.setText(programme.description); - if(programme.description.length() == 0) - text.setVisibility(View.GONE); - } - - String s = buildSeriesInfoString(programme.seriesInfo); - if(s.length() > 0) { - text = (TextView) findViewById(R.id.pr_series_info); - text.setText(s); - } else { - View v = findViewById(R.id.pr_series_info_row); - v.setVisibility(View.GONE); - v = findViewById(R.id.pr_series_info_sep); - v.setVisibility(View.GONE); - } - - SparseArray contentTypes = TVHGuideApplication.getContentTypes(this); - s = contentTypes.get(programme.contentType, ""); - if(s.length() > 0) { - text = (TextView) findViewById(R.id.pr_content_type); - text.setText(s); - } else { - View v = findViewById(R.id.pr_content_type_row); - v.setVisibility(View.GONE); - v = findViewById(R.id.pr_content_type_sep); - v.setVisibility(View.GONE); - } - - if(programme.starRating > 0) { - RatingBar starRating = (RatingBar)findViewById(R.id.pr_star_rating); - starRating.setRating((float)programme.starRating / 10.0f); - - text = (TextView) findViewById(R.id.pr_star_rating_txt); - text.setText("(" - + programme.starRating - + "/" - + 100 - + ")"); - } else { - View v = findViewById(R.id.pr_star_rating_row); - v.setVisibility(View.GONE); - } - } - - + private Programme programme; + + @Override + public void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(savedInstanceState); + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel channel = app.getChannel(getIntent().getLongExtra("channelId", + 0)); + if (channel == null) { + finish(); + return; + } + + long eventId = getIntent().getLongExtra("eventId", 0); + for (Programme p : channel.epg) { + if (p.id == eventId) { + programme = p; + break; + } + } + + if (programme == null) { + finish(); + return; + } + + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + setContentView(R.layout.programme_layout); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.programme_title); + TextView t = (TextView) findViewById(R.id.ct_title); + t.setText(channel.name); + + if (channel.iconBitmap != null) { + ImageView iv = (ImageView) findViewById(R.id.ct_logo); + iv.setImageBitmap(channel.iconBitmap); + } + + TextView text = (TextView) findViewById(R.id.pr_title); + text.setText(programme.title); + + text = (TextView) findViewById(R.id.pr_channel); + text.setText(channel.name); + + text = (TextView) findViewById(R.id.pr_airing); + text.setText(DateFormat.getLongDateFormat(text.getContext()).format( + programme.start) + + " " + + DateFormat.getTimeFormat(text.getContext()).format( + programme.start) + + " - " + + DateFormat.getTimeFormat(text.getContext()).format( + programme.stop)); + + if (programme.summary.length() == 0 + && programme.description.length() == 0) { + View v = findViewById(R.id.pr_summay_and_desc_layout); + v.setVisibility(View.GONE); + } else { + text = (TextView) findViewById(R.id.pr_summary); + text.setText(programme.summary); + if (programme.summary.length() == 0) + text.setVisibility(View.GONE); + + text = (TextView) findViewById(R.id.pr_desc); + text.setText(programme.description); + if (programme.description.length() == 0) + text.setVisibility(View.GONE); + } + + String s = buildSeriesInfoString(programme.seriesInfo); + if (s.length() > 0) { + text = (TextView) findViewById(R.id.pr_series_info); + text.setText(s); + } else { + View v = findViewById(R.id.pr_series_info_row); + v.setVisibility(View.GONE); + v = findViewById(R.id.pr_series_info_sep); + v.setVisibility(View.GONE); + } + + SparseArray contentTypes = TVHGuideApplication + .getContentTypes(this.getResources()); + s = contentTypes.get(programme.contentType, ""); + if (s.length() > 0) { + text = (TextView) findViewById(R.id.pr_content_type); + text.setText(s); + } else { + View v = findViewById(R.id.pr_content_type_row); + v.setVisibility(View.GONE); + v = findViewById(R.id.pr_content_type_sep); + v.setVisibility(View.GONE); + } + + if (programme.starRating > 0) { + RatingBar starRating = (RatingBar) findViewById(R.id.pr_star_rating); + starRating.setRating((float) programme.starRating / 10.0f); + + text = (TextView) findViewById(R.id.pr_star_rating_txt); + text.setText("(" + programme.starRating + "/" + 100 + ")"); + } else { + View v = findViewById(R.id.pr_star_rating_row); + v.setVisibility(View.GONE); + } + } + public String buildSeriesInfoString(SeriesInfo info) { if (info.onScreen != null && info.onScreen.length() > 0) return info.onScreen; @@ -170,108 +169,114 @@ public String buildSeriesInfoString(SeriesInfo info) { String season = this.getResources().getString(string.pr_season); String episode = this.getResources().getString(string.pr_episode); String part = this.getResources().getString(string.pr_part); - - if(info.onScreen.length() > 0) { + + if (info.onScreen.length() > 0) { return info.onScreen; } - + if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), info.seasonNumber); - if(info.seasonCount > 0) + s += String.format("%s %02d", season.toLowerCase(), + info.seasonNumber); + if (info.seasonCount > 0) s += String.format("/%02d", info.seasonCount); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), info.episodeNumber); - if(info.episodeCount > 0) + s += String.format("%s %02d", episode.toLowerCase(), + info.episodeNumber); + if (info.episodeCount > 0) s += String.format("/%02d", info.episodeCount); } if (info.partNumber > 0) { if (s.length() > 0) s += ", "; s += String.format("%s %d", part.toLowerCase(), info.partNumber); - if(info.partCount > 0) + if (info.partCount > 0) s += String.format("/%02d", info.partCount); } - if(s.length() > 0) { - s = s.substring(0,1).toUpperCase() + s.substring(1); + if (s.length() > 0) { + s = s.substring(0, 1).toUpperCase() + s.substring(1); } - + return s; } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuItem item = null; - - if (programme.title != null) { - item = menu.add(Menu.NONE, android.R.string.search_go, Menu.NONE, android.R.string.search_go); - item.setIntent(new SearchEPGIntent(this, programme.title)); - item.setIcon(android.R.drawable.ic_menu_search); - - item = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, "IMDb"); - item.setIntent(new SearchIMDbIntent(this, programme.title)); - item.setIcon(android.R.drawable.ic_menu_info_details); - } - - Intent intent = new Intent(this, HTSService.class); - - if (programme.recording == null) { - intent.setAction(HTSService.ACTION_DVR_ADD); - intent.putExtra("eventId", programme.id); - intent.putExtra("channelId", programme.channel.id); - item = menu.add(Menu.NONE, R.string.menu_record, Menu.NONE, R.string.menu_record); - item.setIcon(android.R.drawable.ic_menu_save); - } else if (programme.isRecording() || programme.isScheduled()) { - intent.setAction(HTSService.ACTION_DVR_CANCEL); - intent.putExtra("id", programme.recording.id); - item = menu.add(Menu.NONE, R.string.menu_record_cancel, Menu.NONE, R.string.menu_record_cancel); - item.setIcon(android.R.drawable.ic_menu_close_clear_cancel); - } else { - intent.setAction(HTSService.ACTION_DVR_DELETE); - intent.putExtra("id", programme.recording.id); - item = menu.add(Menu.NONE, R.string.menu_record_remove, Menu.NONE, R.string.menu_record_remove); - item.setIcon(android.R.drawable.ic_menu_delete); - } - - item.setIntent(intent); - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - boolean rebuild = false; - if (programme.recording == null) { - rebuild = menu.findItem(R.string.menu_record) == null; - } else if (programme.isRecording() || programme.isScheduled()) { - rebuild = menu.findItem(R.string.menu_record_cancel) == null; - } else { - rebuild = menu.findItem(R.string.menu_record_remove) == null; - } - - if (rebuild) { - menu.clear(); - return onCreateOptionsMenu(menu); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.string.menu_record_remove: - case R.string.menu_record_cancel: - case R.string.menu_record: - startService(item.getIntent()); - return true; - default: - return super.onOptionsItemSelected(item); - } - } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuItem item = null; + + if (programme.title != null) { + item = menu.add(Menu.NONE, android.R.string.search_go, Menu.NONE, + android.R.string.search_go); + item.setIntent(new SearchEPGIntent(this, programme.title)); + item.setIcon(android.R.drawable.ic_menu_search); + + item = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, "IMDb"); + item.setIntent(new SearchIMDbIntent(this, programme.title)); + item.setIcon(android.R.drawable.ic_menu_info_details); + } + + Intent intent = new Intent(this, HTSService.class); + + if (programme.recording == null) { + intent.setAction(HTSService.ACTION_DVR_ADD); + intent.putExtra("eventId", programme.id); + intent.putExtra("channelId", programme.channel.id); + item = menu.add(Menu.NONE, R.string.menu_record, Menu.NONE, + R.string.menu_record); + item.setIcon(android.R.drawable.ic_menu_save); + } else if (programme.isRecording() || programme.isScheduled()) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + intent.putExtra("id", programme.recording.id); + item = menu.add(Menu.NONE, R.string.menu_record_cancel, Menu.NONE, + R.string.menu_record_cancel); + item.setIcon(android.R.drawable.ic_menu_close_clear_cancel); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + intent.putExtra("id", programme.recording.id); + item = menu.add(Menu.NONE, R.string.menu_record_remove, Menu.NONE, + R.string.menu_record_remove); + item.setIcon(android.R.drawable.ic_menu_delete); + } + + item.setIntent(intent); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean rebuild = false; + if (programme.recording == null) { + rebuild = menu.findItem(R.string.menu_record) == null; + } else if (programme.isRecording() || programme.isScheduled()) { + rebuild = menu.findItem(R.string.menu_record_cancel) == null; + } else { + rebuild = menu.findItem(R.string.menu_record_remove) == null; + } + + if (rebuild) { + menu.clear(); + return onCreateOptionsMenu(menu); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record_remove: + case R.string.menu_record_cancel: + case R.string.menu_record: + startService(item.getIntent()); + return true; + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java index 73a1457..2b4abf3 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java @@ -18,14 +18,27 @@ */ package org.tvheadend.tvhguide; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.tvheadend.tvhguide.R.string; +import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.intent.SearchEPGIntent; +import org.tvheadend.tvhguide.intent.SearchIMDbIntent; +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.Recording; +import org.tvheadend.tvhguide.model.SeriesInfo; + import android.app.Activity; import android.app.ListActivity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import android.text.format.DateFormat; -import android.text.format.DateUtils; import android.util.SparseArray; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -43,277 +56,272 @@ import android.widget.ListView; import android.widget.TextView; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.R.string; -import org.tvheadend.tvhguide.htsp.HTSListener; -import org.tvheadend.tvhguide.htsp.HTSService; -import org.tvheadend.tvhguide.intent.SearchEPGIntent; -import org.tvheadend.tvhguide.intent.SearchIMDbIntent; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.Programme; -import org.tvheadend.tvhguide.model.Recording; -import org.tvheadend.tvhguide.model.SeriesInfo; - /** - * + * * @author john-tornblom */ public class ProgrammeListActivity extends ListActivity implements HTSListener { - private ProgrammeListAdapter prAdapter; - private Channel channel; - private SparseArray contentTypes; - - @Override - public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - - super.onCreate(icicle); - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - channel = app.getChannel(getIntent().getLongExtra("channelId", 0)); - - if (channel == null) { - finish(); - return; - } - - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - Button btn = new Button(this); - btn.setText(R.string.pr_get_more); - btn.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - Programme p = null; - - Iterator it = channel.epg.iterator(); - long nextId = 0; - - while (it.hasNext()) { - p = it.next(); - if (p.id != nextId && nextId != 0) { - break; - } - nextId = p.nextId; - } - if(p == null) - return; - - if (nextId == 0) { - nextId = p.nextId; - } - if (nextId == 0) { - nextId = p.id; - } - Intent intent = new Intent(ProgrammeListActivity.this, HTSService.class); - intent.setAction(HTSService.ACTION_GET_EVENTS); - intent.putExtra("eventId", nextId); - intent.putExtra("channelId", channel.id); - intent.putExtra("count", 10); - startService(intent); - } - }); - - getListView().addFooterView(btn); - - List prList = new ArrayList(); - prList.addAll(channel.epg); - prAdapter = new ProgrammeListAdapter(this, prList); - prAdapter.sort(); - setListAdapter(prAdapter); - - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.programme_list_title); - TextView t = (TextView) findViewById(R.id.ct_title); - t.setText(channel.name); - - if (channel.iconBitmap != null) { - ImageView iv = (ImageView) findViewById(R.id.ct_logo); - iv.setImageBitmap(channel.iconBitmap); - } - - View v = findViewById(R.id.ct_btn); - v.setOnClickListener(new android.view.View.OnClickListener() { - - public void onClick(View arg0) { - Intent intent = new Intent(ProgrammeListActivity.this, PlaybackActivity.class); - intent.putExtra("channelId", channel.id); - startActivity(intent); - } - }); - - registerForContextMenu(getListView()); - contentTypes = TVHGuideApplication.getContentTypes(this); - } - - @Override - protected void onResume() { - super.onResume(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addListener(this); - } - - @Override - protected void onPause() { - super.onPause(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeListener(this); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Programme p = (Programme) prAdapter.getItem(position); - - Intent intent = new Intent(this, ProgrammeActivity.class); - intent.putExtra("eventId", p.id); - intent.putExtra("channelId", p.channel.id); - startActivity(intent); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.string.menu_record: - case R.string.menu_record_cancel: - case R.string.menu_record_remove: { - startService(item.getIntent()); - return true; - } - default: { - return super.onContextItemSelected(item); - } - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Programme p = prAdapter.getItem(info.position); - - menu.setHeaderTitle(p.title); - - Intent intent = new Intent(this, HTSService.class); - - MenuItem item = null; - - if (p.recording == null) { - intent.setAction(HTSService.ACTION_DVR_ADD); - intent.putExtra("eventId", p.id); - intent.putExtra("channelId", p.channel.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record, ContextMenu.NONE, R.string.menu_record); - } else if (p.isRecording() || p.isScheduled()) { - intent.setAction(HTSService.ACTION_DVR_CANCEL); - intent.putExtra("id", p.recording.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, ContextMenu.NONE, R.string.menu_record_cancel); - } else { - intent.setAction(HTSService.ACTION_DVR_DELETE); - intent.putExtra("id", p.recording.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, ContextMenu.NONE, R.string.menu_record_remove); - } - - item.setIntent(intent); - - item = menu.add(ContextMenu.NONE, R.string.search_hint, ContextMenu.NONE, R.string.search_hint); - item.setIntent(new SearchEPGIntent(this, p.title)); - - item = menu.add(ContextMenu.NONE, ContextMenu.NONE, ContextMenu.NONE, "IMDb"); - item.setIntent(new SearchIMDbIntent(this, p.title)); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuItem item = null; - Intent intent = null; - - item = menu.add(Menu.NONE, android.R.string.search_go, Menu.NONE, android.R.string.search_go); - item.setIcon(android.R.drawable.ic_menu_search); - - intent = new Intent(ProgrammeListActivity.this, PlaybackActivity.class); - intent.putExtra("channelId", channel.id); - - item = menu.add(Menu.NONE, R.string.ch_play, Menu.NONE, R.string.ch_play); - item.setIcon(android.R.drawable.ic_menu_view); - item.setIntent(intent); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.string.search_go: { - onSearchRequested(); - return true; - } - default: { - return super.onOptionsItemSelected(item); - } - } - } - - @Override - public boolean onSearchRequested() { - Bundle bundle = new Bundle(); - bundle.putLong("channelId", channel.id); - startSearch(null, false, bundle, false); - return true; - } - - public void onMessage(String action, final Object obj) { - if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - if (channel != null && p.channel.id == channel.id) { - prAdapter.add(p); - prAdapter.notifyDataSetChanged(); - prAdapter.sort(); - } - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - prAdapter.remove(p); - prAdapter.notifyDataSetChanged(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - prAdapter.updateView(getListView(), p); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Recording rec = (Recording) obj; - for (Programme p : prAdapter.list) { - if (rec == p.recording) { - prAdapter.updateView(getListView(), p); - return; - } - } - } - }); - } - } + private ProgrammeListAdapter prAdapter; + private Channel channel; + private SparseArray contentTypes; + + @Override + public void onCreate(Bundle icicle) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(icicle); + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + channel = app.getChannel(getIntent().getLongExtra("channelId", 0)); + + if (channel == null) { + finish(); + return; + } + + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + Button btn = new Button(this); + btn.setText(R.string.pr_get_more); + btn.setOnClickListener(new OnClickListener() { + + public void onClick(View view) { + Programme p = null; + + Iterator it = channel.epg.iterator(); + long nextId = 0; + + while (it.hasNext()) { + p = it.next(); + if (p.id != nextId && nextId != 0) { + break; + } + nextId = p.nextId; + } + if (p == null) + return; + + if (nextId == 0) { + nextId = p.nextId; + } + if (nextId == 0) { + nextId = p.id; + } + Intent intent = new Intent(ProgrammeListActivity.this, + HTSService.class); + intent.setAction(HTSService.ACTION_GET_EVENTS); + intent.putExtra("eventId", nextId); + intent.putExtra("channelId", channel.id); + intent.putExtra("count", 10); + startService(intent); + } + }); + + getListView().addFooterView(btn); + + List prList = new ArrayList(); + prList.addAll(channel.epg); + prAdapter = new ProgrammeListAdapter(this, prList); + prAdapter.sort(); + setListAdapter(prAdapter); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.programme_list_title); + TextView t = (TextView) findViewById(R.id.ct_title); + t.setText(channel.name); + + if (channel.iconBitmap != null) { + ImageView iv = (ImageView) findViewById(R.id.ct_logo); + iv.setImageBitmap(channel.iconBitmap); + } + + View v = findViewById(R.id.ct_btn); + v.setOnClickListener(new android.view.View.OnClickListener() { + + public void onClick(View arg0) { + Intent intent = new Intent(ProgrammeListActivity.this, + PlaybackActivity.class); + intent.putExtra("channelId", channel.id); + startActivity(intent); + } + }); + + registerForContextMenu(getListView()); + contentTypes = TVHGuideApplication.getContentTypes(this.getResources()); + } + + @Override + protected void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeListener(this); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Programme p = (Programme) prAdapter.getItem(position); + + Intent intent = new Intent(this, ProgrammeActivity.class); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + startActivity(intent); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record: + case R.string.menu_record_cancel: + case R.string.menu_record_remove: { + startService(item.getIntent()); + return true; + } + default: { + return super.onContextItemSelected(item); + } + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Programme p = prAdapter.getItem(info.position); + + menu.setHeaderTitle(p.title); + + Intent intent = new Intent(this, HTSService.class); + + MenuItem item = null; + + if (p.recording == null) { + intent.setAction(HTSService.ACTION_DVR_ADD); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record, + ContextMenu.NONE, R.string.menu_record); + } else if (p.isRecording() || p.isScheduled()) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, + ContextMenu.NONE, R.string.menu_record_cancel); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, + ContextMenu.NONE, R.string.menu_record_remove); + } + + item.setIntent(intent); + + item = menu.add(ContextMenu.NONE, R.string.search_hint, + ContextMenu.NONE, R.string.search_hint); + item.setIntent(new SearchEPGIntent(this, p.title)); + + item = menu.add(ContextMenu.NONE, ContextMenu.NONE, ContextMenu.NONE, + "IMDb"); + item.setIntent(new SearchIMDbIntent(this, p.title)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuItem item = null; + Intent intent = null; + + item = menu.add(Menu.NONE, android.R.string.search_go, Menu.NONE, + android.R.string.search_go); + item.setIcon(android.R.drawable.ic_menu_search); + + intent = new Intent(ProgrammeListActivity.this, PlaybackActivity.class); + intent.putExtra("channelId", channel.id); + + item = menu.add(Menu.NONE, R.string.ch_play, Menu.NONE, + R.string.ch_play); + item.setIcon(android.R.drawable.ic_menu_view); + item.setIntent(intent); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.string.search_go: { + onSearchRequested(); + return true; + } + default: { + return super.onOptionsItemSelected(item); + } + } + } + + @Override + public boolean onSearchRequested() { + Bundle bundle = new Bundle(); + bundle.putLong("channelId", channel.id); + startSearch(null, false, bundle, false); + return true; + } + + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + if (channel != null && p.channel.id == channel.id) { + prAdapter.add(p); + prAdapter.notifyDataSetChanged(); + prAdapter.sort(); + } + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + prAdapter.remove(p); + prAdapter.notifyDataSetChanged(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + prAdapter.updateView(getListView(), p); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Recording rec = (Recording) obj; + for (Programme p : prAdapter.list) { + if (rec == p.recording) { + prAdapter.updateView(getListView(), p); + return; + } + } + } + }); + } + } public String buildSeriesInfoString(SeriesInfo info) { if (info.onScreen != null && info.onScreen.length() > 0) @@ -323,20 +331,22 @@ public String buildSeriesInfoString(SeriesInfo info) { String season = this.getResources().getString(string.pr_season); String episode = this.getResources().getString(string.pr_episode); String part = this.getResources().getString(string.pr_part); - - if(info.onScreen.length() > 0) { + + if (info.onScreen.length() > 0) { return info.onScreen; } - + if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), info.seasonNumber); + s += String.format("%s %02d", season.toLowerCase(), + info.seasonNumber); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), info.episodeNumber); + s += String.format("%s %02d", episode.toLowerCase(), + info.episodeNumber); } if (info.partNumber > 0) { if (s.length() > 0) @@ -344,154 +354,73 @@ public String buildSeriesInfoString(SeriesInfo info) { s += String.format("%s %d", part.toLowerCase(), info.partNumber); } - if(s.length() > 0) { - s = s.substring(0,1).toUpperCase() + s.substring(1); + if (s.length() > 0) { + s = s.substring(0, 1).toUpperCase() + s.substring(1); } - + return s; } - - private class ViewWarpper { - - TextView title; - TextView time; - TextView seriesInfo; - TextView date; - TextView description; - ImageView state; - - public ViewWarpper(View base) { - title = (TextView) base.findViewById(R.id.pr_title); - description = (TextView) base.findViewById(R.id.pr_desc); - seriesInfo = (TextView) base.findViewById(R.id.pr_series_info); - - time = (TextView) base.findViewById(R.id.pr_time); - date = (TextView) base.findViewById(R.id.pr_date); - - state = (ImageView) base.findViewById(R.id.pr_state); - } - - public void repaint(Programme p) { - title.setText(p.title); - - if (p.recording == null) { - state.setImageDrawable(null); - } else if (p.recording.error != null) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("completed".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_success_small); - } else if ("invalid".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("missed".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("recording".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_rec_small); - } else if ("scheduled".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_schedule_small); - } else { - state.setImageDrawable(null); - } - - title.invalidate(); - - String s = buildSeriesInfoString(p.seriesInfo); - if(s.length() == 0) { - s = contentTypes.get(p.contentType); - } - - seriesInfo.setText(s); - seriesInfo.invalidate(); - - if (p.description.length() > 0) { - description.setText(p.description); - description.setVisibility(TextView.VISIBLE); - } else { - description.setText(""); - description.setVisibility(TextView.GONE); - } - description.invalidate(); - - if (DateUtils.isToday(p.start.getTime())) { - date.setText(getString(R.string.today)); - } else if(p.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*2 && - p.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2) { - date.setText(DateUtils.getRelativeTimeSpanString(p.start.getTime(), - System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS)); - } else if(p.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*6 && - p.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2) { - date.setText(new SimpleDateFormat("EEEE").format(p.start.getTime())); - } else { - date.setText(DateFormat.getDateFormat(date.getContext()).format(p.start)); - } - - date.invalidate(); - - time.setText( - DateFormat.getTimeFormat(time.getContext()).format(p.start) - + " - " - + DateFormat.getTimeFormat(time.getContext()).format(p.stop)); - time.invalidate(); - } - } - - class ProgrammeListAdapter extends ArrayAdapter { - - Activity context; - List list; - - ProgrammeListAdapter(Activity context, List list) { - super(context, R.layout.programme_list_widget, list); - this.context = context; - this.list = list; - } - - public void sort() { - sort(new Comparator() { - - public int compare(Programme x, Programme y) { - return x.compareTo(y); - } - }); - } - - public void updateView(ListView listView, Programme programme) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Programme pr = (Programme) listView.getItemAtPosition(pos); - - if (view.getTag() == null || pr == null) { - continue; - } - - if (programme.id != pr.id) { - continue; - } - - ViewWarpper wrapper = (ViewWarpper) view.getTag(); - wrapper.repaint(programme); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - ViewWarpper wrapper = null; - - if (row == null) { - LayoutInflater inflater = context.getLayoutInflater(); - row = inflater.inflate(R.layout.programme_list_widget, null, false); - - wrapper = new ViewWarpper(row); - row.setTag(wrapper); - - } else { - wrapper = (ViewWarpper) row.getTag(); - } - - Programme p = getItem(position); - wrapper.repaint(p); - return row; - } - } + + class ProgrammeListAdapter extends ArrayAdapter { + + Activity context; + List list; + + ProgrammeListAdapter(Activity context, List list) { + super(context, R.layout.programme_list_widget, list); + this.context = context; + this.list = list; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Programme x, Programme y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Programme programme) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Programme pr = (Programme) listView.getItemAtPosition(pos); + + if (view.getTag() == null || pr == null) { + continue; + } + + if (programme.id != pr.id) { + continue; + } + + ProgrammeListViewWrapper wrapper = (ProgrammeListViewWrapper) view + .getTag(); + wrapper.repaint(programme); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + ProgrammeListViewWrapper wrapper = null; + + if (row == null) { + LayoutInflater inflater = context.getLayoutInflater(); + row = inflater.inflate(R.layout.programme_list_widget, null, + false); + + wrapper = new ProgrammeListViewWrapper(row); + row.setTag(wrapper); + + } else { + wrapper = (ProgrammeListViewWrapper) row.getTag(); + } + + Programme p = getItem(position); + wrapper.repaint(p); + return row; + } + } } diff --git a/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java new file mode 100644 index 0000000..d1c8b85 --- /dev/null +++ b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java @@ -0,0 +1,147 @@ +package org.tvheadend.tvhguide; + +import java.text.SimpleDateFormat; + +import org.tvheadend.tvhguide.R.string; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.SeriesInfo; + +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.util.SparseArray; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +class ProgrammeListViewWrapper { + + TextView title; + TextView time; + TextView seriesInfo; + TextView date; + TextView description; + ImageView state; + private final View base; + private SparseArray contentTypes; + + public ProgrammeListViewWrapper(View base) { + this.base = base; + title = (TextView) base.findViewById(R.id.pr_title); + description = (TextView) base.findViewById(R.id.pr_desc); + seriesInfo = (TextView) base.findViewById(R.id.pr_series_info); + + time = (TextView) base.findViewById(R.id.pr_time); + date = (TextView) base.findViewById(R.id.pr_date); + + state = (ImageView) base.findViewById(R.id.pr_state); + + contentTypes = TVHGuideApplication.getContentTypes(base.getResources()); + + } + + public void repaint(Programme p) { + title.setText(p.title); + + if (p.recording == null) { + state.setImageDrawable(null); + } else if (p.recording.error != null) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("completed".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_success_small); + } else if ("invalid".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("missed".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("recording".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_rec_small); + } else if ("scheduled".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_schedule_small); + } else { + state.setImageDrawable(null); + } + + title.invalidate(); + + String s = buildSeriesInfoString(base, p.seriesInfo); + if (s.length() == 0) { + s = contentTypes.get(p.contentType); + } + + seriesInfo.setText(s); + seriesInfo.invalidate(); + + if (p.description.length() > 0) { + description.setText(p.description); + description.setVisibility(TextView.VISIBLE); + } else { + description.setText(""); + description.setVisibility(TextView.GONE); + } + description.invalidate(); + + if (DateUtils.isToday(p.start.getTime())) { + date.setText(base.getResources().getString(R.string.today)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 * 60 + * 60 * 24 * 2 + && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 + * 60 * 24 * 2) { + date.setText(DateUtils.getRelativeTimeSpanString(p.start.getTime(), + System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 * 60 + * 60 * 24 * 6 + && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 + * 60 * 24 * 2) { + date.setText(new SimpleDateFormat("EEEE").format(p.start.getTime())); + } else { + date.setText(DateFormat.getDateFormat(date.getContext()).format( + p.start)); + } + + date.invalidate(); + + time.setText(DateFormat.getTimeFormat(time.getContext()) + .format(p.start) + + " - " + + DateFormat.getTimeFormat(time.getContext()).format(p.stop)); + time.invalidate(); + } + + public String buildSeriesInfoString(View context, SeriesInfo info) { + if (info.onScreen != null && info.onScreen.length() > 0) + return info.onScreen; + + String s = ""; + String season = context.getResources().getString(string.pr_season); + String episode = context.getResources().getString(string.pr_episode); + String part = context.getResources().getString(string.pr_part); + + if (info.onScreen.length() > 0) { + return info.onScreen; + } + + if (info.seasonNumber > 0) { + if (s.length() > 0) + s += ", "; + s += String.format("%s %02d", season.toLowerCase(), + info.seasonNumber); + } + if (info.episodeNumber > 0) { + if (s.length() > 0) + s += ", "; + s += String.format("%s %02d", episode.toLowerCase(), + info.episodeNumber); + } + if (info.partNumber > 0) { + if (s.length() > 0) + s += ", "; + s += String.format("%s %d", part.toLowerCase(), info.partNumber); + } + + if (s.length() > 0) { + s = s.substring(0, 1).toUpperCase() + s.substring(1); + } + + return s; + } + +} \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/SearchResultActivity.java b/src/org/tvheadend/tvhguide/SearchResultActivity.java index 4ab31a3..aa26abe 100644 --- a/src/org/tvheadend/tvhguide/SearchResultActivity.java +++ b/src/org/tvheadend/tvhguide/SearchResultActivity.java @@ -18,6 +18,20 @@ */ package org.tvheadend.tvhguide; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; + +import org.tvheadend.tvhguide.R.string; +import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.Recording; +import org.tvheadend.tvhguide.model.SeriesInfo; + import android.app.Activity; import android.app.ListActivity; import android.app.SearchManager; @@ -41,237 +55,229 @@ import android.widget.ListView; import android.widget.TextView; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.regex.Pattern; - -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.R.string; -import org.tvheadend.tvhguide.htsp.HTSListener; -import org.tvheadend.tvhguide.htsp.HTSService; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.Programme; -import org.tvheadend.tvhguide.model.Recording; -import org.tvheadend.tvhguide.model.SeriesInfo; - /** - * + * * @author john-tornblom */ public class SearchResultActivity extends ListActivity implements HTSListener { - private SearchResultAdapter srAdapter; - private SparseArray contentTypes; - private Pattern pattern; - private Channel channel; - - @Override - public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - - super.onCreate(icicle); - - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - registerForContextMenu(getListView()); - - contentTypes = TVHGuideApplication.getContentTypes(this); - - List srList = new ArrayList(); - srAdapter = new SearchResultAdapter(this, srList); - srAdapter.sort(); - setListAdapter(srAdapter); - - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.search_result_title); - - View v = findViewById(R.id.ct_btn); - v.setOnClickListener(new android.view.View.OnClickListener() { - - public void onClick(View arg0) { - onSearchRequested(); - } - }); - - onNewIntent(getIntent()); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - if (!Intent.ACTION_SEARCH.equals(intent.getAction()) - || !intent.hasExtra(SearchManager.QUERY)) { - return; - } - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - - Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA); - if (appData != null) { - channel = app.getChannel(appData.getLong("channelId")); - } else { - channel = null; - } - - srAdapter.clear(); - - String query = intent.getStringExtra(SearchManager.QUERY); - pattern = Pattern.compile(query, Pattern.CASE_INSENSITIVE); - intent = new Intent(SearchResultActivity.this, HTSService.class); - intent.setAction(HTSService.ACTION_EPG_QUERY); - intent.putExtra("query", query); - if (channel != null) { - intent.putExtra("channelId", channel.id); - } - - startService(intent); - - if (channel == null) { - for (Channel ch : app.getChannels()) { - for (Programme p : ch.epg) { - if (pattern.matcher(p.title).find()) { - srAdapter.add(p); - } - } - } - } else { - for (Programme p : channel.epg) { - if (pattern.matcher(p.title).find()) { - srAdapter.add(p); - } - } - } - - ImageView iv = (ImageView) findViewById(R.id.ct_logo); - if (channel != null && channel.iconBitmap != null) { - iv.setImageBitmap(channel.iconBitmap); - } else { - iv.setImageResource(R.drawable.logo_72); - } - - TextView t = (TextView) findViewById(R.id.ct_title); - t.setText(this.getString(android.R.string.search_go) + ": " + query); - } - - @Override - protected void onResume() { - super.onResume(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addListener(this); - } - - @Override - protected void onPause() { - super.onPause(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeListener(this); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Programme p = (Programme) srAdapter.getItem(position); - - Intent intent = new Intent(this, ProgrammeActivity.class); - intent.putExtra("eventId", p.id); - intent.putExtra("channelId", p.channel.id); - startActivity(intent); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.string.menu_record: - case R.string.menu_record_cancel: - case R.string.menu_record_remove: { - startService(item.getIntent()); - return true; - } - default: { - return super.onContextItemSelected(item); - } - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Programme p = srAdapter.getItem(info.position); - - menu.setHeaderTitle(p.title); - - Intent intent = new Intent(this, HTSService.class); - - MenuItem item = null; - - if (p.recording == null) { - intent.setAction(HTSService.ACTION_DVR_ADD); - intent.putExtra("eventId", p.id); - intent.putExtra("channelId", p.channel.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record, ContextMenu.NONE, R.string.menu_record); - } else if ("recording".equals(p.recording.state) || "scheduled".equals(p.recording.state)) { - intent.setAction(HTSService.ACTION_DVR_CANCEL); - intent.putExtra("id", p.recording.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, ContextMenu.NONE, R.string.menu_record_cancel); - } else { - intent.setAction(HTSService.ACTION_DVR_DELETE); - intent.putExtra("id", p.recording.id); - item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, ContextMenu.NONE, R.string.menu_record_remove); - } - - item.setIntent(intent); - } - - public void onMessage(String action, final Object obj) { - if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - if (pattern != null && pattern.matcher(p.title).find()) { - srAdapter.add(p); - srAdapter.notifyDataSetChanged(); - srAdapter.sort(); - } - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - srAdapter.remove(p); - srAdapter.notifyDataSetChanged(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Programme p = (Programme) obj; - srAdapter.updateView(getListView(), p); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Recording rec = (Recording) obj; - for (Programme p : srAdapter.list) { - if (rec == p.recording) { - srAdapter.updateView(getListView(), p); - return; - } - } - } - }); - } - } + private SearchResultAdapter srAdapter; + private SparseArray contentTypes; + private Pattern pattern; + private Channel channel; + + @Override + public void onCreate(Bundle icicle) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(icicle); + + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + registerForContextMenu(getListView()); + + contentTypes = TVHGuideApplication.getContentTypes(this.getResources()); + + List srList = new ArrayList(); + srAdapter = new SearchResultAdapter(this, srList); + srAdapter.sort(); + setListAdapter(srAdapter); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.search_result_title); + + View v = findViewById(R.id.ct_btn); + v.setOnClickListener(new android.view.View.OnClickListener() { + + public void onClick(View arg0) { + onSearchRequested(); + } + }); + + onNewIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + if (!Intent.ACTION_SEARCH.equals(intent.getAction()) + || !intent.hasExtra(SearchManager.QUERY)) { + return; + } + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + + Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA); + if (appData != null) { + channel = app.getChannel(appData.getLong("channelId")); + } else { + channel = null; + } + + srAdapter.clear(); + + String query = intent.getStringExtra(SearchManager.QUERY); + pattern = Pattern.compile(query, Pattern.CASE_INSENSITIVE); + intent = new Intent(SearchResultActivity.this, HTSService.class); + intent.setAction(HTSService.ACTION_EPG_QUERY); + intent.putExtra("query", query); + if (channel != null) { + intent.putExtra("channelId", channel.id); + } + + startService(intent); + + if (channel == null) { + for (Channel ch : app.getChannels()) { + for (Programme p : ch.epg) { + if (pattern.matcher(p.title).find()) { + srAdapter.add(p); + } + } + } + } else { + for (Programme p : channel.epg) { + if (pattern.matcher(p.title).find()) { + srAdapter.add(p); + } + } + } + + ImageView iv = (ImageView) findViewById(R.id.ct_logo); + if (channel != null && channel.iconBitmap != null) { + iv.setImageBitmap(channel.iconBitmap); + } else { + iv.setImageResource(R.drawable.logo_72); + } + + TextView t = (TextView) findViewById(R.id.ct_title); + t.setText(this.getString(android.R.string.search_go) + ": " + query); + } + + @Override + protected void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeListener(this); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Programme p = (Programme) srAdapter.getItem(position); + + Intent intent = new Intent(this, ProgrammeActivity.class); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + startActivity(intent); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record: + case R.string.menu_record_cancel: + case R.string.menu_record_remove: { + startService(item.getIntent()); + return true; + } + default: { + return super.onContextItemSelected(item); + } + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Programme p = srAdapter.getItem(info.position); + + menu.setHeaderTitle(p.title); + + Intent intent = new Intent(this, HTSService.class); + + MenuItem item = null; + + if (p.recording == null) { + intent.setAction(HTSService.ACTION_DVR_ADD); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record, + ContextMenu.NONE, R.string.menu_record); + } else if ("recording".equals(p.recording.state) + || "scheduled".equals(p.recording.state)) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, + ContextMenu.NONE, R.string.menu_record_cancel); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, + ContextMenu.NONE, R.string.menu_record_remove); + } + + item.setIntent(intent); + } + + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + if (pattern != null && pattern.matcher(p.title).find()) { + srAdapter.add(p); + srAdapter.notifyDataSetChanged(); + srAdapter.sort(); + } + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + srAdapter.remove(p); + srAdapter.notifyDataSetChanged(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + srAdapter.updateView(getListView(), p); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Recording rec = (Recording) obj; + for (Programme p : srAdapter.list) { + if (rec == p.recording) { + srAdapter.updateView(getListView(), p); + return; + } + } + } + }); + } + } public String buildSeriesInfoString(SeriesInfo info) { if (info.onScreen != null && info.onScreen.length() > 0) @@ -281,20 +287,22 @@ public String buildSeriesInfoString(SeriesInfo info) { String season = this.getResources().getString(string.pr_season); String episode = this.getResources().getString(string.pr_episode); String part = this.getResources().getString(string.pr_part); - - if(info.onScreen.length() > 0) { + + if (info.onScreen.length() > 0) { return info.onScreen; } - + if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), info.seasonNumber); + s += String.format("%s %02d", season.toLowerCase(), + info.seasonNumber); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), info.episodeNumber); + s += String.format("%s %02d", episode.toLowerCase(), + info.episodeNumber); } if (info.partNumber > 0) { if (s.length() > 0) @@ -302,163 +310,172 @@ public String buildSeriesInfoString(SeriesInfo info) { s += String.format("%s %d", part.toLowerCase(), info.partNumber); } - if(s.length() > 0) { - s = s.substring(0,1).toUpperCase() + s.substring(1); + if (s.length() > 0) { + s = s.substring(0, 1).toUpperCase() + s.substring(1); } - + return s; } - - private class ViewWarpper { - - TextView title; - TextView channel; - TextView time; - TextView date; - TextView description; - ImageView icon; - ImageView state; - - public ViewWarpper(View base) { - title = (TextView) base.findViewById(R.id.sr_title); - channel = (TextView) base.findViewById(R.id.sr_channel); - description = (TextView) base.findViewById(R.id.sr_desc); - - time = (TextView) base.findViewById(R.id.sr_time); - date = (TextView) base.findViewById(R.id.sr_date); - - icon = (ImageView) base.findViewById(R.id.sr_icon); - state = (ImageView) base.findViewById(R.id.sr_state); - } - - public void repaint(Programme p) { - Channel ch = p.channel; - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(icon.getContext()); - Boolean showIcons = prefs.getBoolean("showIconPref", false); - icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setImageBitmap(ch.iconBitmap); - - title.setText(p.title); - - if (p.recording == null) { - state.setImageDrawable(null); - } else if (p.recording.error != null) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("completed".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_success_small); - } else if ("invalid".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("missed".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_error_small); - } else if ("recording".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_rec_small); - } else if ("scheduled".equals(p.recording.state)) { - state.setImageResource(R.drawable.ic_schedule_small); - } else { - state.setImageDrawable(null); - } - - title.invalidate(); - - String s = buildSeriesInfoString(p.seriesInfo); - if(s.length() == 0) { - s = p.description; - } - - description.setText(s); - description.invalidate(); - - String contentType = contentTypes.get(p.contentType, ""); - if (contentType.length() > 0) { - channel.setText(ch.name + " (" + contentType + ")"); - } else { - channel.setText(ch.name); - } - channel.invalidate(); - - if (DateUtils.isToday(p.start.getTime())) { - date.setText(getString(R.string.today)); - } else if(p.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*2 && - p.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2) { - date.setText(DateUtils.getRelativeTimeSpanString(p.start.getTime(), - System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS)); - } else if(p.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*6 && - p.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2) { - date.setText(new SimpleDateFormat("EEEE").format(p.start.getTime())); - } else { - date.setText(DateFormat.getDateFormat(date.getContext()).format(p.start)); - } - - date.invalidate(); - - - time.setText( - DateFormat.getTimeFormat(time.getContext()).format(p.start) - + " - " - + DateFormat.getTimeFormat(time.getContext()).format(p.stop)); - time.invalidate(); - } - } - - class SearchResultAdapter extends ArrayAdapter { - - Activity context; - List list; - - SearchResultAdapter(Activity context, List list) { - super(context, R.layout.search_result_widget, list); - this.context = context; - this.list = list; - } - - public void sort() { - sort(new Comparator() { - - public int compare(Programme x, Programme y) { - return x.compareTo(y); - } - }); - } - - public void updateView(ListView listView, Programme programme) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Programme pr = (Programme) listView.getItemAtPosition(pos); - - if (view.getTag() == null || pr == null) { - continue; - } - - if (programme.id != pr.id) { - continue; - } - - ViewWarpper wrapper = (ViewWarpper) view.getTag(); - wrapper.repaint(programme); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - ViewWarpper wrapper = null; - - if (row == null) { - LayoutInflater inflater = context.getLayoutInflater(); - row = inflater.inflate(R.layout.search_result_widget, null, false); - - wrapper = new ViewWarpper(row); - row.setTag(wrapper); - - } else { - wrapper = (ViewWarpper) row.getTag(); - } - - Programme p = getItem(position); - wrapper.repaint(p); - return row; - } - } + + private class ViewWarpper { + + TextView title; + TextView channel; + TextView time; + TextView date; + TextView description; + ImageView icon; + ImageView state; + + public ViewWarpper(View base) { + title = (TextView) base.findViewById(R.id.sr_title); + channel = (TextView) base.findViewById(R.id.sr_channel); + description = (TextView) base.findViewById(R.id.sr_desc); + + time = (TextView) base.findViewById(R.id.sr_time); + date = (TextView) base.findViewById(R.id.sr_date); + + icon = (ImageView) base.findViewById(R.id.sr_icon); + state = (ImageView) base.findViewById(R.id.sr_state); + } + + public void repaint(Programme p) { + Channel ch = p.channel; + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(icon.getContext()); + Boolean showIcons = prefs.getBoolean("showIconPref", false); + icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + icon.setImageBitmap(ch.iconBitmap); + + title.setText(p.title); + + if (p.recording == null) { + state.setImageDrawable(null); + } else if (p.recording.error != null) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("completed".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_success_small); + } else if ("invalid".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("missed".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("recording".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_rec_small); + } else if ("scheduled".equals(p.recording.state)) { + state.setImageResource(R.drawable.ic_schedule_small); + } else { + state.setImageDrawable(null); + } + + title.invalidate(); + + String s = buildSeriesInfoString(p.seriesInfo); + if (s.length() == 0) { + s = p.description; + } + + description.setText(s); + description.invalidate(); + + String contentType = contentTypes.get(p.contentType, ""); + if (contentType.length() > 0) { + channel.setText(ch.name + " (" + contentType + ")"); + } else { + channel.setText(ch.name); + } + channel.invalidate(); + + if (DateUtils.isToday(p.start.getTime())) { + date.setText(getString(R.string.today)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 2 + && p.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(DateUtils.getRelativeTimeSpanString( + p.start.getTime(), System.currentTimeMillis(), + DateUtils.DAY_IN_MILLIS)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 6 + && p.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(new SimpleDateFormat("EEEE").format(p.start + .getTime())); + } else { + date.setText(DateFormat.getDateFormat(date.getContext()) + .format(p.start)); + } + + date.invalidate(); + + time.setText(DateFormat.getTimeFormat(time.getContext()).format( + p.start) + + " - " + + DateFormat.getTimeFormat(time.getContext()) + .format(p.stop)); + time.invalidate(); + } + } + + class SearchResultAdapter extends ArrayAdapter { + + Activity context; + List list; + + SearchResultAdapter(Activity context, List list) { + super(context, R.layout.search_result_widget, list); + this.context = context; + this.list = list; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Programme x, Programme y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Programme programme) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Programme pr = (Programme) listView.getItemAtPosition(pos); + + if (view.getTag() == null || pr == null) { + continue; + } + + if (programme.id != pr.id) { + continue; + } + + ViewWarpper wrapper = (ViewWarpper) view.getTag(); + wrapper.repaint(programme); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + ViewWarpper wrapper = null; + + if (row == null) { + LayoutInflater inflater = context.getLayoutInflater(); + row = inflater.inflate(R.layout.search_result_widget, null, + false); + + wrapper = new ViewWarpper(row); + row.setTag(wrapper); + + } else { + wrapper = (ViewWarpper) row.getTag(); + } + + Programme p = getItem(position); + wrapper.repaint(p); + return row; + } + } } diff --git a/src/org/tvheadend/tvhguide/SettingsActivity.java b/src/org/tvheadend/tvhguide/SettingsActivity.java index 9cea132..86c2ee2 100644 --- a/src/org/tvheadend/tvhguide/SettingsActivity.java +++ b/src/org/tvheadend/tvhguide/SettingsActivity.java @@ -18,6 +18,8 @@ */ package org.tvheadend.tvhguide; +import org.tvheadend.tvhguide.htsp.HTSService; + import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -25,66 +27,71 @@ import android.preference.PreferenceManager; import android.util.Log; import android.view.Window; -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.htsp.HTSService; /** - * + * * @author john-tornblom */ public class SettingsActivity extends PreferenceActivity { - private int oldPort; - private String oldHostname; - private String oldUser; - private String oldPw; + private int oldPort; + private String oldHostname; + private String oldUser; + private String oldPw; - @Override - public void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? android.R.style.Theme_Light : android.R.style.Theme); + @Override + public void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? android.R.style.Theme_Light : android.R.style.Theme); - requestWindowFeature(Window.FEATURE_LEFT_ICON); - super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_LEFT_ICON); + super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.preferences); - setTitle(getString(R.string.app_name) + " - " + getString(R.string.menu_settings)); - setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.logo_72); - } + addPreferencesFromResource(R.xml.preferences); + setTitle(getString(R.string.app_name) + " - " + + getString(R.string.menu_settings)); + setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.logo_72); + } - @Override - protected void onStart() { - super.onStart(); + @Override + protected void onStart() { + super.onStart(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - oldHostname = prefs.getString("serverHostPref", ""); - oldPort = Integer.parseInt(prefs.getString("serverPortPref", "")); - oldUser = prefs.getString("usernamePref", ""); - oldPw = prefs.getString("passwordPref", ""); - } + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + oldHostname = prefs.getString("serverHostPref", "mediaserver"); + oldPort = Integer.parseInt(prefs.getString("serverPortPref", "")); + oldUser = prefs.getString("usernamePref", ""); + oldPw = prefs.getString("passwordPref", ""); + } - @Override - protected void onPause() { - super.onPause(); + @Override + protected void onPause() { + super.onPause(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean reconnect = false; - reconnect |= !oldHostname.equals(prefs.getString("serverHostPref", "")); - reconnect |= oldPort != Integer.parseInt(prefs.getString("serverPortPref", "")); - reconnect |= !oldUser.equals(prefs.getString("usernamePref", "")); - reconnect |= !oldPw.equals(prefs.getString("passwordPref", "")); + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + boolean reconnect = false; + reconnect |= !oldHostname.equals(prefs.getString("serverHostPref", "")); + reconnect |= oldPort != Integer.parseInt(prefs.getString( + "serverPortPref", "")); + reconnect |= !oldUser.equals(prefs.getString("usernamePref", "")); + reconnect |= !oldPw.equals(prefs.getString("passwordPref", "")); - if (reconnect) { - Log.d("SettingsActivity", "Connectivity settings chaned, forcing a reconnect"); - Intent intent = new Intent(SettingsActivity.this, HTSService.class); - intent.setAction(HTSService.ACTION_CONNECT); - intent.putExtra("hostname", prefs.getString("serverHostPref", "")); - intent.putExtra("port", Integer.parseInt(prefs.getString("serverPortPref", ""))); - intent.putExtra("username", prefs.getString("usernamePref", "")); - intent.putExtra("password", prefs.getString("passwordPref", "")); - intent.putExtra("force", true); - startService(intent); - } - } + if (reconnect) { + Log.d("SettingsActivity", + "Connectivity settings chaned, forcing a reconnect"); + Intent intent = new Intent(SettingsActivity.this, HTSService.class); + intent.setAction(HTSService.ACTION_CONNECT); + intent.putExtra("hostname", prefs.getString("serverHostPref", "")); + intent.putExtra("port", + Integer.parseInt(prefs.getString("serverPortPref", ""))); + intent.putExtra("username", prefs.getString("usernamePref", "")); + intent.putExtra("password", prefs.getString("passwordPref", "")); + intent.putExtra("force", true); + startService(intent); + } + } } diff --git a/src/org/tvheadend/tvhguide/TVHGuideApplication.java b/src/org/tvheadend/tvhguide/TVHGuideApplication.java index fcb9a0d..25ed687 100644 --- a/src/org/tvheadend/tvhguide/TVHGuideApplication.java +++ b/src/org/tvheadend/tvhguide/TVHGuideApplication.java @@ -18,16 +18,10 @@ */ package org.tvheadend.tvhguide; -import android.app.Application; -import android.content.Context; -import android.os.Handler; -import android.util.SparseArray; -import android.widget.Toast; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.tvheadend.tvhguide.R; import org.tvheadend.tvhguide.htsp.HTSListener; import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.ChannelTag; @@ -37,377 +31,386 @@ import org.tvheadend.tvhguide.model.Recording; import org.tvheadend.tvhguide.model.Subscription; +import android.app.Application; +import android.content.res.Resources; +import android.os.Handler; +import android.util.SparseArray; +import android.widget.Toast; + /** - * + * * @author john-tornblom */ public class TVHGuideApplication extends Application { - public static final String ACTION_CHANNEL_ADD = "org.me.tvhguide.CHANNEL_ADD"; - public static final String ACTION_CHANNEL_DELETE = "org.me.tvhguide.CHANNEL_DELETE"; - public static final String ACTION_CHANNEL_UPDATE = "org.me.tvhguide.CHANNEL_UPDATE"; - public static final String ACTION_TAG_ADD = "org.me.tvhguide.TAG_ADD"; - public static final String ACTION_TAG_DELETE = "org.me.tvhguide.TAG_DELETE"; - public static final String ACTION_TAG_UPDATE = "org.me.tvhguide.TAG_UPDATE"; - public static final String ACTION_DVR_ADD = "org.me.tvhguide.DVR_ADD"; - public static final String ACTION_DVR_DELETE = "org.me.tvhguide.DVR_DELETE"; - public static final String ACTION_DVR_UPDATE = "org.me.tvhguide.DVR_UPDATE"; - public static final String ACTION_PROGRAMME_ADD = "org.me.tvhguide.PROGRAMME_ADD"; - public static final String ACTION_PROGRAMME_DELETE = "org.me.tvhguide.PROGRAMME_DELETE"; - public static final String ACTION_PROGRAMME_UPDATE = "org.me.tvhguide.PROGRAMME_UPDATE"; - public static final String ACTION_SUBSCRIPTION_ADD = "org.me.tvhguide.SUBSCRIPTION_ADD"; - public static final String ACTION_SUBSCRIPTION_DELETE = "org.me.tvhguide.SUBSCRIPTION_DELETE"; - public static final String ACTION_SUBSCRIPTION_UPDATE = "org.me.tvhguide.SUBSCRIPTION_UPDATE"; - public static final String ACTION_SIGNAL_STATUS = "org.me.tvhguide.SIGNAL_STATUS"; - public static final String ACTION_PLAYBACK_PACKET = "org.me.tvhguide.PLAYBACK_PACKET"; - public static final String ACTION_LOADING = "org.me.tvhguide.LOADING"; - public static final String ACTION_TICKET_ADD = "org.me.tvhguide.TICKET"; - public static final String ACTION_ERROR = "org.me.tvhguide.ERROR"; - private final List listeners = new ArrayList(); - private final List tags = Collections.synchronizedList(new ArrayList()); - private final List channels = Collections.synchronizedList(new ArrayList()); - private final List recordings = Collections.synchronizedList(new ArrayList()); - private final List subscriptions = Collections.synchronizedList(new ArrayList()); - private volatile boolean loading = false; - private Handler handler = new Handler(); - - public void addListener(HTSListener l) { - listeners.add(l); - } - - public void removeListener(HTSListener l) { - listeners.remove(l); - } - - private void broadcastMessage(String action, Object obj) { - synchronized (listeners) { - for (HTSListener l : listeners) { - l.onMessage(action, obj); - } - } - } - - public void broadcastError(final String error) { - //Don't show error if no views are open - synchronized (listeners) { - if (listeners.isEmpty()) { - return; - } - } - handler.post(new Runnable() { - - public void run() { - - try { - Toast toast = Toast.makeText(TVHGuideApplication.this, error, Toast.LENGTH_LONG); - toast.show(); - } catch (Throwable ex) { - } - } - }); - broadcastMessage(ACTION_ERROR, error); - } - - public void broadcastPacket(Packet p) { - broadcastMessage(ACTION_PLAYBACK_PACKET, p); - } - - public List getChannelTags() { - return tags; - } - - public void addChannelTag(ChannelTag tag) { - tags.add(tag); - - if (!loading) { - broadcastMessage(ACTION_TAG_ADD, tag); - } - } - - public void removeChannelTag(ChannelTag tag) { - tags.remove(tag); - - if (!loading) { - broadcastMessage(ACTION_TAG_DELETE, tag); - } - } - - public void removeChannelTag(long id) { - for (ChannelTag tag : getChannelTags()) { - if (tag.id == id) { - removeChannelTag(tag); - return; - } - } - } - - public ChannelTag getChannelTag(long id) { - for (ChannelTag tag : getChannelTags()) { - if (tag.id == id) { - return tag; - } - } - return null; - } - - public void updateChannelTag(ChannelTag tag) { - if (!loading) { - broadcastMessage(ACTION_TAG_UPDATE, tag); - } - } - - public void addChannel(Channel channel) { - channels.add(channel); - - if (!loading) { - broadcastMessage(ACTION_CHANNEL_ADD, channel); - } - } - - public List getChannels() { - return channels; - } - - public void removeChannel(Channel channel) { - channels.remove(channel); - - if (!loading) { - broadcastMessage(ACTION_CHANNEL_DELETE, channel); - } - } - - public Channel getChannel(long id) { - for (Channel ch : getChannels()) { - if (ch.id == id) { - return ch; - } - } - return null; - } - - public void removeChannel(long id) { - for (Channel ch : getChannels()) { - if (ch.id == id) { - removeChannel(ch); - return; - } - } - } - - public void updateChannel(Channel ch) { - if (!loading) { - broadcastMessage(ACTION_CHANNEL_UPDATE, ch); - } - } - - public void addProgramme(Programme p) { - if (!loading) { - broadcastMessage(ACTION_PROGRAMME_ADD, p); - } - } - - public void removeProgramme(Programme p) { - if (!loading) { - broadcastMessage(ACTION_PROGRAMME_DELETE, p); - } - } - - public void updateProgramme(Programme p) { - if (!loading) { - broadcastMessage(ACTION_PROGRAMME_UPDATE, p); - } - } - - public void addRecording(Recording rec) { - recordings.add(rec); - - if (!loading) { - broadcastMessage(ACTION_DVR_ADD, rec); - } - } - - public List getRecordings() { - return recordings; - } - - public void removeRecording(Recording rec) { - recordings.remove(rec); - - if (!loading) { - broadcastMessage(ACTION_DVR_DELETE, rec); - } - } - - public Recording getRecording(long id) { - for (Recording rec : getRecordings()) { - if (rec.id == id) { - return rec; - } - } - return null; - } - - public void removeRecording(long id) { - for (Recording rec : getRecordings()) { - if (rec.id == id) { - removeRecording(rec); - return; - } - } - } - - public void updateRecording(Recording rec) { - if (!loading) { - broadcastMessage(ACTION_DVR_UPDATE, rec); - } - } - - public void setLoading(boolean b) { - if (loading != b) { - broadcastMessage(ACTION_LOADING, b); - } - loading = b; - } - - public void clearAll() { - tags.clear(); - recordings.clear(); - - for (Channel ch : channels) { - ch.epg.clear(); - ch.recordings.clear(); - } - channels.clear(); - - for (Subscription s : subscriptions) { - s.streams.clear(); - } - subscriptions.clear(); - - ChannelTag tag = new ChannelTag(); - tag.id = 0; - tag.name = getString(R.string.pr_all_channels); - tags.add(tag); - } - - public void addSubscription(Subscription s) { - subscriptions.add(s); - - if (!loading) { - broadcastMessage(ACTION_SUBSCRIPTION_ADD, s); - } - } - - public List getSubscriptions() { - return subscriptions; - } - - public void removeSubscription(Subscription s) { - s.streams.clear(); - subscriptions.remove(s); - - if (!loading) { - broadcastMessage(ACTION_SUBSCRIPTION_DELETE, s); - } - } - - public Subscription getSubscription(long id) { - for (Subscription s : getSubscriptions()) { - if (s.id == id) { - return s; - } - } - return null; - } - - public void removeSubscription(long id) { - for (Subscription s : getSubscriptions()) { - if (s.id == id) { - removeSubscription(s); - return; - } - } - } - - public void updateSubscription(Subscription s) { - if (!loading) { - broadcastMessage(ACTION_SUBSCRIPTION_UPDATE, s); - } - } - - - public void addTicket(HttpTicket t) { - broadcastMessage(ACTION_TICKET_ADD, t); - } - - public boolean isLoading() { - return loading; - } - - - public static SparseArray getContentTypes(Context ctx) { + public static final String ACTION_CHANNEL_ADD = "org.me.tvhguide.CHANNEL_ADD"; + public static final String ACTION_CHANNEL_DELETE = "org.me.tvhguide.CHANNEL_DELETE"; + public static final String ACTION_CHANNEL_UPDATE = "org.me.tvhguide.CHANNEL_UPDATE"; + public static final String ACTION_TAG_ADD = "org.me.tvhguide.TAG_ADD"; + public static final String ACTION_TAG_DELETE = "org.me.tvhguide.TAG_DELETE"; + public static final String ACTION_TAG_UPDATE = "org.me.tvhguide.TAG_UPDATE"; + public static final String ACTION_DVR_ADD = "org.me.tvhguide.DVR_ADD"; + public static final String ACTION_DVR_DELETE = "org.me.tvhguide.DVR_DELETE"; + public static final String ACTION_DVR_UPDATE = "org.me.tvhguide.DVR_UPDATE"; + public static final String ACTION_PROGRAMME_ADD = "org.me.tvhguide.PROGRAMME_ADD"; + public static final String ACTION_PROGRAMME_DELETE = "org.me.tvhguide.PROGRAMME_DELETE"; + public static final String ACTION_PROGRAMME_UPDATE = "org.me.tvhguide.PROGRAMME_UPDATE"; + public static final String ACTION_SUBSCRIPTION_ADD = "org.me.tvhguide.SUBSCRIPTION_ADD"; + public static final String ACTION_SUBSCRIPTION_DELETE = "org.me.tvhguide.SUBSCRIPTION_DELETE"; + public static final String ACTION_SUBSCRIPTION_UPDATE = "org.me.tvhguide.SUBSCRIPTION_UPDATE"; + public static final String ACTION_SIGNAL_STATUS = "org.me.tvhguide.SIGNAL_STATUS"; + public static final String ACTION_PLAYBACK_PACKET = "org.me.tvhguide.PLAYBACK_PACKET"; + public static final String ACTION_LOADING = "org.me.tvhguide.LOADING"; + public static final String ACTION_TICKET_ADD = "org.me.tvhguide.TICKET"; + public static final String ACTION_ERROR = "org.me.tvhguide.ERROR"; + private final List listeners = new ArrayList(); + private final List tags = Collections + .synchronizedList(new ArrayList()); + private final List channels = Collections + .synchronizedList(new ArrayList()); + private final List recordings = Collections + .synchronizedList(new ArrayList()); + private final List subscriptions = Collections + .synchronizedList(new ArrayList()); + private volatile boolean loading = false; + private Handler handler = new Handler(); + + public void addListener(HTSListener l) { + listeners.add(l); + } + + public void removeListener(HTSListener l) { + listeners.remove(l); + } + + private void broadcastMessage(String action, Object obj) { + synchronized (listeners) { + for (HTSListener l : listeners) { + l.onMessage(action, obj); + } + } + } + + public void broadcastError(final String error) { + // Don't show error if no views are open + synchronized (listeners) { + if (listeners.isEmpty()) { + return; + } + } + handler.post(new Runnable() { + + public void run() { + + try { + Toast toast = Toast.makeText(TVHGuideApplication.this, + error, Toast.LENGTH_LONG); + toast.show(); + } catch (Throwable ex) { + } + } + }); + broadcastMessage(ACTION_ERROR, error); + } + + public void broadcastPacket(Packet p) { + broadcastMessage(ACTION_PLAYBACK_PACKET, p); + } + + public List getChannelTags() { + return tags; + } + + public void addChannelTag(ChannelTag tag) { + tags.add(tag); + + if (!loading) { + broadcastMessage(ACTION_TAG_ADD, tag); + } + } + + public void removeChannelTag(ChannelTag tag) { + tags.remove(tag); + + if (!loading) { + broadcastMessage(ACTION_TAG_DELETE, tag); + } + } + + public void removeChannelTag(long id) { + for (ChannelTag tag : getChannelTags()) { + if (tag.id == id) { + removeChannelTag(tag); + return; + } + } + } + + public ChannelTag getChannelTag(long id) { + for (ChannelTag tag : getChannelTags()) { + if (tag.id == id) { + return tag; + } + } + return null; + } + + public void updateChannelTag(ChannelTag tag) { + if (!loading) { + broadcastMessage(ACTION_TAG_UPDATE, tag); + } + } + + public void addChannel(Channel channel) { + channels.add(channel); + + if (!loading) { + broadcastMessage(ACTION_CHANNEL_ADD, channel); + } + } + + public List getChannels() { + return channels; + } + + public void removeChannel(Channel channel) { + channels.remove(channel); + + if (!loading) { + broadcastMessage(ACTION_CHANNEL_DELETE, channel); + } + } + + public Channel getChannel(long id) { + for (Channel ch : getChannels()) { + if (ch.id == id) { + return ch; + } + } + return null; + } + + public void removeChannel(long id) { + for (Channel ch : getChannels()) { + if (ch.id == id) { + removeChannel(ch); + return; + } + } + } + + public void updateChannel(Channel ch) { + if (!loading) { + broadcastMessage(ACTION_CHANNEL_UPDATE, ch); + } + } + + public void addProgramme(Programme p) { + if (!loading) { + broadcastMessage(ACTION_PROGRAMME_ADD, p); + } + } + + public void removeProgramme(Programme p) { + if (!loading) { + broadcastMessage(ACTION_PROGRAMME_DELETE, p); + } + } + + public void updateProgramme(Programme p) { + if (!loading) { + broadcastMessage(ACTION_PROGRAMME_UPDATE, p); + } + } + + public void addRecording(Recording rec) { + recordings.add(rec); + + if (!loading) { + broadcastMessage(ACTION_DVR_ADD, rec); + } + } + + public List getRecordings() { + return recordings; + } + + public void removeRecording(Recording rec) { + recordings.remove(rec); + + if (!loading) { + broadcastMessage(ACTION_DVR_DELETE, rec); + } + } + + public Recording getRecording(long id) { + for (Recording rec : getRecordings()) { + if (rec.id == id) { + return rec; + } + } + return null; + } + + public void removeRecording(long id) { + for (Recording rec : getRecordings()) { + if (rec.id == id) { + removeRecording(rec); + return; + } + } + } + + public void updateRecording(Recording rec) { + if (!loading) { + broadcastMessage(ACTION_DVR_UPDATE, rec); + } + } + + public void setLoading(boolean b) { + if (loading != b) { + broadcastMessage(ACTION_LOADING, b); + } + loading = b; + } + + public void clearAll() { + tags.clear(); + recordings.clear(); + + for (Channel ch : channels) { + ch.epg.clear(); + ch.recordings.clear(); + } + channels.clear(); + + for (Subscription s : subscriptions) { + s.streams.clear(); + } + subscriptions.clear(); + + ChannelTag tag = new ChannelTag(); + tag.id = 0; + tag.name = getString(R.string.pr_all_channels); + tags.add(tag); + } + + public void addSubscription(Subscription s) { + subscriptions.add(s); + + if (!loading) { + broadcastMessage(ACTION_SUBSCRIPTION_ADD, s); + } + } + + public List getSubscriptions() { + return subscriptions; + } + + public void removeSubscription(Subscription s) { + s.streams.clear(); + subscriptions.remove(s); + + if (!loading) { + broadcastMessage(ACTION_SUBSCRIPTION_DELETE, s); + } + } + + public Subscription getSubscription(long id) { + for (Subscription s : getSubscriptions()) { + if (s.id == id) { + return s; + } + } + return null; + } + + public void removeSubscription(long id) { + for (Subscription s : getSubscriptions()) { + if (s.id == id) { + removeSubscription(s); + return; + } + } + } + + public void updateSubscription(Subscription s) { + if (!loading) { + broadcastMessage(ACTION_SUBSCRIPTION_UPDATE, s); + } + } + + public void addTicket(HttpTicket t) { + broadcastMessage(ACTION_TICKET_ADD, t); + } + + public boolean isLoading() { + return loading; + } + + public static SparseArray getContentTypes(Resources resources) { SparseArray ret = new SparseArray(); - - String[] s = ctx.getResources().getStringArray(R.array.pr_content_type0); - for(int i=0; i responseHandelers; - private LinkedList messageQueue; - private boolean auth; - private Selector selector; - - public HTSConnection(HTSConnectionListener listener, String clientName, String clientVersion) { - running = false; - lock = new ReentrantLock(); - inBuf = ByteBuffer.allocateDirect(1024 * 1024); - inBuf.limit(4); - responseHandelers = new HashMap(); - messageQueue = new LinkedList(); - - this.listener = listener; - this.clientName = clientName; - this.clientVersion = clientVersion; - } - - public void setRunning(boolean b) { - try { - lock.lock(); - running = false; - } finally { - lock.unlock(); - } - } - - //sychronized, blocking connect - public void open(String hostname, int port) { - if (running) { - return; - } - - final Object signal = new Object(); - - lock.lock(); - try { - selector = Selector.open(); - socketChannel = SocketChannel.open(); - socketChannel.configureBlocking(false); - socketChannel.socket().setKeepAlive(true); - socketChannel.socket().setSoTimeout(5000); - socketChannel.register(selector, SelectionKey.OP_CONNECT, signal); - socketChannel.connect(new InetSocketAddress(hostname, port)); - - running = true; - start(); - } catch (Exception ex) { - Log.e(TAG, "Can't open connection", ex); - listener.onError(CONNECTION_REFUSED_ERROR); - return; - } finally { - lock.unlock(); - } - - synchronized (signal) { - try { - signal.wait(5000); - if (socketChannel.isConnectionPending()) { - listener.onError(TIMEOUT_ERROR); - close(); - } - } catch (InterruptedException ex) { - } - } - } - - public boolean isConnected() { - return socketChannel != null - && socketChannel.isOpen() - && socketChannel.isConnected() - && running; - } - - //sycnronized, blocking auth - public void authenticate(String username, final String password) { - if (auth || !running) { - return; - } - - auth = false; - final HTSMessage authMessage = new HTSMessage(); - authMessage.setMethod("enableAsyncMetadata"); - authMessage.putField("username", username); - final HTSResponseHandler authHandler = new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - auth = response.getInt("noaccess", 0) != 1; - if (!auth) { - listener.onError(HTS_AUTH_ERROR); - } - synchronized (authMessage) { - authMessage.notify(); - } - } - }; - - HTSMessage helloMessage = new HTSMessage(); - helloMessage.setMethod("hello"); - helloMessage.putField("clientname", this.clientName); - helloMessage.putField("clientversion", this.clientVersion); - helloMessage.putField("htspversion", HTSMessage.HTSP_VERSION); - helloMessage.putField("username", username); - sendMessage(helloMessage, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - - protocolVersion = response.getInt("htspversion"); - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA1"); - md.update(password.getBytes()); - md.update(response.getByteArray("challenge")); - authMessage.putField("digest", md.digest()); - sendMessage(authMessage, authHandler); - } catch (NoSuchAlgorithmException ex) { - return; - } - } - }); - - synchronized (authMessage) { - try { - authMessage.wait(5000); - if (!auth) { - listener.onError(TIMEOUT_ERROR); - } - return; - } catch (InterruptedException ex) { - return; - } - } - } - - public boolean isAuthenticated() { - return auth; - } - - public void sendMessage(HTSMessage message, HTSResponseHandler listener) { - if (!isConnected()) { - return; - } - - lock.lock(); - try { - seq++; - message.putField("seq", seq); - responseHandelers.put(seq, listener); - socketChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ | SelectionKey.OP_CONNECT); - messageQueue.add(message); - selector.wakeup(); - } catch (Exception ex) { - Log.e(TAG, "Can't transmit message", ex); - this.listener.onError(ex); - } finally { - lock.unlock(); - } - } - - public void close() { - lock.lock(); - try { - responseHandelers.clear(); - messageQueue.clear(); - auth = false; - running = false; - socketChannel.register(selector, 0); - socketChannel.close(); - } catch (Exception ex) { - Log.e(TAG, "Can't close connection", ex); - } finally { - lock.unlock(); - } - } - - @Override - public void run() { - while (running) { - try { - selector.select(5000); - } catch (IOException ex) { - listener.onError(ex); - running = false; - continue; - } - - lock.lock(); - - try { - Iterator it = selector.selectedKeys().iterator(); - while (it.hasNext()) { - SelectionKey selKey = (SelectionKey) it.next(); - it.remove(); - processTcpSelectionKey(selKey); - } - - int ops = SelectionKey.OP_READ; - if (!messageQueue.isEmpty()) { - ops |= SelectionKey.OP_WRITE; - } - socketChannel.register(selector, ops); - } catch (Exception ex) { - Log.e(TAG, "Can't read message", ex); - listener.onError(ex); - running = false; - } finally { - lock.unlock(); - } - } - - close(); - } - - private void processTcpSelectionKey(SelectionKey selKey) throws IOException { - if (selKey.isConnectable() && selKey.isValid()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - sChannel.finishConnect(); - final Object signal = selKey.attachment(); - synchronized (signal) { - signal.notify(); - } - sChannel.register(selector, SelectionKey.OP_READ); - } - - if (selKey.isReadable() && selKey.isValid()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - int len = sChannel.read(inBuf); - if (len < 0) { - throw new IOException("Server went down"); - } - - HTSMessage msg = HTSMessage.parse(inBuf); - if (msg != null) { - handleMessage(msg); - } - } - if (selKey.isWritable() && selKey.isValid()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - HTSMessage msg = messageQueue.poll(); - if (msg != null) { - msg.transmit(sChannel); - } - } - } - - private void handleMessage(HTSMessage msg) { - if (msg.containsField("seq")) { - int respSeq = msg.getInt("seq"); - HTSResponseHandler handler = responseHandelers.get(respSeq); - responseHandelers.remove(respSeq); - - if (handler != null) { - handler.handleResponse(msg); - return; - } - } - - listener.onMessage(msg); - } - - public int getProtocolVersion() { - return this.protocolVersion; - } + public static final int TIMEOUT_ERROR = 1; + public static final int CONNECTION_REFUSED_ERROR = 2; + public static final int CONNECTION_LOST_ERROR = 3; + public static final int HTS_AUTH_ERROR = 4; + public static final int HTS_MESSAGE_ERROR = 5; + private static final String TAG = "HTSPConnection"; + private volatile boolean running; + private Lock lock; + private SocketChannel socketChannel; + private ByteBuffer inBuf; + private int seq; + private String clientName; + private String clientVersion; + private int protocolVersion; + + private HTSConnectionListener listener; + private Map responseHandelers; + private LinkedList messageQueue; + private boolean auth; + private Selector selector; + + public HTSConnection(HTSConnectionListener listener, String clientName, + String clientVersion) { + running = false; + lock = new ReentrantLock(); + inBuf = ByteBuffer.allocateDirect(1024 * 1024); + inBuf.limit(4); + responseHandelers = new HashMap(); + messageQueue = new LinkedList(); + + this.listener = listener; + this.clientName = clientName; + this.clientVersion = clientVersion; + } + + public void setRunning(boolean b) { + try { + lock.lock(); + running = false; + } finally { + lock.unlock(); + } + } + + // sychronized, blocking connect + public void open(String hostname, int port) { + if (running) { + return; + } + + final Object signal = new Object(); + + lock.lock(); + try { + selector = Selector.open(); + socketChannel = SocketChannel.open(); + socketChannel.configureBlocking(false); + socketChannel.socket().setKeepAlive(true); + socketChannel.socket().setSoTimeout(5000); + socketChannel.register(selector, SelectionKey.OP_CONNECT, signal); + socketChannel.connect(new InetSocketAddress(hostname, port)); + + running = true; + start(); + } catch (Exception ex) { + Log.e(TAG, "Can't open connection", ex); + listener.onError(CONNECTION_REFUSED_ERROR); + return; + } finally { + lock.unlock(); + } + + synchronized (signal) { + try { + signal.wait(5000); + if (socketChannel.isConnectionPending() || !running) { + listener.onError(TIMEOUT_ERROR); + close(); + } + } catch (InterruptedException ex) { + } + } + } + + public boolean isConnected() { + return socketChannel != null && socketChannel.isOpen() + && socketChannel.isConnected() && running; + } + + // sycnronized, blocking auth + public void authenticate(String username, final String password) { + if (auth || !running) { + return; + } + + auth = false; + final HTSMessage authMessage = new HTSMessage(); + authMessage.setMethod("enableAsyncMetadata"); + authMessage.putField("username", username); + final HTSResponseHandler authHandler = new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + auth = response.getInt("noaccess", 0) != 1; + if (!auth) { + listener.onError(HTS_AUTH_ERROR); + } + synchronized (authMessage) { + authMessage.notify(); + } + } + }; + + HTSMessage helloMessage = new HTSMessage(); + helloMessage.setMethod("hello"); + helloMessage.putField("clientname", this.clientName); + helloMessage.putField("clientversion", this.clientVersion); + helloMessage.putField("htspversion", HTSMessage.HTSP_VERSION); + helloMessage.putField("username", username); + sendMessage(helloMessage, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + + protocolVersion = response.getInt("htspversion"); + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA1"); + md.update(password.getBytes()); + md.update(response.getByteArray("challenge")); + authMessage.putField("digest", md.digest()); + sendMessage(authMessage, authHandler); + } catch (NoSuchAlgorithmException ex) { + return; + } + } + }); + + synchronized (authMessage) { + try { + authMessage.wait(5000); + if (!auth) { + listener.onError(TIMEOUT_ERROR); + } + return; + } catch (InterruptedException ex) { + return; + } + } + } + + public boolean isAuthenticated() { + return auth; + } + + public void sendMessage(HTSMessage message, HTSResponseHandler listener) { + if (!isConnected()) { + return; + } + + lock.lock(); + try { + seq++; + message.putField("seq", seq); + responseHandelers.put(seq, listener); + socketChannel.register(selector, SelectionKey.OP_WRITE + | SelectionKey.OP_READ | SelectionKey.OP_CONNECT); + messageQueue.add(message); + selector.wakeup(); + } catch (Exception ex) { + Log.e(TAG, "Can't transmit message", ex); + this.listener.onError(ex); + } finally { + lock.unlock(); + } + } + + public void close() { + lock.lock(); + try { + responseHandelers.clear(); + messageQueue.clear(); + auth = false; + running = false; + socketChannel.register(selector, 0); + socketChannel.close(); + } catch (Exception ex) { + Log.e(TAG, "Can't close connection", ex); + } finally { + lock.unlock(); + } + } + + @Override + public void run() { + while (running) { + try { + selector.select(5000); + } catch (IOException ex) { + listener.onError(ex); + running = false; + continue; + } + + lock.lock(); + + try { + Iterator it = selector.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = (SelectionKey) it.next(); + it.remove(); + processTcpSelectionKey(selKey); + } + + int ops = SelectionKey.OP_READ; + if (!messageQueue.isEmpty()) { + ops |= SelectionKey.OP_WRITE; + } + socketChannel.register(selector, ops); + } catch (Exception ex) { + Log.e(TAG, "Can't read message", ex); + listener.onError(ex); + running = false; + } finally { + lock.unlock(); + } + } + + close(); + } + + private void processTcpSelectionKey(SelectionKey selKey) throws IOException { + if (selKey.isConnectable() && selKey.isValid()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + sChannel.finishConnect(); + final Object signal = selKey.attachment(); + synchronized (signal) { + signal.notify(); + } + sChannel.register(selector, SelectionKey.OP_READ); + } + + if (selKey.isReadable() && selKey.isValid()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + int len = sChannel.read(inBuf); + if (len < 0) { + throw new IOException("Server went down"); + } + + HTSMessage msg = HTSMessage.parse(inBuf); + if (msg != null) { + handleMessage(msg); + } + } + if (selKey.isWritable() && selKey.isValid()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + HTSMessage msg = messageQueue.poll(); + if (msg != null) { + msg.transmit(sChannel); + } + } + } + + private void handleMessage(HTSMessage msg) { + if (msg.containsField("seq")) { + int respSeq = msg.getInt("seq"); + HTSResponseHandler handler = responseHandelers.get(respSeq); + responseHandelers.remove(respSeq); + + if (handler != null) { + handler.handleResponse(msg); + return; + } + } + + listener.onMessage(msg); + } + + public int getProtocolVersion() { + return this.protocolVersion; + } } From 05115c073df27acbf693c6037299c06426cf6394 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 11 Apr 2013 16:31:15 +0200 Subject: [PATCH 04/45] implemented epg timeline activity --- AndroidManifest.xml | 11 +- res/layout/epgnow_list_activity.xml | 25 ++- res/layout/epgnow_list_widget.xml | 125 ++++++++------ res/layout/epgtimeline_programme_widget.xml | 56 ++++++ res/layout/epgtimeline_widget.xml | 29 ++++ res/menu/main_menu.xml | 31 ++-- res/values-de/strings.xml | 4 + res/values/arrays.xml | 18 ++ res/values/strings.xml | 11 +- .../tvhguide/ChannelListActivity.java | 11 +- .../tvhguide/EPGTimeListActivity.java | 160 ++++++++++++----- .../tvhguide/EPGTimeListViewWrapper.java | 14 +- .../tvhguide/EPGTimelineActivity.java | 161 ++++++++++++++++++ .../EPGTimelineProgrammeListViewWrapper.java | 56 ++++++ .../tvhguide/EPGTimelineViewWrapper.java | 121 +++++++++++++ .../tvhguide/ProgrammeListViewWrapper.java | 85 +++++---- 16 files changed, 752 insertions(+), 166 deletions(-) create mode 100644 res/layout/epgtimeline_programme_widget.xml create mode 100644 res/layout/epgtimeline_widget.xml create mode 100644 src/org/tvheadend/tvhguide/EPGTimelineActivity.java create mode 100644 src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java create mode 100644 src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fc285e9..1fe7414 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -50,9 +50,6 @@ - @@ -68,6 +65,14 @@ + + + + \ No newline at end of file diff --git a/res/layout/epgnow_list_activity.xml b/res/layout/epgnow_list_activity.xml index c671c39..6f19848 100644 --- a/res/layout/epgnow_list_activity.xml +++ b/res/layout/epgnow_list_activity.xml @@ -1,8 +1,23 @@ - - + tools:context=".EPGTimelineActivity" > + + + + + + \ No newline at end of file diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml index 0b24cdd..d0b331e 100644 --- a/res/layout/epgnow_list_widget.xml +++ b/res/layout/epgnow_list_widget.xml @@ -1,71 +1,90 @@ + android:layout_height="wrap_content" > - - - + android:layout_marginRight="5sp" > - - + android:scaleType="matrix" /> - - - - + android:orientation="vertical" > - - + - - - + + - - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml new file mode 100644 index 0000000..f4facd7 --- /dev/null +++ b/res/layout/epgtimeline_programme_widget.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/epgtimeline_widget.xml b/res/layout/epgtimeline_widget.xml new file mode 100644 index 0000000..e8fb849 --- /dev/null +++ b/res/layout/epgtimeline_widget.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml index 3989727..f0d9ddf 100644 --- a/res/menu/main_menu.xml +++ b/res/menu/main_menu.xml @@ -1,23 +1,30 @@ - - - - - + + - + + + + + + + + + Fertig Verpasst Ungültig + + EPG Liste + Nöchste + Jetzt \ No newline at end of file diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 2c4c12b..51cca55 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -57,4 +57,22 @@ MPEG-PS Pass-through + + + #ff0000 + #00ff00 + #0000ff + #aa0000 + #00aa00 + #0000aa + #990000 + #009900 + #000099 + #660000 + #006600 + #000066 + #330000 + #003300 + #000033 + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 2caa4c1..d12bf74 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5,7 +5,9 @@ Help Settings Refresh + Channels EPG List + Timeline Tags Recordings Record @@ -182,10 +184,7 @@ Search the EPG today Hello world! - TestActivity - - Now - Next - 20:15 - + EPG List + EPG Timeline + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/ChannelListActivity.java b/src/org/tvheadend/tvhguide/ChannelListActivity.java index 91fd421..6cbc76d 100644 --- a/src/org/tvheadend/tvhguide/ChannelListActivity.java +++ b/src/org/tvheadend/tvhguide/ChannelListActivity.java @@ -167,7 +167,7 @@ void connect(boolean force) { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(this); - String hostname = prefs.getString("serverHostPref", "mediaserver"); + String hostname = prefs.getString("serverHostPref", "localhost"); int port = Integer.parseInt(prefs.getString("serverPortPref", "9982")); String username = prefs.getString("usernamePref", ""); String password = prefs.getString("passwordPref", ""); @@ -238,6 +238,15 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivity(intent); return true; } + case R.id.mi_channels: { + return true; + } + case R.id.mi_epg_timeline: { + Intent intent = new Intent(getBaseContext(), + EPGTimelineActivity.class); + startActivity(intent); + return true; + } case R.id.mi_search: { onSearchRequested(); return true; diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 9b9ca7d..8b5299a 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -2,25 +2,29 @@ import java.sql.Time; import java.text.ParseException; +import java.util.ArrayList; +import java.util.Calendar; import java.util.Comparator; import java.util.Date; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.tvheadend.tvhguide.model.Channel; -import android.app.ActionBar; import android.app.Activity; -import android.app.FragmentTransaction; +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.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.ListFragment; +import android.support.v4.view.ViewPager; import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; @@ -33,8 +37,7 @@ * channel * */ -public class EPGTimeListActivity extends FragmentActivity implements - ActionBar.TabListener { +public class EPGTimeListActivity extends FragmentActivity { /** * The serialization (saved instance state) Bundle key representing the @@ -42,6 +45,21 @@ public class EPGTimeListActivity extends FragmentActivity implements */ private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item"; + /** + * The {@link android.support.v4.view.PagerAdapter} that will provide + * fragments for each of the sections. We use a + * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which + * will keep every loaded fragment in memory. If this becomes too memory + * intensive, it may be best to switch to a + * {@link android.support.v4.app.FragmentStatePagerAdapter}. + */ + SectionsPagerAdapter mSectionsPagerAdapter; + + /** + * The {@link ViewPager} that will host the section contents. + */ + ViewPager mViewPager; + @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { @@ -53,21 +71,25 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.epgnow_list_activity); - // Set up the action bar to show tabs. - final ActionBar actionBar = getActionBar(); - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - - // For each timeslot create tab - HashSet defaults = new HashSet(); + // create timelost based on actual time + List timeSlots = new ArrayList(); java.text.DateFormat format = DateFormat.getTimeFormat(this); - defaults.add(format.format(new Time(12, 0, 0))); - defaults.add(format.format(new Time(16, 0, 0))); - defaults.add(format.format(new Time(20, 0, 0))); - Set timeslots = prefs.getStringSet("epg.timeslots", defaults); - for (String timeslot : timeslots) { - actionBar.addTab(actionBar.newTab().setText(timeslot) - .setTabListener(this)); + int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + for (int h = hour; h < 25; h = h + 2) { + timeSlots.add(format.format(new Time(h, 0, 0))); } + // Set timeslots = prefs.getStringSet("epg.timeslots", + // defaults); + + // Create the adapter that will return a fragment for each of the three + // primary sections of the app. + mSectionsPagerAdapter = new SectionsPagerAdapter( + getSupportFragmentManager(), + timeSlots.toArray(new String[timeSlots.size()])); + + // Set up the ViewPager with the sections adapter. + mViewPager = (ViewPager) findViewById(R.id.pager); + mViewPager.setAdapter(mSectionsPagerAdapter); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.epgnow_list_title); @@ -82,13 +104,6 @@ public void onRestoreInstanceState(Bundle savedInstanceState) { } } - @Override - public void onSaveInstanceState(Bundle outState) { - // Serialize the current tab position. - outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActionBar() - .getSelectedNavigationIndex()); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. @@ -97,26 +112,85 @@ public boolean onCreateOptionsMenu(Menu menu) { } @Override - public void onTabSelected(ActionBar.Tab tab, - FragmentTransaction fragmentTransaction) { - // When the given tab is selected, show the tab contents in the - // container view. - EPGListFragment fragment = new EPGListFragment(); - Bundle args = new Bundle(); - args.putString(EPGListFragment.ARG_TIME_SLOT, tab.getText().toString()); - fragment.setArguments(args); - getSupportFragmentManager().beginTransaction() - .replace(R.id.container, fragment).commit(); + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mi_settings: { + Intent intent = new Intent(getBaseContext(), SettingsActivity.class); + startActivityForResult(intent, R.id.mi_settings); + return true; + } + // case R.id.mi_refresh: { + // connect(true); + // return true; + // } + case R.id.mi_recordings: { + Intent intent = new Intent(getBaseContext(), + RecordingListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_epg_list: { + return true; + } + case R.id.mi_channels: { + Intent intent = new Intent(getBaseContext(), + ChannelListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_epg_timeline: { + Intent intent = new Intent(getBaseContext(), + EPGTimelineActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_search: { + onSearchRequested(); + return true; + } + // case R.id.mi_tags: { + // tagDialog.show(); + // return true; + // } + default: { + return super.onOptionsItemSelected(item); + } + } } - @Override - public void onTabUnselected(ActionBar.Tab tab, - FragmentTransaction fragmentTransaction) { - } + /** + * A {@link FragmentPagerAdapter} that returns a fragment corresponding to + * one of the sections/tabs/pages. + */ + public class SectionsPagerAdapter extends FragmentPagerAdapter { - @Override - public void onTabReselected(ActionBar.Tab tab, - FragmentTransaction fragmentTransaction) { + private final String[] timeslots; + + public SectionsPagerAdapter(FragmentManager fm, String[] timeslots) { + super(fm); + this.timeslots = timeslots; + } + + @Override + public Fragment getItem(int position) { + // When the given tab is selected, show the tab contents in the + // container view. + EPGListFragment fragment = new EPGListFragment(); + Bundle args = new Bundle(); + args.putString(EPGListFragment.ARG_TIME_SLOT, timeslots[position]); + fragment.setArguments(args); + return fragment; + } + + @Override + public int getCount() { + return timeslots.length; + } + + @Override + public CharSequence getPageTitle(int position) { + return timeslots[position]; + } } /** diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index e5f6f16..aa3eea2 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -28,12 +28,14 @@ public EPGTimeListViewWrapper(View base, Date timeSlot) { @SuppressWarnings("deprecation") public void repaint(Channel channel) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(icon.getContext()); - Boolean showIcons = prefs.getBoolean("showIconPref", false); - icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); - icon.invalidate(); + if (icon != null) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(icon.getContext()); + Boolean showIcons = prefs.getBoolean("showIconPref", false); + icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); + icon.invalidate(); + } Programme pr = getNextProgramme(channel); if (pr == null) { diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java new file mode 100644 index 0000000..afb5c82 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -0,0 +1,161 @@ +package org.tvheadend.tvhguide; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.tvheadend.tvhguide.model.Channel; + +import android.app.Activity; +import android.app.ListActivity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * + * @author mike toggweiler + * + */ +public class EPGTimelineActivity extends ListActivity { + + private TimelineAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(savedInstanceState); + + TVHGuideApplication tvh = (TVHGuideApplication) getApplication(); + adapter = new TimelineAdapter(this, new ArrayList( + tvh.getChannels())); + setListAdapter(adapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mi_settings: { + Intent intent = new Intent(getBaseContext(), SettingsActivity.class); + startActivityForResult(intent, R.id.mi_settings); + return true; + } + // case R.id.mi_refresh: { + // connect(true); + // return true; + // } + case R.id.mi_recordings: { + Intent intent = new Intent(getBaseContext(), + RecordingListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_epg_list: { + Intent intent = new Intent(getBaseContext(), + EPGTimeListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_channels: { + Intent intent = new Intent(getBaseContext(), + ChannelListActivity.class); + startActivity(intent); + return true; + } + case R.id.mi_epg_timeline: { + return true; + } + case R.id.mi_search: { + onSearchRequested(); + return true; + } + // case R.id.mi_tags: { + // tagDialog.show(); + // return true; + // } + default: { + return super.onOptionsItemSelected(item); + } + } + } + + class TimelineAdapter extends ArrayAdapter { + + TimelineAdapter(Activity context, List list) { + super(context, R.layout.epgtimeline_widget, list); + } + + public void sort() { + sort(new Comparator() { + + public int compare(Channel x, Channel y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Channel channel) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Channel ch = (Channel) listView.getItemAtPosition(pos); + + if (view.getTag() == null || ch == null) { + continue; + } + + if (channel.id != ch.id) { + continue; + } + + EPGTimelineViewWrapper wrapper = (EPGTimelineViewWrapper) view + .getTag(); + wrapper.repaint(channel); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + EPGTimelineViewWrapper wrapper; + + Channel ch = getItem(position); + Activity activity = (Activity) getContext(); + + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater + .inflate(R.layout.epgtimeline_widget, null, false); + row.requestLayout(); + wrapper = new EPGTimelineViewWrapper(EPGTimelineActivity.this, + row); + row.setTag(wrapper); + + } else { + wrapper = (EPGTimelineViewWrapper) row.getTag(); + } + + wrapper.repaint(ch); + return row; + } + } +} diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java new file mode 100644 index 0000000..3eb6900 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -0,0 +1,56 @@ +package org.tvheadend.tvhguide; + +import java.util.Calendar; +import java.util.Date; + +import org.tvheadend.tvhguide.model.Programme; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +public class EPGTimelineProgrammeListViewWrapper extends + ProgrammeListViewWrapper { + + private TypedArray colors; + LinearLayout container; + private static final int WIDTH_PER_MINUTE = 10; + + public EPGTimelineProgrammeListViewWrapper(View base) { + super(base); + Resources res = base.getResources(); + colors = res.obtainTypedArray(R.array.pref_color_content_type); + + container = (LinearLayout) base.findViewById(R.id.programme_container); + } + + @Override + public void repaint(Programme p) { + super.repaint(p); + + // colorize based on series category + int color = colors.getColor(p.contentType, 0); + container.setBackgroundColor(color); + + // define width based on duration + Date start = Calendar.getInstance().getTime(); + if (p.stop.after(start)) { + if (p.start.after(start)) { + start = p.start; + } + + long remainingMillis = p.stop.getTime() - start.getTime(); + long minutes = remainingMillis / (60 * 60 * 1000); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + (int) minutes * WIDTH_PER_MINUTE, LayoutParams.MATCH_PARENT); + container.setLayoutParams(layoutParams); + container.setVisibility(LinearLayout.VISIBLE); + } else { + container.setVisibility(LinearLayout.GONE); + } + container.invalidate(); + } +} diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java new file mode 100644 index 0000000..812af3b --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2011 John Törnblom + * + * This file is part of TVHGuide. + * + * TVHGuide is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * TVHGuide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with TVHGuide. If not, see . + */ +package org.tvheadend.tvhguide; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.BitmapDrawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.devsmart.android.ui.HorizontalListView; + +/** + * + * @author mike toggweiler + */ +public class EPGTimelineViewWrapper { + + private ImageView icon; + private LinearLayout timeline; + private final Context context; + + public EPGTimelineViewWrapper(Context context, View base) { + this.context = context; + icon = (ImageView) base.findViewById(R.id.ch_icon); + + timeline = (LinearLayout) base.findViewById(R.id.ch_timeline); + } + + public void repaint(Channel channel) { + timeline.removeAllViews(); + + // SharedPreferences prefs = PreferenceManager + // .getDefaultSharedPreferences(icon.getContext()); + // Boolean showIcons = prefs.getBoolean("showIconPref", false); + // icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); + + if (channel.isRecording()) { + icon.setImageResource(R.drawable.ic_rec_small); + } else { + icon.setImageDrawable(null); + } + icon.invalidate(); + + HorizontalListView horizontialListView = new HorizontalListView( + context, null); + TimelineProgrammeAdapter adapter = new TimelineProgrammeAdapter( + context, new ArrayList(channel.epg)); + horizontialListView.setAdapter(adapter); + + timeline.addView(horizontialListView); + } + + class TimelineProgrammeAdapter extends ArrayAdapter { + + TimelineProgrammeAdapter(Context context, List epg) { + super(context, R.layout.epgtimeline_programme_widget, epg); + } + + public void sort() { + sort(new Comparator() { + + public int compare(Programme x, Programme y) { + return x.compareTo(y); + } + }); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + EPGTimelineProgrammeListViewWrapper wrapper; + + Programme pr = getItem(position); + Activity activity = (Activity) getContext(); + + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate(R.layout.epgtimeline_programme_widget, + null, false); + row.requestLayout(); + wrapper = new EPGTimelineProgrammeListViewWrapper(row); + row.setTag(wrapper); + + } else { + wrapper = (EPGTimelineProgrammeListViewWrapper) row.getTag(); + } + + wrapper.repaint(pr); + return row; + } + } +} diff --git a/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java index d1c8b85..a8aa805 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java @@ -62,48 +62,59 @@ public void repaint(Programme p) { title.invalidate(); - String s = buildSeriesInfoString(base, p.seriesInfo); - if (s.length() == 0) { - s = contentTypes.get(p.contentType); + if (seriesInfo != null) { + String s = buildSeriesInfoString(base, p.seriesInfo); + if (s.length() == 0) { + s = contentTypes.get(p.contentType); + } + + seriesInfo.setText(s); + seriesInfo.invalidate(); } - seriesInfo.setText(s); - seriesInfo.invalidate(); - - if (p.description.length() > 0) { - description.setText(p.description); - description.setVisibility(TextView.VISIBLE); - } else { - description.setText(""); - description.setVisibility(TextView.GONE); - } - description.invalidate(); - - if (DateUtils.isToday(p.start.getTime())) { - date.setText(base.getResources().getString(R.string.today)); - } else if (p.start.getTime() < System.currentTimeMillis() + 1000 * 60 - * 60 * 24 * 2 - && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 - * 60 * 24 * 2) { - date.setText(DateUtils.getRelativeTimeSpanString(p.start.getTime(), - System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS)); - } else if (p.start.getTime() < System.currentTimeMillis() + 1000 * 60 - * 60 * 24 * 6 - && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 - * 60 * 24 * 2) { - date.setText(new SimpleDateFormat("EEEE").format(p.start.getTime())); - } else { - date.setText(DateFormat.getDateFormat(date.getContext()).format( - p.start)); + if (description != null) { + if (p.description.length() > 0) { + description.setText(p.description); + description.setVisibility(TextView.VISIBLE); + } else { + description.setText(""); + description.setVisibility(TextView.GONE); + } + description.invalidate(); } - date.invalidate(); + if (date != null) { + if (DateUtils.isToday(p.start.getTime())) { + date.setText(base.getResources().getString(R.string.today)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 2 + && p.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(DateUtils.getRelativeTimeSpanString( + p.start.getTime(), System.currentTimeMillis(), + DateUtils.DAY_IN_MILLIS)); + } else if (p.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 6 + && p.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(new SimpleDateFormat("EEEE").format(p.start + .getTime())); + } else { + date.setText(DateFormat.getDateFormat(date.getContext()) + .format(p.start)); + } + + date.invalidate(); + } - time.setText(DateFormat.getTimeFormat(time.getContext()) - .format(p.start) - + " - " - + DateFormat.getTimeFormat(time.getContext()).format(p.stop)); - time.invalidate(); + if (time != null) { + time.setText(DateFormat.getTimeFormat(time.getContext()).format( + p.start) + + " - " + + DateFormat.getTimeFormat(time.getContext()) + .format(p.stop)); + time.invalidate(); + } } public String buildSeriesInfoString(View context, SeriesInfo info) { From 47dca574690722f8a8d7b907ebe8e16be30598d8 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 11 Apr 2013 16:53:00 +0200 Subject: [PATCH 05/45] added libraries --- libs/android-support-v4.jar | Bin 0 -> 385685 bytes libs/devsmartlib.jar | Bin 0 -> 75375 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 libs/android-support-v4.jar create mode 100644 libs/devsmartlib.jar diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar new file mode 100644 index 0000000000000000000000000000000000000000..6080877d4ade158f24ad7ccd9a10db2ac9060b47 GIT binary patch literal 385685 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

6HPQhY&mkuc_8%Q=4KIb)@wP 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%0A03X%h@VcJLH+gJm3sMzG=X1S%0K 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 Date: Fri, 12 Apr 2013 15:10:07 +0200 Subject: [PATCH 06/45] fixed time filter in epg list activity --- AndroidManifest.xml | 1 + src/org/tvheadend/tvhguide/EPGTimeListActivity.java | 8 +++++++- src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1fe7414..95627f0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -71,6 +71,7 @@ android:theme="@style/CustomTheme" /> diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 8b5299a..9f5a8b2 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -75,7 +75,7 @@ protected void onCreate(Bundle savedInstanceState) { List timeSlots = new ArrayList(); java.text.DateFormat format = DateFormat.getTimeFormat(this); int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); - for (int h = hour; h < 25; h = h + 2) { + for (int h = hour; h < 25; h++) { timeSlots.add(format.format(new Time(h, 0, 0))); } // Set timeslots = prefs.getStringSet("epg.timeslots", @@ -219,6 +219,12 @@ public void onCreate(Bundle savedInstanceState) { Date date; try { date = format.parse(timeSlot); + Calendar cal = Calendar.getInstance(); + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + cal.set(Calendar.HOUR_OF_DAY, date.getHours()); + date = cal.getTime(); TVHGuideApplication thv = (TVHGuideApplication) getActivity() .getApplication(); diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index aa3eea2..4fe14a2 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -58,7 +58,7 @@ protected Programme getNextProgramme(Channel channel) { // find first programm after timeslot while (it.hasNext()) { Programme pr = it.next(); - if (pr.start.after(timeSlot)) { + if (pr.start.equals(timeSlot) || pr.start.after(timeSlot)) { return pr; } } From f993a87706b44e2f8549092da2ad576876ea110a Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 12 Apr 2013 15:46:32 +0200 Subject: [PATCH 07/45] added support for context menu to record programme --- AndroidManifest.xml | 12 +- .../tvhguide/EPGTimeListActivity.java | 208 ++++++++++++++++-- .../tvhguide/EPGTimeListViewWrapper.java | 23 +- 3 files changed, 202 insertions(+), 41 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 95627f0..d518d73 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -61,12 +61,7 @@ android:name="org.tvheadend.tvhguide.PlaybackActivity" android:configChanges="orientation" android:theme="@android:style/Theme.NoTitleBar" /> - - - - + + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 9f5a8b2..eb7fe8e 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -6,9 +6,16 @@ import java.util.Calendar; import java.util.Comparator; import java.util.Date; +import java.util.Iterator; import java.util.List; +import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.intent.SearchEPGIntent; +import org.tvheadend.tvhguide.intent.SearchIMDbIntent; import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.Recording; import android.app.Activity; import android.content.Intent; @@ -22,12 +29,15 @@ import android.support.v4.app.ListFragment; import android.support.v4.view.ViewPager; import android.text.format.DateFormat; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; @@ -179,6 +189,7 @@ public Fragment getItem(int position) { Bundle args = new Bundle(); args.putString(EPGListFragment.ARG_TIME_SLOT, timeslots[position]); fragment.setArguments(args); + return fragment; } @@ -197,7 +208,8 @@ public CharSequence getPageTitle(int position) { * A dummy fragment representing a section of the app, but that simply * displays dummy text. */ - public static class EPGListFragment extends ListFragment { + public static class EPGListFragment extends ListFragment implements + HTSListener { /** * The fragment argument representing the section number for this * fragment. @@ -236,21 +248,163 @@ public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated catch block e.printStackTrace(); } + } - // @Override - // public View onCreateView(LayoutInflater inflater, - // ViewGroup convertView, Bundle savedInstanceState) { - // getArguments().getInt(ARG_SECTION_NUMBER); - // - // // generate own view - // View rowview = convertView; - // if (null == rowview) { - // rowview = inflater.inflate(R.layout.epgnow_list_widget, null); - // } - // - // return rowview; - // } + @Override + public void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getActivity() + .getApplication(); + app.addListener(this); + } + + @Override + public void onPause() { + TVHGuideApplication app = (TVHGuideApplication) getActivity() + .getApplication(); + app.removeListener(this); + super.onPause(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + registerForContextMenu(getListView()); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record: + case R.string.menu_record_cancel: + case R.string.menu_record_remove: { + getActivity().startService(item.getIntent()); + return true; + } + default: { + return super.onContextItemSelected(item); + } + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Programme p = prAdapter.getProgrammeAt(info.position); + + menu.setHeaderTitle(p.title); + + Intent intent = new Intent(getActivity(), HTSService.class); + + MenuItem item = null; + + if (p != null) { + if (p.recording == null) { + intent.setAction(HTSService.ACTION_DVR_ADD); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record, + ContextMenu.NONE, R.string.menu_record); + } else if (p.isRecording() || p.isScheduled()) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, + R.string.menu_record_cancel, ContextMenu.NONE, + R.string.menu_record_cancel); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, + R.string.menu_record_remove, ContextMenu.NONE, + R.string.menu_record_remove); + } + + item.setIntent(intent); + + item = menu.add(ContextMenu.NONE, R.string.search_hint, + ContextMenu.NONE, R.string.search_hint); + item.setIntent(new SearchEPGIntent(getActivity(), p.title)); + + item = menu.add(ContextMenu.NONE, ContextMenu.NONE, + ContextMenu.NONE, "IMDb"); + item.setIntent(new SearchIMDbIntent(getActivity(), p.title)); + } + } + + @Override + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + prAdapter.add((Channel) obj); + prAdapter.notifyDataSetChanged(); + prAdapter.sort(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_DELETE)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + prAdapter.remove((Channel) obj); + prAdapter.notifyDataSetChanged(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_UPDATE)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + Channel channel = (Channel) obj; + prAdapter.updateView(getListView(), channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + prAdapter.updateView(getListView(), p.channel); + } + }); + } else if (action + .equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + prAdapter.updateView(getListView(), p.channel); + } + }); + } else if (action + .equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + prAdapter.updateView(getListView(), p.channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { + getActivity().runOnUiThread(new Runnable() { + + public void run() { + Recording rec = (Recording) obj; + for (Channel c : prAdapter.list) { + for (Programme p : c.epg) { + if (rec == p.recording) { + prAdapter.updateView(getListView(), c); + return; + } + } + } + } + }); + } + } } static class EPGTimeListAdapter extends ArrayAdapter { @@ -275,6 +429,11 @@ public int compare(Channel x, Channel y) { }); } + public Programme getProgrammeAt(int position) { + Channel item = getItem(position); + return getProgrammeStartingAfter(item, timeSlot); + } + public void updateView(ListView listView, Channel channel) { for (int i = 0; i < listView.getChildCount(); i++) { View view = listView.getChildAt(i); @@ -319,4 +478,25 @@ public View getView(int position, View convertView, ViewGroup parent) { } + /** + * get next program based on channel and timeslot + * + * @return + */ + public static Programme getProgrammeStartingAfter(Channel channel, + Date timeSlot) { + Iterator it = channel.epg.iterator(); + if (!channel.isTransmitting) { + return null; + } + // find first programm after timeslot + while (it.hasNext()) { + Programme pr = it.next(); + if (pr.start.equals(timeSlot) || pr.start.after(timeSlot)) { + return pr; + } + } + return null; + } + } diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index 4fe14a2..1472eef 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -1,7 +1,6 @@ package org.tvheadend.tvhguide; import java.util.Date; -import java.util.Iterator; import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.Programme; @@ -37,7 +36,8 @@ public void repaint(Channel channel) { icon.invalidate(); } - Programme pr = getNextProgramme(channel); + Programme pr = EPGTimeListActivity.getProgrammeStartingAfter(channel, + timeSlot); if (pr == null) { title.setText(R.string.ch_no_transmission); } else { @@ -45,23 +45,4 @@ public void repaint(Channel channel) { } } - /** - * get next program based on channel and timeslot - * - * @return - */ - protected Programme getNextProgramme(Channel channel) { - Iterator it = channel.epg.iterator(); - if (!channel.isTransmitting) { - return null; - } - // find first programm after timeslot - while (it.hasNext()) { - Programme pr = it.next(); - if (pr.start.equals(timeSlot) || pr.start.after(timeSlot)) { - return pr; - } - } - return null; - } } From 1df979a926016ef212a8970f44a14a584ce36fbb Mon Sep 17 00:00:00 2001 From: toggm Date: Mon, 15 Apr 2013 22:37:08 +0200 Subject: [PATCH 08/45] tried synchronizing viewpage listviews --- .../tvhguide/EPGTimeListActivity.java | 65 +++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index eb7fe8e..c03c5d8 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -37,6 +37,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; @@ -70,6 +72,8 @@ public class EPGTimeListActivity extends FragmentActivity { */ ViewPager mViewPager; + private List m_scrollListeners = new ArrayList(); + @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { @@ -85,8 +89,8 @@ protected void onCreate(Bundle savedInstanceState) { List timeSlots = new ArrayList(); java.text.DateFormat format = DateFormat.getTimeFormat(this); int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); - for (int h = hour; h < 25; h++) { - timeSlots.add(format.format(new Time(h, 0, 0))); + for (int i = 0; i < 12; i++) { + timeSlots.add(format.format(new Time(hour + i, 0, 0))); } // Set timeslots = prefs.getStringSet("epg.timeslots", // defaults); @@ -202,14 +206,29 @@ public int getCount() { public CharSequence getPageTitle(int position) { return timeslots[position]; } + + } + + private void registerEPGScrollListener(EPGListScrollListener listener) { + m_scrollListeners.add(listener); + } + + private void unregisterEPGScrollListener(EPGListScrollListener listener) { + m_scrollListeners.remove(listener); + } + + private void notifyEPGScrollListener(AbsListView view, int position) { + for (EPGListScrollListener listener : m_scrollListeners) { + listener.scrollTo(view, position); + } } /** * A dummy fragment representing a section of the app, but that simply * displays dummy text. */ - public static class EPGListFragment extends ListFragment implements - HTSListener { + public class EPGListFragment extends ListFragment implements HTSListener, + EPGListScrollListener { /** * The fragment argument representing the section number for this * fragment. @@ -248,7 +267,6 @@ public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated catch block e.printStackTrace(); } - } @Override @@ -257,6 +275,8 @@ public void onResume() { TVHGuideApplication app = (TVHGuideApplication) getActivity() .getApplication(); app.addListener(this); + + registerEPGScrollListener(this); } @Override @@ -264,6 +284,9 @@ public void onPause() { TVHGuideApplication app = (TVHGuideApplication) getActivity() .getApplication(); app.removeListener(this); + + unregisterEPGScrollListener(this); + super.onPause(); } @@ -272,6 +295,29 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); registerForContextMenu(getListView()); + + getListView().setOnScrollListener(new OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, + int scrollState) { + + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, + int visibleItemCount, int totalItemCount) { + notifyEPGScrollListener(view, firstVisibleItem); + } + }); + } + + @Override + public void scrollTo(AbsListView view, int position) { + // getListView().scrollTo(0, position); + // getListView().getSelectedView().getTop(); + if (getListView() != view) { + + } } @Override @@ -367,7 +413,10 @@ public void run() { public void run() { Programme p = (Programme) obj; - prAdapter.updateView(getListView(), p.channel); + try { + prAdapter.updateView(getListView(), p.channel); + } catch (Exception e) { + } } }); } else if (action @@ -407,6 +456,10 @@ public void run() { } } + static interface EPGListScrollListener { + public void scrollTo(AbsListView view, int position); + } + static class EPGTimeListAdapter extends ArrayAdapter { Activity context; From 4f8f6102b349ce7b3457c3976d94042bcf1b858d Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 16 Apr 2013 15:40:38 +0200 Subject: [PATCH 09/45] fixed linking of scroll positons between listviews within page adapter --- .../tvhguide/EPGTimeListActivity.java | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index c03c5d8..0e480db 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -72,6 +72,10 @@ public class EPGTimeListActivity extends FragmentActivity { */ ViewPager mViewPager; + private int firstVibleItem = 0; + private int top = 0; + private boolean lock; + private List m_scrollListeners = new ArrayList(); @SuppressWarnings("deprecation") @@ -217,9 +221,19 @@ private void unregisterEPGScrollListener(EPGListScrollListener listener) { m_scrollListeners.remove(listener); } - private void notifyEPGScrollListener(AbsListView view, int position) { - for (EPGListScrollListener listener : m_scrollListeners) { - listener.scrollTo(view, position); + private void notifyEPGScrollListener(AbsListView view, int position, int top) { + if (lock) { + return; + } + try { + lock = true; + this.firstVibleItem = position; + this.top = top; + for (EPGListScrollListener listener : m_scrollListeners) { + listener.scrollTo(view, position, top); + } + } finally { + lock = false; } } @@ -264,7 +278,6 @@ public void onCreate(Bundle savedInstanceState) { prAdapter.sort(); setListAdapter(prAdapter); } catch (ParseException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -276,6 +289,9 @@ public void onResume() { .getApplication(); app.addListener(this); + // scroll to aquired position + getListView().setSelectionFromTop(firstVibleItem, top); + registerEPGScrollListener(this); } @@ -296,6 +312,9 @@ public void onActivityCreated(Bundle savedInstanceState) { registerForContextMenu(getListView()); + // scroll to acquired position + getListView().setSelectionFromTop(firstVibleItem, top); + getListView().setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, @@ -306,17 +325,24 @@ public void onScrollStateChanged(AbsListView view, @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - notifyEPGScrollListener(view, firstVisibleItem); + if (visibleItemCount == 0) { + return; + } + View v = view.getChildAt(0); + int top = (v == null) ? 0 : v.getTop(); + if (firstVisibleItem != EPGTimeListActivity.this.firstVibleItem + || top != EPGTimeListActivity.this.top) { + + notifyEPGScrollListener(view, firstVisibleItem, top); + } } }); } @Override - public void scrollTo(AbsListView view, int position) { - // getListView().scrollTo(0, position); - // getListView().getSelectedView().getTop(); + public void scrollTo(AbsListView view, int position, int top) { if (getListView() != view) { - + getListView().setSelectionFromTop(position, top); } } @@ -340,8 +366,15 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + if (info == null) { + return; + } Programme p = prAdapter.getProgrammeAt(info.position); + if (p == null) { + return; + } + menu.setHeaderTitle(p.title); Intent intent = new Intent(getActivity(), HTSService.class); @@ -457,7 +490,7 @@ public void run() { } static interface EPGListScrollListener { - public void scrollTo(AbsListView view, int position); + public void scrollTo(AbsListView view, int position, int top); } static class EPGTimeListAdapter extends ArrayAdapter { From dcac39ec32da1a6ab064e50a6b2ec3f15a887298 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 17 Apr 2013 21:48:44 +0200 Subject: [PATCH 10/45] fixed date parsing after midnights --- .../tvhguide/EPGTimeListActivity.java | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 0e480db..3c5729e 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -1,7 +1,5 @@ package org.tvheadend.tvhguide; -import java.sql.Time; -import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Comparator; @@ -90,11 +88,16 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.epgnow_list_activity); // create timelost based on actual time - List timeSlots = new ArrayList(); - java.text.DateFormat format = DateFormat.getTimeFormat(this); - int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + List timeSlots = new ArrayList(); + + Calendar cal = Calendar.getInstance(); + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + for (int i = 0; i < 12; i++) { - timeSlots.add(format.format(new Time(hour + i, 0, 0))); + timeSlots.add(cal.getTime()); + cal.add(Calendar.HOUR_OF_DAY, 1); } // Set timeslots = prefs.getStringSet("epg.timeslots", // defaults); @@ -103,7 +106,7 @@ protected void onCreate(Bundle savedInstanceState) { // primary sections of the app. mSectionsPagerAdapter = new SectionsPagerAdapter( getSupportFragmentManager(), - timeSlots.toArray(new String[timeSlots.size()])); + timeSlots.toArray(new Date[timeSlots.size()])); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); @@ -182,20 +185,22 @@ public boolean onOptionsItemSelected(MenuItem item) { */ public class SectionsPagerAdapter extends FragmentPagerAdapter { - private final String[] timeslots; + private final Date[] timeslots; + private java.text.DateFormat timeFormat; - public SectionsPagerAdapter(FragmentManager fm, String[] timeslots) { + public SectionsPagerAdapter(FragmentManager fm, Date[] timeslots) { super(fm); this.timeslots = timeslots; + timeFormat = DateFormat.getTimeFormat(getApplicationContext()); } @Override public Fragment getItem(int position) { // When the given tab is selected, show the tab contents in the // container view. - EPGListFragment fragment = new EPGListFragment(); + EPGListFragment fragment = new EPGListFragment(timeslots[position]); Bundle args = new Bundle(); - args.putString(EPGListFragment.ARG_TIME_SLOT, timeslots[position]); + // args.put(EPGListFragment.ARG_TIME_SLOT, ); fragment.setArguments(args); return fragment; @@ -208,7 +213,7 @@ public int getCount() { @Override public CharSequence getPageTitle(int position) { - return timeslots[position]; + return timeFormat.format(timeslots[position]); } } @@ -251,35 +256,23 @@ public class EPGListFragment extends ListFragment implements HTSListener, private EPGTimeListAdapter prAdapter; - public EPGListFragment() { + private Date m_timeslot; + + public EPGListFragment(Date timeslot) { + m_timeslot = timeslot; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String timeSlot = getArguments().getString(ARG_TIME_SLOT); - java.text.DateFormat format = DateFormat - .getTimeFormat(getActivity()); - Date date; - try { - date = format.parse(timeSlot); - Calendar cal = Calendar.getInstance(); - cal.clear(Calendar.MINUTE); - cal.clear(Calendar.SECOND); - cal.clear(Calendar.MILLISECOND); - cal.set(Calendar.HOUR_OF_DAY, date.getHours()); - date = cal.getTime(); - - TVHGuideApplication thv = (TVHGuideApplication) getActivity() - .getApplication(); - List list = thv.getChannels(); - prAdapter = new EPGTimeListAdapter(getActivity(), list, date); - prAdapter.sort(); - setListAdapter(prAdapter); - } catch (ParseException e) { - e.printStackTrace(); - } + // String timeSlot = getArguments().getString(ARG_TIME_SLOT); + TVHGuideApplication thv = (TVHGuideApplication) getActivity() + .getApplication(); + List list = thv.getChannels(); + prAdapter = new EPGTimeListAdapter(getActivity(), list, m_timeslot); + prAdapter.sort(); + setListAdapter(prAdapter); } @Override From f1daede222884f72e7243e069cae75b1c153fa78 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 17 Apr 2013 22:48:11 +0200 Subject: [PATCH 11/45] added shared channel tag handling and channel tag filtering support for epg time list activity --- res/layout/epgnow_list_activity.xml | 2 + res/layout/epgnow_list_title.xml | 62 +++-- .../tvhguide/ChannelListActivity.java | 8 + .../tvhguide/EPGTimeListActivity.java | 220 ++++++++++-------- .../tvhguide/TVHGuideApplication.java | 33 +++ 5 files changed, 216 insertions(+), 109 deletions(-) diff --git a/res/layout/epgnow_list_activity.xml b/res/layout/epgnow_list_activity.xml index 6f19848..632e7f8 100644 --- a/res/layout/epgnow_list_activity.xml +++ b/res/layout/epgnow_list_activity.xml @@ -9,6 +9,8 @@ This title strip will display the currently visible page title, as well as the page titles for adjacent pages. --> + + - - - + xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - \ No newline at end of file + android:text="@string/pr_all_channels" + android:layout_alignParentTop="true" + android:layout_toRightOf="@+id/ct_logo" + /> + + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/ChannelListActivity.java b/src/org/tvheadend/tvhguide/ChannelListActivity.java index 6cbc76d..66a89b1 100644 --- a/src/org/tvheadend/tvhguide/ChannelListActivity.java +++ b/src/org/tvheadend/tvhguide/ChannelListActivity.java @@ -111,6 +111,10 @@ public void onClick(View arg0) { } }); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + currentTag = app.getCurrentTag(); + setCurrentTag(currentTag); + registerForContextMenu(getListView()); } @@ -191,12 +195,16 @@ private void setCurrentTag(ChannelTag t) { tagImageView.setImageResource(R.drawable.logo_72); } else { tagTextView.setText(currentTag.name); + if (currentTag.iconBitmap != null) { tagImageView.setImageBitmap(currentTag.iconBitmap); } else { tagImageView.setImageResource(R.drawable.logo_72); } } + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.setCurrentTag(t); } private void populateList() { diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 3c5729e..d317c4d 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.Calendar; -import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -12,10 +11,12 @@ import org.tvheadend.tvhguide.intent.SearchEPGIntent; import org.tvheadend.tvhguide.intent.SearchIMDbIntent; import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.ChannelTag; import org.tvheadend.tvhguide.model.Programme; import org.tvheadend.tvhguide.model.Recording; -import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -29,17 +30,16 @@ import android.text.format.DateFormat; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.Window; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.ListView; +import android.widget.ImageView; +import android.widget.TextView; /** * @@ -70,11 +70,18 @@ public class EPGTimeListActivity extends FragmentActivity { */ ViewPager mViewPager; + private AlertDialog tagDialog; + private int firstVibleItem = 0; private int top = 0; private boolean lock; - private List m_scrollListeners = new ArrayList(); + private List m_fragmentListeners = new ArrayList(); + + private ArrayAdapter tagAdapter; + + private TextView tagTextView; + private ImageView tagImageView; @SuppressWarnings("deprecation") @Override @@ -85,6 +92,7 @@ protected void onCreate(Bundle savedInstanceState) { setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.epgnow_list_activity); // create timelost based on actual time @@ -114,6 +122,30 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.epgnow_list_title); + tagTextView = (TextView) findViewById(R.id.ct_title); + tagImageView = (ImageView) findViewById(R.id.ct_logo); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.menu_tags); + + tagAdapter = new ArrayAdapter(this, + android.R.layout.simple_dropdown_item_1line, + new ArrayList()); + + builder.setAdapter(tagAdapter, + new android.content.DialogInterface.OnClickListener() { + + public void onClick(DialogInterface arg0, int pos) { + ChannelTag tag = tagAdapter.getItem(pos); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.setCurrentTag(tag); + + setCurrentTag(tag); + notifyEPGChannelListChanged(); + } + }); + + tagDialog = builder.create(); } @Override @@ -169,10 +201,10 @@ public boolean onOptionsItemSelected(MenuItem item) { onSearchRequested(); return true; } - // case R.id.mi_tags: { - // tagDialog.show(); - // return true; - // } + case R.id.mi_tags: { + tagDialog.show(); + return true; + } default: { return super.onOptionsItemSelected(item); } @@ -218,12 +250,12 @@ public CharSequence getPageTitle(int position) { } - private void registerEPGScrollListener(EPGListScrollListener listener) { - m_scrollListeners.add(listener); + private void registerEPGFragmentListener(EPGFragmentListener listener) { + m_fragmentListeners.add(listener); } - private void unregisterEPGScrollListener(EPGListScrollListener listener) { - m_scrollListeners.remove(listener); + private void unregisterEPGFragmentListener(EPGFragmentListener listener) { + m_fragmentListeners.remove(listener); } private void notifyEPGScrollListener(AbsListView view, int position, int top) { @@ -234,7 +266,7 @@ private void notifyEPGScrollListener(AbsListView view, int position, int top) { lock = true; this.firstVibleItem = position; this.top = top; - for (EPGListScrollListener listener : m_scrollListeners) { + for (EPGFragmentListener listener : m_fragmentListeners) { listener.scrollTo(view, position, top); } } finally { @@ -242,12 +274,47 @@ private void notifyEPGScrollListener(AbsListView view, int position, int top) { } } + private void notifyEPGChannelListChanged() { + for (EPGFragmentListener listener : m_fragmentListeners) { + listener.populateChannelList(); + } + } + + private void setLoading(boolean loading) { + if (loading) { + // + } else { + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + tagAdapter.clear(); + for (ChannelTag t : app.getChannelTags()) { + tagAdapter.add(t); + } + setCurrentTag(app.getCurrentTag()); + } + } + + private void setCurrentTag(ChannelTag t) { + if (t == null) { + tagTextView.setText(R.string.pr_all_channels); + tagImageView.setImageResource(R.drawable.logo_72); + } else { + tagTextView.setText(t.name); + + if (t.iconBitmap != null) { + tagImageView.setImageBitmap(t.iconBitmap); + } else { + tagImageView.setImageResource(R.drawable.logo_72); + } + } + } + /** * A dummy fragment representing a section of the app, but that simply * displays dummy text. */ public class EPGListFragment extends ListFragment implements HTSListener, - EPGListScrollListener { + EPGFragmentListener { /** * The fragment argument representing the section number for this * fragment. @@ -267,10 +334,8 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // String timeSlot = getArguments().getString(ARG_TIME_SLOT); - TVHGuideApplication thv = (TVHGuideApplication) getActivity() - .getApplication(); - List list = thv.getChannels(); - prAdapter = new EPGTimeListAdapter(getActivity(), list, m_timeslot); + prAdapter = new EPGTimeListAdapter(getActivity(), + new ArrayList(), m_timeslot); prAdapter.sort(); setListAdapter(prAdapter); } @@ -285,7 +350,9 @@ public void onResume() { // scroll to aquired position getListView().setSelectionFromTop(firstVibleItem, top); - registerEPGScrollListener(this); + registerEPGFragmentListener(this); + + setLoading(app.isLoading()); } @Override @@ -294,11 +361,36 @@ public void onPause() { .getApplication(); app.removeListener(this); - unregisterEPGScrollListener(this); + unregisterEPGFragmentListener(this); super.onPause(); } + private void setLoading(boolean loading) { + EPGTimeListActivity.this.setLoading(loading); + if (loading) { + // + } else { + populateChannelList(); + } + } + + @Override + public void populateChannelList() { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + ChannelTag currentTag = app.getCurrentTag(); + prAdapter.clear(); + + for (Channel ch : app.getChannels()) { + if (currentTag == null || ch.hasTag(currentTag.id)) { + prAdapter.add(ch); + } + } + + prAdapter.sort(); + prAdapter.notifyDataSetChanged(); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -409,7 +501,16 @@ public void onCreateContextMenu(ContextMenu menu, View v, @Override public void onMessage(String action, final Object obj) { - if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { + if (action.equals(TVHGuideApplication.ACTION_LOADING)) { + + runOnUiThread(new Runnable() { + + public void run() { + boolean loading = (Boolean) obj; + setLoading(loading); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { getActivity().runOnUiThread(new Runnable() { public void run() { @@ -482,79 +583,10 @@ public void run() { } } - static interface EPGListScrollListener { + static interface EPGFragmentListener { public void scrollTo(AbsListView view, int position, int top); - } - - static class EPGTimeListAdapter extends ArrayAdapter { - - Activity context; - List list; - Date timeSlot; - - EPGTimeListAdapter(Activity context, List list, Date timeSlot) { - super(context, R.layout.epgnow_list_widget, list); - this.context = context; - this.list = list; - this.timeSlot = timeSlot; - } - - public void sort() { - sort(new Comparator() { - - public int compare(Channel x, Channel y) { - return x.compareTo(y); - } - }); - } - - public Programme getProgrammeAt(int position) { - Channel item = getItem(position); - return getProgrammeStartingAfter(item, timeSlot); - } - - public void updateView(ListView listView, Channel channel) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Channel pr = (Channel) listView.getItemAtPosition(pos); - - if (view.getTag() == null || pr == null) { - continue; - } - - if (channel.id != pr.id) { - continue; - } - - EPGTimeListViewWrapper wrapper = (EPGTimeListViewWrapper) view - .getTag(); - wrapper.repaint(channel); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - EPGTimeListViewWrapper wrapper = null; - - if (row == null) { - LayoutInflater inflater = context.getLayoutInflater(); - row = inflater - .inflate(R.layout.epgnow_list_widget, null, false); - - wrapper = new EPGTimeListViewWrapper(row, timeSlot); - row.setTag(wrapper); - - } else { - wrapper = (EPGTimeListViewWrapper) row.getTag(); - } - - Channel channel = getItem(position); - wrapper.repaint(channel); - return row; - } + public void populateChannelList(); } /** diff --git a/src/org/tvheadend/tvhguide/TVHGuideApplication.java b/src/org/tvheadend/tvhguide/TVHGuideApplication.java index 25ed687..6e0ab7a 100644 --- a/src/org/tvheadend/tvhguide/TVHGuideApplication.java +++ b/src/org/tvheadend/tvhguide/TVHGuideApplication.java @@ -32,8 +32,10 @@ import org.tvheadend.tvhguide.model.Subscription; import android.app.Application; +import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Handler; +import android.preference.PreferenceManager; import android.util.SparseArray; import android.widget.Toast; @@ -74,6 +76,10 @@ public class TVHGuideApplication extends Application { .synchronizedList(new ArrayList()); private volatile boolean loading = false; private Handler handler = new Handler(); + private ChannelTag m_currentTag; + private long m_selectedChannelTagId; + + private static final String PARAM_CHANNEL_TAG = "channel.tag"; public void addListener(HTSListener l) { listeners.add(l); @@ -91,6 +97,15 @@ private void broadcastMessage(String action, Object obj) { } } + @Override + public void onCreate() { + super.onCreate(); + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + m_selectedChannelTagId = prefs.getLong(PARAM_CHANNEL_TAG, -1); + } + public void broadcastError(final String error) { // Don't show error if no views are open synchronized (listeners) { @@ -124,6 +139,10 @@ public List getChannelTags() { public void addChannelTag(ChannelTag tag) { tags.add(tag); + if (tag.id == m_selectedChannelTagId) { + m_currentTag = tag; + } + if (!loading) { broadcastMessage(ACTION_TAG_ADD, tag); } @@ -413,4 +432,18 @@ public static SparseArray getContentTypes(Resources resources) { return ret; } + + public void setCurrentTag(ChannelTag t) { + m_currentTag = t; + + // store as well in shared preferences as last selected channel tag + long id = (t == null) ? -1 : t.id; + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + prefs.edit().putLong(PARAM_CHANNEL_TAG, id); + } + + public ChannelTag getCurrentTag() { + return m_currentTag; + } } From 704cc4a9341d4ba323b5695964a83151325ffdf3 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 17 Apr 2013 22:59:14 +0200 Subject: [PATCH 12/45] added constant to parse initial program informations --- .../tvhguide/EPGTimeListActivity.java | 2 +- .../tvheadend/tvhguide/htsp/HTSService.java | 1675 +++++++++-------- 2 files changed, 844 insertions(+), 833 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index d317c4d..03579f9 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -103,7 +103,7 @@ protected void onCreate(Bundle savedInstanceState) { cal.clear(Calendar.SECOND); cal.clear(Calendar.MILLISECOND); - for (int i = 0; i < 12; i++) { + for (int i = 0; i < HTSService.INITIAL_CHANNEL_LOADING; i++) { timeSlots.add(cal.getTime()); cal.add(Calendar.HOUR_OF_DAY, 1); } diff --git a/src/org/tvheadend/tvhguide/htsp/HTSService.java b/src/org/tvheadend/tvhguide/htsp/HTSService.java index 0b5ed1f..fb708fa 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSService.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSService.java @@ -18,15 +18,6 @@ */ package org.tvheadend.tvhguide.htsp; -import android.app.Service; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; @@ -42,6 +33,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; + import org.tvheadend.tvhguide.R; import org.tvheadend.tvhguide.TVHGuideApplication; import org.tvheadend.tvhguide.model.Channel; @@ -54,832 +46,851 @@ import org.tvheadend.tvhguide.model.Stream; import org.tvheadend.tvhguide.model.Subscription; +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + /** - * + * * @author john-tornblom */ public class HTSService extends Service implements HTSConnectionListener { - public static final String ACTION_CONNECT = "org.me.tvhguide.htsp.CONNECT"; - public static final String ACTION_DISCONNECT = "org.me.tvhguide.htsp.DISCONNECT"; - public static final String ACTION_EPG_QUERY = "org.me.tvhguide.htsp.EPG_QUERY"; - public static final String ACTION_GET_EVENT = "org.me.tvhguide.htsp.GET_EVENT"; - public static final String ACTION_GET_EVENTS = "org.me.tvhguide.htsp.GET_EVENTS"; - public static final String ACTION_DVR_ADD = "org.me.tvhguide.htsp.DVR_ADD"; - public static final String ACTION_DVR_DELETE = "org.me.tvhguide.htsp.DVR_DELETE"; - public static final String ACTION_DVR_CANCEL = "org.me.tvhguide.htsp.DVR_CANCEL"; - public static final String ACTION_SUBSCRIBE = "org.me.tvhguide.htsp.SUBSCRIBE"; - public static final String ACTION_UNSUBSCRIBE = "org.me.tvhguide.htsp.UNSUBSCRIBE"; - public static final String ACTION_FEEDBACK = "org.me.tvhguide.htsp.FEEDBACK"; - public static final String ACTION_GET_TICKET = "org.me.tvhguide.htsp.GET_TICKET"; - private static final String TAG = "HTSService"; - private ScheduledExecutorService execService; - private HTSConnection connection; - PackageInfo packInfo; - - public class LocalBinder extends Binder { - - HTSService getService() { - return HTSService.this; - } - } - - @Override - public void onCreate() { - execService = Executors.newScheduledThreadPool(5); - try { - packInfo = getPackageManager().getPackageInfo(getPackageName(), 0); - } catch (NameNotFoundException ex) { - Log.e(TAG, "Can't get package info", ex); - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (ACTION_CONNECT.equals(intent.getAction())) { - boolean force = intent.getBooleanExtra("force", false); - final String hostname = intent.getStringExtra("hostname"); - final int port = intent.getIntExtra("port", 9982); - final String username = intent.getStringExtra("username"); - final String password = intent.getStringExtra("password"); - - if (connection != null && force) { - connection.close(); - } - - if (connection == null || !connection.isConnected()) { - final TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.clearAll(); - app.setLoading(true); - connection = new HTSConnection(this, packInfo.packageName, packInfo.versionName); - - //Since this is blocking, spawn to a new thread - execService.execute(new Runnable() { - - public void run() { - connection.open(hostname, port); - connection.authenticate(username, password); - } - }); - } - } else if (connection == null || !connection.isConnected()) { - Log.e(TAG, "No connection to perform " + intent.getAction()); - } else if (ACTION_DISCONNECT.equals(intent.getAction())) { - connection.close(); - } else if (ACTION_GET_EVENT.equals(intent.getAction())) { - getEvent(intent.getLongExtra("eventId", 0)); - } else if (ACTION_GET_EVENTS.equals(intent.getAction())) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); - getEvents(ch, - intent.getLongExtra("eventId", 0), - intent.getIntExtra("count", 10)); - } else if (ACTION_DVR_ADD.equals(intent.getAction())) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); - addDvrEntry(ch, intent.getLongExtra("eventId", 0)); - } else if (ACTION_DVR_DELETE.equals(intent.getAction())) { - deleteDvrEntry(intent.getLongExtra("id", 0)); - } else if (ACTION_DVR_CANCEL.equals(intent.getAction())) { - cancelDvrEntry(intent.getLongExtra("id", 0)); - } else if (ACTION_EPG_QUERY.equals(intent.getAction())) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); - epgQuery(ch, - intent.getStringExtra("query"), - intent.getLongExtra("tagId", 0)); - } else if (ACTION_SUBSCRIBE.equals(intent.getAction())) { - subscribe(intent.getLongExtra("channelId", 0), - intent.getLongExtra("subscriptionId", 0), - intent.getIntExtra("maxWidth", 0), - intent.getIntExtra("maxHeight", 0), - intent.getStringExtra("audioCodec"), - intent.getStringExtra("videoCodec")); - } else if (ACTION_UNSUBSCRIBE.equals(intent.getAction())) { - unsubscribe(intent.getLongExtra("subscriptionId", 0)); - } else if (ACTION_FEEDBACK.equals(intent.getAction())) { - feedback(intent.getLongExtra("subscriptionId", 0), - intent.getIntExtra("speed", 0)); - } else if (ACTION_GET_TICKET.equals(intent.getAction())) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); - Recording rec = app.getRecording(intent.getLongExtra("dvrId", 0)); - if (ch != null) { - getTicket(ch); - } else if (rec != null) { - getTicket(rec); - } - } - - - return START_NOT_STICKY; - } - - @Override - public void onDestroy() { - execService.shutdown(); - if (connection != null) { - connection.close(); - } - } - - private void showError(final String error) { - if (error == null || error.length() < 0) { - return; - } - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.setLoading(false); - app.broadcastError(error); - } - - private void showError(int recourceId) { - showError(getString(recourceId)); - } - - public void onError(int errorCode) { - switch (errorCode) { - case HTSConnection.CONNECTION_LOST_ERROR: - showError(R.string.err_con_lost); - break; - case HTSConnection.TIMEOUT_ERROR: - showError("Connection timeout"); - break; - case HTSConnection.CONNECTION_REFUSED_ERROR: - showError(R.string.err_connect); - break; - case HTSConnection.HTS_AUTH_ERROR: - showError(R.string.err_auth); - break; - } - } - - public void onError(Exception ex) { - showError(ex.getLocalizedMessage()); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - private final IBinder mBinder = new LocalBinder(); - - private void onTagAdd(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - ChannelTag tag = new ChannelTag(); - tag.id = msg.getLong("tagId"); - tag.name = msg.getString("tagName", null); - tag.icon = msg.getString("tagIcon", null); - //tag.members = response.getIntList("members"); - app.addChannelTag(tag); - if (tag.icon != null) { - getChannelTagIcon(tag); - } - } - - private void onTagUpdate(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - ChannelTag tag = app.getChannelTag(msg.getLong("tagId")); - if (tag == null) { - return; - } - - tag.name = msg.getString("tagName", tag.name); - String icon = msg.getString("tagIcon", tag.icon); - if (icon == null) { - tag.icon = null; - tag.iconBitmap = null; - } else if (!icon.equals(tag.icon)) { - tag.icon = icon; - getChannelTagIcon(tag); - } - } - - private void onTagDelete(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeChannelTag(msg.getLong("tagId")); - } - - private void onChannelAdd(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - final Channel ch = new Channel(); - ch.id = msg.getLong("channelId"); - ch.name = msg.getString("channelName", null); - ch.number = msg.getInt("channelNumber", 0); - ch.icon = msg.getString("channelIcon", null); - ch.tags = msg.getIntList("tags", ch.tags); - - if (ch.number == 0) { - ch.number = (int) (ch.id + 25000); - } - - app.addChannel(ch); - if (ch.icon != null) { - getChannelIcon(ch); - } - long currEventId = msg.getLong("eventId", 0); - long nextEventId = msg.getLong("nextEventId", 0); - - ch.isTransmitting = currEventId != 0; - - if (currEventId > 0) { - getEvents(ch, currEventId, 5); - } else if (nextEventId > 0) { - getEvents(ch, nextEventId, 5); - } - } - - private void onChannelUpdate(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - - final Channel ch = app.getChannel(msg.getLong("channelId")); - if (ch == null) { - return; - } - - ch.name = msg.getString("channelName", ch.name); - ch.number = msg.getInt("channelNumber", ch.number); - String icon = msg.getString("channelIcon", ch.icon); - ch.tags = msg.getIntList("tags", ch.tags); - - if (icon == null) { - ch.icon = null; - ch.iconBitmap = null; - } else if (!icon.equals(ch.icon)) { - ch.icon = icon; - getChannelIcon(ch); - } - //Remove programmes that have ended - long currEventId = msg.getLong("eventId", 0); - long nextEventId = msg.getLong("nextEventId", 0); - - ch.isTransmitting = currEventId != 0; - - Iterator it = ch.epg.iterator(); - ArrayList tmp = new ArrayList(); - - while (it.hasNext() && currEventId > 0) { - Programme p = it.next(); - if (p.id != currEventId) { - tmp.add(p); - } else { - break; - } - } - ch.epg.removeAll(tmp); - - for (Programme p : tmp) { - app.removeProgramme(p); - } - - final long eventId = currEventId != 0 ? currEventId : nextEventId; - if (eventId > 0 && ch.epg.size() < 2) { - execService.schedule(new Runnable() { - - public void run() { - getEvents(ch, eventId, 5); - } - }, 30, TimeUnit.SECONDS); - } else { - app.updateChannel(ch); - } - } - - private void onChannelDelete(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeChannel(msg.getLong("channelId")); - } - - private void onDvrEntryAdd(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Recording rec = new Recording(); - rec.id = msg.getLong("id"); - rec.description = msg.getString("description", ""); - rec.summary = msg.getString("summary", ""); - rec.error = msg.getString("error", null); - rec.start = msg.getDate("start"); - rec.state = msg.getString("state", null); - rec.stop = msg.getDate("stop"); - rec.title = msg.getString("title", null); - rec.channel = app.getChannel(msg.getLong("channel")); - if (rec.channel != null) { - rec.channel.recordings.add(rec); - } - app.addRecording(rec); - } - - private void onDvrEntryUpdate(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Recording rec = app.getRecording(msg.getLong("id")); - if (rec == null) { - return; - } - - rec.description = msg.getString("description", rec.description); - rec.summary = msg.getString("summary", rec.summary); - rec.error = msg.getString("error", rec.error); - rec.start = msg.getDate("start"); - rec.state = msg.getString("state", rec.state); - rec.stop = msg.getDate("stop"); - rec.title = msg.getString("title", rec.title); - app.updateRecording(rec); - } - - private void onDvrEntryDelete(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Recording rec = app.getRecording(msg.getLong("id")); - - if (rec == null || rec.channel == null) { - return; - } - - rec.channel.recordings.remove(rec); - for (Programme p : rec.channel.epg) { - if (p.recording == rec) { - p.recording = null; - app.updateProgramme(p); - break; - } - } - app.removeRecording(rec); - } - - private void onInitialSyncCompleted(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.setLoading(false); - } - - private void onStartSubscription(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Subscription subscription = app.getSubscription(msg.getLong("subscriptionId")); - if (subscription == null) { - return; - } - - for (Object obj : msg.getList("streams")) { - Stream s = new Stream(); - HTSMessage sub = (HTSMessage) obj; - - s.index = sub.getInt("index"); - s.type = sub.getString("type"); - s.language = sub.getString("language", ""); - s.width = sub.getInt("width", 0); - s.height = sub.getInt("height", 0); - - subscription.streams.add(s); - } - } - - private void onSubscriptionStatus(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Subscription s = app.getSubscription(msg.getLong("subscriptionId")); - if (s == null) { - return; - } - - String status = msg.getString("status", null); - if (s.status == null ? status != null : !s.status.equals(status)) { - s.status = status; - app.updateSubscription(s); - } - } - - private void onSubscriptionStop(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Subscription s = app.getSubscription(msg.getLong("subscriptionId")); - if (s == null) { - return; - } - - String status = msg.getString("status", null); - if (s.status == null ? status != null : !s.status.equals(status)) { - s.status = status; - app.updateSubscription(s); - } - app.removeSubscription(s); - } - - private void onMuxPacket(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Subscription sub = app.getSubscription(msg.getLong("subscriptionId")); - if (sub == null) { - return; - } - - Packet packet = new Packet(); - packet.dts = msg.getLong("dts", 0); - packet.pts = msg.getLong("pts", 0); - packet.duration = msg.getLong("duration"); - packet.frametype = msg.getInt("frametype"); - packet.payload = msg.getByteArray("payload"); - - for (Stream st : sub.streams) { - if (st.index == msg.getInt("stream")) { - packet.stream = st; - } - } - packet.subscription = sub; - app.broadcastPacket(packet); - } - - private void onQueueStatus(HTSMessage msg) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Subscription sub = app.getSubscription(msg.getLong("subscriptionId")); - if (sub == null) { - return; - } - if (msg.containsField("delay")) { - BigInteger delay = msg.getBigInteger("delay"); - delay = delay.divide(BigInteger.valueOf((1000))); - sub.delay = delay.longValue(); - } - sub.droppedBFrames = msg.getLong("Bdrops", sub.droppedBFrames); - sub.droppedIFrames = msg.getLong("Idrops", sub.droppedIFrames); - sub.droppedPFrames = msg.getLong("Pdrops", sub.droppedPFrames); - sub.packetCount = msg.getLong("packets", sub.packetCount); - sub.queSize = msg.getLong("bytes", sub.queSize); - - app.updateSubscription(sub); - } - - public void onMessage(HTSMessage msg) { - String method = msg.getMethod(); - if (method.equals("tagAdd")) { - onTagAdd(msg); - } else if (method.equals("tagUpdate")) { - onTagUpdate(msg); - } else if (method.equals("tagDelete")) { - onTagDelete(msg); - } else if (method.equals("channelAdd")) { - onChannelAdd(msg); - } else if (method.equals("channelUpdate")) { - onChannelUpdate(msg); - } else if (method.equals("channelDelete")) { - onChannelDelete(msg); - } else if (method.equals("initialSyncCompleted")) { - onInitialSyncCompleted(msg); - } else if (method.equals("dvrEntryAdd")) { - onDvrEntryAdd(msg); - } else if (method.equals("dvrEntryUpdate")) { - onDvrEntryUpdate(msg); - } else if (method.equals("dvrEntryDelete")) { - onDvrEntryDelete(msg); - } else if (method.equals("subscriptionStart")) { - onStartSubscription(msg); - } else if (method.equals("subscriptionStatus")) { - onSubscriptionStatus(msg); - } else if (method.equals("subscriptionStop")) { - onSubscriptionStop(msg); - } else if (method.equals("muxpkt")) { - onMuxPacket(msg); - } else if (method.equals("queueStatus")) { - onQueueStatus(msg); - } else { - Log.d(TAG, method.toString()); - } - } - - public String hashString(String s) { - try { - MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); - digest.update(s.getBytes()); - byte messageDigest[] = digest.digest(); - - StringBuilder hexString = new StringBuilder(); - for (int i = 0; i < messageDigest.length; i++) { - hexString.append(Integer.toHexString(0xFF & messageDigest[i])); - } - return hexString.toString(); - - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, "Can't create hash string", e); - } - - return ""; - } - - public void cacheImage(String url, File f) throws MalformedURLException, IOException { - Log.d(TAG, "Caching " + url + " as " + f.toString()); - - BufferedInputStream is = new BufferedInputStream(new URL(url).openStream()); - OutputStream os = new FileOutputStream(f); - - float scale = getResources().getDisplayMetrics().scaledDensity; - int width = (int) (64 * scale); - int height = (int) (64 * scale); - - BitmapFactory.Options o = new BitmapFactory.Options(); - o.outWidth = width; - o.outHeight = height; - - Bitmap bitmap = BitmapFactory.decodeStream(is, null, o); - - Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); - bitmap.recycle(); - - resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, os); - resizedBitmap.recycle(); - - os.close(); - is.close(); - } - - private Bitmap getIcon(final String url) throws MalformedURLException, IOException { - if (url == null || url.length() == 0) { - return null; - } - - File dir = getCacheDir(); - File f = new File(dir, hashString(url) + ".png"); - - if (!f.exists()) { - cacheImage(url, f); - } - - return BitmapFactory.decodeFile(f.toString()); - } - - private void getChannelIcon(final Channel ch) { - execService.execute(new Runnable() { - - public void run() { - - try { - ch.iconBitmap = getIcon(ch.icon); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.updateChannel(ch); - } catch (Throwable ex) { - Log.e(TAG, "Can't load channel icon", ex); - } - } - }); - } - - private void getChannelTagIcon(final ChannelTag tag) { - execService.execute(new Runnable() { - - public void run() { - - try { - tag.iconBitmap = getIcon(tag.icon); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.updateChannelTag(tag); - } catch (Throwable ex) { - Log.e(TAG, "Can't load tag icon", ex); - } - } - }); - } - - private void getEvents(final Channel ch, final long eventId, int cnt) { - if (ch == null) { - return; - } - - HTSMessage request = new HTSMessage(); - request.setMethod("getEvents"); - request.putField("eventId", eventId); - request.putField("numFollowing", cnt); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - - if (!response.containsKey("events")) { - return; - } - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - - for (Object obj : response.getList("events")) { - Programme p = new Programme(); - HTSMessage sub = (HTSMessage) obj; - p.id = sub.getLong("eventId", 0); - p.nextId = sub.getLong("nextEventId", 0); - p.description = sub.getString("description", ""); - p.summary = sub.getString("summary", ""); - p.recording = app.getRecording(sub.getLong("dvrId", 0)); - p.contentType = sub.getInt("contentType", 0); - p.title = sub.getString("title"); - p.start = sub.getDate("start"); - p.stop = sub.getDate("stop"); - p.seriesInfo = buildSeriesInfo(sub); - p.starRating = sub.getInt("starRating", -1); - - p.channel = ch; - if (ch.epg.add(p)) { - app.addProgramme(p); - } - } - app.updateChannel(ch); - } - }); - } - - private void getEvent(long eventId) { - HTSMessage request = new HTSMessage(); - request.setMethod("getEvent"); - request.putField("eventId", eventId); - - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(response.getLong("channelId")); - Programme p = new Programme(); - p.id = response.getLong("eventId"); - p.nextId = response.getLong("nextEventId", 0); - p.description = response.getString("description", ""); - p.summary = response.getString("summary", ""); - p.recording = app.getRecording(response.getLong("dvrId", 0)); - p.contentType = response.getInt("contentType", 0); - p.title = response.getString("title"); - p.start = response.getDate("start"); - p.stop = response.getDate("stop"); - p.seriesInfo = buildSeriesInfo(response); - p.starRating = response.getInt("starRating", -1); - - p.channel = ch; - - if (ch.epg.add(p)) { - app.addProgramme(p); - app.updateChannel(ch); - } - } - }); - } - - private SeriesInfo buildSeriesInfo(HTSMessage msg) { - SeriesInfo info = new SeriesInfo(); - - info.episodeCount = msg.getInt("episodeCount", 0); - info.episodeNumber = msg.getInt("episodeNumber", 0); - info.onScreen = msg.getString("onScreen", ""); - info.partCount = msg.getInt("partCount", 0); - info.partNumber = msg.getInt("partNumber", 0); - info.seasonCount = msg.getInt("seasonCount", 0); - info.seasonNumber = msg.getInt("seasonNumber", 0); - - return info; - } - - private void epgQuery(final Channel ch, String query, long tagId) { - HTSMessage request = new HTSMessage(); - request.setMethod("epgQuery"); - request.putField("query", query); - if (ch != null) { - request.putField("channelId", ch.id); - } - if (tagId > 0) { - request.putField("tagId", tagId); - } - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - - if (!response.containsKey("eventIds")) { - return; - } - - for (Long id : response.getLongList("eventIds")) { - getEvent(id); - } - } - }); - } - - private void cancelDvrEntry(long id) { - HTSMessage request = new HTSMessage(); - request.setMethod("cancelDvrEntry"); - request.putField("id", id); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - - boolean success = response.getInt("success", 0) == 1; - } - }); - } - - private void deleteDvrEntry(long id) { - HTSMessage request = new HTSMessage(); - request.setMethod("deleteDvrEntry"); - request.putField("id", id); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - - boolean success = response.getInt("success", 0) == 1; - } - }); - } - - private void addDvrEntry(final Channel ch, final long eventId) { - HTSMessage request = new HTSMessage(); - request.setMethod("addDvrEntry"); - request.putField("eventId", eventId); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - if (response.getInt("success", 0) == 1) { - for (Programme p : ch.epg) { - if (p.id == eventId) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - p.recording = app.getRecording(response.getLong("id", 0)); - app.updateProgramme(p); - break; - } - } - } - String error = response.getString("error", null); - } - }); - } - - private void subscribe(long channelId, long subscriptionId, int maxWidth, int maxHeight, String aCodec, String vCodec) { - Subscription subscription = new Subscription(); - subscription.id = subscriptionId; - subscription.status = "Subscribing"; - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addSubscription(subscription); - - HTSMessage request = new HTSMessage(); - request.setMethod("subscribe"); - request.putField("channelId", channelId); - request.putField("maxWidth", maxWidth); - request.putField("maxHeight", maxHeight); - request.putField("audioCodec", aCodec); - request.putField("videoCodec", vCodec); - request.putField("subscriptionId", subscriptionId); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - //NOP - } - }); - } - - private void unsubscribe(long subscriptionId) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeSubscription(subscriptionId); - - HTSMessage request = new HTSMessage(); - request.setMethod("unsubscribe"); - request.putField("subscriptionId", subscriptionId); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - //NOP - } - }); - } - - private void feedback(long subscriptionId, int speed) { - HTSMessage request = new HTSMessage(); - request.setMethod("feedback"); - request.putField("subscriptionId", subscriptionId); - request.putField("speed", speed); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - //NOP - } - }); - } - - private void getTicket(Channel ch) { - HTSMessage request = new HTSMessage(); - request.setMethod("getTicket"); - request.putField("channelId", ch.id); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - String path = response.getString("path", null); - String ticket = response.getString("ticket", null); - - if (path != null && ticket != null) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addTicket(new HttpTicket(path, ticket)); - } - } - }); - } - - private void getTicket(Recording rec) { - HTSMessage request = new HTSMessage(); - request.setMethod("getTicket"); - request.putField("dvrId", rec.id); - connection.sendMessage(request, new HTSResponseHandler() { - - public void handleResponse(HTSMessage response) { - String path = response.getString("path", null); - String ticket = response.getString("ticket", null); - - if (path != null && ticket != null) { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addTicket(new HttpTicket(path, ticket)); - } - } - }); - } + public static final String ACTION_CONNECT = "org.me.tvhguide.htsp.CONNECT"; + public static final String ACTION_DISCONNECT = "org.me.tvhguide.htsp.DISCONNECT"; + public static final String ACTION_EPG_QUERY = "org.me.tvhguide.htsp.EPG_QUERY"; + public static final String ACTION_GET_EVENT = "org.me.tvhguide.htsp.GET_EVENT"; + public static final String ACTION_GET_EVENTS = "org.me.tvhguide.htsp.GET_EVENTS"; + public static final String ACTION_DVR_ADD = "org.me.tvhguide.htsp.DVR_ADD"; + public static final String ACTION_DVR_DELETE = "org.me.tvhguide.htsp.DVR_DELETE"; + public static final String ACTION_DVR_CANCEL = "org.me.tvhguide.htsp.DVR_CANCEL"; + public static final String ACTION_SUBSCRIBE = "org.me.tvhguide.htsp.SUBSCRIBE"; + public static final String ACTION_UNSUBSCRIBE = "org.me.tvhguide.htsp.UNSUBSCRIBE"; + public static final String ACTION_FEEDBACK = "org.me.tvhguide.htsp.FEEDBACK"; + public static final String ACTION_GET_TICKET = "org.me.tvhguide.htsp.GET_TICKET"; + private static final String TAG = "HTSService"; + private ScheduledExecutorService execService; + private HTSConnection connection; + PackageInfo packInfo; + + public static final int INITIAL_CHANNEL_LOADING = 10; + + public class LocalBinder extends Binder { + + HTSService getService() { + return HTSService.this; + } + } + + @Override + public void onCreate() { + execService = Executors.newScheduledThreadPool(5); + try { + packInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + } catch (NameNotFoundException ex) { + Log.e(TAG, "Can't get package info", ex); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (ACTION_CONNECT.equals(intent.getAction())) { + boolean force = intent.getBooleanExtra("force", false); + final String hostname = intent.getStringExtra("hostname"); + final int port = intent.getIntExtra("port", 9982); + final String username = intent.getStringExtra("username"); + final String password = intent.getStringExtra("password"); + + if (connection != null && force) { + connection.close(); + } + + if (connection == null || !connection.isConnected()) { + final TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.clearAll(); + app.setLoading(true); + connection = new HTSConnection(this, packInfo.packageName, + packInfo.versionName); + + // Since this is blocking, spawn to a new thread + execService.execute(new Runnable() { + + public void run() { + connection.open(hostname, port); + connection.authenticate(username, password); + } + }); + } + } else if (connection == null || !connection.isConnected()) { + Log.e(TAG, "No connection to perform " + intent.getAction()); + } else if (ACTION_DISCONNECT.equals(intent.getAction())) { + connection.close(); + } else if (ACTION_GET_EVENT.equals(intent.getAction())) { + getEvent(intent.getLongExtra("eventId", 0)); + } else if (ACTION_GET_EVENTS.equals(intent.getAction())) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); + getEvents(ch, intent.getLongExtra("eventId", 0), + intent.getIntExtra("count", 10)); + } else if (ACTION_DVR_ADD.equals(intent.getAction())) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); + addDvrEntry(ch, intent.getLongExtra("eventId", 0)); + } else if (ACTION_DVR_DELETE.equals(intent.getAction())) { + deleteDvrEntry(intent.getLongExtra("id", 0)); + } else if (ACTION_DVR_CANCEL.equals(intent.getAction())) { + cancelDvrEntry(intent.getLongExtra("id", 0)); + } else if (ACTION_EPG_QUERY.equals(intent.getAction())) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); + epgQuery(ch, intent.getStringExtra("query"), + intent.getLongExtra("tagId", 0)); + } else if (ACTION_SUBSCRIBE.equals(intent.getAction())) { + subscribe(intent.getLongExtra("channelId", 0), + intent.getLongExtra("subscriptionId", 0), + intent.getIntExtra("maxWidth", 0), + intent.getIntExtra("maxHeight", 0), + intent.getStringExtra("audioCodec"), + intent.getStringExtra("videoCodec")); + } else if (ACTION_UNSUBSCRIBE.equals(intent.getAction())) { + unsubscribe(intent.getLongExtra("subscriptionId", 0)); + } else if (ACTION_FEEDBACK.equals(intent.getAction())) { + feedback(intent.getLongExtra("subscriptionId", 0), + intent.getIntExtra("speed", 0)); + } else if (ACTION_GET_TICKET.equals(intent.getAction())) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); + Recording rec = app.getRecording(intent.getLongExtra("dvrId", 0)); + if (ch != null) { + getTicket(ch); + } else if (rec != null) { + getTicket(rec); + } + } + + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + execService.shutdown(); + if (connection != null) { + connection.close(); + } + } + + private void showError(final String error) { + if (error == null || error.length() < 0) { + return; + } + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.setLoading(false); + app.broadcastError(error); + } + + private void showError(int recourceId) { + showError(getString(recourceId)); + } + + public void onError(int errorCode) { + switch (errorCode) { + case HTSConnection.CONNECTION_LOST_ERROR: + showError(R.string.err_con_lost); + break; + case HTSConnection.TIMEOUT_ERROR: + showError("Connection timeout"); + break; + case HTSConnection.CONNECTION_REFUSED_ERROR: + showError(R.string.err_connect); + break; + case HTSConnection.HTS_AUTH_ERROR: + showError(R.string.err_auth); + break; + } + } + + public void onError(Exception ex) { + showError(ex.getLocalizedMessage()); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + private final IBinder mBinder = new LocalBinder(); + + private void onTagAdd(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + ChannelTag tag = new ChannelTag(); + tag.id = msg.getLong("tagId"); + tag.name = msg.getString("tagName", null); + tag.icon = msg.getString("tagIcon", null); + // tag.members = response.getIntList("members"); + app.addChannelTag(tag); + if (tag.icon != null) { + getChannelTagIcon(tag); + } + } + + private void onTagUpdate(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + ChannelTag tag = app.getChannelTag(msg.getLong("tagId")); + if (tag == null) { + return; + } + + tag.name = msg.getString("tagName", tag.name); + String icon = msg.getString("tagIcon", tag.icon); + if (icon == null) { + tag.icon = null; + tag.iconBitmap = null; + } else if (!icon.equals(tag.icon)) { + tag.icon = icon; + getChannelTagIcon(tag); + } + } + + private void onTagDelete(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeChannelTag(msg.getLong("tagId")); + } + + private void onChannelAdd(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + final Channel ch = new Channel(); + ch.id = msg.getLong("channelId"); + ch.name = msg.getString("channelName", null); + ch.number = msg.getInt("channelNumber", 0); + ch.icon = msg.getString("channelIcon", null); + ch.tags = msg.getIntList("tags", ch.tags); + + if (ch.number == 0) { + ch.number = (int) (ch.id + 25000); + } + + app.addChannel(ch); + if (ch.icon != null) { + getChannelIcon(ch); + } + long currEventId = msg.getLong("eventId", 0); + long nextEventId = msg.getLong("nextEventId", 0); + + ch.isTransmitting = currEventId != 0; + + if (currEventId > 0) { + getEvents(ch, currEventId, INITIAL_CHANNEL_LOADING); + } else if (nextEventId > 0) { + getEvents(ch, nextEventId, INITIAL_CHANNEL_LOADING); + } + } + + private void onChannelUpdate(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + + final Channel ch = app.getChannel(msg.getLong("channelId")); + if (ch == null) { + return; + } + + ch.name = msg.getString("channelName", ch.name); + ch.number = msg.getInt("channelNumber", ch.number); + String icon = msg.getString("channelIcon", ch.icon); + ch.tags = msg.getIntList("tags", ch.tags); + + if (icon == null) { + ch.icon = null; + ch.iconBitmap = null; + } else if (!icon.equals(ch.icon)) { + ch.icon = icon; + getChannelIcon(ch); + } + // Remove programmes that have ended + long currEventId = msg.getLong("eventId", 0); + long nextEventId = msg.getLong("nextEventId", 0); + + ch.isTransmitting = currEventId != 0; + + Iterator it = ch.epg.iterator(); + ArrayList tmp = new ArrayList(); + + while (it.hasNext() && currEventId > 0) { + Programme p = it.next(); + if (p.id != currEventId) { + tmp.add(p); + } else { + break; + } + } + ch.epg.removeAll(tmp); + + for (Programme p : tmp) { + app.removeProgramme(p); + } + + final long eventId = currEventId != 0 ? currEventId : nextEventId; + if (eventId > 0 && ch.epg.size() < 2) { + execService.schedule(new Runnable() { + + public void run() { + getEvents(ch, eventId, INITIAL_CHANNEL_LOADING); + } + }, 30, TimeUnit.SECONDS); + } else { + app.updateChannel(ch); + } + } + + private void onChannelDelete(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeChannel(msg.getLong("channelId")); + } + + private void onDvrEntryAdd(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Recording rec = new Recording(); + rec.id = msg.getLong("id"); + rec.description = msg.getString("description", ""); + rec.summary = msg.getString("summary", ""); + rec.error = msg.getString("error", null); + rec.start = msg.getDate("start"); + rec.state = msg.getString("state", null); + rec.stop = msg.getDate("stop"); + rec.title = msg.getString("title", null); + rec.channel = app.getChannel(msg.getLong("channel")); + if (rec.channel != null) { + rec.channel.recordings.add(rec); + } + app.addRecording(rec); + } + + private void onDvrEntryUpdate(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Recording rec = app.getRecording(msg.getLong("id")); + if (rec == null) { + return; + } + + rec.description = msg.getString("description", rec.description); + rec.summary = msg.getString("summary", rec.summary); + rec.error = msg.getString("error", rec.error); + rec.start = msg.getDate("start"); + rec.state = msg.getString("state", rec.state); + rec.stop = msg.getDate("stop"); + rec.title = msg.getString("title", rec.title); + app.updateRecording(rec); + } + + private void onDvrEntryDelete(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Recording rec = app.getRecording(msg.getLong("id")); + + if (rec == null || rec.channel == null) { + return; + } + + rec.channel.recordings.remove(rec); + for (Programme p : rec.channel.epg) { + if (p.recording == rec) { + p.recording = null; + app.updateProgramme(p); + break; + } + } + app.removeRecording(rec); + } + + private void onInitialSyncCompleted(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.setLoading(false); + } + + private void onStartSubscription(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Subscription subscription = app.getSubscription(msg + .getLong("subscriptionId")); + if (subscription == null) { + return; + } + + for (Object obj : msg.getList("streams")) { + Stream s = new Stream(); + HTSMessage sub = (HTSMessage) obj; + + s.index = sub.getInt("index"); + s.type = sub.getString("type"); + s.language = sub.getString("language", ""); + s.width = sub.getInt("width", 0); + s.height = sub.getInt("height", 0); + + subscription.streams.add(s); + } + } + + private void onSubscriptionStatus(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Subscription s = app.getSubscription(msg.getLong("subscriptionId")); + if (s == null) { + return; + } + + String status = msg.getString("status", null); + if (s.status == null ? status != null : !s.status.equals(status)) { + s.status = status; + app.updateSubscription(s); + } + } + + private void onSubscriptionStop(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Subscription s = app.getSubscription(msg.getLong("subscriptionId")); + if (s == null) { + return; + } + + String status = msg.getString("status", null); + if (s.status == null ? status != null : !s.status.equals(status)) { + s.status = status; + app.updateSubscription(s); + } + app.removeSubscription(s); + } + + private void onMuxPacket(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Subscription sub = app.getSubscription(msg.getLong("subscriptionId")); + if (sub == null) { + return; + } + + Packet packet = new Packet(); + packet.dts = msg.getLong("dts", 0); + packet.pts = msg.getLong("pts", 0); + packet.duration = msg.getLong("duration"); + packet.frametype = msg.getInt("frametype"); + packet.payload = msg.getByteArray("payload"); + + for (Stream st : sub.streams) { + if (st.index == msg.getInt("stream")) { + packet.stream = st; + } + } + packet.subscription = sub; + app.broadcastPacket(packet); + } + + private void onQueueStatus(HTSMessage msg) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Subscription sub = app.getSubscription(msg.getLong("subscriptionId")); + if (sub == null) { + return; + } + if (msg.containsField("delay")) { + BigInteger delay = msg.getBigInteger("delay"); + delay = delay.divide(BigInteger.valueOf((1000))); + sub.delay = delay.longValue(); + } + sub.droppedBFrames = msg.getLong("Bdrops", sub.droppedBFrames); + sub.droppedIFrames = msg.getLong("Idrops", sub.droppedIFrames); + sub.droppedPFrames = msg.getLong("Pdrops", sub.droppedPFrames); + sub.packetCount = msg.getLong("packets", sub.packetCount); + sub.queSize = msg.getLong("bytes", sub.queSize); + + app.updateSubscription(sub); + } + + public void onMessage(HTSMessage msg) { + String method = msg.getMethod(); + if (method.equals("tagAdd")) { + onTagAdd(msg); + } else if (method.equals("tagUpdate")) { + onTagUpdate(msg); + } else if (method.equals("tagDelete")) { + onTagDelete(msg); + } else if (method.equals("channelAdd")) { + onChannelAdd(msg); + } else if (method.equals("channelUpdate")) { + onChannelUpdate(msg); + } else if (method.equals("channelDelete")) { + onChannelDelete(msg); + } else if (method.equals("initialSyncCompleted")) { + onInitialSyncCompleted(msg); + } else if (method.equals("dvrEntryAdd")) { + onDvrEntryAdd(msg); + } else if (method.equals("dvrEntryUpdate")) { + onDvrEntryUpdate(msg); + } else if (method.equals("dvrEntryDelete")) { + onDvrEntryDelete(msg); + } else if (method.equals("subscriptionStart")) { + onStartSubscription(msg); + } else if (method.equals("subscriptionStatus")) { + onSubscriptionStatus(msg); + } else if (method.equals("subscriptionStop")) { + onSubscriptionStop(msg); + } else if (method.equals("muxpkt")) { + onMuxPacket(msg); + } else if (method.equals("queueStatus")) { + onQueueStatus(msg); + } else { + Log.d(TAG, method.toString()); + } + } + + public String hashString(String s) { + try { + MessageDigest digest = java.security.MessageDigest + .getInstance("MD5"); + digest.update(s.getBytes()); + byte messageDigest[] = digest.digest(); + + StringBuilder hexString = new StringBuilder(); + for (int i = 0; i < messageDigest.length; i++) { + hexString.append(Integer.toHexString(0xFF & messageDigest[i])); + } + return hexString.toString(); + + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "Can't create hash string", e); + } + + return ""; + } + + public void cacheImage(String url, File f) throws MalformedURLException, + IOException { + Log.d(TAG, "Caching " + url + " as " + f.toString()); + + BufferedInputStream is = new BufferedInputStream( + new URL(url).openStream()); + OutputStream os = new FileOutputStream(f); + + float scale = getResources().getDisplayMetrics().scaledDensity; + int width = (int) (64 * scale); + int height = (int) (64 * scale); + + BitmapFactory.Options o = new BitmapFactory.Options(); + o.outWidth = width; + o.outHeight = height; + + Bitmap bitmap = BitmapFactory.decodeStream(is, null, o); + + Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, width, height, + true); + bitmap.recycle(); + + resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, os); + resizedBitmap.recycle(); + + os.close(); + is.close(); + } + + private Bitmap getIcon(final String url) throws MalformedURLException, + IOException { + if (url == null || url.length() == 0) { + return null; + } + + File dir = getCacheDir(); + File f = new File(dir, hashString(url) + ".png"); + + if (!f.exists()) { + cacheImage(url, f); + } + + return BitmapFactory.decodeFile(f.toString()); + } + + private void getChannelIcon(final Channel ch) { + execService.execute(new Runnable() { + + public void run() { + + try { + ch.iconBitmap = getIcon(ch.icon); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.updateChannel(ch); + } catch (Throwable ex) { + Log.e(TAG, "Can't load channel icon", ex); + } + } + }); + } + + private void getChannelTagIcon(final ChannelTag tag) { + execService.execute(new Runnable() { + + public void run() { + + try { + tag.iconBitmap = getIcon(tag.icon); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.updateChannelTag(tag); + } catch (Throwable ex) { + Log.e(TAG, "Can't load tag icon", ex); + } + } + }); + } + + private void getEvents(final Channel ch, final long eventId, int cnt) { + if (ch == null) { + return; + } + + HTSMessage request = new HTSMessage(); + request.setMethod("getEvents"); + request.putField("eventId", eventId); + request.putField("numFollowing", cnt); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + + if (!response.containsKey("events")) { + return; + } + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + + for (Object obj : response.getList("events")) { + Programme p = new Programme(); + HTSMessage sub = (HTSMessage) obj; + p.id = sub.getLong("eventId", 0); + p.nextId = sub.getLong("nextEventId", 0); + p.description = sub.getString("description", ""); + p.summary = sub.getString("summary", ""); + p.recording = app.getRecording(sub.getLong("dvrId", 0)); + p.contentType = sub.getInt("contentType", 0); + p.title = sub.getString("title"); + p.start = sub.getDate("start"); + p.stop = sub.getDate("stop"); + p.seriesInfo = buildSeriesInfo(sub); + p.starRating = sub.getInt("starRating", -1); + + p.channel = ch; + if (ch.epg.add(p)) { + app.addProgramme(p); + } + } + app.updateChannel(ch); + } + }); + } + + private void getEvent(long eventId) { + HTSMessage request = new HTSMessage(); + request.setMethod("getEvent"); + request.putField("eventId", eventId); + + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + Channel ch = app.getChannel(response.getLong("channelId")); + Programme p = new Programme(); + p.id = response.getLong("eventId"); + p.nextId = response.getLong("nextEventId", 0); + p.description = response.getString("description", ""); + p.summary = response.getString("summary", ""); + p.recording = app.getRecording(response.getLong("dvrId", 0)); + p.contentType = response.getInt("contentType", 0); + p.title = response.getString("title"); + p.start = response.getDate("start"); + p.stop = response.getDate("stop"); + p.seriesInfo = buildSeriesInfo(response); + p.starRating = response.getInt("starRating", -1); + + p.channel = ch; + + if (ch.epg.add(p)) { + app.addProgramme(p); + app.updateChannel(ch); + } + } + }); + } + + private SeriesInfo buildSeriesInfo(HTSMessage msg) { + SeriesInfo info = new SeriesInfo(); + + info.episodeCount = msg.getInt("episodeCount", 0); + info.episodeNumber = msg.getInt("episodeNumber", 0); + info.onScreen = msg.getString("onScreen", ""); + info.partCount = msg.getInt("partCount", 0); + info.partNumber = msg.getInt("partNumber", 0); + info.seasonCount = msg.getInt("seasonCount", 0); + info.seasonNumber = msg.getInt("seasonNumber", 0); + + return info; + } + + private void epgQuery(final Channel ch, String query, long tagId) { + HTSMessage request = new HTSMessage(); + request.setMethod("epgQuery"); + request.putField("query", query); + if (ch != null) { + request.putField("channelId", ch.id); + } + if (tagId > 0) { + request.putField("tagId", tagId); + } + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + + if (!response.containsKey("eventIds")) { + return; + } + + for (Long id : response.getLongList("eventIds")) { + getEvent(id); + } + } + }); + } + + private void cancelDvrEntry(long id) { + HTSMessage request = new HTSMessage(); + request.setMethod("cancelDvrEntry"); + request.putField("id", id); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + + boolean success = response.getInt("success", 0) == 1; + } + }); + } + + private void deleteDvrEntry(long id) { + HTSMessage request = new HTSMessage(); + request.setMethod("deleteDvrEntry"); + request.putField("id", id); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + + boolean success = response.getInt("success", 0) == 1; + } + }); + } + + private void addDvrEntry(final Channel ch, final long eventId) { + HTSMessage request = new HTSMessage(); + request.setMethod("addDvrEntry"); + request.putField("eventId", eventId); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + if (response.getInt("success", 0) == 1) { + for (Programme p : ch.epg) { + if (p.id == eventId) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + p.recording = app.getRecording(response.getLong( + "id", 0)); + app.updateProgramme(p); + break; + } + } + } + String error = response.getString("error", null); + } + }); + } + + private void subscribe(long channelId, long subscriptionId, int maxWidth, + int maxHeight, String aCodec, String vCodec) { + Subscription subscription = new Subscription(); + subscription.id = subscriptionId; + subscription.status = "Subscribing"; + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addSubscription(subscription); + + HTSMessage request = new HTSMessage(); + request.setMethod("subscribe"); + request.putField("channelId", channelId); + request.putField("maxWidth", maxWidth); + request.putField("maxHeight", maxHeight); + request.putField("audioCodec", aCodec); + request.putField("videoCodec", vCodec); + request.putField("subscriptionId", subscriptionId); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + // NOP + } + }); + } + + private void unsubscribe(long subscriptionId) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeSubscription(subscriptionId); + + HTSMessage request = new HTSMessage(); + request.setMethod("unsubscribe"); + request.putField("subscriptionId", subscriptionId); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + // NOP + } + }); + } + + private void feedback(long subscriptionId, int speed) { + HTSMessage request = new HTSMessage(); + request.setMethod("feedback"); + request.putField("subscriptionId", subscriptionId); + request.putField("speed", speed); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + // NOP + } + }); + } + + private void getTicket(Channel ch) { + HTSMessage request = new HTSMessage(); + request.setMethod("getTicket"); + request.putField("channelId", ch.id); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + String path = response.getString("path", null); + String ticket = response.getString("ticket", null); + + if (path != null && ticket != null) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addTicket(new HttpTicket(path, ticket)); + } + } + }); + } + + private void getTicket(Recording rec) { + HTSMessage request = new HTSMessage(); + request.setMethod("getTicket"); + request.putField("dvrId", rec.id); + connection.sendMessage(request, new HTSResponseHandler() { + + public void handleResponse(HTSMessage response) { + String path = response.getString("path", null); + String ticket = response.getString("ticket", null); + + if (path != null && ticket != null) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addTicket(new HttpTicket(path, ticket)); + } + } + }); + } } From db99d80ceea7ed5c6dc5bbbaf2333b1fe35d6bc1 Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 14:08:13 +0200 Subject: [PATCH 13/45] added missing file --- .../tvhguide/EPGTimeListAdapter.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/org/tvheadend/tvhguide/EPGTimeListAdapter.java diff --git a/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java new file mode 100644 index 0000000..a1659d8 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java @@ -0,0 +1,86 @@ +package org.tvheadend.tvhguide; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; + +import android.app.Activity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +class EPGTimeListAdapter extends ArrayAdapter { + + Activity context; + List list; + Date timeSlot; + + EPGTimeListAdapter(Activity context, List list, Date timeSlot) { + super(context, R.layout.epgnow_list_widget, list); + this.context = context; + this.list = list; + this.timeSlot = timeSlot; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Channel x, Channel y) { + return x.compareTo(y); + } + }); + } + + public Programme getProgrammeAt(int position) { + Channel item = getItem(position); + return EPGTimeListActivity.getProgrammeStartingAfter(item, timeSlot); + } + + public void updateView(ListView listView, Channel channel) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Channel pr = (Channel) listView.getItemAtPosition(pos); + + if (view.getTag() == null || pr == null) { + continue; + } + + if (channel.id != pr.id) { + continue; + } + + EPGTimeListViewWrapper wrapper = (EPGTimeListViewWrapper) view + .getTag(); + wrapper.repaint(channel); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + EPGTimeListViewWrapper wrapper = null; + + if (row == null) { + LayoutInflater inflater = context.getLayoutInflater(); + row = inflater + .inflate(R.layout.epgnow_list_widget, null, false); + + wrapper = new EPGTimeListViewWrapper(row, timeSlot); + row.setTag(wrapper); + + } else { + wrapper = (EPGTimeListViewWrapper) row.getTag(); + } + + Channel channel = getItem(position); + wrapper.repaint(channel); + return row; + } + +} \ No newline at end of file From 684d159e028504498b6a2970362a8fa412de800a Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 14:42:44 +0200 Subject: [PATCH 14/45] added dynamic loading of next programmes in epg list mode --- .../tvhguide/EPGTimeListActivity.java | 20 +++-- .../tvhguide/EPGTimeListAdapter.java | 5 +- .../tvhguide/EPGTimeListViewWrapper.java | 24 +++++- .../tvheadend/tvhguide/htsp/HTSService.java | 2 +- src/org/tvheadend/tvhguide/model/Channel.java | 73 ++++++++++--------- 5 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 03579f9..539724e 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -49,6 +49,8 @@ */ public class EPGTimeListActivity extends FragmentActivity { + private static final int DEFAULT_HOURS = 24; + /** * The serialization (saved instance state) Bundle key representing the * current tab position. @@ -83,7 +85,6 @@ public class EPGTimeListActivity extends FragmentActivity { private TextView tagTextView; private ImageView tagImageView; - @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager @@ -103,12 +104,11 @@ protected void onCreate(Bundle savedInstanceState) { cal.clear(Calendar.SECOND); cal.clear(Calendar.MILLISECOND); - for (int i = 0; i < HTSService.INITIAL_CHANNEL_LOADING; i++) { + int maxHours = prefs.getInt("epg.timeslots", DEFAULT_HOURS); + for (int i = 0; i < maxHours; i++) { timeSlots.add(cal.getTime()); cal.add(Calendar.HOUR_OF_DAY, 1); } - // Set timeslots = prefs.getStringSet("epg.timeslots", - // defaults); // Create the adapter that will return a fragment for each of the three // primary sections of the app. @@ -172,10 +172,10 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivityForResult(intent, R.id.mi_settings); return true; } - // case R.id.mi_refresh: { - // connect(true); - // return true; - // } + case R.id.mi_refresh: { + // connect(true); + // return true; + } case R.id.mi_recordings: { Intent intent = new Intent(getBaseContext(), RecordingListActivity.class); @@ -597,9 +597,7 @@ static interface EPGFragmentListener { public static Programme getProgrammeStartingAfter(Channel channel, Date timeSlot) { Iterator it = channel.epg.iterator(); - if (!channel.isTransmitting) { - return null; - } + // find first programm after timeslot while (it.hasNext()) { Programme pr = it.next(); diff --git a/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java index a1659d8..4bb27eb 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java @@ -68,10 +68,9 @@ public View getView(int position, View convertView, ViewGroup parent) { if (row == null) { LayoutInflater inflater = context.getLayoutInflater(); - row = inflater - .inflate(R.layout.epgnow_list_widget, null, false); + row = inflater.inflate(R.layout.epgnow_list_widget, null, false); - wrapper = new EPGTimeListViewWrapper(row, timeSlot); + wrapper = new EPGTimeListViewWrapper(context, row, timeSlot); row.setTag(wrapper); } else { diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index 1472eef..77addc8 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -2,9 +2,12 @@ import java.util.Date; +import org.tvheadend.tvhguide.htsp.HTSService; import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.Programme; +import android.app.Activity; +import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.BitmapDrawable; import android.preference.PreferenceManager; @@ -15,9 +18,11 @@ public class EPGTimeListViewWrapper extends ProgrammeListViewWrapper { private final Date timeSlot; private ImageView icon; + private final Activity context; - public EPGTimeListViewWrapper(View base, Date timeSlot) { + public EPGTimeListViewWrapper(Activity context, View base, Date timeSlot) { super(base); + this.context = context; icon = (ImageView) base.findViewById(R.id.ch_icon); @@ -38,8 +43,23 @@ public void repaint(Channel channel) { Programme pr = EPGTimeListActivity.getProgrammeStartingAfter(channel, timeSlot); - if (pr == null) { + if (!channel.isTransmitting) { title.setText(R.string.ch_no_transmission); + } else if (pr == null) { + // title.setText(R.string.ch_no_transmission); + // if last, preload next programmes of this channel + pr = channel.epg.last(); + long nextId = pr.nextId; + if (nextId == 0) { + nextId = pr.id; + } + + Intent intent = new Intent(context, HTSService.class); + intent.setAction(HTSService.ACTION_GET_EVENTS); + intent.putExtra("eventId", nextId); + intent.putExtra("channelId", channel.id); + intent.putExtra("count", 2); + context.startService(intent); } else { super.repaint(pr); } diff --git a/src/org/tvheadend/tvhguide/htsp/HTSService.java b/src/org/tvheadend/tvhguide/htsp/HTSService.java index fb708fa..4e9a4ec 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSService.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSService.java @@ -79,7 +79,7 @@ public class HTSService extends Service implements HTSConnectionListener { private HTSConnection connection; PackageInfo packInfo; - public static final int INITIAL_CHANNEL_LOADING = 10; + public static final int INITIAL_CHANNEL_LOADING = 5; public class LocalBinder extends Binder { diff --git a/src/org/tvheadend/tvhguide/model/Channel.java b/src/org/tvheadend/tvhguide/model/Channel.java index fab9f66..4135f58 100644 --- a/src/org/tvheadend/tvhguide/model/Channel.java +++ b/src/org/tvheadend/tvhguide/model/Channel.java @@ -18,51 +18,54 @@ */ package org.tvheadend.tvhguide.model; -import android.graphics.Bitmap; import java.util.Collections; import java.util.List; -import java.util.Set; +import java.util.SortedSet; import java.util.TreeSet; +import android.graphics.Bitmap; + /** - * + * * @author john-tornblom */ public class Channel implements Comparable { - public long id; - public String name; - public String icon; - public int number; - public Set epg = Collections.synchronizedSortedSet(new TreeSet()); - public Set recordings = Collections.synchronizedSortedSet(new TreeSet()); - public List tags; - public Bitmap iconBitmap; - public boolean isTransmitting; - - public int compareTo(Channel that) { - return this.number - that.number; - } + public long id; + public String name; + public String icon; + public int number; + public SortedSet epg = Collections + .synchronizedSortedSet(new TreeSet()); + public SortedSet recordings = Collections + .synchronizedSortedSet(new TreeSet()); + public List tags; + public Bitmap iconBitmap; + public boolean isTransmitting; + + public int compareTo(Channel that) { + return this.number - that.number; + } - public boolean hasTag(long id) { - if (id == 0) { - return true; - } + public boolean hasTag(long id) { + if (id == 0) { + return true; + } - for (Integer i : tags) { - if (i == id) { - return true; - } - } - return false; - } + for (Integer i : tags) { + if (i == id) { + return true; + } + } + return false; + } - public boolean isRecording() { - for (Recording rec : recordings) { - if ("recording".equals(rec.state)) { - return true; - } - } - return false; - } + public boolean isRecording() { + for (Recording rec : recordings) { + if ("recording".equals(rec.state)) { + return true; + } + } + return false; + } } From de8c7a5fb1c4f29e4e5d14d9bda439632eee5bb4 Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 15:13:35 +0200 Subject: [PATCH 15/45] enhanced logo handling --- res/layout/channel_list_widget.xml | 8 ++++---- res/layout/epgnow_list_widget.xml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/res/layout/channel_list_widget.xml b/res/layout/channel_list_widget.xml index 09c94af..5685e2f 100644 --- a/res/layout/channel_list_widget.xml +++ b/res/layout/channel_list_widget.xml @@ -11,12 +11,12 @@ android:layout_marginRight="5sp" > - + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:layout_margin="3dip" /> - From 74785241cf3dbe491e4b1cb4b59c604c9013a5c1 Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 21:20:38 +0200 Subject: [PATCH 16/45] enhanced logo handling --- res/layout/channel_list_widget.xml | 5 +++-- res/layout/epgnow_list_widget.xml | 7 ++++--- res/layout/epgtimeline_widget.xml | 9 +++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/res/layout/channel_list_widget.xml b/res/layout/channel_list_widget.xml index 5685e2f..11a9a8b 100644 --- a/res/layout/channel_list_widget.xml +++ b/res/layout/channel_list_widget.xml @@ -11,12 +11,13 @@ android:layout_marginRight="5sp" > - + android:layout_margin="3dip" + android:adjustViewBounds="true"/> - + android:scaleType="fitXY" + android:layout_margin="3dip" + android:adjustViewBounds="true"/> - + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:layout_margin="3dip" + android:adjustViewBounds="true"/> + Date: Fri, 19 Apr 2013 21:31:48 +0200 Subject: [PATCH 17/45] enhanced logo handling --- AndroidManifest.xml | 2 +- res/layout/channel_list_widget.xml | 20 +- res/values/strings.xml | 2 + .../tvhguide/ChannelListViewWrapper.java | 184 +++++++++--------- 4 files changed, 108 insertions(+), 100 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d518d73..79172fe 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -7,7 +7,7 @@ - - + android:layout_width="fill_parent" + android:layout_marginLeft="5sp" + android:layout_marginRight="5sp"> + android:adjustViewBounds="true" + android:contentDescription="@string/icon"/> + android:src="@android:drawable/progress_horizontal" + android:contentDescription="@string/elapsed_time"/> - \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index d12bf74..7a156d5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -186,5 +186,7 @@ Hello world! EPG List EPG Timeline + elapsed time + icon \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java b/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java index 37c68d6..8a2df8e 100644 --- a/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java @@ -18,7 +18,14 @@ */ package org.tvheadend.tvhguide; +import java.util.Date; +import java.util.Iterator; + +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; + import android.content.SharedPreferences; +import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ClipDrawable; import android.preference.PreferenceManager; @@ -27,97 +34,100 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import java.util.Date; -import java.util.Iterator; -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.Programme; /** - * + * * @author john-tornblom */ public class ChannelListViewWrapper { - private TextView name; - private TextView nowTitle; - private TextView nowTime; - private TextView nextTitle; - private TextView nextTime; - private ImageView icon; - private ImageView nowProgressImage; - private ClipDrawable nowProgress; - - public ChannelListViewWrapper(View base) { - name = (TextView) base.findViewById(R.id.ch_name); - nowTitle = (TextView) base.findViewById(R.id.ch_now_title); - - nowProgressImage = (ImageView) base.findViewById(R.id.ch_elapsedtime); - nowProgress = new ClipDrawable(nowProgressImage.getDrawable(), Gravity.LEFT, ClipDrawable.HORIZONTAL); - nowProgressImage.setBackgroundDrawable(nowProgress); - - nowTime = (TextView) base.findViewById(R.id.ch_now_time); - nextTitle = (TextView) base.findViewById(R.id.ch_next_title); - nextTime = (TextView) base.findViewById(R.id.ch_next_time); - icon = (ImageView) base.findViewById(R.id.ch_icon); - } - - public void repaint(Channel channel) { - nowTime.setText(""); - nowTitle.setText(""); - nextTime.setText(""); - nextTitle.setText(""); - nowProgress.setLevel(0); - - name.setText(channel.name); - name.invalidate(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(icon.getContext()); - Boolean showIcons = prefs.getBoolean("showIconPref", false); - icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); - - if (channel.isRecording()) { - icon.setImageResource(R.drawable.ic_rec_small); - } else { - icon.setImageDrawable(null); - } - icon.invalidate(); - - Iterator it = channel.epg.iterator(); - if (!channel.isTransmitting && it.hasNext()) { - nowTitle.setText(R.string.ch_no_transmission); - } else if (it.hasNext()) { - Programme p = it.next(); - nowTime.setText( - DateFormat.getTimeFormat(nowTime.getContext()).format(p.start) - + " - " - + DateFormat.getTimeFormat(nowTime.getContext()).format(p.stop)); - - double duration = (p.stop.getTime() - p.start.getTime()); - double elapsed = new Date().getTime() - p.start.getTime(); - double percent = elapsed / duration; - - nowProgressImage.setVisibility(ImageView.VISIBLE); - nowProgress.setLevel((int) Math.floor(percent * 10000)); - nowTitle.setText(p.title); - } else { - nowProgressImage.setVisibility(ImageView.GONE); - } - nowProgressImage.invalidate(); - nowTime.invalidate(); - nowTitle.invalidate(); - - if (it.hasNext()) { - Programme p = it.next(); - nextTime.setText( - DateFormat.getTimeFormat(nextTime.getContext()).format(p.start) - + " - " - + DateFormat.getTimeFormat(nextTime.getContext()).format(p.stop)); - - nextTitle.setText(p.title); - } - nextTime.invalidate(); - nextTitle.invalidate(); - } + private TextView name; + private TextView nowTitle; + private TextView nowTime; + private TextView nextTitle; + private TextView nextTime; + private ImageView icon; + private ImageView nowProgressImage; + private ClipDrawable nowProgress; + private Resources resources; + + public ChannelListViewWrapper(View base) { + name = (TextView) base.findViewById(R.id.ch_name); + nowTitle = (TextView) base.findViewById(R.id.ch_now_title); + + nowProgressImage = (ImageView) base.findViewById(R.id.ch_elapsedtime); + nowProgress = new ClipDrawable(nowProgressImage.getDrawable(), + Gravity.LEFT, ClipDrawable.HORIZONTAL); + nowProgressImage.setBackground(nowProgress); + + nowTime = (TextView) base.findViewById(R.id.ch_now_time); + nextTitle = (TextView) base.findViewById(R.id.ch_next_title); + nextTime = (TextView) base.findViewById(R.id.ch_next_time); + icon = (ImageView) base.findViewById(R.id.ch_icon); + resources = base.getResources(); + } + + public void repaint(Channel channel) { + nowTime.setText(""); + nowTitle.setText(""); + nextTime.setText(""); + nextTitle.setText(""); + nowProgress.setLevel(0); + + name.setText(channel.name); + name.invalidate(); + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(icon.getContext()); + Boolean showIcons = prefs.getBoolean("showIconPref", false); + icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + BitmapDrawable bitmapDrawable = new BitmapDrawable(resources, + channel.iconBitmap); + icon.setBackground(bitmapDrawable); + + if (channel.isRecording()) { + icon.setImageResource(R.drawable.ic_rec_small); + } else { + icon.setImageDrawable(null); + } + icon.invalidate(); + + Iterator it = channel.epg.iterator(); + if (!channel.isTransmitting && it.hasNext()) { + nowTitle.setText(R.string.ch_no_transmission); + } else if (it.hasNext()) { + Programme p = it.next(); + nowTime.setText(DateFormat.getTimeFormat(nowTime.getContext()) + .format(p.start) + + " - " + + DateFormat.getTimeFormat(nowTime.getContext()).format( + p.stop)); + + double duration = (p.stop.getTime() - p.start.getTime()); + double elapsed = new Date().getTime() - p.start.getTime(); + double percent = elapsed / duration; + + nowProgressImage.setVisibility(ImageView.VISIBLE); + nowProgress.setLevel((int) Math.floor(percent * 10000)); + nowTitle.setText(p.title); + } else { + nowProgressImage.setVisibility(ImageView.GONE); + } + nowProgressImage.invalidate(); + nowTime.invalidate(); + nowTitle.invalidate(); + + if (it.hasNext()) { + Programme p = it.next(); + nextTime.setText(DateFormat.getTimeFormat(nextTime.getContext()) + .format(p.start) + + " - " + + DateFormat.getTimeFormat(nextTime.getContext()).format( + p.stop)); + + nextTitle.setText(p.title); + } + nextTime.invalidate(); + nextTitle.invalidate(); + } } From cbf9c03dd899f2259f8b4135592f2aa6683ce968 Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 21:52:18 +0200 Subject: [PATCH 18/45] added support for tag filter within timeline --- res/layout/epgnow_list_widget.xml | 16 +- res/layout/epgtimeline_programme_widget.xml | 4 +- res/layout/epgtimeline_widget.xml | 12 +- .../tvhguide/EPGTimelineActivity.java | 205 +++++++++++++----- .../EPGTimelineProgrammeListViewWrapper.java | 3 +- 5 files changed, 163 insertions(+), 77 deletions(-) diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml index 5d0e9de..9ceaa31 100644 --- a/res/layout/epgnow_list_widget.xml +++ b/res/layout/epgnow_list_widget.xml @@ -1,21 +1,18 @@ - - - - + android:adjustViewBounds="true" + android:contentDescription="@string/icon"/> @@ -86,6 +83,5 @@ android:maxLines="2" > - \ No newline at end of file diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index f4facd7..b5fca22 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -4,7 +4,9 @@ android:id="@+id/programme_container" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="vertical" > + android:orientation="vertical" + android:layout_marginRight="2dp" + android:paddingRight="2dp"> - - + android:adjustViewBounds="true" + android:contentDescription="@string/icon"/> - \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index afb5c82..13d3858 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -1,33 +1,39 @@ package org.tvheadend.tvhguide; import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; +import org.tvheadend.tvhguide.htsp.HTSListener; import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.ChannelTag; -import android.app.Activity; +import android.app.AlertDialog; import android.app.ListActivity; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.Window; import android.widget.ArrayAdapter; -import android.widget.ListView; +import android.widget.ImageView; +import android.widget.TextView; /** * * @author mike toggweiler * */ -public class EPGTimelineActivity extends ListActivity { +public class EPGTimelineActivity extends ListActivity implements HTSListener { - private TimelineAdapter adapter; + private EPGTimelineAdapter adapter; + + private AlertDialog tagDialog; + private ArrayAdapter tagAdapter; + + private TextView tagTextView; + private ImageView tagImageView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -37,11 +43,68 @@ protected void onCreate(Bundle savedInstanceState) { setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); TVHGuideApplication tvh = (TVHGuideApplication) getApplication(); - adapter = new TimelineAdapter(this, new ArrayList( - tvh.getChannels())); + adapter = new EPGTimelineAdapter(this, new ArrayList()); setListAdapter(adapter); + + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.epgnow_list_title); + tagTextView = (TextView) findViewById(R.id.ct_title); + tagImageView = (ImageView) findViewById(R.id.ct_logo); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.menu_tags); + + tagAdapter = new ArrayAdapter(this, + android.R.layout.simple_dropdown_item_1line, + new ArrayList()); + + builder.setAdapter(tagAdapter, + new android.content.DialogInterface.OnClickListener() { + + public void onClick(DialogInterface arg0, int pos) { + ChannelTag tag = tagAdapter.getItem(pos); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.setCurrentTag(tag); + + setCurrentTag(tag); + populateChannelList(); + } + }); + + tagDialog = builder.create(); + } + + private void setCurrentTag(ChannelTag t) { + if (t == null) { + tagTextView.setText(R.string.pr_all_channels); + tagImageView.setImageResource(R.drawable.logo_72); + } else { + tagTextView.setText(t.name); + + if (t.iconBitmap != null) { + tagImageView.setImageBitmap(t.iconBitmap); + } else { + tagImageView.setImageResource(R.drawable.logo_72); + } + } + } + + public void populateChannelList() { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + ChannelTag currentTag = app.getCurrentTag(); + adapter.clear(); + + for (Channel ch : app.getChannels()) { + if (currentTag == null || ch.hasTag(currentTag.id)) { + adapter.add(ch); + } + } + + adapter.sort(); + adapter.notifyDataSetChanged(); } @Override @@ -88,74 +151,102 @@ public boolean onOptionsItemSelected(MenuItem item) { onSearchRequested(); return true; } - // case R.id.mi_tags: { - // tagDialog.show(); - // return true; - // } + case R.id.mi_tags: { + tagDialog.show(); + return true; + } default: { return super.onOptionsItemSelected(item); } } } - class TimelineAdapter extends ArrayAdapter { + @Override + protected void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addListener(this); - TimelineAdapter(Activity context, List list) { - super(context, R.layout.epgtimeline_widget, list); - } + setLoading(app.isLoading()); + } + + @Override + protected void onPause() { + super.onPause(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeListener(this); + } + + @Override + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_LOADING)) { - public void sort() { - sort(new Comparator() { + runOnUiThread(new Runnable() { - public int compare(Channel x, Channel y) { - return x.compareTo(y); + public void run() { + boolean loading = (Boolean) obj; + setLoading(loading); } }); - } - - public void updateView(ListView listView, Channel channel) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Channel ch = (Channel) listView.getItemAtPosition(pos); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_ADD)) { + runOnUiThread(new Runnable() { - if (view.getTag() == null || ch == null) { - continue; + public void run() { + adapter.add((Channel) obj); + adapter.notifyDataSetChanged(); + adapter.sort(); } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_DELETE)) { + runOnUiThread(new Runnable() { - if (channel.id != ch.id) { - continue; + public void run() { + adapter.remove((Channel) obj); + adapter.notifyDataSetChanged(); } + }); + } else if (action.equals(TVHGuideApplication.ACTION_CHANNEL_UPDATE)) { + runOnUiThread(new Runnable() { - EPGTimelineViewWrapper wrapper = (EPGTimelineViewWrapper) view - .getTag(); - wrapper.repaint(channel); - } - } + public void run() { + Channel channel = (Channel) obj; + adapter.updateView(getListView(), channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_ADD)) { + runOnUiThread(new Runnable() { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - EPGTimelineViewWrapper wrapper; + public void run() { + ChannelTag tag = (ChannelTag) obj; + tagAdapter.add(tag); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_DELETE)) { + runOnUiThread(new Runnable() { - Channel ch = getItem(position); - Activity activity = (Activity) getContext(); + public void run() { + ChannelTag tag = (ChannelTag) obj; + tagAdapter.remove(tag); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_TAG_UPDATE)) { + // NOP + } + } - if (row == null) { - LayoutInflater inflater = activity.getLayoutInflater(); - row = inflater - .inflate(R.layout.epgtimeline_widget, null, false); - row.requestLayout(); - wrapper = new EPGTimelineViewWrapper(EPGTimelineActivity.this, - row); - row.setTag(wrapper); + private void setLoading(boolean loading) { + if (loading) { + // + } else { - } else { - wrapper = (EPGTimelineViewWrapper) row.getTag(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + tagAdapter.clear(); + for (ChannelTag t : app.getChannelTags()) { + tagAdapter.add(t); } + setCurrentTag(app.getCurrentTag()); - wrapper.repaint(ch); - return row; + populateChannelList(); } } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 3eb6900..c51d0ce 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -31,7 +31,8 @@ public void repaint(Programme p) { super.repaint(p); // colorize based on series category - int color = colors.getColor(p.contentType, 0); + int index = p.contentType % colors.length(); + int color = colors.getColor(index, 0); container.setBackgroundColor(color); // define width based on duration From 2a84e33103d6eef9c60ecd5d84f3a300d8761e0f Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 22:05:54 +0200 Subject: [PATCH 19/45] added onitem click listener to show program details --- .../tvheadend/tvhguide/EPGTimeListActivity.java | 11 +++++++++++ .../tvhguide/EPGTimelineViewWrapper.java | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 539724e..76ae095 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -39,6 +39,7 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; +import android.widget.ListView; import android.widget.TextView; /** @@ -391,6 +392,16 @@ public void populateChannelList() { prAdapter.notifyDataSetChanged(); } + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + Programme p = (Programme) prAdapter.getProgrammeAt(position); + + Intent intent = new Intent(getActivity(), ProgrammeActivity.class); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + startActivity(intent); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index 812af3b..694f425 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -27,10 +27,13 @@ import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; @@ -41,7 +44,7 @@ * * @author mike toggweiler */ -public class EPGTimelineViewWrapper { +public class EPGTimelineViewWrapper implements OnItemClickListener { private ImageView icon; private LinearLayout timeline; @@ -72,6 +75,8 @@ public void repaint(Channel channel) { HorizontalListView horizontialListView = new HorizontalListView( context, null); + horizontialListView.setClickable(true); + horizontialListView.setOnItemClickListener(this); TimelineProgrammeAdapter adapter = new TimelineProgrammeAdapter( context, new ArrayList(channel.epg)); horizontialListView.setAdapter(adapter); @@ -79,6 +84,16 @@ public void repaint(Channel channel) { timeline.addView(horizontialListView); } + @Override + public void onItemClick(AdapterView adapterView, View view, + int position, long id) { + Programme p = (Programme) adapterView.getItemAtPosition(position); + Intent intent = new Intent(context, ProgrammeActivity.class); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + context.startActivity(intent); + } + class TimelineProgrammeAdapter extends ArrayAdapter { TimelineProgrammeAdapter(Context context, List epg) { From 396558d9570325b880d467dcd86fb2d7c8790780 Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 22:08:04 +0200 Subject: [PATCH 20/45] fixed warnings --- src/org/tvheadend/tvhguide/EPGTimelineActivity.java | 1 - src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java | 3 ++- src/org/tvheadend/tvhguide/ProgrammeListActivity.java | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 13d3858..9017560 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -45,7 +45,6 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - TVHGuideApplication tvh = (TVHGuideApplication) getApplication(); adapter = new EPGTimelineAdapter(this, new ArrayList()); setListAdapter(adapter); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index 694f425..351cc25 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -64,7 +64,8 @@ public void repaint(Channel channel) { // .getDefaultSharedPreferences(icon.getContext()); // Boolean showIcons = prefs.getBoolean("showIconPref", false); // icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setBackgroundDrawable(new BitmapDrawable(channel.iconBitmap)); + icon.setBackground(new BitmapDrawable(context.getResources(), + channel.iconBitmap)); if (channel.isRecording()) { icon.setImageResource(R.drawable.ic_rec_small); diff --git a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java index 2b4abf3..51daed4 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java @@ -39,7 +39,6 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import android.util.SparseArray; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -64,7 +63,6 @@ public class ProgrammeListActivity extends ListActivity implements HTSListener { private ProgrammeListAdapter prAdapter; private Channel channel; - private SparseArray contentTypes; @Override public void onCreate(Bundle icicle) { @@ -151,7 +149,6 @@ public void onClick(View arg0) { }); registerForContextMenu(getListView()); - contentTypes = TVHGuideApplication.getContentTypes(this.getResources()); } @Override From e010e432b5140bbdb17573b24a598d001fca0c7f Mon Sep 17 00:00:00 2001 From: toggm Date: Fri, 19 Apr 2013 23:30:50 +0200 Subject: [PATCH 21/45] added support for context menu in horizontalview, added contextmenu to epg timeline --- .../tvhguide/EPGTimeListViewWrapper.java | 2 +- .../tvhguide/EPGTimelineActivity.java | 78 ++++ .../tvhguide/EPGTimelineAdapter.java | 75 ++++ .../tvhguide/EPGTimelineViewWrapper.java | 9 +- .../tvhguide/ui/HorizontalListView.java | 400 ++++++++++++++++++ 5 files changed, 559 insertions(+), 5 deletions(-) create mode 100644 src/org/tvheadend/tvhguide/EPGTimelineAdapter.java create mode 100644 src/org/tvheadend/tvhguide/ui/HorizontalListView.java diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index 77addc8..2c104be 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -43,7 +43,7 @@ public void repaint(Channel channel) { Programme pr = EPGTimeListActivity.getProgrammeStartingAfter(channel, timeSlot); - if (!channel.isTransmitting) { + if (!channel.isTransmitting || channel.epg.size() == 0) { title.setText(R.string.ch_no_transmission); } else if (pr == null) { // title.setText(R.string.ch_no_transmission); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 9017560..b6134a5 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -3,8 +3,13 @@ import java.util.ArrayList; import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.intent.SearchEPGIntent; +import org.tvheadend.tvhguide.intent.SearchIMDbIntent; import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.ChannelTag; +import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.ui.HorizontalListView; import android.app.AlertDialog; import android.app.ListActivity; @@ -13,9 +18,13 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.Window; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; @@ -113,6 +122,75 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + if (info == null) { + return; + } + + HorizontalListView hv = (HorizontalListView) v; + Programme p = (Programme) hv.getItemAtPosition(info.position); + + if (p == null) { + return; + } + + menu.setHeaderTitle(p.title); + + Intent intent = new Intent(this, HTSService.class); + + MenuItem item = null; + + if (p != null) { + if (p.recording == null) { + intent.setAction(HTSService.ACTION_DVR_ADD); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record, + ContextMenu.NONE, R.string.menu_record); + } else if (p.isRecording() || p.isScheduled()) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, + ContextMenu.NONE, R.string.menu_record_cancel); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + intent.putExtra("id", p.recording.id); + item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, + ContextMenu.NONE, R.string.menu_record_remove); + } + + item.setIntent(intent); + + item = menu.add(ContextMenu.NONE, R.string.search_hint, + ContextMenu.NONE, R.string.search_hint); + item.setIntent(new SearchEPGIntent(this, p.title)); + + item = menu.add(ContextMenu.NONE, ContextMenu.NONE, + ContextMenu.NONE, "IMDb"); + item.setIntent(new SearchIMDbIntent(this, p.title)); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record: + case R.string.menu_record_cancel: + case R.string.menu_record_remove: { + startService(item.getIntent()); + return true; + } + default: { + return super.onContextItemSelected(item); + } + } + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java new file mode 100644 index 0000000..abd7105 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -0,0 +1,75 @@ +package org.tvheadend.tvhguide; + +import java.util.Comparator; +import java.util.List; + +import org.tvheadend.tvhguide.model.Channel; + +import android.app.Activity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +class EPGTimelineAdapter extends ArrayAdapter { + + private final Activity context; + + EPGTimelineAdapter(Activity context, List list) { + super(context, R.layout.epgtimeline_widget, list); + this.context = context; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Channel x, Channel y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Channel channel) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Channel ch = (Channel) listView.getItemAtPosition(pos); + + if (view.getTag() == null || ch == null) { + continue; + } + + if (channel.id != ch.id) { + continue; + } + + EPGTimelineViewWrapper wrapper = (EPGTimelineViewWrapper) view + .getTag(); + wrapper.repaint(channel); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + EPGTimelineViewWrapper wrapper; + + Channel ch = getItem(position); + Activity activity = (Activity) getContext(); + + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate(R.layout.epgtimeline_widget, null, false); + row.requestLayout(); + wrapper = new EPGTimelineViewWrapper(context, row); + row.setTag(wrapper); + + } else { + wrapper = (EPGTimelineViewWrapper) row.getTag(); + } + + wrapper.repaint(ch); + return row; + } +} \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index 351cc25..de055c8 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -24,6 +24,7 @@ import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.ui.HorizontalListView; import android.app.Activity; import android.content.Context; @@ -38,8 +39,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; -import com.devsmart.android.ui.HorizontalListView; - /** * * @author mike toggweiler @@ -48,9 +47,9 @@ public class EPGTimelineViewWrapper implements OnItemClickListener { private ImageView icon; private LinearLayout timeline; - private final Context context; + private final Activity context; - public EPGTimelineViewWrapper(Context context, View base) { + public EPGTimelineViewWrapper(Activity context, View base) { this.context = context; icon = (ImageView) base.findViewById(R.id.ch_icon); @@ -82,6 +81,8 @@ public void repaint(Channel channel) { context, new ArrayList(channel.epg)); horizontialListView.setAdapter(adapter); + context.registerForContextMenu(horizontialListView); + timeline.addView(horizontialListView); } diff --git a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java new file mode 100644 index 0000000..32acbe0 --- /dev/null +++ b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java @@ -0,0 +1,400 @@ +package org.tvheadend.tvhguide.ui; + +import java.util.LinkedList; +import java.util.Queue; + +import android.content.Context; +import android.database.DataSetObserver; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.OnGestureListener; +import android.view.MotionEvent; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.Scroller; + +public class HorizontalListView extends AdapterView { + + public boolean mAlwaysOverrideTouch = true; + protected ListAdapter mAdapter; + private int mLeftViewIndex = -1; + private int mRightViewIndex = 0; + protected int mCurrentX; + protected int mNextX; + private int mMaxX = Integer.MAX_VALUE; + private int mDisplayOffset = 0; + protected Scroller mScroller; + private GestureDetector mGesture; + private Queue mRemovedViewQueue = new LinkedList(); + private OnItemSelectedListener mOnItemSelected; + private OnItemClickListener mOnItemClicked; + private OnItemLongClickListener mOnItemLongClicked; + private boolean mDataChanged = false; + private Rect mTouchFrame; + private int selectedIndex; + + public HorizontalListView(Context context, AttributeSet attrs) { + super(context, attrs); + initView(); + } + + private synchronized void initView() { + mLeftViewIndex = -1; + mRightViewIndex = 0; + mDisplayOffset = 0; + mCurrentX = 0; + mNextX = 0; + mMaxX = Integer.MAX_VALUE; + mScroller = new Scroller(getContext()); + mGesture = new GestureDetector(getContext(), mOnGesture); + } + + @Override + public void setOnItemSelectedListener( + AdapterView.OnItemSelectedListener listener) { + mOnItemSelected = listener; + } + + @Override + public void setOnItemClickListener(AdapterView.OnItemClickListener listener) { + mOnItemClicked = listener; + } + + @Override + protected ContextMenuInfo getContextMenuInfo() { + View child = getChildAt(selectedIndex); + return new AdapterView.AdapterContextMenuInfo(child, mLeftViewIndex + 1 + + selectedIndex, mAdapter.getItemId(mLeftViewIndex + 1 + + selectedIndex)); + } + + @Override + public void setOnItemLongClickListener( + AdapterView.OnItemLongClickListener listener) { + mOnItemLongClicked = listener; + } + + private DataSetObserver mDataObserver = new DataSetObserver() { + + @Override + public void onChanged() { + synchronized (HorizontalListView.this) { + mDataChanged = true; + } + invalidate(); + requestLayout(); + } + + @Override + public void onInvalidated() { + reset(); + invalidate(); + requestLayout(); + } + + }; + + @Override + public ListAdapter getAdapter() { + return mAdapter; + } + + @Override + public View getSelectedView() { + // TODO: implement + return null; + } + + @Override + public void createContextMenu(ContextMenu menu) { + super.createContextMenu(menu); + } + + @Override + public void setAdapter(ListAdapter adapter) { + if (mAdapter != null) { + mAdapter.unregisterDataSetObserver(mDataObserver); + } + mAdapter = adapter; + mAdapter.registerDataSetObserver(mDataObserver); + reset(); + } + + private synchronized void reset() { + initView(); + removeAllViewsInLayout(); + requestLayout(); + } + + @Override + public void setSelection(int position) { + // TODO: implement + } + + private void addAndMeasureChild(final View child, int viewPos) { + LayoutParams params = child.getLayoutParams(); + if (params == null) { + params = new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + addViewInLayout(child, viewPos, params, true); + child.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); + } + + @Override + protected synchronized void onLayout(boolean changed, int left, int top, + int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (mAdapter == null) { + return; + } + + if (mDataChanged) { + int oldCurrentX = mCurrentX; + initView(); + removeAllViewsInLayout(); + mNextX = oldCurrentX; + mDataChanged = false; + } + + if (mScroller.computeScrollOffset()) { + int scrollx = mScroller.getCurrX(); + mNextX = scrollx; + } + + if (mNextX <= 0) { + mNextX = 0; + mScroller.forceFinished(true); + } + if (mNextX >= mMaxX) { + mNextX = mMaxX; + mScroller.forceFinished(true); + } + + int dx = mCurrentX - mNextX; + + removeNonVisibleItems(dx); + fillList(dx); + positionItems(dx); + + mCurrentX = mNextX; + + if (!mScroller.isFinished()) { + post(new Runnable() { + @Override + public void run() { + requestLayout(); + } + }); + + } + } + + private void fillList(final int dx) { + int edge = 0; + View child = getChildAt(getChildCount() - 1); + if (child != null) { + edge = child.getRight(); + } + fillListRight(edge, dx); + + edge = 0; + child = getChildAt(0); + if (child != null) { + edge = child.getLeft(); + } + fillListLeft(edge, dx); + + } + + private void fillListRight(int rightEdge, final int dx) { + while (rightEdge + dx < getWidth() + && mRightViewIndex < mAdapter.getCount()) { + + View child = mAdapter.getView(mRightViewIndex, + mRemovedViewQueue.poll(), this); + addAndMeasureChild(child, -1); + rightEdge += child.getMeasuredWidth(); + + if (mRightViewIndex == mAdapter.getCount() - 1) { + mMaxX = mCurrentX + rightEdge - getWidth(); + } + + if (mMaxX < 0) { + mMaxX = 0; + } + mRightViewIndex++; + } + + } + + private void fillListLeft(int leftEdge, final int dx) { + while (leftEdge + dx > 0 && mLeftViewIndex >= 0) { + View child = mAdapter.getView(mLeftViewIndex, + mRemovedViewQueue.poll(), this); + addAndMeasureChild(child, 0); + leftEdge -= child.getMeasuredWidth(); + mLeftViewIndex--; + mDisplayOffset -= child.getMeasuredWidth(); + } + } + + private void removeNonVisibleItems(final int dx) { + View child = getChildAt(0); + while (child != null && child.getRight() + dx <= 0) { + mDisplayOffset += child.getMeasuredWidth(); + mRemovedViewQueue.offer(child); + removeViewInLayout(child); + mLeftViewIndex++; + child = getChildAt(0); + + } + + child = getChildAt(getChildCount() - 1); + while (child != null && child.getLeft() + dx >= getWidth()) { + mRemovedViewQueue.offer(child); + removeViewInLayout(child); + mRightViewIndex--; + child = getChildAt(getChildCount() - 1); + } + } + + private void positionItems(final int dx) { + if (getChildCount() > 0) { + mDisplayOffset += dx; + int left = mDisplayOffset; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + int childWidth = child.getMeasuredWidth(); + child.layout(left, 0, left + childWidth, + child.getMeasuredHeight()); + left += childWidth + child.getPaddingRight(); + } + } + } + + public synchronized void scrollTo(int x) { + mScroller.startScroll(mNextX, 0, x - mNextX, 0); + requestLayout(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean handled = super.dispatchTouchEvent(ev); + handled |= mGesture.onTouchEvent(ev); + return handled; + } + + protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + synchronized (HorizontalListView.this) { + mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); + } + requestLayout(); + + return true; + } + + protected boolean onDown(MotionEvent e) { + mScroller.forceFinished(true); + return true; + } + + private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { + + @Override + public boolean onDown(MotionEvent e) { + return HorizontalListView.this.onDown(e); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + return HorizontalListView.this + .onFling(e1, e2, velocityX, velocityY); + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + + synchronized (HorizontalListView.this) { + mNextX += (int) distanceX; + } + requestLayout(); + + return true; + } + + public void onShowPress(MotionEvent e) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (isEventWithinView(e, child)) { + selectedIndex = i; + return; + } + } + }; + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (isEventWithinView(e, child)) { + if (mOnItemClicked != null) { + mOnItemClicked.onItemClick(HorizontalListView.this, + child, mLeftViewIndex + 1 + i, + mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } + if (mOnItemSelected != null) { + mOnItemSelected.onItemSelected(HorizontalListView.this, + child, mLeftViewIndex + 1 + i, + mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } + break; + } + + } + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (isEventWithinView(e, child)) { + if (mOnItemLongClicked != null) { + mOnItemLongClicked.onItemLongClick( + HorizontalListView.this, child, mLeftViewIndex + + 1 + i, + mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } + break; + } + + } + } + + private boolean isEventWithinView(MotionEvent e, View child) { + Rect viewRect = new Rect(); + int[] childPosition = new int[2]; + child.getLocationOnScreen(childPosition); + int left = childPosition[0]; + int right = left + child.getWidth(); + int top = childPosition[1]; + int bottom = top + child.getHeight(); + viewRect.set(left, top, right, bottom); + return viewRect.contains((int) e.getRawX(), (int) e.getRawY()); + } + }; + +} \ No newline at end of file From 45798d08c9d58e545e5078f6b38911f2317b157e Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 14:50:33 +0200 Subject: [PATCH 22/45] enhanded calculation of shown programme in time list, use new method to guess around timeslot --- .../tvhguide/EPGTimeListActivity.java | 33 +++++++++++++++++-- .../tvhguide/EPGTimeListAdapter.java | 3 +- .../tvhguide/EPGTimeListViewWrapper.java | 4 +-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 76ae095..d6f871b 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -51,6 +51,7 @@ public class EPGTimeListActivity extends FragmentActivity { private static final int DEFAULT_HOURS = 24; + private static final int DEFAULT_EPG_LIST_MAX_START_TIME = 30; /** * The serialization (saved instance state) Bundle key representing the @@ -85,6 +86,7 @@ public class EPGTimeListActivity extends FragmentActivity { private TextView tagTextView; private ImageView tagImageView; + private int m_maxStartTimeAfterTimeSlotInMinutes; @Override protected void onCreate(Bundle savedInstanceState) { @@ -110,6 +112,9 @@ protected void onCreate(Bundle savedInstanceState) { timeSlots.add(cal.getTime()); cal.add(Calendar.HOUR_OF_DAY, 1); } + m_maxStartTimeAfterTimeSlotInMinutes = prefs + .getInt("epg.timeslots.max_start_time", + DEFAULT_EPG_LIST_MAX_START_TIME); // Create the adapter that will return a fragment for each of the three // primary sections of the app. @@ -605,16 +610,38 @@ static interface EPGFragmentListener { * * @return */ - public static Programme getProgrammeStartingAfter(Channel channel, - Date timeSlot) { + public Programme getProgrammeStartingAfter(Channel channel, Date timeSlot) { Iterator it = channel.epg.iterator(); + Calendar cal = Calendar.getInstance(); + cal.setTime(timeSlot); + cal.add(Calendar.MINUTE, m_maxStartTimeAfterTimeSlotInMinutes); + Date maxStartTime = cal.getTime(); + // find first programm after timeslot + Programme lastPr = null; while (it.hasNext()) { Programme pr = it.next(); - if (pr.start.equals(timeSlot) || pr.start.after(timeSlot)) { + + // first check if programm starts at given time + if (pr.start.equals(timeSlot)) { + return pr; + } + + // secondly check if program starts within next 30 minutes + // (configurable via settings) + if (pr.start.after(timeSlot) && pr.start.before(maxStartTime)) { return pr; } + + // secondly check if last Programme is still running + if (lastPr != null) { + if (lastPr.stop.after(timeSlot)) { + return lastPr; + } + } + + lastPr = pr; } return null; } diff --git a/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java index 4bb27eb..9ccfc69 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListAdapter.java @@ -38,7 +38,8 @@ public int compare(Channel x, Channel y) { public Programme getProgrammeAt(int position) { Channel item = getItem(position); - return EPGTimeListActivity.getProgrammeStartingAfter(item, timeSlot); + return ((EPGTimeListActivity) getContext()).getProgrammeStartingAfter( + item, timeSlot); } public void updateView(ListView listView, Channel channel) { diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index 2c104be..c5ce703 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -41,8 +41,8 @@ public void repaint(Channel channel) { icon.invalidate(); } - Programme pr = EPGTimeListActivity.getProgrammeStartingAfter(channel, - timeSlot); + Programme pr = ((EPGTimeListActivity) context) + .getProgrammeStartingAfter(channel, timeSlot); if (!channel.isTransmitting || channel.epg.size() == 0) { title.setText(R.string.ch_no_transmission); } else if (pr == null) { From 090986c34b05583454dcb4769cb8f2bb1672cb99 Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 15:02:32 +0200 Subject: [PATCH 23/45] added link to programm list from epg list to list all available programms of current channel --- res/values/strings.xml | 1 + src/org/tvheadend/tvhguide/EPGTimeListActivity.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 7a156d5..fa245e7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13,6 +13,7 @@ Record Cancel recording Remove recording + Program list Hostname Enter Server Hostname Port diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index d6f871b..2abf503 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -470,6 +470,7 @@ public void onCreateContextMenu(ContextMenu menu, View v, if (info == null) { return; } + Channel channel = prAdapter.getItem(info.position); Programme p = prAdapter.getProgrammeAt(info.position); if (p == null) { @@ -502,7 +503,6 @@ public void onCreateContextMenu(ContextMenu menu, View v, R.string.menu_record_remove, ContextMenu.NONE, R.string.menu_record_remove); } - item.setIntent(intent); item = menu.add(ContextMenu.NONE, R.string.search_hint, @@ -513,6 +513,15 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.NONE, "IMDb"); item.setIntent(new SearchIMDbIntent(getActivity(), p.title)); } + if (channel != null) { + intent = new Intent(getBaseContext(), + ProgrammeListActivity.class); + intent.putExtra("channelId", channel.id); + item = menu.add(ContextMenu.NONE, + R.string.menu_show_program_list, ContextMenu.NONE, + R.string.menu_show_program_list); + item.setIntent(intent); + } } @Override From f79ead335505ab7d9a69501391cc023459dea8e2 Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 20:37:48 +0200 Subject: [PATCH 24/45] disable context menu when long clicking while scrolling --- .../tvhguide/ui/HorizontalListView.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java index 32acbe0..1d9965c 100644 --- a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java +++ b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java @@ -97,6 +97,7 @@ public void onInvalidated() { } }; + private boolean m_scrolling; @Override public ListAdapter getAdapter() { @@ -111,7 +112,9 @@ public View getSelectedView() { @Override public void createContextMenu(ContextMenu menu) { - super.createContextMenu(menu); + if (!m_scrolling) { + super.createContextMenu(menu); + } } @Override @@ -295,6 +298,8 @@ public boolean dispatchTouchEvent(MotionEvent ev) { protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + m_scrolling = true; + synchronized (HorizontalListView.this) { mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); } @@ -304,10 +309,16 @@ protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, } protected boolean onDown(MotionEvent e) { + m_scrolling = false; mScroller.forceFinished(true); return true; } + protected void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + m_scrolling = true; + } + private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { @Override @@ -325,7 +336,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - + HorizontalListView.this.onScroll(e1, e2, distanceX, distanceY); synchronized (HorizontalListView.this) { mNextX += (int) distanceX; } @@ -372,7 +383,7 @@ public void onLongPress(MotionEvent e) { for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (isEventWithinView(e, child)) { - if (mOnItemLongClicked != null) { + if (mOnItemLongClicked != null && !m_scrolling) { mOnItemLongClicked.onItemLongClick( HorizontalListView.this, child, mLeftViewIndex + 1 + i, From 5c39fd10e82738e81342e5d1b9032af280657c62 Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 21:48:17 +0200 Subject: [PATCH 25/45] synchronized scrolling of epg lists --- .../tvhguide/EPGTimelineActivity.java | 54 ++++++++++++++++++ .../tvhguide/EPGTimelineAdapter.java | 11 +++- .../tvhguide/EPGTimelineViewWrapper.java | 57 +++++++++++++++++-- .../tvhguide/ui/HorizontalListView.java | 16 +++++- 4 files changed, 130 insertions(+), 8 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index b6134a5..a1b9676 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -1,6 +1,8 @@ package org.tvheadend.tvhguide; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.tvheadend.tvhguide.htsp.HTSListener; import org.tvheadend.tvhguide.htsp.HTSService; @@ -20,8 +22,11 @@ import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.OnGestureListener; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.widget.AdapterView; @@ -44,6 +49,11 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { private TextView tagTextView; private ImageView tagImageView; + private GestureDetector mGesture; + + private List mListeners = Collections + .synchronizedList(new ArrayList()); + @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager @@ -83,6 +93,8 @@ public void onClick(DialogInterface arg0, int pos) { }); tagDialog = builder.create(); + + mGesture = new GestureDetector(this, mOnGesture); } private void setCurrentTag(ChannelTag t) { @@ -104,6 +116,7 @@ public void populateChannelList() { TVHGuideApplication app = (TVHGuideApplication) getApplication(); ChannelTag currentTag = app.getCurrentTag(); adapter.clear(); + // mListeners.clear(); for (Channel ch : app.getChannels()) { if (currentTag == null || ch.hasTag(currentTag.id)) { @@ -326,4 +339,45 @@ private void setLoading(boolean loading) { populateChannelList(); } } + + public void onHorizontalViewTouch(MotionEvent event) { + mGesture.onTouchEvent(event); + } + + public void registerOnScrollListener(OnEPGScrollListener listener) { + mListeners.add(listener); + } + + public void unregisterOnScrollListener(OnEPGScrollListener listener) { + mListeners.remove(listener); + } + + private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + for (OnEPGScrollListener listener : mListeners) { + listener.onFling(e1, e2, velocityX, velocityY); + } + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + for (OnEPGScrollListener listener : mListeners) { + listener.onScroll(e1, e2, distanceX, distanceY); + } + return false; + } + }; + + public static interface OnEPGScrollListener { + public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY); + + public void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY); + } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index abd7105..53ab0fe 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -14,9 +14,9 @@ class EPGTimelineAdapter extends ArrayAdapter { - private final Activity context; + private final EPGTimelineActivity context; - EPGTimelineAdapter(Activity context, List list) { + EPGTimelineAdapter(EPGTimelineActivity context, List list) { super(context, R.layout.epgtimeline_widget, list); this.context = context; } @@ -63,6 +63,7 @@ public View getView(int position, View convertView, ViewGroup parent) { row = inflater.inflate(R.layout.epgtimeline_widget, null, false); row.requestLayout(); wrapper = new EPGTimelineViewWrapper(context, row); + context.registerOnScrollListener(wrapper); row.setTag(wrapper); } else { @@ -72,4 +73,10 @@ public View getView(int position, View convertView, ViewGroup parent) { wrapper.repaint(ch); return row; } + + @Override + public void clear() { + + super.clear(); + } } \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index de055c8..edf27bf 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -22,6 +22,7 @@ import java.util.Comparator; import java.util.List; +import org.tvheadend.tvhguide.EPGTimelineActivity.OnEPGScrollListener; import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.Programme; import org.tvheadend.tvhguide.ui.HorizontalListView; @@ -31,7 +32,9 @@ import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; @@ -43,13 +46,16 @@ * * @author mike toggweiler */ -public class EPGTimelineViewWrapper implements OnItemClickListener { +public class EPGTimelineViewWrapper implements OnItemClickListener, + OnTouchListener, OnEPGScrollListener { private ImageView icon; private LinearLayout timeline; - private final Activity context; + private final EPGTimelineActivity context; + private HorizontalListView horizontialListView; + private boolean locked; - public EPGTimelineViewWrapper(Activity context, View base) { + public EPGTimelineViewWrapper(EPGTimelineActivity context, View base) { this.context = context; icon = (ImageView) base.findViewById(R.id.ch_icon); @@ -57,6 +63,13 @@ public EPGTimelineViewWrapper(Activity context, View base) { } public void repaint(Channel channel) { + + if (horizontialListView != null) { + horizontialListView.setOnTouchListener(null); + horizontialListView.setOnItemClickListener(null); + horizontialListView = null; + } + timeline.removeAllViews(); // SharedPreferences prefs = PreferenceManager @@ -73,19 +86,30 @@ public void repaint(Channel channel) { } icon.invalidate(); - HorizontalListView horizontialListView = new HorizontalListView( - context, null); + horizontialListView = new HorizontalListView(context, null); horizontialListView.setClickable(true); horizontialListView.setOnItemClickListener(this); TimelineProgrammeAdapter adapter = new TimelineProgrammeAdapter( context, new ArrayList(channel.epg)); horizontialListView.setAdapter(adapter); + horizontialListView.setOnTouchListener(this); context.registerForContextMenu(horizontialListView); timeline.addView(horizontialListView); } + @Override + public boolean onTouch(View view, MotionEvent event) { + try { + locked = true; + context.onHorizontalViewTouch(event); + } finally { + locked = false; + } + return false; + } + @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { @@ -135,4 +159,27 @@ public View getView(int position, View convertView, ViewGroup parent) { return row; } } + + @Override + public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + if (locked) { + return; + } + synchronized (horizontialListView) { + horizontialListView.flingBy((int) velocityX); + } + } + + @Override + public void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + if (locked) { + return; + } + synchronized (horizontialListView) { + horizontialListView.scrollTo((int) (horizontialListView + .getScrollPositionX() + distanceX)); + } + } } diff --git a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java index 1d9965c..d1392c8 100644 --- a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java +++ b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java @@ -285,10 +285,24 @@ private void positionItems(final int dx) { } public synchronized void scrollTo(int x) { - mScroller.startScroll(mNextX, 0, x - mNextX, 0); + synchronized (HorizontalListView.this) { + mNextX = x; + } requestLayout(); } + public synchronized void flingBy(int velocityX) { + + synchronized (HorizontalListView.this) { + mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); + } + requestLayout(); + } + + public int getScrollPositionX() { + return mNextX; + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { boolean handled = super.dispatchTouchEvent(ev); From 78a25a9f070fc2a517f65d01e6bf81a48c8ffea1 Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 22:17:39 +0200 Subject: [PATCH 26/45] refactoring of synchronisation --- res/layout/epgtimeline_widget.xml | 17 ++-- .../tvhguide/EPGTimelineActivity.java | 56 +++++------- .../tvhguide/EPGTimelineViewWrapper.java | 85 +++++++++++-------- .../tvhguide/ui/HorizontalListView.java | 2 +- 4 files changed, 82 insertions(+), 78 deletions(-) diff --git a/res/layout/epgtimeline_widget.xml b/res/layout/epgtimeline_widget.xml index 11f8a91..9f20be8 100644 --- a/res/layout/epgtimeline_widget.xml +++ b/res/layout/epgtimeline_widget.xml @@ -16,11 +16,16 @@ android:adjustViewBounds="true" android:contentDescription="@string/icon"/> - - + + + + + + + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index a1b9676..521928a 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -22,11 +22,8 @@ import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.GestureDetector; -import android.view.GestureDetector.OnGestureListener; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.widget.AdapterView; @@ -49,11 +46,11 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { private TextView tagTextView; private ImageView tagImageView; - private GestureDetector mGesture; - private List mListeners = Collections .synchronizedList(new ArrayList()); + private int mLastEPGScrollPosition; + @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager @@ -93,8 +90,6 @@ public void onClick(DialogInterface arg0, int pos) { }); tagDialog = builder.create(); - - mGesture = new GestureDetector(this, mOnGesture); } private void setCurrentTag(ChannelTag t) { @@ -116,7 +111,7 @@ public void populateChannelList() { TVHGuideApplication app = (TVHGuideApplication) getApplication(); ChannelTag currentTag = app.getCurrentTag(); adapter.clear(); - // mListeners.clear(); + mListeners.clear(); for (Channel ch : app.getChannels()) { if (currentTag == null || ch.hasTag(currentTag.id)) { @@ -340,8 +335,21 @@ private void setLoading(boolean loading) { } } - public void onHorizontalViewTouch(MotionEvent event) { - mGesture.onTouchEvent(event); + public void notifyOnScoll(int scrollTo) { + mLastEPGScrollPosition = scrollTo; + for (OnEPGScrollListener listener : mListeners) { + listener.scrollTo(scrollTo); + } + } + + public int getLastEPGScrollPosition() { + return mLastEPGScrollPosition; + } + + public void notifyOnFling(float velocityX) { + for (OnEPGScrollListener listener : mListeners) { + listener.flingBy(velocityX); + } } public void registerOnScrollListener(OnEPGScrollListener listener) { @@ -352,32 +360,10 @@ public void unregisterOnScrollListener(OnEPGScrollListener listener) { mListeners.remove(listener); } - private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY) { - for (OnEPGScrollListener listener : mListeners) { - listener.onFling(e1, e2, velocityX, velocityY); - } - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, - float distanceX, float distanceY) { - for (OnEPGScrollListener listener : mListeners) { - listener.onScroll(e1, e2, distanceX, distanceY); - } - return false; - } - }; - public static interface OnEPGScrollListener { - public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY); - public void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, - float distanceY); + public void scrollTo(int scrollTo); + + public void flingBy(float velocityX); } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index edf27bf..0013848 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -31,6 +31,8 @@ import android.content.Context; import android.content.Intent; import android.graphics.drawable.BitmapDrawable; +import android.view.GestureDetector; +import android.view.GestureDetector.OnGestureListener; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -40,7 +42,6 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; -import android.widget.LinearLayout; /** * @@ -50,27 +51,53 @@ public class EPGTimelineViewWrapper implements OnItemClickListener, OnTouchListener, OnEPGScrollListener { private ImageView icon; - private LinearLayout timeline; private final EPGTimelineActivity context; - private HorizontalListView horizontialListView; + private HorizontalListView horizontalListView; private boolean locked; + private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + try { + locked = true; + context.notifyOnScoll(horizontalListView.getScrollPositionX()); + } finally { + locked = false; + } + return false; + } + + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + try { + locked = true; + context.notifyOnFling(velocityX); + } finally { + locked = false; + } + return false; + }; + }; + private GestureDetector mGesture; + public EPGTimelineViewWrapper(EPGTimelineActivity context, View base) { this.context = context; icon = (ImageView) base.findViewById(R.id.ch_icon); - timeline = (LinearLayout) base.findViewById(R.id.ch_timeline); - } + mGesture = new GestureDetector(context, mOnGesture); - public void repaint(Channel channel) { + horizontalListView = (HorizontalListView) base + .findViewById(R.id.ch_timeline); + horizontalListView.setClickable(true); + horizontalListView.setOnItemClickListener(this); + horizontalListView.setOnTouchListener(this); - if (horizontialListView != null) { - horizontialListView.setOnTouchListener(null); - horizontialListView.setOnItemClickListener(null); - horizontialListView = null; - } + context.registerForContextMenu(horizontalListView); + } - timeline.removeAllViews(); + public void repaint(Channel channel) { // SharedPreferences prefs = PreferenceManager // .getDefaultSharedPreferences(icon.getContext()); @@ -86,27 +113,16 @@ public void repaint(Channel channel) { } icon.invalidate(); - horizontialListView = new HorizontalListView(context, null); - horizontialListView.setClickable(true); - horizontialListView.setOnItemClickListener(this); TimelineProgrammeAdapter adapter = new TimelineProgrammeAdapter( context, new ArrayList(channel.epg)); - horizontialListView.setAdapter(adapter); - horizontialListView.setOnTouchListener(this); - - context.registerForContextMenu(horizontialListView); - - timeline.addView(horizontialListView); + horizontalListView.setAdapter(adapter); + horizontalListView.scrollTo(context.getLastEPGScrollPosition()); + horizontalListView.invalidate(); } @Override public boolean onTouch(View view, MotionEvent event) { - try { - locked = true; - context.onHorizontalViewTouch(event); - } finally { - locked = false; - } + mGesture.onTouchEvent(event); return false; } @@ -161,25 +177,22 @@ public View getView(int position, View convertView, ViewGroup parent) { } @Override - public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY) { + public void scrollTo(int scrollTo) { if (locked) { return; } - synchronized (horizontialListView) { - horizontialListView.flingBy((int) velocityX); + synchronized (horizontalListView) { + horizontalListView.scrollTo(scrollTo); } } @Override - public void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, - float distanceY) { + public void flingBy(float velocityX) { if (locked) { return; } - synchronized (horizontialListView) { - horizontialListView.scrollTo((int) (horizontialListView - .getScrollPositionX() + distanceX)); + synchronized (horizontalListView) { + horizontalListView.flingBy(velocityX); } } } diff --git a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java index d1392c8..187139c 100644 --- a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java +++ b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java @@ -291,7 +291,7 @@ public synchronized void scrollTo(int x) { requestLayout(); } - public synchronized void flingBy(int velocityX) { + public synchronized void flingBy(float velocityX) { synchronized (HorizontalListView.this) { mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); From 4f66c1b361e4912ea6c2d2adda07963ca9c152ec Mon Sep 17 00:00:00 2001 From: toggm Date: Sun, 21 Apr 2013 22:40:01 +0200 Subject: [PATCH 27/45] fixed scaling depending on runtime --- res/layout/epgtimeline_programme_widget.xml | 9 ++-- res/layout/epgtimeline_widget.xml | 45 ++++++++----------- .../EPGTimelineProgrammeListViewWrapper.java | 16 ++++++- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index b5fca22..2949a8e 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -2,13 +2,15 @@ + android:paddingRight="2dp" + android:layout_gravity="center_vertical"> - - - - - - - - - - - - + android:layout_height="wrap_content" + android:layout_marginLeft="5sp" + android:layout_marginRight="5sp" > + + + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index c51d0ce..33a0b43 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -16,6 +16,8 @@ public class EPGTimelineProgrammeListViewWrapper extends private TypedArray colors; LinearLayout container; + private LinearLayout container2; + private LinearLayout container3; private static final int WIDTH_PER_MINUTE = 10; public EPGTimelineProgrammeListViewWrapper(View base) { @@ -24,6 +26,10 @@ public EPGTimelineProgrammeListViewWrapper(View base) { colors = res.obtainTypedArray(R.array.pref_color_content_type); container = (LinearLayout) base.findViewById(R.id.programme_container); + container2 = (LinearLayout) base + .findViewById(R.id.programme_container2); + container3 = (LinearLayout) base + .findViewById(R.id.programme_container3); } @Override @@ -43,15 +49,23 @@ public void repaint(Programme p) { } long remainingMillis = p.stop.getTime() - start.getTime(); - long minutes = remainingMillis / (60 * 60 * 1000); + long minutes = remainingMillis / (60 * 1000); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( (int) minutes * WIDTH_PER_MINUTE, LayoutParams.MATCH_PARENT); container.setLayoutParams(layoutParams); container.setVisibility(LinearLayout.VISIBLE); + + container2.setLayoutParams(layoutParams); + container3.setLayoutParams(layoutParams); + + System.out.println("minutes:" + remainingMillis + ":" + minutes + + ", width:" + (minutes * WIDTH_PER_MINUTE)); } else { container.setVisibility(LinearLayout.GONE); } container.invalidate(); + container2.invalidate(); + container3.invalidate(); } } From d5b7393bf60db505e8468eb86a7a4d2612272b9e Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 24 Apr 2013 21:47:36 +0200 Subject: [PATCH 28/45] adjusted layouting --- res/layout/epgtimeline_programme_widget.xml | 13 ++++++++----- res/layout/epgtimeline_widget.xml | 12 +++++++++--- .../EPGTimelineProgrammeListViewWrapper.java | 8 ++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index 2949a8e..71afc07 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/programme_container" android:layout_width="100dp" - android:layout_height="fill_parent" + android:layout_height="65sp" android:orientation="vertical" android:layout_marginRight="2dp" android:paddingRight="2dp" @@ -15,14 +15,16 @@ android:layout_height="wrap_content" android:layout_marginLeft="5sp" android:layout_marginRight="5sp" - android:orientation="horizontal" > + android:orientation="horizontal" + android:layout_gravity="center_vertical" > + android:maxLines="2" + android:singleLine="false" + android:textSize="16sp" > + android:orientation="horizontal" + android:layout_gravity="center_vertical" > + android:scaleType="centerInside" + android:layout_gravity="center_vertical"/> + android:layout_height="65sp" + android:layout_marginBottom="2dip" + android:layout_marginTop="1dip" + android:layout_gravity="center_vertical" /> \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 33a0b43..40b5785 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -9,16 +9,16 @@ import android.content.res.TypedArray; import android.view.View; import android.widget.LinearLayout; -import android.widget.LinearLayout.LayoutParams; public class EPGTimelineProgrammeListViewWrapper extends ProgrammeListViewWrapper { private TypedArray colors; - LinearLayout container; + private LinearLayout container; private LinearLayout container2; private LinearLayout container3; - private static final int WIDTH_PER_MINUTE = 10; + private static final int WIDTH_PER_MINUTE = 5; + private static final int LAYOUT_HEIGHT = 68; public EPGTimelineProgrammeListViewWrapper(View base) { super(base); @@ -52,7 +52,7 @@ public void repaint(Programme p) { long minutes = remainingMillis / (60 * 1000); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - (int) minutes * WIDTH_PER_MINUTE, LayoutParams.MATCH_PARENT); + (int) minutes * WIDTH_PER_MINUTE, LAYOUT_HEIGHT); container.setLayoutParams(layoutParams); container.setVisibility(LinearLayout.VISIBLE); From f3a6fd4d4e3a8b9851541adc2985d48c26e7b547 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 24 Apr 2013 22:10:38 +0200 Subject: [PATCH 29/45] added visualizing of programm/recording --- AndroidManifest.xml | 2 +- res/layout/epgtimeline_programme_widget.xml | 2 +- .../tvhguide/EPGTimeListViewWrapper.java | 3 +- .../tvhguide/EPGTimelineActivity.java | 32 +++++++++++++++++++ .../tvhguide/EPGTimelineAdapter.java | 2 ++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 79172fe..d518d73 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -7,7 +7,7 @@ diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index c5ce703..ed78702 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -46,7 +46,6 @@ public void repaint(Channel channel) { if (!channel.isTransmitting || channel.epg.size() == 0) { title.setText(R.string.ch_no_transmission); } else if (pr == null) { - // title.setText(R.string.ch_no_transmission); // if last, preload next programmes of this channel pr = channel.epg.last(); long nextId = pr.nextId; @@ -60,6 +59,8 @@ public void repaint(Channel channel) { intent.putExtra("channelId", channel.id); intent.putExtra("count", 2); context.startService(intent); + + // title.setText(R.string.ch_no_transmission); } else { super.repaint(pr); } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 521928a..b2556ee 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -11,6 +11,7 @@ import org.tvheadend.tvhguide.model.Channel; import org.tvheadend.tvhguide.model.ChannelTag; import org.tvheadend.tvhguide.model.Programme; +import org.tvheadend.tvhguide.model.Recording; import org.tvheadend.tvhguide.ui.HorizontalListView; import android.app.AlertDialog; @@ -314,6 +315,37 @@ public void run() { tagAdapter.remove(tag); } }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + adapter.updateView(getListView(), p.channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + adapter.updateView(getListView(), p.channel); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Recording rec = (Recording) obj; + for (Channel c : adapter.list) { + for (Programme p : c.epg) { + if (rec == p.recording) { + adapter.updateView(getListView(), c); + return; + } + } + } + } + }); } else if (action.equals(TVHGuideApplication.ACTION_TAG_UPDATE)) { // NOP } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index 53ab0fe..52f5908 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -15,10 +15,12 @@ class EPGTimelineAdapter extends ArrayAdapter { private final EPGTimelineActivity context; + final List list; EPGTimelineAdapter(EPGTimelineActivity context, List list) { super(context, R.layout.epgtimeline_widget, list); this.context = context; + this.list = list; } public void sort() { From 9ec417e71bbf42978e749a0b50856c1a7bda1236 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 24 Apr 2013 22:46:53 +0200 Subject: [PATCH 30/45] added recording state informations --- res/layout/programme_layout.xml | 30 +- .../tvheadend/tvhguide/ProgrammeActivity.java | 38 ++- .../tvhguide/ProgrammeListViewWrapper.java | 18 +- .../tvheadend/tvhguide/RecordingActivity.java | 287 +++++++++--------- 4 files changed, 208 insertions(+), 165 deletions(-) diff --git a/res/layout/programme_layout.xml b/res/layout/programme_layout.xml index 3b530e2..0c660b8 100644 --- a/res/layout/programme_layout.xml +++ b/res/layout/programme_layout.xml @@ -8,13 +8,29 @@ android:layout_height="wrap_content" android:orientation="vertical" > - + android:orientation="horizontal" > + + + + + + @@ -104,9 +120,9 @@ @@ -116,6 +132,7 @@ android:layout_marginRight="5sp" android:text="@string/pr_content_type" android:textAppearance="?android:attr/textAppearanceMedium" /> + - Date: Thu, 25 Apr 2013 21:36:40 +0200 Subject: [PATCH 31/45] added progressbar to indicate loading --- res/layout/epgnow_list_widget.xml | 10 +++++++++- src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml index 9ceaa31..e7415dc 100644 --- a/res/layout/epgnow_list_widget.xml +++ b/res/layout/epgnow_list_widget.xml @@ -25,6 +25,14 @@ android:layout_marginLeft="5sp" android:layout_marginRight="5sp" android:orientation="horizontal" > + + - + Date: Thu, 25 Apr 2013 21:59:41 +0200 Subject: [PATCH 32/45] added missing file --- src/org/tvheadend/tvhguide/RecordUtil.java | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/org/tvheadend/tvhguide/RecordUtil.java diff --git a/src/org/tvheadend/tvhguide/RecordUtil.java b/src/org/tvheadend/tvhguide/RecordUtil.java new file mode 100644 index 0000000..a06ed2c --- /dev/null +++ b/src/org/tvheadend/tvhguide/RecordUtil.java @@ -0,0 +1,27 @@ +package org.tvheadend.tvhguide; + +import org.tvheadend.tvhguide.model.Recording; + +import android.widget.ImageView; + +public class RecordUtil { + public static void applyRecording(Recording rec, ImageView state) { + if (rec == null) { + state.setImageDrawable(null); + } else if (rec.error != null) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("completed".equals(rec.state)) { + state.setImageResource(R.drawable.ic_success_small); + } else if ("invalid".equals(rec.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("missed".equals(rec.state)) { + state.setImageResource(R.drawable.ic_error_small); + } else if ("recording".equals(rec.state)) { + state.setImageResource(R.drawable.ic_rec_small); + } else if ("scheduled".equals(rec.state)) { + state.setImageResource(R.drawable.ic_schedule_small); + } else { + state.setImageDrawable(null); + } + } +} From efc7d87390ece3c8e2cc9df9725b46dee5d6d707 Mon Sep 17 00:00:00 2001 From: toggm Date: Thu, 25 Apr 2013 22:27:36 +0200 Subject: [PATCH 33/45] added support for loading next events from within timeline view --- res/layout/epgtimeline_widget.xml | 2 + .../tvhguide/EPGTimelineActivity.java | 11 +++ .../tvhguide/EPGTimelineAdapter.java | 30 +++++- .../EPGTimelineProgrammeListViewWrapper.java | 3 - .../tvhguide/EPGTimelineViewWrapper.java | 98 +++++++++++++++---- .../tvheadend/tvhguide/EventLoadHandler.java | 5 + .../tvheadend/tvhguide/htsp/HTSService.java | 7 +- 7 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 src/org/tvheadend/tvhguide/EventLoadHandler.java diff --git a/res/layout/epgtimeline_widget.xml b/res/layout/epgtimeline_widget.xml index 5cf7a82..e4093c0 100644 --- a/res/layout/epgtimeline_widget.xml +++ b/res/layout/epgtimeline_widget.xml @@ -24,5 +24,7 @@ android:layout_marginBottom="2dip" android:layout_marginTop="1dip" android:layout_gravity="center_vertical" /> + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index b2556ee..11c8ef6 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -315,6 +315,17 @@ public void run() { tagAdapter.remove(tag); } }); + } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + Programme p = (Programme) obj; + try { + adapter.updateView(getListView(), p.channel); + } catch (Exception e) { + } + } + }); } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_DELETE)) { runOnUiThread(new Runnable() { diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index 52f5908..adf6378 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -3,16 +3,20 @@ import java.util.Comparator; import java.util.List; +import org.tvheadend.tvhguide.htsp.HTSService; import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Programme; import android.app.Activity; +import android.content.Intent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; -class EPGTimelineAdapter extends ArrayAdapter { +class EPGTimelineAdapter extends ArrayAdapter implements + EventLoadHandler { private final EPGTimelineActivity context; final List list; @@ -64,7 +68,7 @@ public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = activity.getLayoutInflater(); row = inflater.inflate(R.layout.epgtimeline_widget, null, false); row.requestLayout(); - wrapper = new EPGTimelineViewWrapper(context, row); + wrapper = new EPGTimelineViewWrapper(context, row, this); context.registerOnScrollListener(wrapper); row.setTag(wrapper); @@ -76,6 +80,28 @@ public View getView(int position, View convertView, ViewGroup parent) { return row; } + @Override + public void loadNextEvents() { + for (int i = 0; i < getCount(); ++i) { + + Channel ch = getItem(i); + if (ch.epg.size() > 0) { + Programme last = ch.epg.last(); + long nextId = last.nextId; + if (nextId == 0) { + nextId = last.id; + } + + Intent intent = new Intent(context, HTSService.class); + intent.setAction(HTSService.ACTION_GET_EVENTS); + intent.putExtra("eventId", nextId); + intent.putExtra("channelId", ch.id); + intent.putExtra("count", 5); + context.startService(intent); + } + } + } + @Override public void clear() { diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 40b5785..f64a592 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -58,9 +58,6 @@ public void repaint(Programme p) { container2.setLayoutParams(layoutParams); container3.setLayoutParams(layoutParams); - - System.out.println("minutes:" + remainingMillis + ":" + minutes - + ", width:" + (minutes * WIDTH_PER_MINUTE)); } else { container.setVisibility(LinearLayout.GONE); } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index 0013848..5229ebb 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -41,6 +41,7 @@ import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; +import android.widget.Button; import android.widget.ImageView; /** @@ -81,9 +82,12 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, }; }; private GestureDetector mGesture; + private final EventLoadHandler loadHandler; - public EPGTimelineViewWrapper(EPGTimelineActivity context, View base) { + public EPGTimelineViewWrapper(EPGTimelineActivity context, View base, + EventLoadHandler loadHandler) { this.context = context; + this.loadHandler = loadHandler; icon = (ImageView) base.findViewById(R.id.ch_icon); mGesture = new GestureDetector(context, mOnGesture); @@ -103,7 +107,7 @@ public void repaint(Channel channel) { // .getDefaultSharedPreferences(icon.getContext()); // Boolean showIcons = prefs.getBoolean("showIconPref", false); // icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setBackground(new BitmapDrawable(context.getResources(), + icon.setBackgroundDrawable(new BitmapDrawable(context.getResources(), channel.iconBitmap)); if (channel.isRecording()) { @@ -130,16 +134,31 @@ public boolean onTouch(View view, MotionEvent event) { public void onItemClick(AdapterView adapterView, View view, int position, long id) { Programme p = (Programme) adapterView.getItemAtPosition(position); - Intent intent = new Intent(context, ProgrammeActivity.class); - intent.putExtra("eventId", p.id); - intent.putExtra("channelId", p.channel.id); - context.startActivity(intent); + + if (p == null) { + // load next + loadHandler.loadNextEvents(); + } else { + Intent intent = new Intent(context, ProgrammeActivity.class); + intent.putExtra("eventId", p.id); + intent.putExtra("channelId", p.channel.id); + context.startActivity(intent); + } + } + + protected void loadNext() { + } class TimelineProgrammeAdapter extends ArrayAdapter { + public static final int VIEW_TYPE_END = 100; + private Button button; + TimelineProgrammeAdapter(Context context, List epg) { super(context, R.layout.epgtimeline_programme_widget, epg); + button = new Button(context); + button.setBackgroundResource(android.R.drawable.ic_menu_more); } public void sort() { @@ -151,28 +170,67 @@ public int compare(Programme x, Programme y) { }); } + @Override + public int getCount() { + return super.getCount() + 1; + } + + @Override + public int getItemViewType(int position) { + if (position == super.getCount()) { + return VIEW_TYPE_END; + } + return super.getItemViewType(position); + } + + @Override + public Programme getItem(int position) { + if (position == super.getCount()) { + return null; + } + return super.getItem(position); + } + + @Override + public long getItemId(int position) { + if (position == super.getCount()) { + return VIEW_TYPE_END; + } + return super.getItemId(position); + } + + @Override + public int getViewTypeCount() { + return super.getViewTypeCount() + 1; + } + @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; EPGTimelineProgrammeListViewWrapper wrapper; - Programme pr = getItem(position); - Activity activity = (Activity) getContext(); + if (position == super.getCount()) { + return button; + } else { + Programme pr = getItem(position); + Activity activity = (Activity) getContext(); - if (row == null) { - LayoutInflater inflater = activity.getLayoutInflater(); - row = inflater.inflate(R.layout.epgtimeline_programme_widget, - null, false); - row.requestLayout(); - wrapper = new EPGTimelineProgrammeListViewWrapper(row); - row.setTag(wrapper); + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate( + R.layout.epgtimeline_programme_widget, null, false); + row.requestLayout(); + wrapper = new EPGTimelineProgrammeListViewWrapper(row); + row.setTag(wrapper); - } else { - wrapper = (EPGTimelineProgrammeListViewWrapper) row.getTag(); - } + } else { + wrapper = (EPGTimelineProgrammeListViewWrapper) row + .getTag(); + } - wrapper.repaint(pr); - return row; + wrapper.repaint(pr); + return row; + } } } diff --git a/src/org/tvheadend/tvhguide/EventLoadHandler.java b/src/org/tvheadend/tvhguide/EventLoadHandler.java new file mode 100644 index 0000000..97574aa --- /dev/null +++ b/src/org/tvheadend/tvhguide/EventLoadHandler.java @@ -0,0 +1,5 @@ +package org.tvheadend.tvhguide; + +public interface EventLoadHandler { + public void loadNextEvents(); +} \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/htsp/HTSService.java b/src/org/tvheadend/tvhguide/htsp/HTSService.java index 4e9a4ec..5ca6cc1 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSService.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSService.java @@ -135,9 +135,10 @@ public void run() { getEvent(intent.getLongExtra("eventId", 0)); } else if (ACTION_GET_EVENTS.equals(intent.getAction())) { TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); - getEvents(ch, intent.getLongExtra("eventId", 0), - intent.getIntExtra("count", 10)); + long channelId = intent.getLongExtra("channelId", 0); + int count = intent.getIntExtra("count", 10); + Channel ch = app.getChannel(channelId); + getEvents(ch, intent.getLongExtra("eventId", 0), count); } else if (ACTION_DVR_ADD.equals(intent.getAction())) { TVHGuideApplication app = (TVHGuideApplication) getApplication(); Channel ch = app.getChannel(intent.getLongExtra("channelId", 0)); From 03f4ce66bc9aba180f0faca44ac9fa95aa6b2daf Mon Sep 17 00:00:00 2001 From: toggm Date: Mon, 29 Apr 2013 22:07:12 +0200 Subject: [PATCH 34/45] added prime time timeslots menu --- AndroidManifest.xml | 5 +- res/menu/main_menu.xml | 3 + res/values/strings.xml | 5 +- .../tvhguide/ChannelListActivity.java | 8 +- .../tvhguide/EPGHourlyTimeListActivity.java | 35 +++++++ .../tvhguide/EPGPrimeTimeListActivity.java | 96 +++++++++++++++++++ .../tvhguide/EPGTimeListActivity.java | 83 +++++++++++----- .../tvhguide/EPGTimeListViewWrapper.java | 11 ++- .../tvhguide/EPGTimelineActivity.java | 8 +- 9 files changed, 222 insertions(+), 32 deletions(-) create mode 100644 src/org/tvheadend/tvhguide/EPGHourlyTimeListActivity.java create mode 100644 src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d518d73..1bfa1e8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -62,7 +62,10 @@ android:configChanges="orientation" android:theme="@android:style/Theme.NoTitleBar" /> + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index fa245e7..35af862 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5,8 +5,9 @@ Help Settings Refresh - Channels - EPG List + Now + Hourly + Primetime Timeline Tags Recordings diff --git a/src/org/tvheadend/tvhguide/ChannelListActivity.java b/src/org/tvheadend/tvhguide/ChannelListActivity.java index 66a89b1..1eec3a9 100644 --- a/src/org/tvheadend/tvhguide/ChannelListActivity.java +++ b/src/org/tvheadend/tvhguide/ChannelListActivity.java @@ -240,9 +240,15 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivity(intent); return true; } + case R.id.mi_epg_prime: { + Intent intent = new Intent(getBaseContext(), + EPGPrimeTimeListActivity.class); + startActivity(intent); + return true; + } case R.id.mi_epg_list: { Intent intent = new Intent(getBaseContext(), - EPGTimeListActivity.class); + EPGHourlyTimeListActivity.class); startActivity(intent); return true; } diff --git a/src/org/tvheadend/tvhguide/EPGHourlyTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGHourlyTimeListActivity.java new file mode 100644 index 0000000..543fda7 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGHourlyTimeListActivity.java @@ -0,0 +1,35 @@ +package org.tvheadend.tvhguide; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public class EPGHourlyTimeListActivity extends EPGTimeListActivity { + + private static final int DEFAULT_HOURS = 24; + + @Override + protected List createTimeSlots() { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + + List timeSlots = new ArrayList(); + + Calendar cal = Calendar.getInstance(); + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + + int maxHours = prefs.getInt("epg.hourly.timeslots", DEFAULT_HOURS); + for (int i = 0; i < maxHours; i++) { + timeSlots.add(cal.getTime()); + cal.add(Calendar.HOUR_OF_DAY, 1); + } + return timeSlots; + } + +} diff --git a/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java new file mode 100644 index 0000000..e2a6e94 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java @@ -0,0 +1,96 @@ +package org.tvheadend.tvhguide; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.text.format.DateFormat; + +public class EPGPrimeTimeListActivity extends EPGTimeListActivity { + + private static final int SLOTS = 24; + + @SuppressWarnings("deprecation") + @Override + protected List createTimeSlots() { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + + java.text.DateFormat format = DateFormat.getTimeFormat(this); + + Set defaultPrimeTimeSlots = new TreeSet(); + defaultPrimeTimeSlots.add("18:30"); + defaultPrimeTimeSlots.add("20:15"); + defaultPrimeTimeSlots.add("22:00"); + + List primeTimeSlots = new ArrayList(prefs.getStringSet( + "epg.prime.timeslots", defaultPrimeTimeSlots)); + + List timeSlots = new ArrayList(); + + // + Calendar cal = Calendar.getInstance(); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + + List primeTimeSlotDates = new ArrayList(); + try { + for (String dateStr : primeTimeSlots) { + primeTimeSlotDates.add(format.parse(dateStr)); + } + } catch (ParseException e) { + e.printStackTrace(); + return timeSlots; + } + + int primeSlotIndex = 0; + + // first find timeslot occurance after actual time + long lastDiff = -1; + for (int i = primeTimeSlots.size() - 1; i >= 0; --i) { + Date time = primeTimeSlotDates.get(i); + int hour = time.getHours(); + int minute = time.getMinutes(); + + Calendar cal2 = Calendar.getInstance(); + cal2.set(Calendar.HOUR_OF_DAY, hour); + cal2.set(Calendar.MINUTE, minute); + cal2.clear(Calendar.SECOND); + cal2.clear(Calendar.MILLISECOND); + + if (cal2.after(cal)) { + long diff = cal2.getTimeInMillis() - cal.getTimeInMillis(); + if (lastDiff == -1 || diff < lastDiff) { + primeSlotIndex = i; + } + lastDiff = diff; + } + } + + for (int i = 0; i < SLOTS; i++) { + Date time = primeTimeSlotDates.get(primeSlotIndex++); + primeSlotIndex %= primeTimeSlotDates.size(); + int hour = time.getHours(); + int minute = time.getMinutes(); + + // find next occurange of timeslot after actual time + int currHour = cal.get(Calendar.HOUR_OF_DAY); + while (currHour != hour) { + cal.add(Calendar.HOUR_OF_DAY, 1); + currHour = cal.get(Calendar.HOUR_OF_DAY); + } + cal.set(Calendar.MINUTE, minute); + + timeSlots.add(cal.getTime()); + cal.add(Calendar.HOUR_OF_DAY, 1); + } + return timeSlots; + } + +} diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 2abf503..3b51bf0 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -48,9 +48,8 @@ * channel * */ -public class EPGTimeListActivity extends FragmentActivity { +public abstract class EPGTimeListActivity extends FragmentActivity { - private static final int DEFAULT_HOURS = 24; private static final int DEFAULT_EPG_LIST_MAX_START_TIME = 30; /** @@ -88,6 +87,8 @@ public class EPGTimeListActivity extends FragmentActivity { private ImageView tagImageView; private int m_maxStartTimeAfterTimeSlotInMinutes; + protected abstract List createTimeSlots(); + @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager @@ -100,18 +101,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.epgnow_list_activity); // create timelost based on actual time - List timeSlots = new ArrayList(); - - Calendar cal = Calendar.getInstance(); - cal.clear(Calendar.MINUTE); - cal.clear(Calendar.SECOND); - cal.clear(Calendar.MILLISECOND); - - int maxHours = prefs.getInt("epg.timeslots", DEFAULT_HOURS); - for (int i = 0; i < maxHours; i++) { - timeSlots.add(cal.getTime()); - cal.add(Calendar.HOUR_OF_DAY, 1); - } + List timeSlots = createTimeSlots(); m_maxStartTimeAfterTimeSlotInMinutes = prefs .getInt("epg.timeslots.max_start_time", DEFAULT_EPG_LIST_MAX_START_TIME); @@ -188,7 +178,22 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivity(intent); return true; } + case R.id.mi_epg_prime: { + if (getClass().isAssignableFrom(EPGPrimeTimeListActivity.class)) { + return true; + } + Intent intent = new Intent(getBaseContext(), + EPGPrimeTimeListActivity.class); + startActivity(intent); + return true; + } case R.id.mi_epg_list: { + if (getClass().isAssignableFrom(EPGHourlyTimeListActivity.class)) { + return true; + } + Intent intent = new Intent(getBaseContext(), + EPGHourlyTimeListActivity.class); + startActivity(intent); return true; } case R.id.mi_channels: { @@ -224,12 +229,10 @@ public boolean onOptionsItemSelected(MenuItem item) { public class SectionsPagerAdapter extends FragmentPagerAdapter { private final Date[] timeslots; - private java.text.DateFormat timeFormat; public SectionsPagerAdapter(FragmentManager fm, Date[] timeslots) { super(fm); this.timeslots = timeslots; - timeFormat = DateFormat.getTimeFormat(getApplicationContext()); } @Override @@ -251,7 +254,7 @@ public int getCount() { @Override public CharSequence getPageTitle(int position) { - return timeFormat.format(timeslots[position]); + return DateFormat.format("E k:mm", timeslots[position]); } } @@ -331,6 +334,8 @@ public class EPGListFragment extends ListFragment implements HTSListener, private Date m_timeslot; + private boolean mCreated; + public EPGListFragment(Date timeslot) { m_timeslot = timeslot; } @@ -344,6 +349,8 @@ public void onCreate(Bundle savedInstanceState) { new ArrayList(), m_timeslot); prAdapter.sort(); setListAdapter(prAdapter); + + mCreated = true; } @Override @@ -556,8 +563,13 @@ public void run() { getActivity().runOnUiThread(new Runnable() { public void run() { - Channel channel = (Channel) obj; - prAdapter.updateView(getListView(), channel); + if (mCreated) { + try { + Channel channel = (Channel) obj; + prAdapter.updateView(getListView(), channel); + } catch (Exception e) { + } + } } }); } else if (action.equals(TVHGuideApplication.ACTION_PROGRAMME_ADD)) { @@ -566,7 +578,9 @@ public void run() { public void run() { Programme p = (Programme) obj; try { - prAdapter.updateView(getListView(), p.channel); + if (mCreated) { + prAdapter.updateView(getListView(), p.channel); + } } catch (Exception e) { } } @@ -577,7 +591,12 @@ public void run() { public void run() { Programme p = (Programme) obj; - prAdapter.updateView(getListView(), p.channel); + if (mCreated) { + try { + prAdapter.updateView(getListView(), p.channel); + } catch (Exception e) { + } + } } }); } else if (action @@ -585,8 +604,13 @@ public void run() { getActivity().runOnUiThread(new Runnable() { public void run() { - Programme p = (Programme) obj; - prAdapter.updateView(getListView(), p.channel); + if (mCreated) { + try { + Programme p = (Programme) obj; + prAdapter.updateView(getListView(), p.channel); + } catch (Exception e) { + } + } } }); } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { @@ -597,8 +621,14 @@ public void run() { for (Channel c : prAdapter.list) { for (Programme p : c.epg) { if (rec == p.recording) { - prAdapter.updateView(getListView(), c); - return; + if (mCreated) { + try { + prAdapter.updateView(getListView(), + c); + return; + } catch (Exception e) { + } + } } } } @@ -620,7 +650,8 @@ static interface EPGFragmentListener { * @return */ public Programme getProgrammeStartingAfter(Channel channel, Date timeSlot) { - Iterator it = channel.epg.iterator(); + Iterator it = new ArrayList(channel.epg) + .iterator(); Calendar cal = Calendar.getInstance(); cal.setTime(timeSlot); diff --git a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java index c37ceee..2aad359 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListViewWrapper.java @@ -57,11 +57,20 @@ public void repaint(Channel channel) { nextId = pr.id; } + long diff = timeSlot.getTime() - pr.stop.getTime(); + int hoursDiff = (int) (diff / (1000 * 60 * 60)); + if (hoursDiff < 0) { + hoursDiff = hoursDiff * -1; + } + if (hoursDiff == 0) { + hoursDiff = 1; + } + // load events for the different of hours Intent intent = new Intent(context, HTSService.class); intent.setAction(HTSService.ACTION_GET_EVENTS); intent.putExtra("eventId", nextId); intent.putExtra("channelId", channel.id); - intent.putExtra("count", 2); + intent.putExtra("count", hoursDiff + 1); context.startService(intent); // show loading diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 11c8ef6..2166404 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -218,9 +218,15 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivity(intent); return true; } + case R.id.mi_epg_prime: { + Intent intent = new Intent(getBaseContext(), + EPGPrimeTimeListActivity.class); + startActivity(intent); + return true; + } case R.id.mi_epg_list: { Intent intent = new Intent(getBaseContext(), - EPGTimeListActivity.class); + EPGHourlyTimeListActivity.class); startActivity(intent); return true; } From 6d407ddd875777e0fc7bdd87d6f587ba580c9c1a Mon Sep 17 00:00:00 2001 From: toggm Date: Mon, 29 Apr 2013 22:15:34 +0200 Subject: [PATCH 35/45] fixed preloading in difference to other channels --- .../tvhguide/EPGTimelineAdapter.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index adf6378..7cc9d6a 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -1,6 +1,7 @@ package org.tvheadend.tvhguide; import java.util.Comparator; +import java.util.Date; import java.util.List; import org.tvheadend.tvhguide.htsp.HTSService; @@ -82,6 +83,19 @@ public View getView(int position, View convertView, ViewGroup parent) { @Override public void loadNextEvents() { + // get latest loaded programme + Date latestStop = null; + for (int i = 0; i < getCount(); ++i) { + + Channel ch = getItem(i); + if (ch.epg.size() > 0) { + Programme last = ch.epg.last(); + if (latestStop == null || latestStop.before(last.stop)) { + latestStop = last.stop; + } + } + } + for (int i = 0; i < getCount(); ++i) { Channel ch = getItem(i); @@ -92,11 +106,22 @@ public void loadNextEvents() { nextId = last.id; } + // load per hour an event to difference of latest loaded stop + // program + long diff = latestStop.getTime() - last.stop.getTime(); + int hoursDiff = (int) (diff / (1000 * 60 * 60)); + if (hoursDiff < 0) { + hoursDiff = hoursDiff * -1; + } + if (hoursDiff == 0) { + hoursDiff = 1; + } + Intent intent = new Intent(context, HTSService.class); intent.setAction(HTSService.ACTION_GET_EVENTS); intent.putExtra("eventId", nextId); intent.putExtra("channelId", ch.id); - intent.putExtra("count", 5); + intent.putExtra("count", hoursDiff + 5); context.startService(intent); } } From d17311cffecc4aacc7f5af75d441142611f4d333 Mon Sep 17 00:00:00 2001 From: toggm Date: Sat, 4 May 2013 14:41:28 +0200 Subject: [PATCH 36/45] added time header to timeline --- res/layout/epgtimeline_programme_header.xml | 25 ++++ .../tvhguide/EPGTimelineActivity.java | 8 ++ .../tvhguide/EPGTimelineAdapter.java | 14 +++ ...EPGTimelineProgrammeHeaderViewWrapper.java | 57 +++++++++ .../EPGTimelineProgrammeListViewWrapper.java | 3 +- .../tvhguide/EPGTimelineViewWrapper.java | 109 ++++++++++++++---- 6 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 res/layout/epgtimeline_programme_header.xml create mode 100644 src/org/tvheadend/tvhguide/EPGTimelineProgrammeHeaderViewWrapper.java diff --git a/res/layout/epgtimeline_programme_header.xml b/res/layout/epgtimeline_programme_header.xml new file mode 100644 index 0000000..482971f --- /dev/null +++ b/res/layout/epgtimeline_programme_header.xml @@ -0,0 +1,25 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 2166404..77a8dff 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -114,6 +114,7 @@ public void populateChannelList() { adapter.clear(); mListeners.clear(); + adapter.add(new DummyChannel()); for (Channel ch : app.getChannels()) { if (currentTag == null || ch.hasTag(currentTag.id)) { adapter.add(ch); @@ -415,4 +416,11 @@ public static interface OnEPGScrollListener { public void flingBy(float velocityX); } + + public static class DummyChannel extends Channel { + public DummyChannel() { + id = 0; + number = -1; + } + } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index 7cc9d6a..f37d132 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -21,6 +21,7 @@ class EPGTimelineAdapter extends ArrayAdapter implements private final EPGTimelineActivity context; final List list; + private static final int VIEW_TYPE_HEADER = 20000; EPGTimelineAdapter(EPGTimelineActivity context, List list) { super(context, R.layout.epgtimeline_widget, list); @@ -32,12 +33,25 @@ public void sort() { sort(new Comparator() { public int compare(Channel x, Channel y) { + if (x == null) { + return -1; + } return x.compareTo(y); } }); } public void updateView(ListView listView, Channel channel) { + + if (channel == null) { + // update header timerline + View view = listView.getChildAt(0); + EPGTimelineViewWrapper wrapper = (EPGTimelineViewWrapper) view + .getTag(); + wrapper.repaint(channel); + return; + } + for (int i = 0; i < listView.getChildCount(); i++) { View view = listView.getChildAt(i); int pos = listView.getPositionForView(view); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeHeaderViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeHeaderViewWrapper.java new file mode 100644 index 0000000..46930d9 --- /dev/null +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeHeaderViewWrapper.java @@ -0,0 +1,57 @@ +package org.tvheadend.tvhguide; + +import java.util.Calendar; +import java.util.Date; + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class EPGTimelineProgrammeHeaderViewWrapper { + TextView date; + TextView hour; + private LinearLayout container; + private java.text.DateFormat timeFormat; + private java.text.DateFormat dateFormat; + + public EPGTimelineProgrammeHeaderViewWrapper(Context context, View base) { + date = (TextView) base.findViewById(R.id.title_date); + hour = (TextView) base.findViewById(R.id.title_hour); + container = (LinearLayout) base.findViewById(R.id.programme_container); + timeFormat = DateFormat.getTimeFormat(context); + dateFormat = DateFormat.getDateFormat(context); + } + + public void repaint(Date dt) { + date.setText(dateFormat.format(dt)); + hour.setText(timeFormat.format(dt)); + + // calculate remaining minutes till next hour + Calendar cal = Calendar.getInstance(); + cal.setTime(dt); + cal.add(Calendar.HOUR_OF_DAY, 1); + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + + long remainingMillis = cal.getTimeInMillis() - dt.getTime(); + long minutes = remainingMillis / (60 * 1000); + + int width = (int) (minutes * EPGTimelineViewWrapper.WIDTH_PER_MINUTE); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + width, 45); + LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams( + width, LinearLayout.LayoutParams.WRAP_CONTENT); + container.setLayoutParams(layoutParams); + container.setVisibility(LinearLayout.VISIBLE); + + date.setLayoutParams(layoutParams2); + hour.setLayoutParams(layoutParams2); + + date.invalidate(); + hour.invalidate(); + container.invalidate(); + } +} diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index f64a592..57c3099 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -17,7 +17,6 @@ public class EPGTimelineProgrammeListViewWrapper extends private LinearLayout container; private LinearLayout container2; private LinearLayout container3; - private static final int WIDTH_PER_MINUTE = 5; private static final int LAYOUT_HEIGHT = 68; public EPGTimelineProgrammeListViewWrapper(View base) { @@ -52,7 +51,7 @@ public void repaint(Programme p) { long minutes = remainingMillis / (60 * 1000); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - (int) minutes * WIDTH_PER_MINUTE, LAYOUT_HEIGHT); + (int) minutes * EPGTimelineViewWrapper.WIDTH_PER_MINUTE, LAYOUT_HEIGHT); container.setLayoutParams(layoutParams); container.setVisibility(LinearLayout.VISIBLE); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index 5229ebb..c15c139 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -19,7 +19,9 @@ package org.tvheadend.tvhguide; import java.util.ArrayList; +import java.util.Calendar; import java.util.Comparator; +import java.util.Date; import java.util.List; import org.tvheadend.tvhguide.EPGTimelineActivity.OnEPGScrollListener; @@ -83,6 +85,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, }; private GestureDetector mGesture; private final EventLoadHandler loadHandler; + static final int WIDTH_PER_MINUTE = 5; public EPGTimelineViewWrapper(EPGTimelineActivity context, View base, EventLoadHandler loadHandler) { @@ -101,8 +104,32 @@ public EPGTimelineViewWrapper(EPGTimelineActivity context, View base, context.registerForContextMenu(horizontalListView); } + public void repaintHeader() { + Calendar cal = Calendar.getInstance(); + List dates = new ArrayList(); + dates.add(cal.getTime()); + + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + cal.clear(Calendar.MILLISECOND); + for (int i = 0; i < 32; ++i) { + cal.add(Calendar.HOUR_OF_DAY, 1); + dates.add(cal.getTime()); + } + + TimelineHeaderAdaper adapter = new TimelineHeaderAdaper(context, dates); + horizontalListView.setAdapter(adapter); + horizontalListView.scrollTo(context.getLastEPGScrollPosition()); + horizontalListView.invalidate(); + } + public void repaint(Channel channel) { + if (channel.id == 0) { + repaintHeader(); + return; + } + // SharedPreferences prefs = PreferenceManager // .getDefaultSharedPreferences(icon.getContext()); // Boolean showIcons = prefs.getBoolean("showIconPref", false); @@ -133,7 +160,11 @@ public boolean onTouch(View view, MotionEvent event) { @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { - Programme p = (Programme) adapterView.getItemAtPosition(position); + Object obj = adapterView.getItemAtPosition(position); + if (!obj instanceof Programme) { + return; + } + Programme p = (Programme) obj; if (p == null) { // load next @@ -150,6 +181,58 @@ protected void loadNext() { } + @Override + public void scrollTo(int scrollTo) { + if (locked) { + return; + } + synchronized (horizontalListView) { + horizontalListView.scrollTo(scrollTo); + } + } + + @Override + public void flingBy(float velocityX) { + if (locked) { + return; + } + synchronized (horizontalListView) { + horizontalListView.flingBy(velocityX); + } + } + + class TimelineHeaderAdaper extends ArrayAdapter { + TimelineHeaderAdaper(Context context, List epg) { + super(context, R.layout.epgtimeline_programme_header, epg); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + EPGTimelineProgrammeHeaderViewWrapper wrapper; + + Activity activity = (Activity) getContext(); + Date dt = getItem(position); + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate(R.layout.epgtimeline_programme_header, + null, false); + row.requestLayout(); + wrapper = new EPGTimelineProgrammeHeaderViewWrapper(context, + row); + row.setTag(wrapper); + + } else { + wrapper = (EPGTimelineProgrammeHeaderViewWrapper) row.getTag(); + } + + if (wrapper != null) { + wrapper.repaint(dt); + } + return row; + } + } + class TimelineProgrammeAdapter extends ArrayAdapter { public static final int VIEW_TYPE_END = 100; @@ -228,29 +311,11 @@ public View getView(int position, View convertView, ViewGroup parent) { .getTag(); } - wrapper.repaint(pr); + if (wrapper != null) { + wrapper.repaint(pr); + } return row; } } } - - @Override - public void scrollTo(int scrollTo) { - if (locked) { - return; - } - synchronized (horizontalListView) { - horizontalListView.scrollTo(scrollTo); - } - } - - @Override - public void flingBy(float velocityX) { - if (locked) { - return; - } - synchronized (horizontalListView) { - horizontalListView.flingBy(velocityX); - } - } } From 5b2857839196fcf70a0fb76281e5d4734f235f91 Mon Sep 17 00:00:00 2001 From: toggm Date: Sat, 4 May 2013 14:42:16 +0200 Subject: [PATCH 37/45] fixed compiler error --- src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index c15c139..d8feb63 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -161,7 +161,7 @@ public boolean onTouch(View view, MotionEvent event) { public void onItemClick(AdapterView adapterView, View view, int position, long id) { Object obj = adapterView.getItemAtPosition(position); - if (!obj instanceof Programme) { + if (!(obj instanceof Programme)) { return; } Programme p = (Programme) obj; From ccb3c28a569c3ad143298111622c3e25d353e21c Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 8 May 2013 21:33:15 +0200 Subject: [PATCH 38/45] fixed errors, enhances dynamically loading of next programs in timeline view --- .../tvhguide/EPGTimelineAdapter.java | 83 ++++++++++--------- .../tvhguide/EPGTimelineViewWrapper.java | 22 +++-- 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java index f37d132..59cf2ee 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineAdapter.java @@ -21,7 +21,7 @@ class EPGTimelineAdapter extends ArrayAdapter implements private final EPGTimelineActivity context; final List list; - private static final int VIEW_TYPE_HEADER = 20000; + private boolean mLocked; EPGTimelineAdapter(EPGTimelineActivity context, List list) { super(context, R.layout.epgtimeline_widget, list); @@ -97,47 +97,56 @@ public View getView(int position, View convertView, ViewGroup parent) { @Override public void loadNextEvents() { - // get latest loaded programme - Date latestStop = null; - for (int i = 0; i < getCount(); ++i) { - - Channel ch = getItem(i); - if (ch.epg.size() > 0) { - Programme last = ch.epg.last(); - if (latestStop == null || latestStop.before(last.stop)) { - latestStop = last.stop; - } - } + if (mLocked) { + return; } - - for (int i = 0; i < getCount(); ++i) { - - Channel ch = getItem(i); - if (ch.epg.size() > 0) { - Programme last = ch.epg.last(); - long nextId = last.nextId; - if (nextId == 0) { - nextId = last.id; + mLocked = true; + try { + // get latest loaded programme + Date latestStop = null; + for (int i = 0; i < getCount(); ++i) { + + Channel ch = getItem(i); + if (ch.epg.size() > 0) { + Programme last = ch.epg.last(); + if (latestStop == null || latestStop.before(last.stop)) { + latestStop = last.stop; + } } + } - // load per hour an event to difference of latest loaded stop - // program - long diff = latestStop.getTime() - last.stop.getTime(); - int hoursDiff = (int) (diff / (1000 * 60 * 60)); - if (hoursDiff < 0) { - hoursDiff = hoursDiff * -1; + for (int i = 0; i < getCount(); ++i) { + + Channel ch = getItem(i); + if (ch.epg.size() > 0) { + Programme last = ch.epg.last(); + long nextId = last.nextId; + if (nextId == 0) { + nextId = last.id; + } + + // load per hour an event to difference of latest loaded + // stop + // program + long diff = latestStop.getTime() - last.stop.getTime(); + int hoursDiff = (int) (diff / (1000 * 60 * 60)); + if (hoursDiff < 0) { + hoursDiff = hoursDiff * -1; + } + if (hoursDiff == 0) { + hoursDiff = 1; + } + + Intent intent = new Intent(context, HTSService.class); + intent.setAction(HTSService.ACTION_GET_EVENTS); + intent.putExtra("eventId", nextId); + intent.putExtra("channelId", ch.id); + intent.putExtra("count", hoursDiff + 5); + context.startService(intent); } - if (hoursDiff == 0) { - hoursDiff = 1; - } - - Intent intent = new Intent(context, HTSService.class); - intent.setAction(HTSService.ACTION_GET_EVENTS); - intent.putExtra("eventId", nextId); - intent.putExtra("channelId", ch.id); - intent.putExtra("count", hoursDiff + 5); - context.startService(intent); } + } finally { + mLocked = false; } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index d8feb63..d14521a 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -43,8 +43,8 @@ import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; -import android.widget.Button; import android.widget.ImageView; +import android.widget.ProgressBar; /** * @@ -112,11 +112,15 @@ public void repaintHeader() { cal.clear(Calendar.MINUTE); cal.clear(Calendar.SECOND); cal.clear(Calendar.MILLISECOND); - for (int i = 0; i < 32; ++i) { + for (int i = 0; i < 100; ++i) { cal.add(Calendar.HOUR_OF_DAY, 1); dates.add(cal.getTime()); } + icon.setBackgroundDrawable(null); + icon.setImageDrawable(null); + icon.invalidate(); + TimelineHeaderAdaper adapter = new TimelineHeaderAdaper(context, dates); horizontalListView.setAdapter(adapter); horizontalListView.scrollTo(context.getLastEPGScrollPosition()); @@ -213,7 +217,8 @@ public View getView(int position, View convertView, ViewGroup parent) { Activity activity = (Activity) getContext(); Date dt = getItem(position); - if (row == null) { + if (row == null + || !(row.getTag() instanceof EPGTimelineProgrammeHeaderViewWrapper)) { LayoutInflater inflater = activity.getLayoutInflater(); row = inflater.inflate(R.layout.epgtimeline_programme_header, null, false); @@ -236,12 +241,11 @@ public View getView(int position, View convertView, ViewGroup parent) { class TimelineProgrammeAdapter extends ArrayAdapter { public static final int VIEW_TYPE_END = 100; - private Button button; + private ProgressBar mProgressbar; TimelineProgrammeAdapter(Context context, List epg) { super(context, R.layout.epgtimeline_programme_widget, epg); - button = new Button(context); - button.setBackgroundResource(android.R.drawable.ic_menu_more); + mProgressbar = new ProgressBar(context); } public void sort() { @@ -293,12 +297,14 @@ public View getView(int position, View convertView, ViewGroup parent) { EPGTimelineProgrammeListViewWrapper wrapper; if (position == super.getCount()) { - return button; + loadHandler.loadNextEvents(); + return mProgressbar; } else { Programme pr = getItem(position); Activity activity = (Activity) getContext(); - if (row == null) { + if (row == null + || !(row.getTag() instanceof EPGTimelineProgrammeListViewWrapper)) { LayoutInflater inflater = activity.getLayoutInflater(); row = inflater.inflate( R.layout.epgtimeline_programme_widget, null, false); From a1f0c5f52dab982a4280ba8f59ff3d10a8519b28 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 8 May 2013 21:49:28 +0200 Subject: [PATCH 39/45] fixed synching scrolling --- src/org/tvheadend/tvhguide/EPGTimelineActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index 77a8dff..d06a25b 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -112,7 +112,7 @@ public void populateChannelList() { TVHGuideApplication app = (TVHGuideApplication) getApplication(); ChannelTag currentTag = app.getCurrentTag(); adapter.clear(); - mListeners.clear(); + // mListeners.clear(); adapter.add(new DummyChannel()); for (Channel ch : app.getChannels()) { From 09e97bcef4bba683bd87483dec4bab1605a9ae90 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 8 May 2013 22:39:32 +0200 Subject: [PATCH 40/45] adjusted colorizing of timeline --- res/layout/epgtimeline_programme_widget.xml | 3 ++- res/values/arrays.xml | 26 ++++++++----------- .../EPGTimelineProgrammeListViewWrapper.java | 21 +++++++++++++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index abf2532..13967b3 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -23,7 +23,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="2" - android:singleLine="false" + android:singleLine="false" + android:textColor="#000000" android:textSize="16sp" > diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 51cca55..0603c2c 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -59,20 +59,16 @@ - #ff0000 - #00ff00 - #0000ff - #aa0000 - #00aa00 - #0000aa - #990000 - #009900 - #000099 - #660000 - #006600 - #000066 - #330000 - #003300 - #000033 + #55a4ff + #ff7755 + #ff8a34 + #fff899 + #55fff1 + #ff99c2 + #b8ff8c + #ff4738 + #378b2e + #63bea9 + #9ba4a2 \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 57c3099..3849a2e 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -36,8 +36,24 @@ public void repaint(Programme p) { super.repaint(p); // colorize based on series category - int index = p.contentType % colors.length(); + // the first byte of hex number represents the main category + int type = 0; + if (p.contentType > 0) { + type = ((p.contentType) / 16) - 1; + } + // there are 11 categories, calculate modulo if more categories are + // returned than colors are defined + int index = type % colors.length(); int color = colors.getColor(index, 0); + + // use first byte of hex number to calculate color offset + if (type > 0) { + int subType = p.contentType & 0x0F; + color -= subType * 0x040404; + } + + System.out.println(p.title + "-" + p.contentType + ":" + type + ":" + + index + ":" + color); container.setBackgroundColor(color); // define width based on duration @@ -51,7 +67,8 @@ public void repaint(Programme p) { long minutes = remainingMillis / (60 * 1000); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - (int) minutes * EPGTimelineViewWrapper.WIDTH_PER_MINUTE, LAYOUT_HEIGHT); + (int) minutes * EPGTimelineViewWrapper.WIDTH_PER_MINUTE, + LAYOUT_HEIGHT); container.setLayoutParams(layoutParams); container.setVisibility(LinearLayout.VISIBLE); From c908d541b5ade4513322f96dadbb773981f1d0c0 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 8 May 2013 22:46:40 +0200 Subject: [PATCH 41/45] adjusted primetime slots, added verbose log output --- .../tvheadend/tvhguide/EPGPrimeTimeListActivity.java | 6 +++++- .../tvhguide/EPGTimelineProgrammeListViewWrapper.java | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java index e2a6e94..bf18253 100644 --- a/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGPrimeTimeListActivity.java @@ -24,10 +24,13 @@ protected List createTimeSlots() { java.text.DateFormat format = DateFormat.getTimeFormat(this); + // TODO: define primetime slots via settings Set defaultPrimeTimeSlots = new TreeSet(); + defaultPrimeTimeSlots.add("12:00"); defaultPrimeTimeSlots.add("18:30"); defaultPrimeTimeSlots.add("20:15"); defaultPrimeTimeSlots.add("22:00"); + defaultPrimeTimeSlots.add("24:00"); List primeTimeSlots = new ArrayList(prefs.getStringSet( "epg.prime.timeslots", defaultPrimeTimeSlots)); @@ -38,6 +41,7 @@ protected List createTimeSlots() { Calendar cal = Calendar.getInstance(); cal.clear(Calendar.SECOND); cal.clear(Calendar.MILLISECOND); + cal.add(Calendar.HOUR_OF_DAY, -2); List primeTimeSlotDates = new ArrayList(); try { @@ -51,7 +55,7 @@ protected List createTimeSlots() { int primeSlotIndex = 0; - // first find timeslot occurance after actual time + // first find timeslot occurance after actual time - 2hours long lastDiff = -1; for (int i = primeTimeSlots.size() - 1; i >= 0; --i) { Date time = primeTimeSlotDates.get(i); diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 3849a2e..94ca310 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -7,6 +7,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; +import android.util.Log; import android.view.View; import android.widget.LinearLayout; @@ -19,6 +20,8 @@ public class EPGTimelineProgrammeListViewWrapper extends private LinearLayout container3; private static final int LAYOUT_HEIGHT = 68; + private static final String TAG = "EPGTimelineProgrammeListViewWrapper"; + public EPGTimelineProgrammeListViewWrapper(View base) { super(base); Resources res = base.getResources(); @@ -47,13 +50,15 @@ public void repaint(Programme p) { int color = colors.getColor(index, 0); // use first byte of hex number to calculate color offset + int subType = 0; if (type > 0) { - int subType = p.contentType & 0x0F; + subType = p.contentType & 0x0F; color -= subType * 0x040404; } - System.out.println(p.title + "-" + p.contentType + ":" + type + ":" - + index + ":" + color); + Log.v(TAG, p.title + ", content-type:" + p.contentType + ", type:" + + type + ", subtype:" + subType + ", index:" + index + + ", color:" + color); container.setBackgroundColor(color); // define width based on duration From 600627bfefa2b7116c4ba575ce206bc519440a43 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 8 May 2013 22:48:07 +0200 Subject: [PATCH 42/45] adjusted colorizing of timeline --- res/layout/epgtimeline_programme_widget.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index 13967b3..a5aa6f0 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:maxLines="2" android:singleLine="false" - android:textColor="#000000" + android:textColor="#333333" android:textSize="16sp" > From 1bc552fefcc066f8e11282b29c2bf1a67d7ba0df Mon Sep 17 00:00:00 2001 From: toggm Date: Thu, 9 May 2013 13:36:06 +0200 Subject: [PATCH 43/45] adjusted for honeycromb without custom window_title_feature --- AndroidManifest.xml | 19 +++--- res/layout/channel_list_title.xml | 2 +- res/xml/preferences.xml | 9 +-- .../tvhguide/ChannelListActivity.java | 64 ++++++------------- .../tvhguide/ChannelListViewWrapper.java | 4 +- .../tvhguide/EPGTimeListActivity.java | 39 +++++------ .../tvhguide/EPGTimelineActivity.java | 41 ++++-------- 7 files changed, 65 insertions(+), 113 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1bfa1e8..9343c95 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -19,8 +19,7 @@ android:value=".SearchResultActivity" /> + android:name="org.tvheadend.tvhguide.ChannelListActivity" > @@ -29,11 +28,11 @@ + /> + > @@ -46,13 +45,13 @@ + /> + /> + /> + /> + /> diff --git a/res/layout/channel_list_title.xml b/res/layout/channel_list_title.xml index 645f163..5a94c3a 100644 --- a/res/layout/channel_list_title.xml +++ b/res/layout/channel_list_title.xml @@ -10,7 +10,7 @@ android:layout_width="wrap_content" android:layout_margin="6sp" android:layout_alignParentTop="true" - android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" android:clickable="true" android:orientation="horizontal"> - - + android:title="@string/pref_ui"> tagAdapter; private AlertDialog tagDialog; - private TextView tagTextView; - private ImageView tagImageView; - private View tagBtn; - private ProgressBar pb; private ChannelTag currentTag; @Override public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - super.onCreate(icicle); - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - chAdapter = new ChannelListAdapter(this, new ArrayList()); setListAdapter(chAdapter); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, - R.layout.channel_list_title); - tagTextView = (TextView) findViewById(R.id.ct_title); - tagImageView = (ImageView) findViewById(R.id.ct_logo); - - pb = (ProgressBar) findViewById(R.id.ct_loading); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.menu_tags); @@ -103,17 +83,9 @@ public void onClick(DialogInterface arg0, int pos) { }); tagDialog = builder.create(); - tagBtn = findViewById(R.id.ct_btn); - tagBtn.setOnClickListener(new android.view.View.OnClickListener() { - - public void onClick(View arg0) { - tagDialog.show(); - } - }); TVHGuideApplication app = (TVHGuideApplication) getApplication(); currentTag = app.getCurrentTag(); - setCurrentTag(currentTag); registerForContextMenu(getListView()); } @@ -122,6 +94,9 @@ public void onClick(View arg0) { public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); + + setCurrentTag(currentTag); + return true; } @@ -190,16 +165,22 @@ void connect(boolean force) { private void setCurrentTag(ChannelTag t) { currentTag = t; - if (t == null) { - tagTextView.setText(R.string.pr_all_channels); - tagImageView.setImageResource(R.drawable.logo_72); - } else { - tagTextView.setText(currentTag.name); + if (getActionBar() != null) { - if (currentTag.iconBitmap != null) { - tagImageView.setImageBitmap(currentTag.iconBitmap); + if (t == null) { + getActionBar().setTitle(R.string.pr_all_channels); + getActionBar().setIcon(R.drawable.logo_72); } else { - tagImageView.setImageResource(R.drawable.logo_72); + getActionBar().setTitle(currentTag.name); + getActionBar().setIcon(R.drawable.logo_72); + + if (currentTag.iconBitmap != null) { + getActionBar().setIcon( + new BitmapDrawable(getResources(), + currentTag.iconBitmap)); + } else { + getActionBar().setIcon(R.drawable.logo_72); + } } } @@ -307,14 +288,11 @@ protected void onListItemClick(ListView l, View v, int position, long id) { } private void setLoading(boolean loading) { - tagBtn.setEnabled(!loading); if (loading) { - pb.setVisibility(ProgressBar.VISIBLE); - tagTextView.setText(R.string.inf_load); - tagImageView.setVisibility(ImageView.INVISIBLE); + // pb.setVisibility(ProgressBar.VISIBLE); + // getActionBar().setTitle(R.string.inf_load); } else { - pb.setVisibility(ProgressBar.GONE); - tagImageView.setVisibility(ImageView.VISIBLE); + // pb.setVisibility(ProgressBar.GONE); TVHGuideApplication app = (TVHGuideApplication) getApplication(); tagAdapter.clear(); diff --git a/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java b/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java index 8a2df8e..2642674 100644 --- a/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/ChannelListViewWrapper.java @@ -58,7 +58,7 @@ public ChannelListViewWrapper(View base) { nowProgressImage = (ImageView) base.findViewById(R.id.ch_elapsedtime); nowProgress = new ClipDrawable(nowProgressImage.getDrawable(), Gravity.LEFT, ClipDrawable.HORIZONTAL); - nowProgressImage.setBackground(nowProgress); + nowProgressImage.setImageDrawable(nowProgress); nowTime = (TextView) base.findViewById(R.id.ch_now_time); nextTitle = (TextView) base.findViewById(R.id.ch_next_title); @@ -83,7 +83,7 @@ public void repaint(Channel channel) { icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); BitmapDrawable bitmapDrawable = new BitmapDrawable(resources, channel.iconBitmap); - icon.setBackground(bitmapDrawable); + icon.setBackgroundDrawable(bitmapDrawable); if (channel.isRecording()) { icon.setImageResource(R.drawable.ic_rec_small); diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 3b51bf0..78e293b 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -19,6 +19,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; @@ -33,14 +34,11 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.ImageView; import android.widget.ListView; -import android.widget.TextView; /** * @@ -83,8 +81,6 @@ public abstract class EPGTimeListActivity extends FragmentActivity { private ArrayAdapter tagAdapter; - private TextView tagTextView; - private ImageView tagImageView; private int m_maxStartTimeAfterTimeSlotInMinutes; protected abstract List createTimeSlots(); @@ -93,11 +89,7 @@ public abstract class EPGTimeListActivity extends FragmentActivity { protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.epgnow_list_activity); // create timelost based on actual time @@ -116,11 +108,6 @@ protected void onCreate(Bundle savedInstanceState) { mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, - R.layout.epgnow_list_title); - tagTextView = (TextView) findViewById(R.id.ct_title); - tagImageView = (ImageView) findViewById(R.id.ct_logo); - AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.menu_tags); @@ -155,6 +142,9 @@ public void onRestoreInstanceState(Bundle savedInstanceState) { @Override public boolean onCreateOptionsMenu(Menu menu) { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + setCurrentTag(app.getCurrentTag()); + // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main_menu, menu); return true; @@ -304,16 +294,21 @@ private void setLoading(boolean loading) { } private void setCurrentTag(ChannelTag t) { - if (t == null) { - tagTextView.setText(R.string.pr_all_channels); - tagImageView.setImageResource(R.drawable.logo_72); - } else { - tagTextView.setText(t.name); + if (getActionBar() != null) { - if (t.iconBitmap != null) { - tagImageView.setImageBitmap(t.iconBitmap); + if (t == null) { + getActionBar().setTitle(R.string.pr_all_channels); + getActionBar().setIcon(R.drawable.logo_72); } else { - tagImageView.setImageResource(R.drawable.logo_72); + getActionBar().setTitle(t.name); + getActionBar().setIcon(R.drawable.logo_72); + + if (t.iconBitmap != null) { + getActionBar().setIcon( + new BitmapDrawable(getResources(), t.iconBitmap)); + } else { + getActionBar().setIcon(R.drawable.logo_72); + } } } } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index d06a25b..da2553b 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -18,19 +18,15 @@ import android.app.ListActivity; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; /** * @@ -44,9 +40,6 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { private AlertDialog tagDialog; private ArrayAdapter tagAdapter; - private TextView tagTextView; - private ImageView tagImageView; - private List mListeners = Collections .synchronizedList(new ArrayList()); @@ -54,22 +47,11 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { @Override protected void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); adapter = new EPGTimelineAdapter(this, new ArrayList()); setListAdapter(adapter); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, - R.layout.epgnow_list_title); - tagTextView = (TextView) findViewById(R.id.ct_title); - tagImageView = (ImageView) findViewById(R.id.ct_logo); - AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.menu_tags); @@ -94,16 +76,21 @@ public void onClick(DialogInterface arg0, int pos) { } private void setCurrentTag(ChannelTag t) { - if (t == null) { - tagTextView.setText(R.string.pr_all_channels); - tagImageView.setImageResource(R.drawable.logo_72); - } else { - tagTextView.setText(t.name); + if (getActionBar() != null) { - if (t.iconBitmap != null) { - tagImageView.setImageBitmap(t.iconBitmap); + if (t == null) { + getActionBar().setTitle(R.string.pr_all_channels); + getActionBar().setIcon(R.drawable.logo_72); } else { - tagImageView.setImageResource(R.drawable.logo_72); + getActionBar().setTitle(t.name); + getActionBar().setIcon(R.drawable.logo_72); + + if (t.iconBitmap != null) { + getActionBar().setIcon( + new BitmapDrawable(getResources(), t.iconBitmap)); + } else { + getActionBar().setIcon(R.drawable.logo_72); + } } } } From c7ffd056cc63debbf8358591d4ad18d46ec72f9f Mon Sep 17 00:00:00 2001 From: toggm Date: Thu, 9 May 2013 14:44:14 +0200 Subject: [PATCH 44/45] adjusted for honeycromb without custom window_title_feature --- .../tvhguide/EPGTimeListActivity.java | 1 + .../tvhguide/EPGTimelineActivity.java | 7 ++- .../tvheadend/tvhguide/ProgrammeActivity.java | 34 ++++++--------- .../tvhguide/ProgrammeListActivity.java | 43 +++++-------------- 4 files changed, 29 insertions(+), 56 deletions(-) diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 78e293b..9d7b84d 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -34,6 +34,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.Window; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index da2553b..c2bfbaf 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -18,8 +18,10 @@ import android.app.ListActivity; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; +import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; @@ -47,6 +49,8 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { @Override protected void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); super.onCreate(savedInstanceState); adapter = new EPGTimelineAdapter(this, new ArrayList()); @@ -87,7 +91,8 @@ private void setCurrentTag(ChannelTag t) { if (t.iconBitmap != null) { getActionBar().setIcon( - new BitmapDrawable(getResources(), t.iconBitmap)); + new BitmapDrawable(getResources(), + t.iconBitmap)); } else { getActionBar().setIcon(R.drawable.logo_72); } diff --git a/src/org/tvheadend/tvhguide/ProgrammeActivity.java b/src/org/tvheadend/tvhguide/ProgrammeActivity.java index abf4886..cdb143d 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeActivity.java @@ -29,15 +29,13 @@ import android.app.Activity; import android.content.Intent; -import android.content.SharedPreferences; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.preference.PreferenceManager; import android.text.format.DateFormat; import android.util.SparseArray; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; @@ -50,19 +48,14 @@ public class ProgrammeActivity extends Activity implements HTSListener { private Programme programme; private ImageView state; + private Channel channel; @Override public void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - super.onCreate(savedInstanceState); TVHGuideApplication app = (TVHGuideApplication) getApplication(); - Channel channel = app.getChannel(getIntent().getLongExtra("channelId", - 0)); + channel = app.getChannel(getIntent().getLongExtra("channelId", 0)); if (channel == null) { finish(); return; @@ -81,20 +74,8 @@ public void onCreate(Bundle savedInstanceState) { return; } - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - setContentView(R.layout.programme_layout); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, - R.layout.programme_title); - TextView t = (TextView) findViewById(R.id.ct_title); - t.setText(channel.name); - - if (channel.iconBitmap != null) { - ImageView iv = (ImageView) findViewById(R.id.ct_logo); - iv.setImageBitmap(channel.iconBitmap); - } - TextView text = (TextView) findViewById(R.id.pr_title); text.setText(programme.title); @@ -279,6 +260,15 @@ public boolean onCreateOptionsMenu(Menu menu) { item.setIcon(android.R.drawable.ic_menu_delete); } + if (getActionBar() != null && channel != null) { + getActionBar().setTitle(channel.name); + + if (channel.iconBitmap != null) { + getActionBar().setIcon( + new BitmapDrawable(getResources(), channel.iconBitmap)); + } + } + item.setIntent(intent); return true; diff --git a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java index 51daed4..3dcdfc9 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java @@ -36,9 +36,8 @@ import android.app.Activity; import android.app.ListActivity; import android.content.Intent; -import android.content.SharedPreferences; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -47,13 +46,10 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.view.Window; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.ImageView; import android.widget.ListView; -import android.widget.TextView; /** * @@ -66,11 +62,6 @@ public class ProgrammeListActivity extends ListActivity implements HTSListener { @Override public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - super.onCreate(icicle); TVHGuideApplication app = (TVHGuideApplication) getApplication(); @@ -81,8 +72,6 @@ public void onCreate(Bundle icicle) { return; } - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - Button btn = new Button(this); btn.setText(R.string.pr_get_more); btn.setOnClickListener(new OnClickListener() { @@ -127,27 +116,6 @@ public void onClick(View view) { prAdapter.sort(); setListAdapter(prAdapter); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, - R.layout.programme_list_title); - TextView t = (TextView) findViewById(R.id.ct_title); - t.setText(channel.name); - - if (channel.iconBitmap != null) { - ImageView iv = (ImageView) findViewById(R.id.ct_logo); - iv.setImageBitmap(channel.iconBitmap); - } - - View v = findViewById(R.id.ct_btn); - v.setOnClickListener(new android.view.View.OnClickListener() { - - public void onClick(View arg0) { - Intent intent = new Intent(ProgrammeListActivity.this, - PlaybackActivity.class); - intent.putExtra("channelId", channel.id); - startActivity(intent); - } - }); - registerForContextMenu(getListView()); } @@ -250,6 +218,15 @@ public boolean onCreateOptionsMenu(Menu menu) { item.setIcon(android.R.drawable.ic_menu_view); item.setIntent(intent); + if (getActionBar() != null && channel != null) { + getActionBar().setTitle(channel.name); + + if (channel.iconBitmap != null) { + getActionBar().setIcon( + new BitmapDrawable(getResources(), channel.iconBitmap)); + } + } + return true; } From 90d878dd662f5ae9b09c8d8821c9544e02073192 Mon Sep 17 00:00:00 2001 From: toggm Date: Wed, 12 Feb 2014 22:50:41 +0100 Subject: [PATCH 45/45] cleanup --- AndroidManifest.xml | 3 +- res/drawable/ic_error.png | Bin 13703 -> 0 bytes res/drawable/ic_rec.png | Bin 13758 -> 0 bytes res/drawable/ic_schedule.png | Bin 10409 -> 0 bytes res/drawable/ic_success.png | Bin 15566 -> 0 bytes res/drawable/logo.png | Bin 72388 -> 0 bytes res/layout/channel_list_title.xml | 67 -- res/layout/epgnow_list_title.xml | 58 -- res/layout/epgnow_list_widget.xml | 3 +- res/layout/epgtimeline_programme_widget.xml | 5 +- res/layout/programme_layout.xml | 3 +- res/layout/programme_list_title.xml | 58 -- res/layout/programme_list_widget.xml | 5 +- res/layout/programme_title.xml | 26 - res/layout/recording_list_title.xml | 3 +- res/layout/recording_list_widget.xml | 11 +- res/layout/recording_title.xml | 3 +- res/layout/search_result_title.xml | 2 + res/layout/search_result_widget.xml | 166 ++-- res/menu/pr_menu.xml | 6 - res/menu/rc_menu.xml | 6 - res/values-de/arrays.xml | 30 + res/values-de/strings.xml | 49 +- res/values-sv/arrays.xml | 30 + res/values-sv/strings.xml | 29 +- res/values/arrays.xml | 44 - res/values/strings.xml | 74 +- .../tvhguide/EPGTimeListActivity.java | 64 +- .../tvhguide/EPGTimelineActivity.java | 7 +- .../EPGTimelineProgrammeListViewWrapper.java | 11 +- .../tvhguide/EPGTimelineViewWrapper.java | 4 +- .../tvheadend/tvhguide/ProgrammeActivity.java | 15 +- .../tvhguide/ProgrammeListActivity.java | 14 +- .../tvhguide/ProgrammeListViewWrapper.java | 18 +- .../tvhguide/RecordingListActivity.java | 683 ++++++++-------- .../tvhguide/SearchResultActivity.java | 18 +- .../tvheadend/tvhguide/SettingsActivity.java | 1 + .../tvhguide/htsp/HTSConnection.java | 11 +- .../tvheadend/tvhguide/htsp/HTSMessage.java | 755 +++++++++--------- .../tvheadend/tvhguide/htsp/HTSService.java | 6 +- .../tvhguide/htsp/SelectionThread.java | 437 +++++----- .../tvhguide/intent/SearchIMDbIntent.java | 23 +- .../tvheadend/tvhguide/model/SeriesInfo.java | 9 +- .../tvhguide/ui/HorizontalListView.java | 19 +- 44 files changed, 1331 insertions(+), 1445 deletions(-) delete mode 100644 res/drawable/ic_error.png delete mode 100644 res/drawable/ic_rec.png delete mode 100644 res/drawable/ic_schedule.png delete mode 100644 res/drawable/ic_success.png delete mode 100644 res/drawable/logo.png delete mode 100644 res/layout/channel_list_title.xml delete mode 100644 res/layout/epgnow_list_title.xml delete mode 100644 res/layout/programme_list_title.xml delete mode 100644 res/layout/programme_title.xml delete mode 100644 res/menu/pr_menu.xml delete mode 100644 res/menu/rc_menu.xml create mode 100644 res/values-de/arrays.xml create mode 100644 res/values-sv/arrays.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9343c95..40f79ec 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -13,7 +13,8 @@ + android:label="TVHGuide" + android:allowBackup="false" > diff --git a/res/drawable/ic_error.png b/res/drawable/ic_error.png deleted file mode 100644 index cdcd80095970d08b6e0bff284af367df27ebe808..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13703 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 T(2ZU$SOEsNjP)vYP}u(i7_-yQ diff --git a/res/drawable/ic_rec.png b/res/drawable/ic_rec.png deleted file mode 100644 index 0ed68ae9259d1e49c576cf2af648adda8be4dbc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13758 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 F{SR|7$Xoyb diff --git a/res/drawable/ic_success.png b/res/drawable/ic_success.png deleted file mode 100644 index a06b49fe0491b70091f8e2faf3854cce569a9b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15566 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 ccvt2B0RX|Mq!~g&Q07*qoM6N<$f(#`Wx&QzG diff --git a/res/drawable/logo.png b/res/drawable/logo.png deleted file mode 100644 index f0bb3997e7e1359cb2dd0981e34770f8fe7dc2d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72388 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 - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/epgnow_list_title.xml b/res/layout/epgnow_list_title.xml deleted file mode 100644 index c6333a7..0000000 --- a/res/layout/epgnow_list_title.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/epgnow_list_widget.xml b/res/layout/epgnow_list_widget.xml index e7415dc..97283ca 100644 --- a/res/layout/epgnow_list_widget.xml +++ b/res/layout/epgnow_list_widget.xml @@ -47,7 +47,8 @@ android:layout_width="fill_parent" android:layout_height="18sp" android:layout_margin="3dip" - android:scaleType="fitEnd" > + android:scaleType="fitEnd" + android:contentDescription="@string/state"> diff --git a/res/layout/epgtimeline_programme_widget.xml b/res/layout/epgtimeline_programme_widget.xml index a5aa6f0..cfb69a0 100644 --- a/res/layout/epgtimeline_programme_widget.xml +++ b/res/layout/epgtimeline_programme_widget.xml @@ -33,7 +33,8 @@ android:layout_width="wrap_content" android:layout_height="18sp" android:layout_margin="3dip" - android:scaleType="fitEnd" > + android:scaleType="fitEnd" + android:contentDescription="@string/state"> @@ -50,6 +51,7 @@ android:id="@+id/pr_date" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textColor="#333333" android:maxLines="1" > @@ -57,6 +59,7 @@ android:id="@+id/pr_time" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textColor="#333333" android:maxLines="1" android:paddingLeft="3dip" > diff --git a/res/layout/programme_layout.xml b/res/layout/programme_layout.xml index 0c660b8..1fc5bd8 100644 --- a/res/layout/programme_layout.xml +++ b/res/layout/programme_layout.xml @@ -28,7 +28,8 @@ android:layout_width="fill_parent" android:layout_height="18sp" android:layout_margin="3dip" - android:scaleType="fitEnd" > + android:scaleType="fitEnd" + android:contentDescription="@string/state"> diff --git a/res/layout/programme_list_title.xml b/res/layout/programme_list_title.xml deleted file mode 100644 index 8937607..0000000 --- a/res/layout/programme_list_title.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/programme_list_widget.xml b/res/layout/programme_list_widget.xml index 0b24cdd..331f7c8 100644 --- a/res/layout/programme_list_widget.xml +++ b/res/layout/programme_list_widget.xml @@ -24,7 +24,8 @@ android:layout_width="fill_parent" android:layout_height="18sp" android:layout_margin="3dip" - android:scaleType="fitEnd" > + android:scaleType="fitEnd" + android:contentDescription="@string/state"> @@ -37,7 +38,7 @@ diff --git a/res/layout/programme_title.xml b/res/layout/programme_title.xml deleted file mode 100644 index 98ec8f1..0000000 --- a/res/layout/programme_title.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/layout/recording_list_title.xml b/res/layout/recording_list_title.xml index 98ec8f1..62a18e4 100644 --- a/res/layout/recording_list_title.xml +++ b/res/layout/recording_list_title.xml @@ -9,7 +9,8 @@ android:layout_height="44sp" android:padding="3sp" android:src="@drawable/logo_72" - android:id="@+id/ct_logo"> + android:id="@+id/ct_logo" + android:contentDescription="@string/logo"> - - + android:id="@+id/rec_icon" + android:contentDescription="@string/recording"> + android:id="@+id/rec_state" + android:contentDescription="@string/state"> - \ No newline at end of file diff --git a/res/layout/recording_title.xml b/res/layout/recording_title.xml index 98ec8f1..62a18e4 100644 --- a/res/layout/recording_title.xml +++ b/res/layout/recording_title.xml @@ -9,7 +9,8 @@ android:layout_height="44sp" android:padding="3sp" android:src="@drawable/logo_72" - android:id="@+id/ct_logo"> + android:id="@+id/ct_logo" + android:contentDescription="@string/logo"> \ No newline at end of file diff --git a/res/layout/search_result_widget.xml b/res/layout/search_result_widget.xml index 8fb4947..ddcdc0d 100644 --- a/res/layout/search_result_widget.xml +++ b/res/layout/search_result_widget.xml @@ -1,84 +1,92 @@ - - + android:layout_marginLeft="5sp" + android:layout_marginRight="5sp" > + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="64sp" + android:orientation="vertical" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/pr_menu.xml b/res/menu/pr_menu.xml deleted file mode 100644 index 059f502..0000000 --- a/res/menu/pr_menu.xml +++ /dev/null @@ -1,6 +0,0 @@ - -

- - \ No newline at end of file diff --git a/res/menu/rc_menu.xml b/res/menu/rc_menu.xml deleted file mode 100644 index 2b05352..0000000 --- a/res/menu/rc_menu.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml new file mode 100644 index 0000000..9b111a9 --- /dev/null +++ b/res/values-de/arrays.xml @@ -0,0 +1,30 @@ + + + + matroska + mpegts + mpegps + pass + + + + Matroska + MPEG-TS + MPEG-PS + Pass-through + + + + #55a4ff + #ff7755 + #ff8a34 + #fff899 + #55fff1 + #ff99c2 + #b8ff8c + #ff4738 + #378b2e + #63bea9 + #9ba4a2 + + \ No newline at end of file diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 0d1c9ab..a45ee58 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -1,19 +1,31 @@ TVHGuide - - Hilfe + Jetzt + Stündlich + Primetime + Timeline Einstellungen Aktualisieren Tags Aufnahmen + Aufnehmen + Aufnahme abbrechen + Aufnahme entfernen + Program Liste Hostname Hostnamen des Servers eingeben - + Ansicht Port Port der Servers eingeben - + Media container + Bevorzugter Media container + Abspielen + HTTP port + Server HTTP Port + Bevorzugter externer Player + Benutze einen externen Player für Live Streams Benutzername Benutzernamen eingeben @@ -24,22 +36,33 @@ Kanal-Logos anzeigen? Zugriff verweigert - Ungültige Antwort des Servers Konnte nicht mit dem Server verbinden Verbindung zum Server verloren - Lade... - Bitte warten Sie einen Moment... - Alle Kanäle - + mehr… + Online + Bewertung + Typ + Serieninformation + Season + Episode + Part + + Abspielen +Keine Übertragung + EPG durchsuchen + heute Geplant Aufnahme Fertig Verpasst Ungültig - - EPG Liste - Nöchste - Jetzt + Status + logo + Aufnahme + Suche + vergangene Zeit + Icon + EPG Timeline \ No newline at end of file diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml new file mode 100644 index 0000000..9b111a9 --- /dev/null +++ b/res/values-sv/arrays.xml @@ -0,0 +1,30 @@ + + + + matroska + mpegts + mpegps + pass + + + + Matroska + MPEG-TS + MPEG-PS + Pass-through + + + + #55a4ff + #ff7755 + #ff8a34 + #fff899 + #55fff1 + #ff99c2 + #b8ff8c + #ff4738 + #378b2e + #63bea9 + #9ba4a2 + + \ No newline at end of file diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 58b2319..8702bdd 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -2,7 +2,6 @@ TVHGuide - Hjälp Inställningar Uppdatera Taggar @@ -26,24 +25,9 @@ Användargränssnitt - Ljust tema - Använd ett ljust tema - Kanallogotyper Visa kanalers logotyper? - Upplösning - Maximal upplösning vid omkodning - - Ljud - Kodek att använda vid omkodning av ljud - - Video - Kodek att använda vid omkodning av video - - Undertexter - Kodek att använda vid omkodning av undertexter - Media container Media container att föredra @@ -52,29 +36,22 @@ HTTP port Skriv in HTTP portnummer - Koda om ström - Begär omkodning av direktsänd media - Föredra extern spelare Använd en extern spelare för direktsänd media (BSPlayer verkar OK) Ã…tkomst nekad - Felaktigt svar frÃ¥n servern Kan inte ansluta till servern Tappade anslutningen till servern - Laddar… - Var god vänta nÃ¥gra sekunder… - Alla kanaler Hämta flera - Säsong - Avsnitt - Del Sänds Omdömme Typ Del + Season + Episode + Part Schemalagd Spelar in diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 0603c2c..9b111a9 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1,49 +1,5 @@ - - 576 - 480 - 384 - 288 - 192 - - - 576p - 480p - 384p - 288p - 192p - - - - AAC - MPEG2AUDIO - - - Advanced Audio Codec (AAC) - MPEG-2 Audio Layer II (MP2) - - - - H264 - MPEG4VIDEO - VP8 - - - MPEG-4 AVC (H.264) - MPEG-4 Part 2 - VP8 - - - - PASS - NONE - - - Pass-through - None - - matroska mpegts diff --git a/res/values/strings.xml b/res/values/strings.xml index 35af862..6889005 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,11 +1,10 @@ - + TVHGuide - Help Settings Refresh - Now + Now Hourly Primetime Timeline @@ -26,42 +25,27 @@ User interface Channel icons Show channel icons? - Light theme - Use a light theme - Resolution - Maximum resolution for the transcoder - Audio codec - Audio codec to use with the transcoder - Video codec - Video codec to use with the transcoder - Subtitle codec - Subtitle codec to use with the transcoder Media container - Prefered media container + Preferred media container Playback HTTP port Enter Server HTTP Port - Transcode stream - Request a transcoded stream during live playback Prefer external player Use an external player for live streams (BSPlayer seems OK) Access denied - Invalid response from server Can\'t connect to server Lost connection to server - Loading… - Please wait a few seconds… All channels Get more - Season - Episode - Part Airing Rating Type Part + Season + Episode + Part - + Movie/Drama News/Current affairs Show/Game show @@ -74,7 +58,7 @@ Leisure hobbies Misc - + Movie/Drama Detective/Thriller Adventure/Western/War @@ -85,20 +69,20 @@ Serious/ClassicalReligion/Historical Adult Movie/Drama - + News/Current affairs News/Weather Report Magazine Documentary Discussion/Interview/Debate - + Show/Game show Game show/Quiz/Contest Variety Talk - + Sports Special Event Magazine @@ -110,24 +94,24 @@ Water Sport Winter Sports Equestrian - Martial sports + Martial sports - + Children\'s / Youth Pre-school Entertainment (6 to 14 year-olds) - Entertainment (10 to 16 year-olds) - Informational/Educational/Schools + Entertainment (10 to 16 year-olds) + Informational/Educational/Schools Cartoons/Puppets Music/Ballet/Dance Rock/Pop - Serious music/Classical Music + Serious music/Classical Music Folk/Traditional music Jazz Musical/Opera - Ballet + Ballet Arts/Culture @@ -136,20 +120,20 @@ Religion Popular Culture/Tradital Arts Literature - Film/Cinema + Film/Cinema Experimental Film/Video Broadcasting/Press New Media - Magazine + Magazine Fashion - + Social/Political issues/Economics - Magazine/Report/Domentary + Magazine/Report/Domentary Economics/Social Advisory Remarkable People - + Education/Science/Factual Nature/Animals/Environment Technology/Natural sciences @@ -169,7 +153,7 @@ Advertisement/Shopping Gardening - + Misc Black and White Unpublished @@ -178,17 +162,19 @@ Scheduled Recording - Completed - Missed + Completed + Missed Invalid Play No transmission Search the EPG today - Hello world! - EPG List - EPG Timeline + State + logo + recording + search elapsed time icon + epg timeline \ No newline at end of file diff --git a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java index 9d7b84d..7ecb199 100644 --- a/src/org/tvheadend/tvhguide/EPGTimeListActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimeListActivity.java @@ -34,7 +34,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; @@ -230,9 +229,10 @@ public SectionsPagerAdapter(FragmentManager fm, Date[] timeslots) { public Fragment getItem(int position) { // When the given tab is selected, show the tab contents in the // container view. - EPGListFragment fragment = new EPGListFragment(timeslots[position]); + EPGListFragment fragment = new EPGListFragment(); Bundle args = new Bundle(); - // args.put(EPGListFragment.ARG_TIME_SLOT, ); + args.putSerializable(EPGListFragment.ARG_TIME_SLOT, + timeslots[position]); fragment.setArguments(args); return fragment; @@ -314,12 +314,20 @@ private void setCurrentTag(ChannelTag t) { } } + private int getTop() { + return top; + } + + private int getFirstVisibleItem() { + return firstVibleItem; + } + /** * A dummy fragment representing a section of the app, but that simply * displays dummy text. */ - public class EPGListFragment extends ListFragment implements HTSListener, - EPGFragmentListener { + public static class EPGListFragment extends ListFragment implements + HTSListener, EPGFragmentListener { /** * The fragment argument representing the section number for this * fragment. @@ -332,8 +340,13 @@ public class EPGListFragment extends ListFragment implements HTSListener, private boolean mCreated; - public EPGListFragment(Date timeslot) { - m_timeslot = timeslot; + public EPGListFragment() { + } + + @Override + public void setArguments(Bundle args) { + super.setArguments(args); + m_timeslot = (Date) args.getSerializable(ARG_TIME_SLOT); } @Override @@ -357,9 +370,13 @@ public void onResume() { app.addListener(this); // scroll to aquired position - getListView().setSelectionFromTop(firstVibleItem, top); + int firstVisibleItem = ((EPGTimeListActivity) getActivity()) + .getFirstVisibleItem(); + int top = ((EPGTimeListActivity) getActivity()).getTop(); + getListView().setSelectionFromTop(firstVisibleItem, top); - registerEPGFragmentListener(this); + ((EPGTimeListActivity) getActivity()) + .registerEPGFragmentListener(this); setLoading(app.isLoading()); } @@ -370,13 +387,14 @@ public void onPause() { .getApplication(); app.removeListener(this); - unregisterEPGFragmentListener(this); + ((EPGTimeListActivity) getActivity()) + .unregisterEPGFragmentListener(this); super.onPause(); } private void setLoading(boolean loading) { - EPGTimeListActivity.this.setLoading(loading); + ((EPGTimeListActivity) getActivity()).setLoading(loading); if (loading) { // } else { @@ -386,7 +404,8 @@ private void setLoading(boolean loading) { @Override public void populateChannelList() { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); + TVHGuideApplication app = (TVHGuideApplication) getActivity() + .getApplication(); ChannelTag currentTag = app.getCurrentTag(); prAdapter.clear(); @@ -417,7 +436,10 @@ public void onActivityCreated(Bundle savedInstanceState) { registerForContextMenu(getListView()); // scroll to acquired position - getListView().setSelectionFromTop(firstVibleItem, top); + int firstVisibleItem = ((EPGTimeListActivity) getActivity()) + .getFirstVisibleItem(); + int top = ((EPGTimeListActivity) getActivity()).getTop(); + getListView().setSelectionFromTop(firstVisibleItem, top); getListView().setOnScrollListener(new OnScrollListener() { @Override @@ -434,10 +456,16 @@ public void onScroll(AbsListView view, int firstVisibleItem, } View v = view.getChildAt(0); int top = (v == null) ? 0 : v.getTop(); - if (firstVisibleItem != EPGTimeListActivity.this.firstVibleItem - || top != EPGTimeListActivity.this.top) { - notifyEPGScrollListener(view, firstVisibleItem, top); + int oldFirstVisibleItem = ((EPGTimeListActivity) getActivity()) + .getFirstVisibleItem(); + int oldTop = ((EPGTimeListActivity) getActivity()).getTop(); + if (firstVisibleItem != oldFirstVisibleItem + || top != oldTop) { + + ((EPGTimeListActivity) getActivity()) + .notifyEPGScrollListener(view, + firstVisibleItem, top); } } }); @@ -517,7 +545,7 @@ public void onCreateContextMenu(ContextMenu menu, View v, item.setIntent(new SearchIMDbIntent(getActivity(), p.title)); } if (channel != null) { - intent = new Intent(getBaseContext(), + intent = new Intent(getActivity().getBaseContext(), ProgrammeListActivity.class); intent.putExtra("channelId", channel.id); item = menu.add(ContextMenu.NONE, @@ -531,7 +559,7 @@ public void onCreateContextMenu(ContextMenu menu, View v, public void onMessage(String action, final Object obj) { if (action.equals(TVHGuideApplication.ACTION_LOADING)) { - runOnUiThread(new Runnable() { + getActivity().runOnUiThread(new Runnable() { public void run() { boolean loading = (Boolean) obj; diff --git a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java index c2bfbaf..da2553b 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineActivity.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineActivity.java @@ -18,10 +18,8 @@ import android.app.ListActivity; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; @@ -49,8 +47,6 @@ public class EPGTimelineActivity extends ListActivity implements HTSListener { @Override protected void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(this); super.onCreate(savedInstanceState); adapter = new EPGTimelineAdapter(this, new ArrayList()); @@ -91,8 +87,7 @@ private void setCurrentTag(ChannelTag t) { if (t.iconBitmap != null) { getActionBar().setIcon( - new BitmapDrawable(getResources(), - t.iconBitmap)); + new BitmapDrawable(getResources(), t.iconBitmap)); } else { getActionBar().setIcon(R.drawable.logo_72); } diff --git a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java index 94ca310..37dbf7e 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineProgrammeListViewWrapper.java @@ -7,6 +7,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Color; import android.util.Log; import android.view.View; import android.widget.LinearLayout; @@ -26,6 +27,7 @@ public EPGTimelineProgrammeListViewWrapper(View base) { super(base); Resources res = base.getResources(); colors = res.obtainTypedArray(R.array.pref_color_content_type); + // colors.recycle(); container = (LinearLayout) base.findViewById(R.id.programme_container); container2 = (LinearLayout) base @@ -47,7 +49,14 @@ public void repaint(Programme p) { // there are 11 categories, calculate modulo if more categories are // returned than colors are defined int index = type % colors.length(); - int color = colors.getColor(index, 0); + int color; + try { + color = colors.getColor(index, 0); + } catch (Exception e) { + Log.d(TAG, "Didn't find color for index:" + index + ", type:" + + type); + color = Color.BLACK; + } // use first byte of hex number to calculate color offset int subType = 0; diff --git a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java index d14521a..4064d4a 100644 --- a/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java +++ b/src/org/tvheadend/tvhguide/EPGTimelineViewWrapper.java @@ -104,6 +104,7 @@ public EPGTimelineViewWrapper(EPGTimelineActivity context, View base, context.registerForContextMenu(horizontalListView); } + @SuppressWarnings("deprecation") public void repaintHeader() { Calendar cal = Calendar.getInstance(); List dates = new ArrayList(); @@ -127,6 +128,7 @@ public void repaintHeader() { horizontalListView.invalidate(); } + @SuppressWarnings("deprecation") public void repaint(Channel channel) { if (channel.id == 0) { @@ -165,7 +167,7 @@ public boolean onTouch(View view, MotionEvent event) { public void onItemClick(AdapterView adapterView, View view, int position, long id) { Object obj = adapterView.getItemAtPosition(position); - if (!(obj instanceof Programme)) { + if (obj != null && !(obj instanceof Programme)) { return; } Programme p = (Programme) obj; diff --git a/src/org/tvheadend/tvhguide/ProgrammeActivity.java b/src/org/tvheadend/tvhguide/ProgrammeActivity.java index cdb143d..305d409 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeActivity.java @@ -18,6 +18,8 @@ */ package org.tvheadend.tvhguide; +import java.util.Locale; + import org.tvheadend.tvhguide.R.string; import org.tvheadend.tvhguide.htsp.HTSListener; import org.tvheadend.tvhguide.htsp.HTSService; @@ -194,15 +196,16 @@ public String buildSeriesInfoString(SeriesInfo info) { if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), - info.seasonNumber); + s += String.format("%s %02d", + season.toLowerCase(Locale.getDefault()), info.seasonNumber); if (info.seasonCount > 0) s += String.format("/%02d", info.seasonCount); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), + s += String.format("%s %02d", + episode.toLowerCase(Locale.getDefault()), info.episodeNumber); if (info.episodeCount > 0) s += String.format("/%02d", info.episodeCount); @@ -210,13 +213,15 @@ public String buildSeriesInfoString(SeriesInfo info) { if (info.partNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %d", part.toLowerCase(), info.partNumber); + s += String.format("%s %d", part.toLowerCase(Locale.getDefault()), + info.partNumber); if (info.partCount > 0) s += String.format("/%02d", info.partCount); } if (s.length() > 0) { - s = s.substring(0, 1).toUpperCase() + s.substring(1); + s = s.substring(0, 1).toUpperCase(Locale.getDefault()) + + s.substring(1); } return s; diff --git a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java index 3dcdfc9..6fd76fb 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListActivity.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListActivity.java @@ -22,6 +22,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.Locale; import org.tvheadend.tvhguide.R.string; import org.tvheadend.tvhguide.htsp.HTSListener; @@ -313,23 +314,26 @@ public String buildSeriesInfoString(SeriesInfo info) { if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), - info.seasonNumber); + s += String.format("%s %02d", + season.toLowerCase(Locale.getDefault()), info.seasonNumber); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), + s += String.format("%s %02d", + episode.toLowerCase(Locale.getDefault()), info.episodeNumber); } if (info.partNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %d", part.toLowerCase(), info.partNumber); + s += String.format("%s %d", part.toLowerCase(Locale.getDefault()), + info.partNumber); } if (s.length() > 0) { - s = s.substring(0, 1).toUpperCase() + s.substring(1); + s = s.substring(0, 1).toUpperCase(Locale.getDefault()) + + s.substring(1); } return s; diff --git a/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java index bd7862d..d993dd3 100644 --- a/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java +++ b/src/org/tvheadend/tvhguide/ProgrammeListViewWrapper.java @@ -1,6 +1,7 @@ package org.tvheadend.tvhguide; import java.text.SimpleDateFormat; +import java.util.Locale; import org.tvheadend.tvhguide.R.string; import org.tvheadend.tvhguide.model.Programme; @@ -81,8 +82,8 @@ public void repaint(Programme p) { * 60 * 60 * 24 * 6 && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 2) { - date.setText(new SimpleDateFormat("EEEE").format(p.start - .getTime())); + date.setText(new SimpleDateFormat("EEEE", Locale.getDefault()) + .format(p.start.getTime())); } else { date.setText(DateFormat.getDateFormat(date.getContext()) .format(p.start)); @@ -117,23 +118,26 @@ public String buildSeriesInfoString(View context, SeriesInfo info) { if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), - info.seasonNumber); + s += String.format("%s %02d", + season.toLowerCase(Locale.getDefault()), info.seasonNumber); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), + s += String.format("%s %02d", + episode.toLowerCase(Locale.getDefault()), info.episodeNumber); } if (info.partNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %d", part.toLowerCase(), info.partNumber); + s += String.format("%s %d", part.toLowerCase(Locale.getDefault()), + info.partNumber); } if (s.length() > 0) { - s = s.substring(0, 1).toUpperCase() + s.substring(1); + s = s.substring(0, 1).toUpperCase(Locale.getDefault()) + + s.substring(1); } return s; diff --git a/src/org/tvheadend/tvhguide/RecordingListActivity.java b/src/org/tvheadend/tvhguide/RecordingListActivity.java index ba30d55..b3396bf 100644 --- a/src/org/tvheadend/tvhguide/RecordingListActivity.java +++ b/src/org/tvheadend/tvhguide/RecordingListActivity.java @@ -18,6 +18,19 @@ */ package org.tvheadend.tvhguide; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +import org.tvheadend.tvhguide.htsp.HTSListener; +import org.tvheadend.tvhguide.htsp.HTSService; +import org.tvheadend.tvhguide.intent.SearchEPGIntent; +import org.tvheadend.tvhguide.intent.SearchIMDbIntent; +import org.tvheadend.tvhguide.model.Channel; +import org.tvheadend.tvhguide.model.Recording; + import android.app.Activity; import android.app.AlertDialog; import android.app.ListActivity; @@ -41,340 +54,348 @@ import android.widget.ListView; import android.widget.TextView; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import org.tvheadend.tvhguide.R; -import org.tvheadend.tvhguide.htsp.HTSListener; -import org.tvheadend.tvhguide.htsp.HTSService; -import org.tvheadend.tvhguide.intent.SearchEPGIntent; -import org.tvheadend.tvhguide.intent.SearchIMDbIntent; -import org.tvheadend.tvhguide.model.Channel; -import org.tvheadend.tvhguide.model.Recording; - /** - * + * * @author john-tornblom */ public class RecordingListActivity extends ListActivity implements HTSListener { - private RecordingListAdapter recAdapter; - - @Override - public void onCreate(Bundle icicle) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Boolean theme = prefs.getBoolean("lightThemePref", false); - setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); - - super.onCreate(icicle); - - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - List recList = new ArrayList(); - recList.addAll(app.getRecordings()); - recAdapter = new RecordingListAdapter(this, recList); - recAdapter.sort(); - setListAdapter(recAdapter); - registerForContextMenu(getListView()); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.recording_list_title); - TextView t = (TextView) findViewById(R.id.ct_title); - - t.setText(R.string.menu_recordings); - } - - @Override - protected void onResume() { - super.onResume(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.addListener(this); - } - - @Override - protected void onPause() { - super.onPause(); - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - app.removeListener(this); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Recording rec = (Recording) recAdapter.getItem(position); - - Intent intent = new Intent(this, RecordingActivity.class); - intent.putExtra("id", rec.id); - startActivity(intent); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - MenuItem item = null; - Intent intent = null; - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Recording rec = recAdapter.getItem(info.position); - - menu.setHeaderTitle(rec.title); - - intent = new Intent(RecordingListActivity.this, HTSService.class); - intent.putExtra("id", rec.id); - - if (rec.isRecording() || rec.isScheduled()) { - intent.setAction(HTSService.ACTION_DVR_CANCEL); - item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, ContextMenu.NONE, R.string.menu_record_cancel); - item.setIntent(intent); - } else { - intent.setAction(HTSService.ACTION_DVR_DELETE); - item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, ContextMenu.NONE, R.string.menu_record_remove); - item.setIntent(intent); - - item = menu.add(ContextMenu.NONE, R.string.ch_play, ContextMenu.NONE, R.string.ch_play); - intent = new Intent(this, ExternalPlaybackActivity.class); - intent.putExtra("dvrId", rec.id); - item.setIntent(intent); - item.setIcon(android.R.drawable.ic_menu_view); - } - - item = menu.add(ContextMenu.NONE, R.string.search_hint, ContextMenu.NONE, R.string.search_hint); - item.setIntent(new SearchEPGIntent(this, rec.title)); - item.setIcon(android.R.drawable.ic_menu_search); - - item = menu.add(ContextMenu.NONE, ContextMenu.NONE, ContextMenu.NONE, "IMDb"); - item.setIntent(new SearchIMDbIntent(this, rec.title)); - item.setIcon(android.R.drawable.ic_menu_info_details); - } - - @Override - public boolean onContextItemSelected(final MenuItem item) { - switch (item.getItemId()) { - case R.string.menu_record: - case R.string.menu_record_cancel: { - startService(item.getIntent()); - return true; - } - case R.string.menu_record_remove: { - - new AlertDialog.Builder(this) - .setTitle(R.string.menu_record_remove) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - startService(item.getIntent()); - } - }) - .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - //NOP - } - }) - .show(); - - return true; - } - default: { - return super.onContextItemSelected(item); - } - } - } - - public void onMessage(String action, final Object obj) { - if (action.equals(TVHGuideApplication.ACTION_LOADING) && !(Boolean) obj) { - - runOnUiThread(new Runnable() { - - public void run() { - TVHGuideApplication app = (TVHGuideApplication) getApplication(); - recAdapter.list.clear(); - recAdapter.list.addAll(app.getRecordings()); - recAdapter.notifyDataSetChanged(); - recAdapter.sort(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_DVR_ADD)) { - runOnUiThread(new Runnable() { - - public void run() { - recAdapter.add((Recording) obj); - recAdapter.notifyDataSetChanged(); - recAdapter.sort(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_DVR_DELETE)) { - runOnUiThread(new Runnable() { - - public void run() { - recAdapter.remove((Recording) obj); - recAdapter.notifyDataSetChanged(); - } - }); - } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { - runOnUiThread(new Runnable() { - - public void run() { - Recording rec = (Recording) obj; - recAdapter.updateView(getListView(), rec); - } - }); - } - } - - private class ViewWarpper { - - TextView title; - TextView channel; - TextView time; - TextView date; - TextView message; - TextView desc; - ImageView icon; - ImageView state; - - public ViewWarpper(View base) { - title = (TextView) base.findViewById(R.id.rec_title); - channel = (TextView) base.findViewById(R.id.rec_channel); - - time = (TextView) base.findViewById(R.id.rec_time); - date = (TextView) base.findViewById(R.id.rec_date); - message = (TextView) base.findViewById(R.id.rec_message); - desc = (TextView) base.findViewById(R.id.rec_desc); - icon = (ImageView) base.findViewById(R.id.rec_icon); - state = (ImageView) base.findViewById(R.id.rec_state); - } - - public void repaint(Recording rec) { - Channel ch = rec.channel; - - title.setText(rec.title); - title.invalidate(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(icon.getContext()); - Boolean showIcons = prefs.getBoolean("showIconPref", false); - icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); - icon.setImageBitmap(ch.iconBitmap); - - channel.setText(ch.name); - channel.invalidate(); - - if (DateUtils.isToday(rec.start.getTime())) { - date.setText(getString(R.string.today)); - } else if(rec.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*2 && - rec.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2) { - date.setText(DateUtils.getRelativeTimeSpanString(rec.start.getTime(), - System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS)); - } else if(rec.start.getTime() < System.currentTimeMillis() + 1000*60*60*24*6 && - rec.start.getTime() > System.currentTimeMillis() - 1000*60*60*24*2 - ) { - date.setText(new SimpleDateFormat("EEEE").format(rec.start.getTime())); - } else { - date.setText(DateFormat.getDateFormat(date.getContext()).format(rec.start)); - } - - date.invalidate(); - - String msg = ""; - if (rec.error != null) { - msg = rec.error; - state.setImageResource(R.drawable.ic_error_small); - } else if ("completed".equals(rec.state)) { - msg = getString(R.string.pvr_completed); - state.setImageResource(R.drawable.ic_success_small); - } else if ("invalid".equals(rec.state)) { - msg = getString(R.string.pvr_invalid); - state.setImageResource(R.drawable.ic_error_small); - } else if ("missed".equals(rec.state)) { - msg = getString(R.string.pvr_missed); - state.setImageResource(R.drawable.ic_error_small); - } else if ("recording".equals(rec.state)) { - msg = getString(R.string.pvr_recording); - state.setImageResource(R.drawable.ic_rec_small); - } else if ("scheduled".equals(rec.state)) { - msg = getString(R.string.pvr_scheduled); - state.setImageResource(R.drawable.ic_schedule_small); - } else { - state.setImageDrawable(null); - } - if (msg.length() > 0) { - message.setText("(" + msg + ")"); - } else { - message.setText(msg); - } - message.invalidate(); - - desc.setText(rec.description); - desc.invalidate(); - - icon.invalidate(); - - time.setText( - DateFormat.getTimeFormat(time.getContext()).format(rec.start) - + " - " - + DateFormat.getTimeFormat(time.getContext()).format(rec.stop)); - time.invalidate(); - } - } - - class RecordingListAdapter extends ArrayAdapter { - - Activity context; - List list; - - RecordingListAdapter(Activity context, List list) { - super(context, R.layout.recording_list_widget, list); - this.context = context; - this.list = list; - } - - public void sort() { - sort(new Comparator() { - - public int compare(Recording x, Recording y) { - return x.compareTo(y); - } - }); - } - - public void updateView(ListView listView, Recording recording) { - for (int i = 0; i < listView.getChildCount(); i++) { - View view = listView.getChildAt(i); - int pos = listView.getPositionForView(view); - Recording rec = (Recording) listView.getItemAtPosition(pos); - - if (view.getTag() == null || rec == null) { - continue; - } - - if (recording.id != rec.id) { - continue; - } - - ViewWarpper wrapper = (ViewWarpper) view.getTag(); - wrapper.repaint(recording); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - ViewWarpper wrapper = null; - - Recording rec = list.get(position); - - if (row == null) { - LayoutInflater inflater = context.getLayoutInflater(); - row = inflater.inflate(R.layout.recording_list_widget, null, false); - - wrapper = new ViewWarpper(row); - row.setTag(wrapper); - - } else { - wrapper = (ViewWarpper) row.getTag(); - } - - wrapper.repaint(rec); - return row; - } - } + private RecordingListAdapter recAdapter; + + @Override + public void onCreate(Bundle icicle) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + Boolean theme = prefs.getBoolean("lightThemePref", false); + setTheme(theme ? R.style.CustomTheme_Light : R.style.CustomTheme); + + super.onCreate(icicle); + + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + List recList = new ArrayList(); + recList.addAll(app.getRecordings()); + recAdapter = new RecordingListAdapter(this, recList); + recAdapter.sort(); + setListAdapter(recAdapter); + registerForContextMenu(getListView()); + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, + R.layout.recording_list_title); + TextView t = (TextView) findViewById(R.id.ct_title); + + t.setText(R.string.menu_recordings); + } + + @Override + protected void onResume() { + super.onResume(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.addListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + app.removeListener(this); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Recording rec = (Recording) recAdapter.getItem(position); + + Intent intent = new Intent(this, RecordingActivity.class); + intent.putExtra("id", rec.id); + startActivity(intent); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + MenuItem item = null; + Intent intent = null; + + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + Recording rec = recAdapter.getItem(info.position); + + menu.setHeaderTitle(rec.title); + + intent = new Intent(RecordingListActivity.this, HTSService.class); + intent.putExtra("id", rec.id); + + if (rec.isRecording() || rec.isScheduled()) { + intent.setAction(HTSService.ACTION_DVR_CANCEL); + item = menu.add(ContextMenu.NONE, R.string.menu_record_cancel, + ContextMenu.NONE, R.string.menu_record_cancel); + item.setIntent(intent); + } else { + intent.setAction(HTSService.ACTION_DVR_DELETE); + item = menu.add(ContextMenu.NONE, R.string.menu_record_remove, + ContextMenu.NONE, R.string.menu_record_remove); + item.setIntent(intent); + + item = menu.add(ContextMenu.NONE, R.string.ch_play, + ContextMenu.NONE, R.string.ch_play); + intent = new Intent(this, ExternalPlaybackActivity.class); + intent.putExtra("dvrId", rec.id); + item.setIntent(intent); + item.setIcon(android.R.drawable.ic_menu_view); + } + + item = menu.add(ContextMenu.NONE, R.string.search_hint, + ContextMenu.NONE, R.string.search_hint); + item.setIntent(new SearchEPGIntent(this, rec.title)); + item.setIcon(android.R.drawable.ic_menu_search); + + item = menu.add(ContextMenu.NONE, ContextMenu.NONE, ContextMenu.NONE, + "IMDb"); + item.setIntent(new SearchIMDbIntent(this, rec.title)); + item.setIcon(android.R.drawable.ic_menu_info_details); + } + + @Override + public boolean onContextItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.string.menu_record: + case R.string.menu_record_cancel: { + startService(item.getIntent()); + return true; + } + case R.string.menu_record_remove: { + + new AlertDialog.Builder(this) + .setTitle(R.string.menu_record_remove) + .setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + startService(item.getIntent()); + } + }) + .setNegativeButton(android.R.string.no, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + // NOP + } + }).show(); + + return true; + } + default: { + return super.onContextItemSelected(item); + } + } + } + + public void onMessage(String action, final Object obj) { + if (action.equals(TVHGuideApplication.ACTION_LOADING) && !(Boolean) obj) { + + runOnUiThread(new Runnable() { + + public void run() { + TVHGuideApplication app = (TVHGuideApplication) getApplication(); + recAdapter.list.clear(); + recAdapter.list.addAll(app.getRecordings()); + recAdapter.notifyDataSetChanged(); + recAdapter.sort(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_ADD)) { + runOnUiThread(new Runnable() { + + public void run() { + recAdapter.add((Recording) obj); + recAdapter.notifyDataSetChanged(); + recAdapter.sort(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_DELETE)) { + runOnUiThread(new Runnable() { + + public void run() { + recAdapter.remove((Recording) obj); + recAdapter.notifyDataSetChanged(); + } + }); + } else if (action.equals(TVHGuideApplication.ACTION_DVR_UPDATE)) { + runOnUiThread(new Runnable() { + + public void run() { + Recording rec = (Recording) obj; + recAdapter.updateView(getListView(), rec); + } + }); + } + } + + private class ViewWarpper { + + TextView title; + TextView channel; + TextView time; + TextView date; + TextView message; + TextView desc; + ImageView icon; + ImageView state; + + public ViewWarpper(View base) { + title = (TextView) base.findViewById(R.id.rec_title); + channel = (TextView) base.findViewById(R.id.rec_channel); + + time = (TextView) base.findViewById(R.id.rec_time); + date = (TextView) base.findViewById(R.id.rec_date); + message = (TextView) base.findViewById(R.id.rec_message); + desc = (TextView) base.findViewById(R.id.rec_desc); + icon = (ImageView) base.findViewById(R.id.rec_icon); + state = (ImageView) base.findViewById(R.id.rec_state); + } + + public void repaint(Recording rec) { + Channel ch = rec.channel; + + title.setText(rec.title); + title.invalidate(); + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(icon.getContext()); + Boolean showIcons = prefs.getBoolean("showIconPref", false); + icon.setVisibility(showIcons ? ImageView.VISIBLE : ImageView.GONE); + icon.setImageBitmap(ch.iconBitmap); + + channel.setText(ch.name); + channel.invalidate(); + + if (DateUtils.isToday(rec.start.getTime())) { + date.setText(getString(R.string.today)); + } else if (rec.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 2 + && rec.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(DateUtils.getRelativeTimeSpanString( + rec.start.getTime(), System.currentTimeMillis(), + DateUtils.DAY_IN_MILLIS)); + } else if (rec.start.getTime() < System.currentTimeMillis() + 1000 + * 60 * 60 * 24 * 6 + && rec.start.getTime() > System.currentTimeMillis() - 1000 + * 60 * 60 * 24 * 2) { + date.setText(new SimpleDateFormat("EEEE", Locale.getDefault()) + .format(rec.start.getTime())); + } else { + date.setText(DateFormat.getDateFormat(date.getContext()) + .format(rec.start)); + } + + date.invalidate(); + + String msg = ""; + if (rec.error != null) { + msg = rec.error; + state.setImageResource(R.drawable.ic_error_small); + } else if ("completed".equals(rec.state)) { + msg = getString(R.string.pvr_completed); + state.setImageResource(R.drawable.ic_success_small); + } else if ("invalid".equals(rec.state)) { + msg = getString(R.string.pvr_invalid); + state.setImageResource(R.drawable.ic_error_small); + } else if ("missed".equals(rec.state)) { + msg = getString(R.string.pvr_missed); + state.setImageResource(R.drawable.ic_error_small); + } else if ("recording".equals(rec.state)) { + msg = getString(R.string.pvr_recording); + state.setImageResource(R.drawable.ic_rec_small); + } else if ("scheduled".equals(rec.state)) { + msg = getString(R.string.pvr_scheduled); + state.setImageResource(R.drawable.ic_schedule_small); + } else { + state.setImageDrawable(null); + } + if (msg.length() > 0) { + message.setText("(" + msg + ")"); + } else { + message.setText(msg); + } + message.invalidate(); + + desc.setText(rec.description); + desc.invalidate(); + + icon.invalidate(); + + time.setText(DateFormat.getTimeFormat(time.getContext()).format( + rec.start) + + " - " + + DateFormat.getTimeFormat(time.getContext()).format( + rec.stop)); + time.invalidate(); + } + } + + class RecordingListAdapter extends ArrayAdapter { + + Activity context; + List list; + + RecordingListAdapter(Activity context, List list) { + super(context, R.layout.recording_list_widget, list); + this.context = context; + this.list = list; + } + + public void sort() { + sort(new Comparator() { + + public int compare(Recording x, Recording y) { + return x.compareTo(y); + } + }); + } + + public void updateView(ListView listView, Recording recording) { + for (int i = 0; i < listView.getChildCount(); i++) { + View view = listView.getChildAt(i); + int pos = listView.getPositionForView(view); + Recording rec = (Recording) listView.getItemAtPosition(pos); + + if (view.getTag() == null || rec == null) { + continue; + } + + if (recording.id != rec.id) { + continue; + } + + ViewWarpper wrapper = (ViewWarpper) view.getTag(); + wrapper.repaint(recording); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + ViewWarpper wrapper = null; + + Recording rec = list.get(position); + + if (row == null) { + LayoutInflater inflater = context.getLayoutInflater(); + row = inflater.inflate(R.layout.recording_list_widget, null, + false); + + wrapper = new ViewWarpper(row); + row.setTag(wrapper); + + } else { + wrapper = (ViewWarpper) row.getTag(); + } + + wrapper.repaint(rec); + return row; + } + } } diff --git a/src/org/tvheadend/tvhguide/SearchResultActivity.java b/src/org/tvheadend/tvhguide/SearchResultActivity.java index aa26abe..2f7eca5 100644 --- a/src/org/tvheadend/tvhguide/SearchResultActivity.java +++ b/src/org/tvheadend/tvhguide/SearchResultActivity.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.regex.Pattern; import org.tvheadend.tvhguide.R.string; @@ -295,23 +296,26 @@ public String buildSeriesInfoString(SeriesInfo info) { if (info.seasonNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", season.toLowerCase(), - info.seasonNumber); + s += String.format("%s %02d", + season.toLowerCase(Locale.getDefault()), info.seasonNumber); } if (info.episodeNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %02d", episode.toLowerCase(), + s += String.format("%s %02d", + episode.toLowerCase(Locale.getDefault()), info.episodeNumber); } if (info.partNumber > 0) { if (s.length() > 0) s += ", "; - s += String.format("%s %d", part.toLowerCase(), info.partNumber); + s += String.format("%s %d", part.toLowerCase(Locale.getDefault()), + info.partNumber); } if (s.length() > 0) { - s = s.substring(0, 1).toUpperCase() + s.substring(1); + s = s.substring(0, 1).toUpperCase(Locale.getDefault()) + + s.substring(1); } return s; @@ -399,8 +403,8 @@ public void repaint(Programme p) { * 60 * 60 * 24 * 6 && p.start.getTime() > System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 2) { - date.setText(new SimpleDateFormat("EEEE").format(p.start - .getTime())); + date.setText(new SimpleDateFormat("EEEE", Locale.getDefault()) + .format(p.start.getTime())); } else { date.setText(DateFormat.getDateFormat(date.getContext()) .format(p.start)); diff --git a/src/org/tvheadend/tvhguide/SettingsActivity.java b/src/org/tvheadend/tvhguide/SettingsActivity.java index 86c2ee2..841e48a 100644 --- a/src/org/tvheadend/tvhguide/SettingsActivity.java +++ b/src/org/tvheadend/tvhguide/SettingsActivity.java @@ -39,6 +39,7 @@ public class SettingsActivity extends PreferenceActivity { private String oldUser; private String oldPw; + @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager diff --git a/src/org/tvheadend/tvhguide/htsp/HTSConnection.java b/src/org/tvheadend/tvhguide/htsp/HTSConnection.java index 916786d..d4a6c92 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSConnection.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSConnection.java @@ -26,14 +26,13 @@ import java.nio.channels.SocketChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; -import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.util.Log; +import android.util.SparseArray; /** * @@ -57,7 +56,7 @@ public class HTSConnection extends Thread { private int protocolVersion; private HTSConnectionListener listener; - private Map responseHandelers; + private SparseArray responseHandelers; private LinkedList messageQueue; private boolean auth; private Selector selector; @@ -68,7 +67,7 @@ public HTSConnection(HTSConnectionListener listener, String clientName, lock = new ReentrantLock(); inBuf = ByteBuffer.allocateDirect(1024 * 1024); inBuf.limit(4); - responseHandelers = new HashMap(); + responseHandelers = new SparseArray(); messageQueue = new LinkedList(); this.listener = listener; @@ -246,9 +245,9 @@ public void run() { lock.lock(); try { - Iterator it = selector.selectedKeys().iterator(); + Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { - SelectionKey selKey = (SelectionKey) it.next(); + SelectionKey selKey = it.next(); it.remove(); processTcpSelectionKey(selKey); } diff --git a/src/org/tvheadend/tvhguide/htsp/HTSMessage.java b/src/org/tvheadend/tvhguide/htsp/HTSMessage.java index 13c1fb5..d2adea5 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSMessage.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSMessage.java @@ -30,382 +30,389 @@ import java.util.Map; /** - * + * * @author john-tornblom */ public class HTSMessage extends HashMap { - public static final long HTSP_VERSION = 8; - private static final byte HMF_MAP = 1; - private static final byte HMF_S64 = 2; - private static final byte HMF_STR = 3; - private static final byte HMF_BIN = 4; - private static final byte HMF_LIST = 5; - private ByteBuffer buf; - - public void putField(String name, Object value) { - if (value != null) { - put(name, value); - } - } - - public void setMethod(String name) { - put("method", name); - } - - public String getMethod() { - return getString("method", ""); - } - - public boolean containsField(String name) { - return containsKey(name); - } - - public BigInteger getBigInteger(String name) { - return (BigInteger) get(name); - } - - public long getLong(String name) { - return getBigInteger(name).longValue(); - } - - public long getLong(String name, long std) { - if (!containsField(name)) { - return std; - } - return getLong(name); - } - - public int getInt(String name) { - return getBigInteger(name).intValue(); - } - - public int getInt(String name, int std) { - if (!containsField(name)) { - return std; - } - return getInt(name); - } - - public String getString(String name, String std) { - if (!containsField(name)) { - return std; - } - return getString(name); - } - - public String getString(String name) { - Object obj = get(name); - if (obj == null) { - return null; - } - return obj.toString(); - } - - public List getLongList(String name) { - ArrayList list = new ArrayList(); - - if (!containsField(name)) { - return list; - } - - for (Object obj : (List) get(name)) { - if (obj instanceof BigInteger) { - list.add(((BigInteger) obj).longValue()); - } - } - - return list; - } - - List getLongList(String name, List std) { - if (!containsField(name)) { - return std; - } - - return getLongList(name); - } - - public List getIntList(String name) { - ArrayList list = new ArrayList(); - - if (!containsField(name)) { - return list; - } - - for (Object obj : (List) get(name)) { - if (obj instanceof BigInteger) { - list.add(((BigInteger) obj).intValue()); - } - } - - return list; - } - - List getIntList(String name, List std) { - if (!containsField(name)) { - return std; - } - - return getIntList(name); - } - - public List getList(String name) { - return (List) get(name); - } - - public byte[] getByteArray(String name) { - return (byte[]) get(name); - } - - public Date getDate(String name) { - return new Date(getLong(name) * 1000); - } - - public boolean transmit(SocketChannel ch) throws IOException { - if (buf == null) { - byte[] data = serializeBinary(this); - int len = data.length; - buf = ByteBuffer.allocateDirect(len + 4); - - buf.put((byte) ((len >> 24) & 0xFF)); - buf.put((byte) ((len >> 16) & 0xFF)); - buf.put((byte) ((len >> 8) & 0xFF)); - buf.put((byte) ((len) & 0xFF)); - buf.put(data); - buf.flip(); - } - - if (ch.write(buf) < 0) { - throw new IOException("Server went down"); - } - - if (buf.hasRemaining()) { - return false; - } else { - buf.flip(); - return true; - } - } - - public static String getHexString(byte[] b) throws Exception { - String result = ""; - for (int i = 0; i < b.length; i++) { - result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1); - } - return result; - } - - private static byte[] toByteArray(BigInteger big) { - byte[] b = big.toByteArray(); - byte b1[] = new byte[b.length]; - - for (int i = 0; i < b.length; i++) { - b1[i] = b[b.length - 1 - i]; - } - - return b1; - } - - private static BigInteger toBigInteger(byte b[]) { - byte b1[] = new byte[b.length + 1]; - - for (int i = 0; i < b.length; i++) { - b1[i + 1] = b[b.length - 1 - i]; - } - - return new BigInteger(b1); - } - - private static long uIntToLong(byte b1, byte b2, byte b3, byte b4) { - long i = 0; - i <<= 8; - i ^= b1 & 0xFF; - i <<= 8; - i ^= b2 & 0xFF; - i <<= 8; - i ^= b3 & 0xFF; - i <<= 8; - i ^= b4 & 0xFF; - return i; - } - - public static HTSMessage parse(ByteBuffer buf) throws IOException { - long len; - - if (buf.position() < 4) { - return null; - } - - len = uIntToLong(buf.get(0), buf.get(1), buf.get(2), buf.get(3)); - - if (len + 4 > buf.capacity()) { - buf.clear(); - throw new IOException("Mesage is to long"); - } - - if (buf.limit() == 4) { - buf.limit((int) (4 + len)); - } - - //Message not yet fully read - if (buf.position() < len + 4) { - return null; - } - - buf.flip(); - buf.getInt(); //drops 4 bytes - HTSMessage msg = deserializeBinary(buf); - - buf.limit(4); - buf.position(0); - return msg; - } - - private static byte[] serializeBinary(String name, Object value) throws IOException { - byte[] bName = name.getBytes(); - byte[] bData = new byte[0]; - byte type; - - if (value instanceof String) { - type = HTSMessage.HMF_STR; - bData = ((String) value).getBytes(); - } else if (value instanceof BigInteger) { - type = HTSMessage.HMF_S64; - bData = toByteArray((BigInteger) value); - } else if (value instanceof Integer) { - type = HTSMessage.HMF_S64; - bData = toByteArray(BigInteger.valueOf((Integer) value)); - } else if (value instanceof Long) { - type = HTSMessage.HMF_S64; - bData = toByteArray(BigInteger.valueOf((Long) value)); - } else if (value instanceof byte[]) { - type = HTSMessage.HMF_BIN; - bData = (byte[]) value; - } else if (value instanceof Map) { - type = HTSMessage.HMF_MAP; - bData = serializeBinary((Map) value); - } else if (value instanceof Collection) { - type = HTSMessage.HMF_LIST; - bData = serializeBinary((Collection) value); - } else if (value == null) { - throw new IOException("HTSP doesn't support null values"); - } else { - throw new IOException("Unhandled class for " + name + ": " + value - + " (" + value.getClass().getSimpleName() + ")"); - } - - byte[] buf = new byte[1 + 1 + 4 + bName.length + bData.length]; - buf[0] = type; - buf[1] = (byte) (bName.length & 0xFF); - buf[2] = (byte) ((bData.length >> 24) & 0xFF); - buf[3] = (byte) ((bData.length >> 16) & 0xFF); - buf[4] = (byte) ((bData.length >> 8) & 0xFF); - buf[5] = (byte) ((bData.length) & 0xFF); - - System.arraycopy(bName, 0, buf, 6, bName.length); - System.arraycopy(bData, 0, buf, 6 + bName.length, bData.length); - - return buf; - } - - private static byte[] serializeBinary(Collection list) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(Short.MAX_VALUE); - - for (Object value : list) { - byte[] sub = serializeBinary("", value); - buf.put(sub); - } - - byte[] bBuf = new byte[buf.position()]; - buf.flip(); - buf.get(bBuf); - - return bBuf; - } - - private static byte[] serializeBinary(Map map) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(Short.MAX_VALUE); - - for (Object key : map.keySet()) { - Object value = map.get(key); - byte[] sub = serializeBinary(key.toString(), value); - buf.put(sub); - } - - byte[] bBuf = new byte[buf.position()]; - buf.flip(); - buf.get(bBuf); - - return bBuf; - } - - private static HTSMessage deserializeBinary(ByteBuffer buf) throws IOException { - byte type, namelen; - long datalen; - - HTSMessage msg = new HTSMessage(); - int cnt = 0; - - while (buf.hasRemaining()) { - type = buf.get(); - namelen = buf.get(); - datalen = uIntToLong(buf.get(), buf.get(), buf.get(), buf.get()); - - if (datalen > Integer.MAX_VALUE) { - throw new IOException("Would get precision losses ;("); - } - if (buf.limit() < namelen + datalen) { - throw new IOException("Buffer limit exceeded"); - } - - //Get the key for the map (the name) - String name = null; - if (namelen == 0) { - name = Integer.toString(cnt++); - } else { - byte[] bName = new byte[namelen]; - buf.get(bName); - name = new String(bName); - } - - //Get the actual content - Object obj = null; - byte[] bData = new byte[(int) datalen]; //Should be long? - buf.get(bData); - - switch (type) { - case HTSMessage.HMF_STR: { - obj = new String(bData); - break; - } - case HMF_BIN: { - obj = bData; - break; - } - case HMF_S64: { - obj = toBigInteger(bData); - break; - } - case HMF_MAP: { - ByteBuffer sub = ByteBuffer.allocateDirect((int) datalen); - sub.put(bData); - sub.flip(); - obj = deserializeBinary(sub); - break; - } - case HMF_LIST: { - ByteBuffer sub = ByteBuffer.allocateDirect((int) datalen); - sub.put(bData); - sub.flip(); - obj = new ArrayList(deserializeBinary(sub).values()); - break; - } - default: - throw new IOException("Unknown data type"); - } - msg.putField(name, obj); - } - return msg; - } + /** + * + */ + private static final long serialVersionUID = 2975403077219488636L; + public static final long HTSP_VERSION = 8; + private static final byte HMF_MAP = 1; + private static final byte HMF_S64 = 2; + private static final byte HMF_STR = 3; + private static final byte HMF_BIN = 4; + private static final byte HMF_LIST = 5; + private ByteBuffer buf; + + public void putField(String name, Object value) { + if (value != null) { + put(name, value); + } + } + + public void setMethod(String name) { + put("method", name); + } + + public String getMethod() { + return getString("method", ""); + } + + public boolean containsField(String name) { + return containsKey(name); + } + + public BigInteger getBigInteger(String name) { + return (BigInteger) get(name); + } + + public long getLong(String name) { + return getBigInteger(name).longValue(); + } + + public long getLong(String name, long std) { + if (!containsField(name)) { + return std; + } + return getLong(name); + } + + public int getInt(String name) { + return getBigInteger(name).intValue(); + } + + public int getInt(String name, int std) { + if (!containsField(name)) { + return std; + } + return getInt(name); + } + + public String getString(String name, String std) { + if (!containsField(name)) { + return std; + } + return getString(name); + } + + public String getString(String name) { + Object obj = get(name); + if (obj == null) { + return null; + } + return obj.toString(); + } + + public List getLongList(String name) { + ArrayList list = new ArrayList(); + + if (!containsField(name)) { + return list; + } + + for (Object obj : (List) get(name)) { + if (obj instanceof BigInteger) { + list.add(((BigInteger) obj).longValue()); + } + } + + return list; + } + + List getLongList(String name, List std) { + if (!containsField(name)) { + return std; + } + + return getLongList(name); + } + + public List getIntList(String name) { + ArrayList list = new ArrayList(); + + if (!containsField(name)) { + return list; + } + + for (Object obj : (List) get(name)) { + if (obj instanceof BigInteger) { + list.add(((BigInteger) obj).intValue()); + } + } + + return list; + } + + List getIntList(String name, List std) { + if (!containsField(name)) { + return std; + } + + return getIntList(name); + } + + public List getList(String name) { + return (List) get(name); + } + + public byte[] getByteArray(String name) { + return (byte[]) get(name); + } + + public Date getDate(String name) { + return new Date(getLong(name) * 1000); + } + + public boolean transmit(SocketChannel ch) throws IOException { + if (buf == null) { + byte[] data = serializeBinary(this); + int len = data.length; + buf = ByteBuffer.allocateDirect(len + 4); + + buf.put((byte) ((len >> 24) & 0xFF)); + buf.put((byte) ((len >> 16) & 0xFF)); + buf.put((byte) ((len >> 8) & 0xFF)); + buf.put((byte) ((len) & 0xFF)); + buf.put(data); + buf.flip(); + } + + if (ch.write(buf) < 0) { + throw new IOException("Server went down"); + } + + if (buf.hasRemaining()) { + return false; + } else { + buf.flip(); + return true; + } + } + + public static String getHexString(byte[] b) throws Exception { + String result = ""; + for (int i = 0; i < b.length; i++) { + result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1); + } + return result; + } + + private static byte[] toByteArray(BigInteger big) { + byte[] b = big.toByteArray(); + byte b1[] = new byte[b.length]; + + for (int i = 0; i < b.length; i++) { + b1[i] = b[b.length - 1 - i]; + } + + return b1; + } + + private static BigInteger toBigInteger(byte b[]) { + byte b1[] = new byte[b.length + 1]; + + for (int i = 0; i < b.length; i++) { + b1[i + 1] = b[b.length - 1 - i]; + } + + return new BigInteger(b1); + } + + private static long uIntToLong(byte b1, byte b2, byte b3, byte b4) { + long i = 0; + i <<= 8; + i ^= b1 & 0xFF; + i <<= 8; + i ^= b2 & 0xFF; + i <<= 8; + i ^= b3 & 0xFF; + i <<= 8; + i ^= b4 & 0xFF; + return i; + } + + public static HTSMessage parse(ByteBuffer buf) throws IOException { + long len; + + if (buf.position() < 4) { + return null; + } + + len = uIntToLong(buf.get(0), buf.get(1), buf.get(2), buf.get(3)); + + if (len + 4 > buf.capacity()) { + buf.clear(); + throw new IOException("Mesage is to long"); + } + + if (buf.limit() == 4) { + buf.limit((int) (4 + len)); + } + + // Message not yet fully read + if (buf.position() < len + 4) { + return null; + } + + buf.flip(); + buf.getInt(); // drops 4 bytes + HTSMessage msg = deserializeBinary(buf); + + buf.limit(4); + buf.position(0); + return msg; + } + + private static byte[] serializeBinary(String name, Object value) + throws IOException { + byte[] bName = name.getBytes(); + byte[] bData = new byte[0]; + byte type; + + if (value instanceof String) { + type = HTSMessage.HMF_STR; + bData = ((String) value).getBytes(); + } else if (value instanceof BigInteger) { + type = HTSMessage.HMF_S64; + bData = toByteArray((BigInteger) value); + } else if (value instanceof Integer) { + type = HTSMessage.HMF_S64; + bData = toByteArray(BigInteger.valueOf((Integer) value)); + } else if (value instanceof Long) { + type = HTSMessage.HMF_S64; + bData = toByteArray(BigInteger.valueOf((Long) value)); + } else if (value instanceof byte[]) { + type = HTSMessage.HMF_BIN; + bData = (byte[]) value; + } else if (value instanceof Map) { + type = HTSMessage.HMF_MAP; + bData = serializeBinary((Map) value); + } else if (value instanceof Collection) { + type = HTSMessage.HMF_LIST; + bData = serializeBinary((Collection) value); + } else if (value == null) { + throw new IOException("HTSP doesn't support null values"); + } else { + throw new IOException("Unhandled class for " + name + ": " + value + + " (" + value.getClass().getSimpleName() + ")"); + } + + byte[] buf = new byte[1 + 1 + 4 + bName.length + bData.length]; + buf[0] = type; + buf[1] = (byte) (bName.length & 0xFF); + buf[2] = (byte) ((bData.length >> 24) & 0xFF); + buf[3] = (byte) ((bData.length >> 16) & 0xFF); + buf[4] = (byte) ((bData.length >> 8) & 0xFF); + buf[5] = (byte) ((bData.length) & 0xFF); + + System.arraycopy(bName, 0, buf, 6, bName.length); + System.arraycopy(bData, 0, buf, 6 + bName.length, bData.length); + + return buf; + } + + private static byte[] serializeBinary(Collection list) + throws IOException { + ByteBuffer buf = ByteBuffer.allocate(Short.MAX_VALUE); + + for (Object value : list) { + byte[] sub = serializeBinary("", value); + buf.put(sub); + } + + byte[] bBuf = new byte[buf.position()]; + buf.flip(); + buf.get(bBuf); + + return bBuf; + } + + private static byte[] serializeBinary(Map map) throws IOException { + ByteBuffer buf = ByteBuffer.allocate(Short.MAX_VALUE); + + for (Object key : map.keySet()) { + Object value = map.get(key); + byte[] sub = serializeBinary(key.toString(), value); + buf.put(sub); + } + + byte[] bBuf = new byte[buf.position()]; + buf.flip(); + buf.get(bBuf); + + return bBuf; + } + + private static HTSMessage deserializeBinary(ByteBuffer buf) + throws IOException { + byte type, namelen; + long datalen; + + HTSMessage msg = new HTSMessage(); + int cnt = 0; + + while (buf.hasRemaining()) { + type = buf.get(); + namelen = buf.get(); + datalen = uIntToLong(buf.get(), buf.get(), buf.get(), buf.get()); + + if (datalen > Integer.MAX_VALUE) { + throw new IOException("Would get precision losses ;("); + } + if (buf.limit() < namelen + datalen) { + throw new IOException("Buffer limit exceeded"); + } + + // Get the key for the map (the name) + String name = null; + if (namelen == 0) { + name = Integer.toString(cnt++); + } else { + byte[] bName = new byte[namelen]; + buf.get(bName); + name = new String(bName); + } + + // Get the actual content + Object obj = null; + byte[] bData = new byte[(int) datalen]; // Should be long? + buf.get(bData); + + switch (type) { + case HTSMessage.HMF_STR: { + obj = new String(bData); + break; + } + case HMF_BIN: { + obj = bData; + break; + } + case HMF_S64: { + obj = toBigInteger(bData); + break; + } + case HMF_MAP: { + ByteBuffer sub = ByteBuffer.allocateDirect((int) datalen); + sub.put(bData); + sub.flip(); + obj = deserializeBinary(sub); + break; + } + case HMF_LIST: { + ByteBuffer sub = ByteBuffer.allocateDirect((int) datalen); + sub.put(bData); + sub.flip(); + obj = new ArrayList(deserializeBinary(sub).values()); + break; + } + default: + throw new IOException("Unknown data type"); + } + msg.putField(name, obj); + } + return msg; + } } diff --git a/src/org/tvheadend/tvhguide/htsp/HTSService.java b/src/org/tvheadend/tvhguide/htsp/HTSService.java index 5ca6cc1..5c4360f 100644 --- a/src/org/tvheadend/tvhguide/htsp/HTSService.java +++ b/src/org/tvheadend/tvhguide/htsp/HTSService.java @@ -765,7 +765,7 @@ private void cancelDvrEntry(long id) { public void handleResponse(HTSMessage response) { - boolean success = response.getInt("success", 0) == 1; + // boolean success = response.getInt("success", 0) == 1; } }); } @@ -778,7 +778,7 @@ private void deleteDvrEntry(long id) { public void handleResponse(HTSMessage response) { - boolean success = response.getInt("success", 0) == 1; + // boolean success = response.getInt("success", 0) == 1; } }); } @@ -801,7 +801,7 @@ public void handleResponse(HTSMessage response) { } } } - String error = response.getString("error", null); + // String error = response.getString("error", null); } }); } diff --git a/src/org/tvheadend/tvhguide/htsp/SelectionThread.java b/src/org/tvheadend/tvhguide/htsp/SelectionThread.java index 64bdb67..b301277 100644 --- a/src/org/tvheadend/tvhguide/htsp/SelectionThread.java +++ b/src/org/tvheadend/tvhguide/htsp/SelectionThread.java @@ -18,7 +18,6 @@ */ package org.tvheadend.tvhguide.htsp; -import android.util.Log; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; @@ -31,247 +30,251 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import android.util.Log; + /** - * + * * @author john-tornblom */ public abstract class SelectionThread extends Thread { - private static final String TAG = "SelectionThread"; - private Selector selector; - private volatile boolean running; - private HashMap regBuf; - private Lock lock; + private static final String TAG = "SelectionThread"; + private Selector selector; + private volatile boolean running; + private HashMap regBuf; + private Lock lock; - public SelectionThread() { - running = false; - lock = new ReentrantLock(); - regBuf = new HashMap(); - } + public SelectionThread() { + running = false; + lock = new ReentrantLock(); + regBuf = new HashMap(); + } - public void setRunning(boolean b) { - try { - lock.lock(); - running = false; - } finally { - lock.unlock(); - } - } + public void setRunning(boolean b) { + try { + lock.lock(); + running = false; + } finally { + lock.unlock(); + } + } - void close(AbstractSelectableChannel channel) throws IOException { - lock.lock(); - try { - regBuf.remove(channel); - channel.close(); - } finally { - lock.unlock(); - } - } + void close(AbstractSelectableChannel channel) throws IOException { + lock.lock(); + try { + regBuf.remove(channel); + channel.close(); + } finally { + lock.unlock(); + } + } - public void register(AbstractSelectableChannel channel, int ops, boolean b) { - lock.lock(); - try { - int oldOps = 0; - if (regBuf.containsKey(channel)) { - oldOps = regBuf.get(channel); - } - if (b) { - ops |= oldOps; - } else { - ops = oldOps & ~ops; - } - regBuf.put(channel, ops); - if (selector != null) { - selector.wakeup(); - } - } finally { - lock.unlock(); - } - } + public void register(AbstractSelectableChannel channel, int ops, boolean b) { + lock.lock(); + try { + int oldOps = 0; + if (regBuf.containsKey(channel)) { + oldOps = regBuf.get(channel); + } + if (b) { + ops |= oldOps; + } else { + ops = oldOps & ~ops; + } + regBuf.put(channel, ops); + if (selector != null) { + selector.wakeup(); + } + } finally { + lock.unlock(); + } + } - @Override - public void run() { - try { - lock.lock(); - selector = Selector.open(); - running = true; - } catch (IOException ex) { - running = false; - Log.e(TAG, "Can't open a selector", ex); - } finally { - lock.unlock(); - } + @Override + public void run() { + try { + lock.lock(); + selector = Selector.open(); + running = true; + } catch (IOException ex) { + running = false; + Log.e(TAG, "Can't open a selector", ex); + } finally { + lock.unlock(); + } - while (running) { - select(5000); - } + while (running) { + select(5000); + } - try { - lock.lock(); - //Clean up - for (SelectionKey key : selector.keys()) { - try { - key.channel().close(); - } catch (IOException ex) { - Log.e(TAG, "Can't close channel", ex); - key.cancel(); - } + try { + lock.lock(); + // Clean up + for (SelectionKey key : selector.keys()) { + try { + key.channel().close(); + } catch (IOException ex) { + Log.e(TAG, "Can't close channel", ex); + key.cancel(); + } - } - try { - selector.close(); - } catch (IOException ex) { - Log.e(TAG, "Can't close selector", ex); - } - } finally { - lock.unlock(); - } - } + } + try { + selector.close(); + } catch (IOException ex) { + Log.e(TAG, "Can't close selector", ex); + } + } finally { + lock.unlock(); + } + } - private void select(int timeout) { - try { - selector.select(timeout); - } catch (IOException ex) { - Log.e(TAG, "Can't select socket", ex); - return; - } + private void select(int timeout) { + try { + selector.select(timeout); + } catch (IOException ex) { + Log.e(TAG, "Can't select socket", ex); + return; + } - Iterator it = selector.selectedKeys().iterator(); + Iterator it = selector.selectedKeys().iterator(); - //Process the selected keys - while (it.hasNext()) { - SelectionKey selKey = (SelectionKey) it.next(); - it.remove(); - processTcpSelectionKey(selKey); - } + // Process the selected keys + while (it.hasNext()) { + SelectionKey selKey = (SelectionKey) it.next(); + it.remove(); + processTcpSelectionKey(selKey); + } - try { - lock.lock(); - ArrayList tmp = new ArrayList(); - for (AbstractSelectableChannel ch : regBuf.keySet()) { - try { - int ops = regBuf.get(ch); - ch.register(selector, ops); - } catch (Throwable t) { - tmp.add(ch); - Log.e(TAG, "Can't register channel", t); - if (ch instanceof SocketChannel) { - onError((SocketChannel) ch); - } - } - } - for (AbstractSelectableChannel ch : tmp) { - regBuf.remove(ch); - } - } finally { - lock.unlock(); - } - } + try { + lock.lock(); + ArrayList tmp = new ArrayList(); + for (AbstractSelectableChannel ch : regBuf.keySet()) { + try { + int ops = regBuf.get(ch); + ch.register(selector, ops); + } catch (Throwable t) { + tmp.add(ch); + Log.e(TAG, "Can't register channel", t); + if (ch instanceof SocketChannel) { + onError((SocketChannel) ch); + } + } + } + for (AbstractSelectableChannel ch : tmp) { + regBuf.remove(ch); + } + } finally { + lock.unlock(); + } + } - private void processTcpSelectionKey(SelectionKey selKey) { - //Incomming connection established - if (selKey.isValid() && selKey.isAcceptable()) { - try { - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - if (sChannel != null) { - sChannel.configureBlocking(false); - try { - onAccept(sChannel); - } catch (Throwable t) { - Log.e(TAG, "Can't establish connection", t); - onError(sChannel); - return; - } - } - } catch (Throwable t) { - Log.e(TAG, "Can't establish connection", t); - return; - } - } + private void processTcpSelectionKey(SelectionKey selKey) { + // Incomming connection established + if (selKey.isValid() && selKey.isAcceptable()) { + try { + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey + .channel(); + SocketChannel sChannel = ssChannel.accept(); + if (sChannel != null) { + sChannel.configureBlocking(false); + try { + onAccept(sChannel); + } catch (Throwable t) { + Log.e(TAG, "Can't establish connection", t); + onError(sChannel); + return; + } + } + } catch (Throwable t) { + Log.e(TAG, "Can't establish connection", t); + return; + } + } - //Outgoing connection established - if (selKey.isValid() && selKey.isConnectable()) { - try { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - if (!sChannel.finishConnect()) { - onError(sChannel); - selKey.cancel(); - return; - } - try { - onConnect(sChannel); - } catch (Throwable t) { - Log.e(TAG, "Can't establish connection", t); - onError(sChannel); - selKey.cancel(); - return; - } - } catch (Throwable t) { - Log.e(TAG, "Can't establish connection", t); - selKey.cancel(); - return; - } - } + // Outgoing connection established + if (selKey.isValid() && selKey.isConnectable()) { + try { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + if (!sChannel.finishConnect()) { + onError(sChannel); + selKey.cancel(); + return; + } + try { + onConnect(sChannel); + } catch (Throwable t) { + Log.e(TAG, "Can't establish connection", t); + onError(sChannel); + selKey.cancel(); + return; + } + } catch (Throwable t) { + Log.e(TAG, "Can't establish connection", t); + selKey.cancel(); + return; + } + } - //Incomming data - if (selKey.isValid() && selKey.isReadable()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - try { - onReadable(sChannel); - } catch (Throwable t) { - Log.e(TAG, "Can't read message", t); - onError(sChannel); - selKey.cancel(); - return; - } - } + // Incomming data + if (selKey.isValid() && selKey.isReadable()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + try { + onReadable(sChannel); + } catch (Throwable t) { + Log.e(TAG, "Can't read message", t); + onError(sChannel); + selKey.cancel(); + return; + } + } - //Clear to send - if (selKey.isValid() && selKey.isWritable()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - try { - onWrtiable(sChannel); - } catch (Throwable t) { - Log.e(TAG, "Can't send message", t); - onError(sChannel); - selKey.cancel(); - return; - } - } - } + // Clear to send + if (selKey.isValid() && selKey.isWritable()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + try { + onWrtiable(sChannel); + } catch (Throwable t) { + Log.e(TAG, "Can't send message", t); + onError(sChannel); + selKey.cancel(); + return; + } + } + } - private void onAccept(SocketChannel ch) throws Exception { - onEvent(SelectionKey.OP_ACCEPT, ch); - } + private void onAccept(SocketChannel ch) throws Exception { + onEvent(SelectionKey.OP_ACCEPT, ch); + } - private void onConnect(SocketChannel ch) throws Exception { - onEvent(SelectionKey.OP_CONNECT, ch); - } + private void onConnect(SocketChannel ch) throws Exception { + onEvent(SelectionKey.OP_CONNECT, ch); + } - private void onReadable(SocketChannel ch) throws Exception { - onEvent(SelectionKey.OP_READ, ch); - } + private void onReadable(SocketChannel ch) throws Exception { + onEvent(SelectionKey.OP_READ, ch); + } - private void onError(SocketChannel ch) { - try { - lock.lock(); - ch.close(); - regBuf.remove(ch); - } catch (Exception ex) { - Log.e(TAG, null, ex); - } finally { - lock.unlock(); - } - try { - onEvent(-1, ch); - } catch (Exception ex) { - } - } + private void onError(SocketChannel ch) { + try { + lock.lock(); + ch.close(); + regBuf.remove(ch); + } catch (Exception ex) { + Log.e(TAG, null, ex); + } finally { + lock.unlock(); + } + try { + onEvent(-1, ch); + } catch (Exception ex) { + } + } - private void onWrtiable(SocketChannel ch) throws Exception { - onEvent(SelectionKey.OP_WRITE, ch); - } + private void onWrtiable(SocketChannel ch) throws Exception { + onEvent(SelectionKey.OP_WRITE, ch); + } - public abstract void onEvent(int selectionKey, SocketChannel ch) throws Exception; + public abstract void onEvent(int selectionKey, SocketChannel ch) + throws Exception; } diff --git a/src/org/tvheadend/tvhguide/intent/SearchIMDbIntent.java b/src/org/tvheadend/tvhguide/intent/SearchIMDbIntent.java index 63ebdb8..4ef810c 100644 --- a/src/org/tvheadend/tvhguide/intent/SearchIMDbIntent.java +++ b/src/org/tvheadend/tvhguide/intent/SearchIMDbIntent.java @@ -4,26 +4,29 @@ */ package org.tvheadend.tvhguide.intent; +import java.net.URLEncoder; + import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; -import java.net.URLEncoder; /** - * + * * @author john-tornblom */ public class SearchIMDbIntent extends Intent { - public SearchIMDbIntent(Context ctx, String query) { - super(Intent.ACTION_VIEW); + public SearchIMDbIntent(Context ctx, String query) { + super(Intent.ACTION_VIEW); - setData(Uri.parse("imdb:///find?s=tt&q=" + URLEncoder.encode(query))); + setData(Uri.parse("imdb:///find?s=tt&q=" + URLEncoder.encode(query))); - PackageManager packageManager = ctx.getPackageManager(); - if (packageManager.queryIntentActivities(this, PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { - setData(Uri.parse("http://akas.imdb.org/find?s=tt&q=" + URLEncoder.encode(query))); - } - } + PackageManager packageManager = ctx.getPackageManager(); + if (packageManager.queryIntentActivities(this, + PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { + setData(Uri.parse("http://akas.imdb.org/find?s=tt&q=" + + URLEncoder.encode(query))); + } + } } diff --git a/src/org/tvheadend/tvhguide/model/SeriesInfo.java b/src/org/tvheadend/tvhguide/model/SeriesInfo.java index 73277db..c587c80 100644 --- a/src/org/tvheadend/tvhguide/model/SeriesInfo.java +++ b/src/org/tvheadend/tvhguide/model/SeriesInfo.java @@ -1,5 +1,7 @@ package org.tvheadend.tvhguide.model; +import java.util.Locale; + public class SeriesInfo { public int seasonNumber; public int seasonCount; @@ -34,10 +36,11 @@ public String toString() { s += String.format("part %d", partNumber); } - if(s.length() > 0) { - s = s.substring(0,1).toUpperCase() + s.substring(1); + if (s.length() > 0) { + s = s.substring(0, 1).toUpperCase(Locale.getDefault()) + + s.substring(1); } - + return s; } } diff --git a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java index 187139c..777a37d 100644 --- a/src/org/tvheadend/tvhguide/ui/HorizontalListView.java +++ b/src/org/tvheadend/tvhguide/ui/HorizontalListView.java @@ -34,11 +34,17 @@ public class HorizontalListView extends AdapterView { private OnItemClickListener mOnItemClicked; private OnItemLongClickListener mOnItemLongClicked; private boolean mDataChanged = false; - private Rect mTouchFrame; private int selectedIndex; + private Runnable mRequestLayout; public HorizontalListView(Context context, AttributeSet attrs) { super(context, attrs); + mRequestLayout = new Runnable() { + @Override + public void run() { + requestLayout(); + } + }; initView(); } @@ -141,8 +147,8 @@ public void setSelection(int position) { private void addAndMeasureChild(final View child, int viewPos) { LayoutParams params = child.getLayoutParams(); if (params == null) { - params = new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); + params = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); } addViewInLayout(child, viewPos, params, true); @@ -191,12 +197,7 @@ protected synchronized void onLayout(boolean changed, int left, int top, mCurrentX = mNextX; if (!mScroller.isFinished()) { - post(new Runnable() { - @Override - public void run() { - requestLayout(); - } - }); + post(mRequestLayout); } }