WIP: headlines list switched to recycler view
known issues: context menu not working
This commit is contained in:
parent
1ca3a7d681
commit
583578fe8a
@ -32,6 +32,7 @@ dependencies {
|
|||||||
compile 'com.bogdwellers:pinchtozoom:0.1'
|
compile 'com.bogdwellers:pinchtozoom:0.1'
|
||||||
compile 'com.github.bumptech.glide:glide:3.8.0'
|
compile 'com.github.bumptech.glide:glide:3.8.0'
|
||||||
compile 'jp.wasabeef:glide-transformations:2.0.2'
|
compile 'jp.wasabeef:glide-transformations:2.0.2'
|
||||||
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||||
compile 'com.android.support:cardview-v7:25.3.1'
|
compile 'com.android.support:cardview-v7:25.3.1'
|
||||||
compile 'com.android.support:support-v4:25.3.1'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||||
|
@ -9,7 +9,6 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.Resources.Theme;
|
import android.content.res.Resources.Theme;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@ -18,13 +17,15 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.app.ActivityOptionsCompat;
|
import android.support.v4.app.ActivityOptionsCompat;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
|
import android.support.v7.widget.DefaultItemAnimator;
|
||||||
|
import android.support.v7.widget.DividerItemDecoration;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.transition.Fade;
|
import android.transition.Fade;
|
||||||
import android.transition.Transition;
|
import android.transition.Transition;
|
||||||
@ -43,12 +44,7 @@ import android.view.View;
|
|||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.AbsListView;
|
|
||||||
import android.widget.AbsListView.OnScrollListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
@ -63,7 +59,6 @@ import com.bumptech.glide.Glide;
|
|||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||||
import com.bumptech.glide.request.RequestListener;
|
import com.bumptech.glide.request.RequestListener;
|
||||||
import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
|
|
||||||
import com.bumptech.glide.request.target.Target;
|
import com.bumptech.glide.request.target.Target;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.shamanland.fab.FloatingActionButton;
|
import com.shamanland.fab.FloatingActionButton;
|
||||||
@ -72,6 +67,7 @@ import com.shamanland.fab.ShowHideOnScroll;
|
|||||||
import org.fox.ttrss.types.Article;
|
import org.fox.ttrss.types.Article;
|
||||||
import org.fox.ttrss.types.ArticleList;
|
import org.fox.ttrss.types.ArticleList;
|
||||||
import org.fox.ttrss.types.Feed;
|
import org.fox.ttrss.types.Feed;
|
||||||
|
import org.fox.ttrss.util.HeaderViewRecyclerAdapter;
|
||||||
import org.fox.ttrss.util.HeadlinesRequest;
|
import org.fox.ttrss.util.HeadlinesRequest;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -85,7 +81,8 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
import jp.wasabeef.glide.transformations.CropCircleTransformation;
|
import jp.wasabeef.glide.transformations.CropCircleTransformation;
|
||||||
|
|
||||||
public class HeadlinesFragment extends Fragment implements OnItemClickListener, OnScrollListener {
|
public class HeadlinesFragment extends Fragment {
|
||||||
|
|
||||||
public enum ArticlesSelection { ALL, NONE, UNREAD }
|
public enum ArticlesSelection { ALL, NONE, UNREAD }
|
||||||
|
|
||||||
public static final int FLAVOR_IMG_MIN_SIZE = 128;
|
public static final int FLAVOR_IMG_MIN_SIZE = 128;
|
||||||
@ -100,15 +97,13 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
private Article m_activeArticle;
|
private Article m_activeArticle;
|
||||||
private String m_searchQuery = "";
|
private String m_searchQuery = "";
|
||||||
private boolean m_refreshInProgress = false;
|
private boolean m_refreshInProgress = false;
|
||||||
private boolean m_autoCatchupDisabled = false;
|
|
||||||
private int m_firstId = 0;
|
private int m_firstId = 0;
|
||||||
private boolean m_lazyLoadDisabled = false;
|
private boolean m_lazyLoadDisabled = false;
|
||||||
|
|
||||||
private SharedPreferences m_prefs;
|
private SharedPreferences m_prefs;
|
||||||
|
|
||||||
private ArticleListAdapter m_adapter;
|
private HeaderViewRecyclerAdapter m_adapter;
|
||||||
private ArticleList m_articles = new ArticleList(); //Application.getInstance().m_loadedArticles;
|
private ArticleList m_articles = new ArticleList();
|
||||||
//private ArticleList m_selectedArticles = new ArticleList();
|
|
||||||
private ArticleList m_readArticles = new ArticleList();
|
private ArticleList m_readArticles = new ArticleList();
|
||||||
private HeadlinesEventListener m_listener;
|
private HeadlinesEventListener m_listener;
|
||||||
private OnlineActivity m_activity;
|
private OnlineActivity m_activity;
|
||||||
@ -116,8 +111,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
private int m_maxImageSize = 0;
|
private int m_maxImageSize = 0;
|
||||||
private boolean m_compactLayoutMode = false;
|
private boolean m_compactLayoutMode = false;
|
||||||
private int m_listPreviousVisibleItem;
|
private int m_listPreviousVisibleItem;
|
||||||
private ListView m_list;
|
private RecyclerView m_list;
|
||||||
//private ImageLoader m_imageLoader = ImageLoader.getInstance();
|
private LinearLayoutManager m_layoutManager;
|
||||||
|
|
||||||
private View m_listLoadingView;
|
private View m_listLoadingView;
|
||||||
private View m_topChangedView;
|
private View m_topChangedView;
|
||||||
private View m_amrFooterView;
|
private View m_amrFooterView;
|
||||||
@ -327,7 +323,18 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_list = (ListView) view.findViewById(R.id.headlines_list);
|
m_list = (RecyclerView) view.findViewById(R.id.headlines_list);
|
||||||
|
|
||||||
|
m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext());
|
||||||
|
m_list.setLayoutManager(m_layoutManager);
|
||||||
|
m_list.setItemAnimator(new DefaultItemAnimator());
|
||||||
|
m_list.addItemDecoration(new DividerItemDecoration(m_list.getContext(), m_layoutManager.getOrientation()));
|
||||||
|
|
||||||
|
ArticleListAdapter adapter = new ArticleListAdapter(getActivity(), R.layout.headlines_row, m_articles);
|
||||||
|
|
||||||
|
m_adapter = new HeaderViewRecyclerAdapter(adapter);
|
||||||
|
|
||||||
|
m_list.setAdapter(m_adapter);
|
||||||
|
|
||||||
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.headlines_fab);
|
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.headlines_fab);
|
||||||
|
|
||||||
@ -358,23 +365,91 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
m_amrFooterView = inflater.inflate(R.layout.headlines_footer, container, false);
|
m_amrFooterView = inflater.inflate(R.layout.headlines_footer, container, false);
|
||||||
m_amrFooterView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, screenHeight));
|
m_amrFooterView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, screenHeight));
|
||||||
|
|
||||||
m_list.addFooterView(m_amrFooterView, null, false);
|
m_adapter.addFooterView(m_amrFooterView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_activity.isSmallScreen()) {
|
if (m_activity.isSmallScreen()) {
|
||||||
View layout = inflater.inflate(R.layout.headlines_heading_spacer, m_list, false);
|
View layout = inflater.inflate(R.layout.headlines_heading_spacer, m_list, false);
|
||||||
m_list.addHeaderView(layout);
|
m_adapter.addHeaderView(layout);
|
||||||
|
|
||||||
m_swipeLayout.setProgressViewOffset(false, 0,
|
m_swipeLayout.setProgressViewOffset(false, 0,
|
||||||
m_activity.getResources().getDimensionPixelSize(R.dimen.abc_action_bar_default_height_material) +
|
m_activity.getResources().getDimensionPixelSize(R.dimen.abc_action_bar_default_height_material) +
|
||||||
m_activity.getResources().getDimensionPixelSize(R.dimen.abc_action_bar_default_padding_end_material));
|
m_activity.getResources().getDimensionPixelSize(R.dimen.abc_action_bar_default_padding_end_material) + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_adapter = new ArticleListAdapter(getActivity(), R.layout.headlines_row, m_articles);
|
m_list.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
m_list.setAdapter(m_adapter);
|
@Override
|
||||||
|
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||||
|
super.onScrollStateChanged(recyclerView, newState);
|
||||||
|
|
||||||
m_list.setOnItemClickListener(this);
|
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
|
||||||
m_list.setOnScrollListener(this);
|
|
||||||
|
try {
|
||||||
|
if (m_mediaPlayer != null && m_mediaPlayer.isPlaying()) {
|
||||||
|
m_mediaPlayer.pause();
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// i guess it was already released, oh well
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState == RecyclerView.SCROLL_STATE_IDLE && m_prefs.getBoolean("headlines_mark_read_scroll", false)) {
|
||||||
|
if (!m_readArticles.isEmpty()) {
|
||||||
|
m_activity.toggleArticlesUnread(m_readArticles);
|
||||||
|
m_activity.refresh(false);
|
||||||
|
m_readArticles.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
|
||||||
|
int firstVisibleItem = m_layoutManager.findFirstVisibleItemPosition();
|
||||||
|
int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition();
|
||||||
|
|
||||||
|
//Log.d(TAG, "onScrolled: FVI=" + firstVisibleItem + " LVI=" + lastVisibleItem);
|
||||||
|
|
||||||
|
if (m_prefs.getBoolean("headlines_mark_read_scroll", false) && firstVisibleItem > m_adapter.getHeaderCount()) {
|
||||||
|
|
||||||
|
if (firstVisibleItem <= m_articles.size()) {
|
||||||
|
|
||||||
|
Article a = (Article) m_articles.get(firstVisibleItem - m_adapter.getHeaderCount() - 1);
|
||||||
|
|
||||||
|
if (a != null && a.unread) {
|
||||||
|
Log.d(TAG, "title=" + a.title);
|
||||||
|
|
||||||
|
a.unread = false;
|
||||||
|
m_readArticles.add(a);
|
||||||
|
m_feed.unread--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_activity.isTablet()) {
|
||||||
|
if (m_adapter.getItemCount() > 0) {
|
||||||
|
if (firstVisibleItem > m_listPreviousVisibleItem) {
|
||||||
|
m_activity.getSupportActionBar().hide();
|
||||||
|
} else if (firstVisibleItem < m_listPreviousVisibleItem) {
|
||||||
|
m_activity.getSupportActionBar().show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_activity.getSupportActionBar().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_listPreviousVisibleItem = firstVisibleItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_refreshInProgress && !m_lazyLoadDisabled && lastVisibleItem >= m_articles.size() - 5) {
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!enableSwipeToDismiss) registerForContextMenu(m_list);
|
if (!enableSwipeToDismiss) registerForContextMenu(m_list);
|
||||||
|
|
||||||
@ -412,38 +487,15 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
m_listener = (HeadlinesEventListener) activity;
|
m_listener = (HeadlinesEventListener) activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> av, View view, int position, long id) {
|
|
||||||
ListView list = (ListView)av;
|
|
||||||
|
|
||||||
Log.d(TAG, "onItemClick=" + position);
|
|
||||||
|
|
||||||
if (list != null) {
|
|
||||||
Article article = (Article)list.getItemAtPosition(position);
|
|
||||||
|
|
||||||
// could be footer or w/e
|
|
||||||
if (article != null && article.id >= 0) {
|
|
||||||
m_listener.onArticleSelected(article);
|
|
||||||
|
|
||||||
// only set active article when it makes sense (in DetailActivity)
|
|
||||||
if (getActivity() instanceof DetailActivity) {
|
|
||||||
m_activeArticle = article;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh(boolean append) {
|
public void refresh(boolean append) {
|
||||||
refresh(append, false);
|
refresh(append, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "serial" })
|
@SuppressWarnings({ "serial" })
|
||||||
public void refresh(boolean append, boolean userInitiated) {
|
public void refresh(final boolean append, boolean userInitiated) {
|
||||||
m_list.removeFooterView(m_listLoadingView);
|
m_adapter.removeFooterView(m_listLoadingView);
|
||||||
m_list.removeFooterView(m_topChangedView);
|
m_adapter.removeFooterView(m_topChangedView);
|
||||||
m_list.removeFooterView(m_amrFooterView);
|
m_adapter.removeFooterView(m_amrFooterView);
|
||||||
|
|
||||||
if (!append) m_lazyLoadDisabled = false;
|
if (!append) m_lazyLoadDisabled = false;
|
||||||
|
|
||||||
@ -452,19 +504,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true);
|
if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true);
|
||||||
|
|
||||||
// new stuff may appear on top, scroll back to show it
|
|
||||||
if (!append) {
|
|
||||||
if (getView() != null) {
|
|
||||||
Log.d(TAG, "scroll hack");
|
|
||||||
m_autoCatchupDisabled = true;
|
|
||||||
m_list.setSelection(0);
|
|
||||||
m_autoCatchupDisabled = false;
|
|
||||||
m_articles.clear();
|
|
||||||
m_adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean fappend = append;
|
|
||||||
final String sessionId = m_activity.getSessionId();
|
final String sessionId = m_activity.getSessionId();
|
||||||
final boolean isCat = m_feed.is_cat;
|
final boolean isCat = m_feed.is_cat;
|
||||||
|
|
||||||
@ -482,10 +521,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false);
|
if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false);
|
||||||
|
|
||||||
//m_listLoadingView.setVisibility(View.GONE);
|
m_adapter.removeFooterView(m_listLoadingView);
|
||||||
m_list.removeFooterView(m_listLoadingView);
|
m_adapter.removeFooterView(m_topChangedView);
|
||||||
m_list.removeFooterView(m_topChangedView);
|
m_adapter.removeFooterView(m_amrFooterView);
|
||||||
m_list.removeFooterView(m_amrFooterView);
|
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
m_refreshInProgress = false;
|
m_refreshInProgress = false;
|
||||||
@ -497,7 +535,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
if (m_firstIdChanged) {
|
if (m_firstIdChanged) {
|
||||||
m_lazyLoadDisabled = true;
|
m_lazyLoadDisabled = true;
|
||||||
|
|
||||||
m_list.addFooterView(m_topChangedView, null, false);
|
m_adapter.addFooterView(m_topChangedView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_amountLoaded < HEADLINES_REQUEST_SIZE) {
|
if (m_amountLoaded < HEADLINES_REQUEST_SIZE) {
|
||||||
@ -507,16 +545,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
HeadlinesFragment.this.m_firstId = m_firstId;
|
HeadlinesFragment.this.m_firstId = m_firstId;
|
||||||
|
|
||||||
m_adapter.notifyDataSetChanged();
|
m_adapter.notifyDataSetChanged();
|
||||||
m_listener.onHeadlinesLoaded(fappend);
|
m_listener.onHeadlinesLoaded(append);
|
||||||
|
|
||||||
// not sure why but listview sometimes gets positioned while ignoring the header so
|
|
||||||
// top headline content becomes partially obscured by the toolbar on phones
|
|
||||||
// (not reproducible on avd)
|
|
||||||
if (!fappend) {
|
|
||||||
m_list.smoothScrollToPosition(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//m_listLoadingView.setVisibility(m_amountLoaded == HEADLINES_REQUEST_SIZE ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) {
|
if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) {
|
||||||
@ -531,7 +560,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_amrFooterView != null) m_list.addFooterView(m_amrFooterView, null, false);
|
if (m_amrFooterView != null) m_adapter.addFooterView(m_amrFooterView);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -562,12 +591,8 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (skip > 0) {
|
if (skip > 0) {
|
||||||
m_list.addFooterView(m_listLoadingView, null, false);
|
m_adapter.addFooterView(m_listLoadingView);
|
||||||
//m_listLoadingView.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
//m_activity.setLoadingStatus(R.string.blank, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final int fskip = skip;
|
final int fskip = skip;
|
||||||
@ -638,7 +663,10 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
out.putBoolean("lazyLoadDisabled", m_lazyLoadDisabled);
|
out.putBoolean("lazyLoadDisabled", m_lazyLoadDisabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class HeadlineViewHolder {
|
static class HeadlineViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public View view;
|
||||||
|
public Article article;
|
||||||
|
|
||||||
public TextView titleView;
|
public TextView titleView;
|
||||||
public TextView feedTitleView;
|
public TextView feedTitleView;
|
||||||
public ImageView markedView;
|
public ImageView markedView;
|
||||||
@ -659,11 +687,39 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
public View topChangedMessage;
|
public View topChangedMessage;
|
||||||
public View flavorImageOverflow;
|
public View flavorImageOverflow;
|
||||||
public SurfaceView flavorVideoView;
|
public SurfaceView flavorVideoView;
|
||||||
public int position;
|
//public int position;
|
||||||
public boolean flavorImageEmbedded;
|
public boolean flavorImageEmbedded;
|
||||||
|
|
||||||
|
public HeadlineViewHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
|
||||||
|
view = v;
|
||||||
|
|
||||||
|
titleView = (TextView)v.findViewById(R.id.title);
|
||||||
|
|
||||||
|
feedTitleView = (TextView)v.findViewById(R.id.feed_title);
|
||||||
|
markedView = (ImageView)v.findViewById(R.id.marked);
|
||||||
|
publishedView = (ImageView)v.findViewById(R.id.published);
|
||||||
|
excerptView = (TextView)v.findViewById(R.id.excerpt);
|
||||||
|
flavorImageView = (ImageView) v.findViewById(R.id.flavor_image);
|
||||||
|
flavorVideoKindView = (ImageView) v.findViewById(R.id.flavor_video_kind);
|
||||||
|
authorView = (TextView)v.findViewById(R.id.author);
|
||||||
|
dateView = (TextView) v.findViewById(R.id.date);
|
||||||
|
selectionBoxView = (CheckBox) v.findViewById(R.id.selected);
|
||||||
|
menuButtonView = (ImageView) v.findViewById(R.id.article_menu_button);
|
||||||
|
flavorImageHolder = (ViewGroup) v.findViewById(R.id.flavorImageHolder);
|
||||||
|
flavorImageLoadingBar = (ProgressBar) v.findViewById(R.id.flavorImageLoadingBar);
|
||||||
|
headlineFooter = v.findViewById(R.id.headline_footer);
|
||||||
|
textImage = (ImageView) v.findViewById(R.id.text_image);
|
||||||
|
textChecked = (ImageView) v.findViewById(R.id.text_checked);
|
||||||
|
headlineHeader = v.findViewById(R.id.headline_header);
|
||||||
|
topChangedMessage = v.findViewById(R.id.headlines_row_top_changed);
|
||||||
|
flavorImageOverflow = v.findViewById(R.id.flavor_image_overflow);
|
||||||
|
flavorVideoView = (SurfaceView) v.findViewById(R.id.flavor_video);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArticleListAdapter extends ArrayAdapter<Article> {
|
private class ArticleListAdapter extends RecyclerView.Adapter<HeadlineViewHolder> {
|
||||||
private ArrayList<Article> items;
|
private ArrayList<Article> items;
|
||||||
|
|
||||||
public static final int VIEW_NORMAL = 0;
|
public static final int VIEW_NORMAL = 0;
|
||||||
@ -678,14 +734,14 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT;
|
private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT;
|
||||||
private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round();
|
private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round();
|
||||||
//private final DisplayImageOptions displayImageOptions;
|
|
||||||
boolean showFlavorImage;
|
boolean showFlavorImage;
|
||||||
private int m_minimumHeightToEmbed;
|
private int m_minimumHeightToEmbed;
|
||||||
boolean m_youtubeInstalled;
|
boolean m_youtubeInstalled;
|
||||||
private int m_screenHeight;
|
private int m_screenHeight;
|
||||||
|
|
||||||
public ArticleListAdapter(Context context, int textViewResourceId, ArrayList<Article> items) {
|
public ArticleListAdapter(Context context, int textViewResourceId, ArrayList<Article> items) {
|
||||||
super(context, textViewResourceId, items);
|
super();
|
||||||
this.items = items;
|
this.items = items;
|
||||||
|
|
||||||
Display display = m_activity.getWindowManager().getDefaultDisplay();
|
Display display = m_activity.getWindowManager().getDefaultDisplay();
|
||||||
@ -702,13 +758,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
theme.resolveAttribute(R.attr.headlineTitleHighScoreUnreadTextColor, tv, true);
|
theme.resolveAttribute(R.attr.headlineTitleHighScoreUnreadTextColor, tv, true);
|
||||||
titleHighScoreUnreadColor = tv.data;
|
titleHighScoreUnreadColor = tv.data;
|
||||||
|
|
||||||
/*displayImageOptions = new DisplayImageOptions.Builder()
|
|
||||||
.cacheInMemory(true)
|
|
||||||
.resetViewBeforeLoading(true)
|
|
||||||
.cacheOnDisk(true)
|
|
||||||
.displayer(new FadeInBitmapDisplayer(500))
|
|
||||||
.build();*/
|
|
||||||
|
|
||||||
List<ApplicationInfo> packages = m_activity.getPackageManager().getInstalledApplications(0);
|
List<ApplicationInfo> packages = m_activity.getPackageManager().getInstalledApplications(0);
|
||||||
for (ApplicationInfo pi : packages) {
|
for (ApplicationInfo pi : packages) {
|
||||||
if (pi.packageName.equals("com.google.android.youtube")) {
|
if (pi.packageName.equals("com.google.android.youtube")) {
|
||||||
@ -718,93 +767,12 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getViewTypeCount() {
|
|
||||||
return VIEW_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public HeadlineViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
Article a = items.get(position);
|
|
||||||
|
|
||||||
if (m_activeArticle != null && a.id == m_activeArticle.id && a.unread) {
|
|
||||||
return VIEW_SELECTED_UNREAD;
|
|
||||||
} else if (m_activeArticle != null && a.id == m_activeArticle.id) {
|
|
||||||
return VIEW_SELECTED;
|
|
||||||
} else if (a.unread) {
|
|
||||||
return VIEW_UNREAD;
|
|
||||||
} else {
|
|
||||||
return VIEW_NORMAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextCheckedState(final HeadlineViewHolder holder, final Article article, final int position) {
|
|
||||||
String tmp = article.title.length() > 0 ? article.title.substring(0, 1).toUpperCase() : "?";
|
|
||||||
|
|
||||||
if (article.selected) {
|
|
||||||
holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161));
|
|
||||||
//holder.textImage.setTag(null);
|
|
||||||
|
|
||||||
holder.textChecked.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
final Drawable textDrawable = m_drawableBuilder.build(tmp, m_colorGenerator.getColor(article.title));
|
|
||||||
|
|
||||||
holder.textImage.setImageDrawable(textDrawable);
|
|
||||||
//holder.textImage.setTag(null);
|
|
||||||
|
|
||||||
//holder.textChecked.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (!showFlavorImage || article.flavorImage == null) {
|
|
||||||
holder.textImage.setImageDrawable(textDrawable);
|
|
||||||
//holder.textImage.setTag(null);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
//final GlideDrawableImageViewTarget glideImage = new GlideDrawableImageViewTarget(holder.textImage);
|
|
||||||
|
|
||||||
Glide.with(HeadlinesFragment.this)
|
|
||||||
.load(article.flavorImageUri)
|
|
||||||
.placeholder(textDrawable)
|
|
||||||
.bitmapTransform(new CropCircleTransformation(getActivity()))
|
|
||||||
.dontAnimate()
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
|
||||||
.skipMemoryCache(false)
|
|
||||||
.listener(new RequestListener<String, GlideDrawable>() {
|
|
||||||
@Override
|
|
||||||
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
|
|
||||||
|
|
||||||
if (resource.getIntrinsicWidth() < THUMB_IMG_MIN_SIZE || resource.getIntrinsicHeight() < THUMB_IMG_MIN_SIZE) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into(holder.textImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.textChecked.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(final int position, final View convertView, ViewGroup parent) {
|
|
||||||
|
|
||||||
View v = convertView;
|
|
||||||
|
|
||||||
final Article article = items.get(position);
|
|
||||||
final HeadlineViewHolder holder;
|
|
||||||
|
|
||||||
int headlineFontSize = Integer.parseInt(m_prefs.getString("headlines_font_size_sp", "13"));
|
|
||||||
int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2));
|
|
||||||
|
|
||||||
if (v == null) {
|
|
||||||
int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row;
|
int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row;
|
||||||
|
|
||||||
switch (getItemViewType(position)) {
|
switch (viewType) {
|
||||||
case VIEW_UNREAD:
|
case VIEW_UNREAD:
|
||||||
layoutId = m_compactLayoutMode ? R.layout.headlines_row_unread_compact : R.layout.headlines_row_unread;
|
layoutId = m_compactLayoutMode ? R.layout.headlines_row_unread_compact : R.layout.headlines_row_unread;
|
||||||
break;
|
break;
|
||||||
@ -816,43 +784,43 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
View v = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
|
||||||
v = vi.inflate(layoutId, null);
|
|
||||||
|
|
||||||
holder = new HeadlineViewHolder();
|
return new HeadlineViewHolder(v);
|
||||||
holder.titleView = (TextView)v.findViewById(R.id.title);
|
|
||||||
|
|
||||||
holder.feedTitleView = (TextView)v.findViewById(R.id.feed_title);
|
|
||||||
holder.markedView = (ImageView)v.findViewById(R.id.marked);
|
|
||||||
holder.publishedView = (ImageView)v.findViewById(R.id.published);
|
|
||||||
holder.excerptView = (TextView)v.findViewById(R.id.excerpt);
|
|
||||||
holder.flavorImageView = (ImageView) v.findViewById(R.id.flavor_image);
|
|
||||||
holder.flavorVideoKindView = (ImageView) v.findViewById(R.id.flavor_video_kind);
|
|
||||||
holder.authorView = (TextView)v.findViewById(R.id.author);
|
|
||||||
holder.dateView = (TextView) v.findViewById(R.id.date);
|
|
||||||
holder.selectionBoxView = (CheckBox) v.findViewById(R.id.selected);
|
|
||||||
holder.menuButtonView = (ImageView) v.findViewById(R.id.article_menu_button);
|
|
||||||
holder.flavorImageHolder = (ViewGroup) v.findViewById(R.id.flavorImageHolder);
|
|
||||||
holder.flavorImageLoadingBar = (ProgressBar) v.findViewById(R.id.flavorImageLoadingBar);
|
|
||||||
holder.headlineFooter = v.findViewById(R.id.headline_footer);
|
|
||||||
holder.textImage = (ImageView) v.findViewById(R.id.text_image);
|
|
||||||
holder.textChecked = (ImageView) v.findViewById(R.id.text_checked);
|
|
||||||
holder.headlineHeader = v.findViewById(R.id.headline_header);
|
|
||||||
holder.topChangedMessage = v.findViewById(R.id.headlines_row_top_changed);
|
|
||||||
holder.flavorImageOverflow = v.findViewById(R.id.flavor_image_overflow);
|
|
||||||
holder.flavorVideoView = (SurfaceView) v.findViewById(R.id.flavor_video);
|
|
||||||
|
|
||||||
v.setTag(holder);
|
|
||||||
|
|
||||||
// http://code.google.com/p/android/issues/detail?id=3414
|
|
||||||
((ViewGroup)v).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
|
||||||
} else {
|
|
||||||
holder = (HeadlineViewHolder) v.getTag();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Log.d(TAG, "getView: " + position + ":" + article.title);
|
@Override
|
||||||
|
public void onBindViewHolder(final HeadlineViewHolder holder, final int position) {
|
||||||
|
holder.article = items.get(position);
|
||||||
|
|
||||||
holder.position = position;
|
int headlineFontSize = Integer.parseInt(m_prefs.getString("headlines_font_size_sp", "13"));
|
||||||
|
int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2));
|
||||||
|
|
||||||
|
final Article article = holder.article;
|
||||||
|
|
||||||
|
//holder.position = position;
|
||||||
|
|
||||||
|
holder.view.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
m_activity.openContextMenu(v);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.view.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
m_listener.onArticleSelected(article);
|
||||||
|
|
||||||
|
// only set active article when it makes sense (in DetailActivity)
|
||||||
|
if (getActivity() instanceof DetailActivity) {
|
||||||
|
m_activeArticle = article;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// block footer clicks to make button/selection clicking easier
|
// block footer clicks to make button/selection clicking easier
|
||||||
if (holder.headlineFooter != null) {
|
if (holder.headlineFooter != null) {
|
||||||
@ -1319,8 +1287,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (holder.menuButtonView != null) {
|
if (holder.menuButtonView != null) {
|
||||||
//if (m_activity.isDarkTheme())
|
|
||||||
// ib.setImageResource(R.drawable.ic_mailbox_collapsed_holo_dark);
|
|
||||||
|
|
||||||
holder.menuButtonView.setOnClickListener(new OnClickListener() {
|
holder.menuButtonView.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -1345,7 +1311,73 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
Article a = items.get(position);
|
||||||
|
|
||||||
|
if (m_activeArticle != null && a.id == m_activeArticle.id && a.unread) {
|
||||||
|
return VIEW_SELECTED_UNREAD;
|
||||||
|
} else if (m_activeArticle != null && a.id == m_activeArticle.id) {
|
||||||
|
return VIEW_SELECTED;
|
||||||
|
} else if (a.unread) {
|
||||||
|
return VIEW_UNREAD;
|
||||||
|
} else {
|
||||||
|
return VIEW_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTextCheckedState(final HeadlineViewHolder holder, final Article article, final int position) {
|
||||||
|
String tmp = article.title.length() > 0 ? article.title.substring(0, 1).toUpperCase() : "?";
|
||||||
|
|
||||||
|
if (article.selected) {
|
||||||
|
holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161));
|
||||||
|
//holder.textImage.setTag(null);
|
||||||
|
|
||||||
|
holder.textChecked.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
final Drawable textDrawable = m_drawableBuilder.build(tmp, m_colorGenerator.getColor(article.title));
|
||||||
|
|
||||||
|
holder.textImage.setImageDrawable(textDrawable);
|
||||||
|
|
||||||
|
if (!showFlavorImage || article.flavorImage == null) {
|
||||||
|
holder.textImage.setImageDrawable(textDrawable);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Glide.with(HeadlinesFragment.this)
|
||||||
|
.load(article.flavorImageUri)
|
||||||
|
.placeholder(textDrawable)
|
||||||
|
.bitmapTransform(new CropCircleTransformation(getActivity()))
|
||||||
|
.dontAnimate()
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||||
|
.skipMemoryCache(false)
|
||||||
|
.listener(new RequestListener<String, GlideDrawable>() {
|
||||||
|
@Override
|
||||||
|
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
|
||||||
|
|
||||||
|
if (resource.getIntrinsicWidth() < THUMB_IMG_MIN_SIZE || resource.getIntrinsicHeight() < THUMB_IMG_MIN_SIZE) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into(holder.textImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.textChecked.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openGalleryForType(Article article, HeadlineViewHolder holder, View transitionView) {
|
private void openGalleryForType(Article article, HeadlineViewHolder holder, View transitionView) {
|
||||||
@ -1430,14 +1462,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
return px;
|
return px;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void repositionFlavorVideo(View view, HeadlineViewHolder holder) {
|
|
||||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
|
|
||||||
|
|
||||||
lp.addRule(RelativeLayout.BELOW, R.id.headline_header);
|
|
||||||
|
|
||||||
view.setLayoutParams(lp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeRepositionFlavorImage(View view, GlideDrawable resource, HeadlineViewHolder holder, boolean forceDown) {
|
private void maybeRepositionFlavorImage(View view, GlideDrawable resource, HeadlineViewHolder holder, boolean forceDown) {
|
||||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
|
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
|
||||||
|
|
||||||
@ -1523,14 +1547,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
m_adapter.notifyDataSetChanged();
|
m_adapter.notifyDataSetChanged();
|
||||||
|
|
||||||
ListView list = (ListView)getView().findViewById(R.id.headlines_list);
|
if (m_list != null) {
|
||||||
|
|
||||||
if (list != null) {
|
|
||||||
int position = getArticlePositionById(article.id);
|
int position = getArticlePositionById(article.id);
|
||||||
|
m_list.smoothScrollToPosition(position);
|
||||||
if (position != -1) {
|
|
||||||
list.smoothScrollToPosition(position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1553,15 +1572,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Article getArticleAtPosition(int position) {
|
public Article getArticleAtPosition(int position) {
|
||||||
try {
|
return m_articles.get(position);
|
||||||
return (Article) m_list.getItemAtPosition(position);
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
return null;
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
|
||||||
return null;
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Article getArticleById(int id) {
|
public Article getArticleById(int id) {
|
||||||
@ -1580,74 +1591,14 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
|
||||||
if (m_prefs.getBoolean("headlines_mark_read_scroll", false) && firstVisibleItem > (m_activity.isSmallScreen() ? 1 : 0) && !m_autoCatchupDisabled) {
|
|
||||||
Article a = (Article) view.getItemAtPosition(firstVisibleItem - 1);
|
|
||||||
|
|
||||||
if (a != null && a.unread) {
|
|
||||||
Log.d(TAG, "title=" + a.title);
|
|
||||||
|
|
||||||
a.unread = false;
|
|
||||||
m_readArticles.add(a);
|
|
||||||
m_feed.unread--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_activity.isTablet()) {
|
|
||||||
if (m_adapter.getCount() > 0) {
|
|
||||||
if (firstVisibleItem > m_listPreviousVisibleItem) {
|
|
||||||
m_activity.getSupportActionBar().hide();
|
|
||||||
} else if (firstVisibleItem < m_listPreviousVisibleItem) {
|
|
||||||
m_activity.getSupportActionBar().show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_activity.getSupportActionBar().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_listPreviousVisibleItem = firstVisibleItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_refreshInProgress && !m_lazyLoadDisabled && firstVisibleItem + visibleItemCount == m_articles.size()) {
|
|
||||||
refresh(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
|
||||||
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
|
|
||||||
|
|
||||||
/*if (m_activeSurface != null) {
|
|
||||||
m_activeSurface.setVisibility(View.GONE);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (m_mediaPlayer != null && m_mediaPlayer.isPlaying()) {
|
|
||||||
m_mediaPlayer.pause();
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// i guess it was already released, oh well
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollState == SCROLL_STATE_IDLE && m_prefs.getBoolean("headlines_mark_read_scroll", false)) {
|
|
||||||
if (!m_readArticles.isEmpty()) {
|
|
||||||
m_activity.toggleArticlesUnread(m_readArticles);
|
|
||||||
m_activity.refresh(false);
|
|
||||||
m_readArticles.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Article getActiveArticle() {
|
public Article getActiveArticle() {
|
||||||
return m_activeArticle;
|
return m_activeArticle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getArticlePositionById(int id) {
|
public int getArticlePositionById(int id) {
|
||||||
for (Article a : m_adapter.items) {
|
for (int i = 0; i < m_articles.size(); i++) {
|
||||||
if (a.id == id) {
|
if (m_articles.get(i).id == id) {
|
||||||
return m_adapter.getPosition(a) + m_list.getHeaderViewsCount();
|
return i + m_adapter.getHeaderCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,214 @@
|
|||||||
|
package org.fox.ttrss.util;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 darnmason
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* RecyclerView adapter designed to wrap an existing adapter allowing the addition of
|
||||||
|
* header views and footer views.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* I implemented it to aid with the transition from ListView to RecyclerView where the ListView's
|
||||||
|
* addHeaderView and addFooterView methods were used. Using this class you may initialize your
|
||||||
|
* header views in the Fragment/Activity and add them to the adapter in the same way you used to
|
||||||
|
* add them to a ListView.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* I also required to be able to swap out multiple adapters with different content, therefore
|
||||||
|
* setAdapter may be called multiple times.
|
||||||
|
* </p>
|
||||||
|
* Created by darnmason on 07/11/2014.
|
||||||
|
*/
|
||||||
|
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private static final int HEADERS_START = Integer.MIN_VALUE;
|
||||||
|
private static final int FOOTERS_START = Integer.MIN_VALUE + 10;
|
||||||
|
private static final int ITEMS_START = Integer.MIN_VALUE + 20;
|
||||||
|
private static final int ADAPTER_MAX_TYPES = 100;
|
||||||
|
|
||||||
|
private RecyclerView.Adapter mWrappedAdapter;
|
||||||
|
private List<View> mHeaderViews, mFooterViews;
|
||||||
|
private Map<Class, Integer> mItemTypesOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new header view recycler adapter
|
||||||
|
* @param adapter The underlying adapter to wrap
|
||||||
|
*/
|
||||||
|
public HeaderViewRecyclerAdapter(RecyclerView.Adapter adapter) {
|
||||||
|
mHeaderViews = new ArrayList<View>();
|
||||||
|
mFooterViews = new ArrayList<View>();
|
||||||
|
mItemTypesOffset = new HashMap<Class, Integer>();
|
||||||
|
setWrappedAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the underlying adapter, notifying RecyclerView of changes
|
||||||
|
* @param adapter The new adapter to wrap
|
||||||
|
*/
|
||||||
|
public void setAdapter(RecyclerView.Adapter adapter) {
|
||||||
|
if(mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) {
|
||||||
|
notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount());
|
||||||
|
}
|
||||||
|
setWrappedAdapter(adapter);
|
||||||
|
notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
int hCount = getHeaderCount();
|
||||||
|
if (position < hCount) return HEADERS_START + position;
|
||||||
|
else {
|
||||||
|
int itemCount = mWrappedAdapter.getItemCount();
|
||||||
|
if (position < hCount + itemCount) {
|
||||||
|
return getAdapterTypeOffset() + mWrappedAdapter.getItemViewType(position - hCount);
|
||||||
|
}
|
||||||
|
else return FOOTERS_START + position - hCount - itemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||||
|
if (viewType < HEADERS_START + getHeaderCount())
|
||||||
|
return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START));
|
||||||
|
else if (viewType < FOOTERS_START + getFooterCount())
|
||||||
|
return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START));
|
||||||
|
else {
|
||||||
|
return mWrappedAdapter.onCreateViewHolder(viewGroup, viewType - getAdapterTypeOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
|
int hCount = getHeaderCount();
|
||||||
|
if (position >= hCount && position < hCount + mWrappedAdapter.getItemCount())
|
||||||
|
mWrappedAdapter.onBindViewHolder(viewHolder, position - hCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
|
||||||
|
* order they were added.
|
||||||
|
* @param view The header view to add
|
||||||
|
*/
|
||||||
|
public void addHeaderView(View view) {
|
||||||
|
mHeaderViews.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
|
||||||
|
* order they were added.
|
||||||
|
* @param view The footer view to add
|
||||||
|
*/
|
||||||
|
public void addFooterView(View view) {
|
||||||
|
mFooterViews.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFooterView(View view) {
|
||||||
|
mFooterViews.remove(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return getHeaderCount() + getFooterCount() + getWrappedItemCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The item count in the underlying adapter
|
||||||
|
*/
|
||||||
|
public int getWrappedItemCount() {
|
||||||
|
return mWrappedAdapter.getItemCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of header views added
|
||||||
|
*/
|
||||||
|
public int getHeaderCount() {
|
||||||
|
return mHeaderViews.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of footer views added
|
||||||
|
*/
|
||||||
|
public int getFooterCount() {
|
||||||
|
return mFooterViews.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setWrappedAdapter(RecyclerView.Adapter adapter) {
|
||||||
|
if (mWrappedAdapter != null) mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver);
|
||||||
|
mWrappedAdapter = adapter;
|
||||||
|
Class adapterClass = mWrappedAdapter.getClass();
|
||||||
|
if(!mItemTypesOffset.containsKey(adapterClass)) putAdapterTypeOffset(adapterClass);
|
||||||
|
mWrappedAdapter.registerAdapterDataObserver(mDataObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putAdapterTypeOffset(Class adapterClass) {
|
||||||
|
mItemTypesOffset.put(adapterClass, ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAdapterTypeOffset() {
|
||||||
|
return mItemTypesOffset.get(mWrappedAdapter.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
|
||||||
|
@Override
|
||||||
|
public void onChanged() {
|
||||||
|
super.onChanged();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeChanged(int positionStart, int itemCount) {
|
||||||
|
super.onItemRangeChanged(positionStart, itemCount);
|
||||||
|
notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||||
|
super.onItemRangeInserted(positionStart, itemCount);
|
||||||
|
notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||||
|
super.onItemRangeRemoved(positionStart, itemCount);
|
||||||
|
notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
|
||||||
|
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
|
||||||
|
int hCount = getHeaderCount();
|
||||||
|
// TODO: No notifyItemRangeMoved method?
|
||||||
|
notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static class StaticViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
public StaticViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,24 +11,13 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" >
|
android:layout_height="match_parent" >
|
||||||
|
|
||||||
<ListView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/headlines_list"
|
android:id="@+id/headlines_list"
|
||||||
android:drawSelectorOnTop="true"
|
android:drawSelectorOnTop="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
</android.support.v4.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
<!-- <TextView
|
|
||||||
android:id="@+id/no_headlines"
|
|
||||||
android:clickable="true"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:text="@string/no_headlines"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
|
||||||
android:visibility="invisible" >
|
|
||||||
</TextView> -->
|
|
||||||
|
|
||||||
<com.shamanland.fab.FloatingActionButton
|
<com.shamanland.fab.FloatingActionButton
|
||||||
android:id="@+id/headlines_fab"
|
android:id="@+id/headlines_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/headlines_row"
|
android:id="@+id/headlines_row"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
tools:ignore="HardcodedText">
|
tools:ignore="HardcodedText">
|
||||||
|
|
||||||
<TableLayout
|
<TableLayout
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/headlines_row"
|
android:id="@+id/headlines_row"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:background="?headlineUnreadBackground"
|
android:background="?headlineUnreadBackground"
|
||||||
tools:ignore="HardcodedText">
|
tools:ignore="HardcodedText">
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user