summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-04-01 22:53:18 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2014-04-01 22:53:18 +0200
commitc37b67172e929289ffb8fe5e901661f4456abaae (patch)
tree0825759715295085832c52c798fd345e55229e35 /src
parent9abf27ca2f32240206d1dae3c55d2046372abf3f (diff)
downloadAntennaPod-c37b67172e929289ffb8fe5e901661f4456abaae.zip
Added navigation drawer + new episodes fragment
Diffstat (limited to 'src')
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java665
-rw-r--r--src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java225
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadObserver.java32
-rw-r--r--src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java265
-rw-r--r--src/de/danoeh/antennapod/storage/DBReader.java26
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java5
6 files changed, 979 insertions, 239 deletions
diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java
index 29e36abc8..03f44a97d 100644
--- a/src/de/danoeh/antennapod/activity/MainActivity.java
+++ b/src/de/danoeh/antennapod/activity/MainActivity.java
@@ -4,183 +4,229 @@ import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
import android.media.AudioManager;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.MenuItemCompat;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
+import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.Window;
+import android.view.*;
+import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
-import de.danoeh.antennapod.fragment.FeedlistFragment;
+import de.danoeh.antennapod.fragment.ItemlistFragment;
+import de.danoeh.antennapod.fragment.NewEpisodesFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
-import de.danoeh.antennapod.service.download.DownloadService;
-import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.storage.DBReader;
-import de.danoeh.antennapod.storage.DBTasks;
-import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.StorageUtils;
+import de.danoeh.antennapod.util.ThemeUtils;
-import java.util.ArrayList;
+import java.util.List;
-/** The activity that is shown when the user launches the app. */
+/**
+ * The activity that is shown when the user launches the app.
+ */
public class MainActivity extends ActionBarActivity {
- private static final String TAG = "MainActivity";
+ private static final String TAG = "MainActivity";
+ private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
+ | EventDistributor.DOWNLOAD_QUEUED
+ | EventDistributor.FEED_LIST_UPDATE
+ | EventDistributor.UNREAD_ITEMS_UPDATE;
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
- | EventDistributor.DOWNLOAD_QUEUED;
+ private ExternalPlayerFragment externalPlayerFragment;
+ private DrawerLayout drawerLayout;
- private ViewPager viewpager;
- private TabsAdapter pagerAdapter;
- private ExternalPlayerFragment externalPlayerFragment;
+ private ListView navList;
+ private NavListAdapter navAdapter;
- private static boolean appLaunched = false;
+ private ActionBarDrawerToggle drawerToogle;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- StorageUtils.checkStorageAvailability(this);
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.main);
+ private CharSequence drawerTitle;
+ private CharSequence currentTitle;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ StorageUtils.checkStorageAvailability(this);
+ setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
- getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
-
- viewpager = (ViewPager) findViewById(R.id.viewpager);
- pagerAdapter = new TabsAdapter(this, viewpager);
-
- viewpager.setAdapter(pagerAdapter);
-
- ActionBar.Tab feedsTab = getSupportActionBar().newTab();
- feedsTab.setText(R.string.podcasts_label);
- ActionBar.Tab episodesTab = getSupportActionBar().newTab();
- episodesTab.setText(R.string.episodes_label);
-
- pagerAdapter.addTab(feedsTab, FeedlistFragment.class, null);
- pagerAdapter.addTab(episodesTab, EpisodesFragment.class, null);
-
- FragmentTransaction transaction = getSupportFragmentManager()
- .beginTransaction();
- externalPlayerFragment = new ExternalPlayerFragment();
- transaction.replace(R.id.playerFragment, externalPlayerFragment);
- transaction.commit();
-
- // executed on application start
- if (!appLaunched && getIntent().getAction() != null
- && getIntent().getAction().equals(Intent.ACTION_MAIN)) {
- appLaunched = true;
- if (DBReader.getNumberOfUnreadItems(this) > 0) {
- // select 'episodes' tab
- getSupportActionBar().setSelectedNavigationItem(1);
- }
- }
- if (savedInstanceState != null) {
- getSupportActionBar().setSelectedNavigationItem(
- savedInstanceState.getInt("tab", 0));
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt("tab", getSupportActionBar()
- .getSelectedNavigationIndex());
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- EventDistributor.getInstance().unregister(contentUpdate);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- StorageUtils.checkStorageAvailability(this);
- updateProgressBarVisibility();
- EventDistributor.getInstance().register(contentUpdate);
-
- }
-
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received contentUpdate Intent.");
- updateProgressBarVisibility();
- }
- }
- };
-
- private void updateProgressBarVisibility() {
- if (DownloadService.isRunning
- && DownloadRequester.getInstance().isDownloadingFeeds()) {
- setSupportProgressBarIndeterminateVisibility(true);
- } else {
- setSupportProgressBarIndeterminateVisibility(false);
- }
- supportInvalidateOptionsMenu();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.add_feed:
- startActivity(new Intent(this, AddFeedActivity.class));
- return true;
- case R.id.all_feed_refresh:
- DBTasks.refreshAllFeeds(this, null);
- return true;
- case R.id.show_downloads:
- startActivity(new Intent(this, DownloadActivity.class));
- return true;
- case R.id.show_preferences:
- startActivity(new Intent(this, PreferenceActivity.class));
- return true;
- case R.id.show_player:
- startActivity(PlaybackService.getPlayerActivityIntent(this));
- return true;
- case R.id.show_playback_history:
- startActivity(new Intent(this, PlaybackHistoryActivity.class));
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
+ drawerTitle = currentTitle = getTitle();
+
+ drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ navList = (ListView) findViewById(R.id.nav_list);
+
+ TypedArray typedArray = obtainStyledAttributes(new int[]{R.attr.nav_drawer_toggle});
+ drawerToogle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) {
+ @Override
+ public void onDrawerOpened(View drawerView) {
+ super.onDrawerOpened(drawerView);
+ getSupportActionBar().setTitle(drawerTitle);
+ supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDrawerClosed(View drawerView) {
+ super.onDrawerClosed(drawerView);
+ getSupportActionBar().setTitle(currentTitle);
+ supportInvalidateOptionsMenu();
+
+ }
+ };
+ typedArray.recycle();
+
+ drawerLayout.setDrawerListener(drawerToogle);
+ FragmentManager fm = getSupportFragmentManager();
+ FragmentTransaction transaction = fm.beginTransaction();
+ externalPlayerFragment = new ExternalPlayerFragment();
+ transaction.replace(R.id.playerFragment, externalPlayerFragment);
+
+
+
+ transaction.commit();
+
+ Fragment mainFragment = fm.findFragmentByTag("main");
+ if (mainFragment != null) {
+ transaction = fm.beginTransaction();
+ transaction.replace(R.id.main_view, mainFragment);
+ transaction.commit();
+ } else {
+ loadFragment(NavListAdapter.VIEW_TYPE_NAV, 0);
+ }
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ navAdapter = new NavListAdapter(itemAccess, this);
+ navList.setAdapter(navAdapter);
+ navList.setOnItemClickListener(navListClickListener);
+
+ loadData();
+
+ }
+
+ private void loadFragment(int viewType, int relPos) {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ FragmentTransaction fT = fragmentManager.beginTransaction();
+ Fragment fragment = null;
+ if (viewType == NavListAdapter.VIEW_TYPE_NAV) {
+ currentTitle = getString(NavListAdapter.NAV_TITLES[relPos]);
+ fragment = new NewEpisodesFragment();
+
+ } else if (viewType == NavListAdapter.VIEW_TYPE_SUBSCRIPTION) {
+ Feed feed = itemAccess.getItem(relPos);
+ currentTitle = feed.getTitle();
+ fragment = ItemlistFragment.newInstance(feed.getId());
+
+ }
+ if (fragment != null) {
+ fT.replace(R.id.main_view, fragment, "main");
+ }
+ fT.commit();
+ }
+
+ private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ int viewType = parent.getAdapter().getItemViewType(position);
+ if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
+ int relPos = (viewType == NavListAdapter.VIEW_TYPE_NAV) ? position : position - NavListAdapter.SUBSCRIPTION_OFFSET;
+ loadFragment(viewType, relPos);
+ drawerLayout.closeDrawer(navList);
+ selectedNavListIndex = position;
+ navAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ drawerToogle.syncState();
+ if (savedInstanceState != null) {
+ currentTitle = savedInstanceState.getString("title");
+ if (!drawerLayout.isDrawerOpen(navList)) {
+ getSupportActionBar().setTitle(currentTitle);
+ }
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ drawerToogle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString("title", currentTitle.toString());
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ StorageUtils.checkStorageAvailability(this);
+ EventDistributor.getInstance().register(contentUpdate);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ cancelLoadTask();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (drawerToogle.onOptionsItemSelected(item)) {
+ return true;
+ }
+ switch (item.getItemId()) {
+ case R.id.show_preferences:
+ startActivity(new Intent(this, PreferenceActivity.class));
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- MenuItem refreshAll = menu.findItem(R.id.all_feed_refresh);
- if (DownloadService.isRunning
- && DownloadRequester.getInstance().isDownloadingFeeds()) {
- refreshAll.setVisible(false);
- } else {
- refreshAll.setVisible(true);
- }
- return true;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ boolean drawerOpen = drawerLayout.isDrawerOpen(navList);
+ menu.findItem(R.id.search_item).setVisible(!drawerOpen);
+
+ return true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.main, menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main, menu);
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
@@ -196,89 +242,242 @@ public class MainActivity extends ActionBarActivity {
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
-
return true;
- }
-
- public static class TabsAdapter extends FragmentPagerAdapter implements
- ActionBar.TabListener, ViewPager.OnPageChangeListener {
- private final Context mContext;
- private final ActionBar mActionBar;
- private final ViewPager mViewPager;
- private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
-
- static final class TabInfo {
- private final Class<?> clss;
- private final Bundle args;
-
- TabInfo(Class<?> _class, Bundle _args) {
- clss = _class;
- args = _args;
- }
- }
-
- public TabsAdapter(MainActivity activity, ViewPager pager) {
- super(activity.getSupportFragmentManager());
- mContext = activity;
- mActionBar = activity.getSupportActionBar();
- mViewPager = pager;
- mViewPager.setAdapter(this);
- mViewPager.setOnPageChangeListener(this);
- }
-
- public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
- TabInfo info = new TabInfo(clss, args);
- tab.setTag(info);
- tab.setTabListener(this);
- mTabs.add(info);
- mActionBar.addTab(tab);
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return mTabs.size();
- }
-
- @Override
- public Fragment getItem(int position) {
- TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.clss.getName(),
- info.args);
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset,
- int positionOffsetPixels) {
- }
-
- @Override
- public void onPageSelected(int position) {
- mActionBar.setSelectedNavigationItem(position);
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
-
- @Override
- public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
- Object tag = tab.getTag();
- for (int i = 0; i < mTabs.size(); i++) {
- if (mTabs.get(i) == tag) {
- mViewPager.setCurrentItem(i);
- }
- }
- }
-
- @Override
- public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
-
- }
-
- @Override
- public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
- }
- }
+ }
+
+ private List<Feed> feeds;
+ private AsyncTask<Void, Void, List<Feed>> loadTask;
+ private int selectedNavListIndex = 0;
+
+ private ItemAccess itemAccess = new ItemAccess() {
+ @Override
+ public int getCount() {
+ if (feeds != null) {
+ return feeds.size();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public Feed getItem(int position) {
+ if (feeds != null && position < feeds.size()) {
+ return feeds.get(position);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public int getSelectedItemIndex() {
+ return selectedNavListIndex;
+ }
+
+
+ };
+
+ private void loadData() {
+ loadTask = new AsyncTask<Void, Void, List<Feed>>() {
+ @Override
+ protected List<Feed> doInBackground(Void... params) {
+ return DBReader.getFeedList(MainActivity.this);
+ }
+
+ @Override
+ protected void onPostExecute(List<Feed> result) {
+ super.onPostExecute(result);
+ feeds = result;
+ navAdapter.notifyDataSetChanged();
+ }
+ };
+ loadTask.execute();
+ }
+
+ private void cancelLoadTask() {
+ if (loadTask != null) {
+ loadTask.cancel(true);
+ }
+ }
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((EVENTS & arg) != 0) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Received contentUpdate Intent.");
+ loadData();
+ }
+ }
+ };
+
+ private static class NavListAdapter extends BaseAdapter {
+
+ static final int VIEW_TYPE_COUNT = 3;
+ static final int VIEW_TYPE_NAV = 0;
+ static final int VIEW_TYPE_SECTION_DIVIDER = 1;
+ static final int VIEW_TYPE_SUBSCRIPTION = 2;
+
+ static final int[] NAV_TITLES = {R.string.new_episodes_label, R.string.queue_label, R.string.downloads_label, R.string.playback_history_label, R.string.add_feed_label};
+
+
+ static final int SUBSCRIPTION_OFFSET = 1 + NAV_TITLES.length;
+
+ private ItemAccess itemAccess;
+ private Context context;
+
+ private NavListAdapter(ItemAccess itemAccess, Context context) {
+ this.itemAccess = itemAccess;
+ this.context = context;
+ }
+
+ @Override
+ public int getCount() {
+ return NAV_TITLES.length + 1 + itemAccess.getCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ int viewType = getItemViewType(position);
+ if (viewType == VIEW_TYPE_NAV) {
+ return context.getString(NAV_TITLES[position]);
+ } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
+ return context.getString(R.string.podcasts_label);
+ } else {
+ return itemAccess.getItem(position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (0 <= position && position < NAV_TITLES.length) {
+ return VIEW_TYPE_NAV;
+ } else if (position < NAV_TITLES.length + 1) {
+ return VIEW_TYPE_SECTION_DIVIDER;
+ } else {
+ return VIEW_TYPE_SUBSCRIPTION;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return VIEW_TYPE_COUNT;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int viewType = getItemViewType(position);
+ View v = null;
+ if (viewType == VIEW_TYPE_NAV) {
+ v = getNavView((String) getItem(position), position, convertView, parent);
+ } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
+ v = getSectionDividerView((String) getItem(position), position, convertView, parent);
+ } else {
+ v = getFeedView(position - SUBSCRIPTION_OFFSET, convertView, parent);
+ }
+ if (v != null) {
+ TextView txtvTitle = (TextView) v.findViewById(R.id.txtvTitle);
+ if (position == itemAccess.getSelectedItemIndex()) {
+ txtvTitle.setTypeface(null, Typeface.BOLD);
+ } else {
+ txtvTitle.setTypeface(null, Typeface.NORMAL);
+ }
+ }
+ return v;
+ }
+
+ private View getNavView(String title, int position, View convertView, ViewGroup parent) {
+ NavHolder holder;
+ if (convertView == null) {
+ holder = new NavHolder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ convertView = inflater.inflate(R.layout.nav_listitem, null);
+
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ convertView.setTag(holder);
+ } else {
+ holder = (NavHolder) convertView.getTag();
+ }
+
+ holder.title.setText(title);
+
+ return convertView;
+ }
+
+ private View getSectionDividerView(String title, int position, View convertView, ViewGroup parent) {
+ SectionHolder holder;
+ if (convertView == null) {
+ holder = new SectionHolder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ convertView = inflater.inflate(R.layout.nav_section_item, null);
+
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ convertView.setTag(holder);
+ } else {
+ holder = (SectionHolder) convertView.getTag();
+ }
+
+ holder.title.setText(title);
+
+ convertView.setEnabled(false);
+ convertView.setOnClickListener(null);
+
+ return convertView;
+ }
+
+ private View getFeedView(int feedPos, View convertView, ViewGroup parent) {
+ FeedHolder holder;
+ Feed feed = itemAccess.getItem(feedPos);
+
+ if (convertView == null) {
+ holder = new FeedHolder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ convertView = inflater.inflate(R.layout.nav_feedlistitem, null);
+
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
+ convertView.setTag(holder);
+ } else {
+ holder = (FeedHolder) convertView.getTag();
+ }
+
+ holder.title.setText(feed.getTitle());
+ ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), holder.image, (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist));
+
+ return convertView;
+ }
+
+ static class NavHolder {
+ TextView title;
+ }
+
+ static class SectionHolder {
+ TextView title;
+ }
+
+ static class FeedHolder {
+ TextView title;
+ ImageView image;
+ }
+ }
+
+ public interface ItemAccess {
+ int getCount();
+
+ Feed getItem(int position);
+
+ int getSelectedItemIndex();
+ }
}
diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
new file mode 100644
index 000000000..105d4c3ca
--- /dev/null
+++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
@@ -0,0 +1,225 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.storage.DownloadRequester;
+import de.danoeh.antennapod.util.Converter;
+
+/**
+ * List adapter for the list of new episodes
+ */
+public class NewEpisodesListAdapter extends BaseAdapter {
+
+ private static final int VIEW_TYPE_FEEDITEM = 0;
+ private static final int VIEW_TYPE_DIVIDER = 1;
+
+ private final Context context;
+ private final ItemAccess itemAccess;
+ private final TypedArray drawables;
+ private final int[] labels;
+
+ public NewEpisodesListAdapter(Context context, ItemAccess itemAccess) {
+ super();
+ this.context = context;
+ this.itemAccess = itemAccess;
+ drawables = context.obtainStyledAttributes(new int[]{
+ R.attr.navigation_accept, R.attr.navigation_refresh, R.attr.av_download});
+ labels = new int[]{R.string.status_downloaded_label, R.string.status_downloading_label, R.string.status_not_downloaded_label};
+ }
+
+ @Override
+ public int getCount() {
+ int unreadItems = itemAccess.getUnreadItemsCount();
+ int recentItems = itemAccess.getRecentItemsCount();
+ return unreadItems + recentItems + 1;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ int unreadItems = itemAccess.getUnreadItemsCount();
+
+ if (position == unreadItems) {
+ return null;
+ }
+ if (position < unreadItems && unreadItems > 0) {
+ return itemAccess.getUnreadItem(position);
+ }
+ return itemAccess.getRecentItem(position - unreadItems - 1);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int unreadItems = itemAccess.getUnreadItemsCount();
+ if (position == unreadItems) {
+ return VIEW_TYPE_DIVIDER;
+ } else {
+ return VIEW_TYPE_FEEDITEM;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int viewType = getItemViewType(position);
+ if (viewType == VIEW_TYPE_FEEDITEM) {
+ return getFeedItemView(position, convertView, parent);
+ } else {
+ return getDividerView(position, convertView, parent);
+ }
+ }
+
+ public View getDividerView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.new_episodes_listdivider,
+ null);
+ convertView.setOnClickListener(null);
+ convertView.setEnabled(false);
+ return convertView;
+ }
+
+ public View getFeedItemView(int position, View convertView, ViewGroup parent) {
+ Holder holder;
+ final FeedItem item = (FeedItem) getItem(position);
+ if (item == null) return null;
+
+ if (convertView == null) {
+ holder = new Holder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.new_episodes_listitem,
+ null);
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.pubDate = (TextView) convertView
+ .findViewById(R.id.txtvPublished);
+ holder.downloadStatus = (ImageView) convertView
+ .findViewById(R.id.imgvDownloadStatus);
+ holder.queueStatus = (ImageView) convertView
+ .findViewById(R.id.imgvInPlaylist);
+ holder.statusPlaying = (ImageView) convertView
+ .findViewById(R.id.statusPlaying);
+ holder.downloadProgress = (ProgressBar) convertView
+ .findViewById(R.id.pbar_download_progress);
+ holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.title.setText(item.getTitle());
+ holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE));
+ FeedItem.State state = item.getState();
+
+ if (state == FeedItem.State.PLAYING) {
+ holder.statusPlaying.setVisibility(View.VISIBLE);
+ } else {
+ holder.statusPlaying.setVisibility(View.INVISIBLE);
+ }
+
+ FeedMedia media = item.getMedia();
+ if (media != null) {
+ final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
+
+ if (media.getDuration() > 0) {
+ holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ } else {
+ holder.txtvDuration.setText("");
+ }
+
+ if (isDownloadingMedia) {
+ holder.downloadProgress.setVisibility(View.VISIBLE);
+ holder.txtvDuration.setVisibility(View.GONE);
+ } else {
+ holder.txtvDuration.setVisibility(View.VISIBLE);
+ holder.downloadProgress.setVisibility(View.GONE);
+ }
+
+ if (!media.isDownloaded()) {
+ if (isDownloadingMedia) {
+ // item is being downloaded
+ holder.downloadStatus.setVisibility(View.VISIBLE);
+ holder.downloadStatus.setImageDrawable(drawables
+ .getDrawable(1));
+ holder.downloadStatus.setContentDescription(context.getString(labels[1]));
+
+ holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
+ } else {
+ // item is not downloaded and not being downloaded
+ holder.downloadStatus.setVisibility(View.VISIBLE);
+ holder.downloadStatus.setImageDrawable(drawables.getDrawable(2));
+ holder.downloadStatus.setContentDescription(context.getString(labels[2]));
+ }
+ } else {
+ // item is not being downloaded
+ holder.downloadStatus.setVisibility(View.VISIBLE);
+ holder.downloadStatus
+ .setImageDrawable(drawables.getDrawable(0));
+ holder.downloadStatus.setContentDescription(context.getString(labels[0]));
+ }
+ } else {
+ holder.downloadStatus.setVisibility(View.INVISIBLE);
+ }
+
+ if (itemAccess.isInQueue(item)) {
+ holder.queueStatus.setVisibility(View.VISIBLE);
+ } else {
+ holder.queueStatus.setVisibility(View.INVISIBLE);
+ }
+
+ ImageLoader.getInstance().loadThumbnailBitmap(
+ item,
+ holder.imageView,
+ (int) convertView.getResources().getDimension(
+ R.dimen.thumbnail_length)
+ );
+ return convertView;
+ }
+
+
+ static class Holder {
+ TextView title;
+ TextView pubDate;
+ ImageView downloadStatus;
+ ImageView queueStatus;
+ ImageView imageView;
+ ImageView statusPlaying;
+ ProgressBar downloadProgress;
+ TextView txtvDuration;
+ }
+
+ public interface ItemAccess {
+ int getUnreadItemsCount();
+
+ int getRecentItemsCount();
+
+ FeedItem getUnreadItem(int position);
+
+ FeedItem getRecentItem(int position);
+
+ int getItemDownloadProgressPercent(FeedItem item);
+
+ boolean isInQueue(FeedItem item);
+ }
+}
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
index 40388cde5..adcac1569 100644
--- a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
+++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
@@ -25,7 +25,7 @@ public class DownloadObserver {
*/
public static final int WAITING_INTERVAL_MS = 1000;
- private final Activity activity;
+ private volatile Activity activity;
private final Handler handler;
private final Callback callback;
@@ -57,12 +57,16 @@ public class DownloadObserver {
public void onResume() {
if (BuildConfig.DEBUG) Log.d(TAG, "DownloadObserver resumed");
activity.registerReceiver(contentChangedReceiver, new IntentFilter(DownloadService.ACTION_DOWNLOADS_CONTENT_CHANGED));
- activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0);
+ connectToDownloadService();
}
public void onPause() {
if (BuildConfig.DEBUG) Log.d(TAG, "DownloadObserver paused");
- activity.unregisterReceiver(contentChangedReceiver);
+ try {
+ activity.unregisterReceiver(contentChangedReceiver);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
activity.unbindService(mConnection);
stopRefresher();
}
@@ -70,6 +74,10 @@ public class DownloadObserver {
private BroadcastReceiver contentChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // reconnect to DownloadService if connection has been closed
+ if (downloadService == null) {
+ connectToDownloadService();
+ }
callback.onContentChanged();
startRefresher();
}
@@ -81,6 +89,10 @@ public class DownloadObserver {
void onDownloadDataAvailable(List<Downloader> downloaderList);
}
+ private void connectToDownloadService() {
+ activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0);
+ }
+
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName className) {
downloadService = null;
@@ -138,13 +150,21 @@ public class DownloadObserver {
@Override
public void run() {
callback.onContentChanged();
- List<Downloader> downloaderList = downloadService.getDownloads();
- if (downloaderList == null || downloaderList.isEmpty()) {
- Thread.currentThread().interrupt();
+ if (downloadService != null) {
+ List<Downloader> downloaderList = downloadService.getDownloads();
+ if (downloaderList == null || downloaderList.isEmpty()) {
+ Thread.currentThread().interrupt();
+ }
}
}
});
}
}
+ public void setActivity(Activity activity) {
+ if (activity == null) throw new IllegalArgumentException("activity = null");
+ this.activity = activity;
+ }
+
}
+
diff --git a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
new file mode 100644
index 000000000..8250e20d3
--- /dev/null
+++ b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -0,0 +1,265 @@
+package de.danoeh.antennapod.fragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import com.mobeta.android.dslv.DragSortListView;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.NewEpisodesListAdapter;
+import de.danoeh.antennapod.asynctask.DownloadObserver;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.service.download.Downloader;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.QueueAccess;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Shows unread or recently published episodes
+ */
+public class NewEpisodesFragment extends Fragment {
+ private static final String TAG = "NewEpisodesFragment";
+ private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
+ EventDistributor.DOWNLOAD_QUEUED |
+ EventDistributor.QUEUE_UPDATE |
+ EventDistributor.UNREAD_ITEMS_UPDATE;
+
+ private static final int RECENT_EPISODES_LIMIT = 150;
+
+ private DragSortListView listView;
+ private NewEpisodesListAdapter listAdapter;
+ private TextView txtvEmpty;
+ private ProgressBar progLoading;
+
+ private List<FeedItem> unreadItems;
+ private List<FeedItem> recentItems;
+ private QueueAccess queueAccess;
+ private List<Downloader> downloaderList;
+
+ private boolean itemsLoaded = false;
+ private boolean viewsCreated = false;
+
+ private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
+
+ private DownloadObserver downloadObserver = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ startItemLoader();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventDistributor.getInstance().register(contentUpdate);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ stopItemLoader();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ this.activity.set((MainActivity) activity);
+ if (downloadObserver != null) {
+ downloadObserver.setActivity(activity);
+ downloadObserver.onResume();
+ }
+ if (viewsCreated && itemsLoaded) {
+ onFragmentLoaded();
+ }
+
+
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ listAdapter = null;
+ activity.set(null);
+ viewsCreated = false;
+ if (downloadObserver != null) {
+ downloadObserver.onPause();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.new_episodes_fragment, container, false);
+ listView = (DragSortListView) root.findViewById(android.R.id.list);
+ txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+
+
+ viewsCreated = true;
+
+ if (itemsLoaded && activity.get() != null) {
+ onFragmentLoaded();
+ }
+
+ return root;
+ }
+
+ private void onFragmentLoaded() {
+ if (listAdapter == null) {
+ listAdapter = new NewEpisodesListAdapter(activity.get(), itemAccess);
+ listView.setAdapter(listAdapter);
+ listView.setEmptyView(txtvEmpty);
+ downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
+ downloadObserver.onResume();
+ }
+ listAdapter.notifyDataSetChanged();
+ }
+
+ private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
+ @Override
+ public void onContentChanged() {
+ if (listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ NewEpisodesFragment.this.downloaderList = downloaderList;
+ if (listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ private NewEpisodesListAdapter.ItemAccess itemAccess = new NewEpisodesListAdapter.ItemAccess() {
+
+
+ @Override
+ public int getUnreadItemsCount() {
+ return (itemsLoaded) ? unreadItems.size() : 0;
+ }
+
+ @Override
+ public int getRecentItemsCount() {
+ return (itemsLoaded) ? recentItems.size() : 0;
+ }
+
+ @Override
+ public FeedItem getUnreadItem(int position) {
+ return (itemsLoaded) ? unreadItems.get(position) : null;
+ }
+
+ @Override
+ public FeedItem getRecentItem(int position) {
+ return (itemsLoaded) ? recentItems.get(position) : null;
+ }
+
+ @Override
+ public int getItemDownloadProgressPercent(FeedItem item) {
+ if (downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ return downloader.getDownloadRequest().getProgressPercent();
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean isInQueue(FeedItem item) {
+ if (itemsLoaded) {
+ return queueAccess.contains(item.getId());
+ } else {
+ return false;
+ }
+ }
+
+
+ };
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ startItemLoader();
+ }
+ }
+ };
+
+ private ItemLoader itemLoader;
+
+ private void startItemLoader() {
+ if (itemLoader != null) {
+ itemLoader.cancel(true);
+ }
+ itemLoader = new ItemLoader();
+ itemLoader.execute();
+ }
+
+ private void stopItemLoader() {
+ if (itemLoader != null) {
+ itemLoader.cancel(true);
+ }
+ }
+
+ private class ItemLoader extends AsyncTask<Void, Void, Object[]> {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ if (viewsCreated && !itemsLoaded) {
+ listView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ protected Object[] doInBackground(Void... params) {
+ Context context = activity.get();
+ if (context != null) {
+ return new Object[]{DBReader.getUnreadItemsList(context),
+ DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT),
+ QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Object[] lists) {
+ super.onPostExecute(lists);
+ listView.setVisibility(View.VISIBLE);
+ progLoading.setVisibility(View.GONE);
+
+ if (lists != null) {
+ unreadItems = (List<FeedItem>) lists[0];
+ recentItems = (List<FeedItem>) lists[1];
+ queueAccess = (QueueAccess) lists[2];
+ itemsLoaded = true;
+ if (viewsCreated && activity.get() != null) {
+ onFragmentLoaded();
+ }
+ }
+ }
+ }
+}
diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java
index 8d4785bd4..859ff2473 100644
--- a/src/de/danoeh/antennapod/storage/DBReader.java
+++ b/src/de/danoeh/antennapod/storage/DBReader.java
@@ -504,6 +504,32 @@ public final class DBReader {
return itemIds;
}
+
+ /**
+ * Loads a list of FeedItems sorted by pubDate in descending order.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @param limit The maximum number of episodes that should be loaded.
+ * */
+ public static List<FeedItem> getRecentlyPublishedEpisodes(Context context, int limit) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Extracting recently published items list");
+
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+
+ Cursor itemlistCursor = adapter.getRecentlyPublishedItemsCursor(limit);
+ List<FeedItem> items = extractItemlistFromCursor(adapter,
+ itemlistCursor);
+ itemlistCursor.close();
+
+ loadFeedDataOfFeedItemlist(context, items);
+
+ adapter.close();
+
+ return items;
+ }
+
/**
* Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode
* has been completed at least once.
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 825b5ac30..f5ee7a83f 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -996,6 +996,11 @@ public class PodDBAdapter {
}
+ public final Cursor getRecentlyPublishedItemsCursor(int limit) {
+ Cursor c = db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, null, null, null, null, KEY_PUBDATE + " DESC LIMIT " + limit);
+ return c;
+ }
+
public Cursor getDownloadedItemsCursor() {
final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
+ " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "