diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2014-04-01 22:53:18 +0200 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2014-04-01 22:53:18 +0200 |
commit | c37b67172e929289ffb8fe5e901661f4456abaae (patch) | |
tree | 0825759715295085832c52c798fd345e55229e35 /src | |
parent | 9abf27ca2f32240206d1dae3c55d2046372abf3f (diff) | |
download | AntennaPod-c37b67172e929289ffb8fe5e901661f4456abaae.zip |
Added navigation drawer + new episodes fragment
Diffstat (limited to 'src')
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 " |