package com.duckduckgo.mobile.android.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.duckduckgo.mobile.android.DDGApplication;
import com.duckduckgo.mobile.android.R;
import com.duckduckgo.mobile.android.actionbar.DDGActionBarManager;
import com.duckduckgo.mobile.android.adapters.MainFeedAdapter;
import com.duckduckgo.mobile.android.adapters.RecyclerMainFeedAdapter;
import com.duckduckgo.mobile.android.bus.BusProvider;
import com.duckduckgo.mobile.android.dialogs.FeedRequestFailureDialogBuilder;
import com.duckduckgo.mobile.android.download.AsyncImageView;
import com.duckduckgo.mobile.android.events.OrbotConnectedEvent;
import com.duckduckgo.mobile.android.events.OverflowButtonClickEvent;
import com.duckduckgo.mobile.android.events.RequestKeepFeedUpdatedEvent;
import com.duckduckgo.mobile.android.events.RequestOpenWebPageEvent;
import com.duckduckgo.mobile.android.events.RequestSyncAdaptersEvent;
import com.duckduckgo.mobile.android.events.SourceFilterEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedCancelCategoryFilterEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedCancelSourceFilterEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedCleanImageTaskEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedItemSelectedEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedRetrieveErrorEvent;
import com.duckduckgo.mobile.android.events.feedEvents.FeedRetrieveSuccessEvent;
import com.duckduckgo.mobile.android.objects.FeedObject;
import com.duckduckgo.mobile.android.tasks.CacheFeedTask;
import com.duckduckgo.mobile.android.tasks.MainFeedTask;
import com.duckduckgo.mobile.android.util.DDGControlVar;
import com.duckduckgo.mobile.android.util.REQUEST_TYPE;
import com.duckduckgo.mobile.android.util.ReadArticlesManager;
import com.duckduckgo.mobile.android.util.SCREEN;
import com.duckduckgo.mobile.android.util.SESSIONTYPE;
import com.duckduckgo.mobile.android.util.TorIntegrationProvider;
import com.duckduckgo.mobile.android.views.DDGOverflowMenu;
import com.duckduckgo.mobile.android.views.MainFeedListView;
import com.squareup.otto.Subscribe;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.HashMap;
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
public static final String TAG = "feed_fragment";
private Activity activity = null;
private RecyclerView recyclerView = null;
private SwipeRefreshLayout swipeRefreshLayout = null;
private View fragmentView;
private RecyclerMainFeedAdapter recyclerAdapter = null;
private MainFeedTask mainFeedTask = null;
private RecyclerView.LayoutManager layoutManager;
// for keeping filter source at same position
public String source_m_objectId = null;
public int source_m_itemHeight;
public int source_m_yOffset;
private Menu feedMenu = null;
private DDGOverflowMenu overflowMenu = null;
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.activity = (Activity) context;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BusProvider.getInstance().register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
BusProvider.getInstance().unregister(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_feed, container, false);
return fragmentView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
activity = getActivity();
init();
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(!hidden) {
keepFeedUpdated();
}
}
@Override
public void onResume() {
super.onResume();
recyclerView.setAdapter(recyclerAdapter);
// lock button etc. can cause MainFeedTask results to be useless for the Activity
// which is restarted (onPostExecute becomes invalid for the new Activity instance)
// ensure we refresh in such cases
keepFeedUpdated();
}
@Override
public void onPause() {
super.onPause();
recyclerView.setAdapter(null);
swipeRefreshLayout.setRefreshing(false);
if (mainFeedTask != null) {
mainFeedTask.cancel(false);
mainFeedTask = null;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
@Override
public void onRefresh() {
// refresh the list
DDGControlVar.hasUpdatedFeed = false;
keepFeedUpdated();
}
public void init() {
recyclerAdapter = new RecyclerMainFeedAdapter(activity, getFragmentManager());
mainFeedTask = null;
swipeRefreshLayout = (SwipeRefreshLayout) fragmentView.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
recyclerView = (RecyclerView) fragmentView.findViewById(R.id.feed_list_view);
layoutManager = new LinearLayoutManager(activity);
recyclerView.setLayoutManager(layoutManager);
feedMenu = new MenuBuilder(activity);
activity.getMenuInflater().inflate(R.menu.main, feedMenu);
}
/**
* Changes the article to read and notifies listeners of the event
*
* @param feedObject
*/
public void feedItemSelected(FeedObject feedObject) {
if(ReadArticlesManager.addReadArticle(feedObject)){
recyclerAdapter.notifyDataSetChanged();
}
}
/**
* Cancels source filter applied with source icon click from feed item
*/
public void cancelSourceFilter() {
DDGControlVar.targetSource = null;
DDGControlVar.hasUpdatedFeed = false;
keepFeedUpdated();
}
/**
* Cancels target filter applied with source icon click from feed item
*/
public void cancelCategoryFilter() {
DDGControlVar.targetCategory = null;
DDGControlVar.hasUpdatedFeed = false;
keepFeedUpdated();
}
/**
* Refresh feed if it's not marked as updated
*/
@SuppressLint("NewApi")
public void keepFeedUpdated(){
if(DDGControlVar.mDuckDuckGoContainer.torIntegration.isOrbotRunningAccordingToSettings()) {
if (!DDGControlVar.hasUpdatedFeed) {
if(!canUpdateFeed()) {
// respect user choice of empty source list: show nothing
BusProvider.getInstance().post(new FeedRetrieveSuccessEvent(new ArrayList<FeedObject>(),
REQUEST_TYPE.FROM_CACHE));
} else {
// cache
CacheFeedTask cacheTask = new CacheFeedTask(activity);
// for HTTP request
mainFeedTask = new MainFeedTask(recyclerView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
cacheTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if (DDGControlVar.automaticFeedUpdate || swipeRefreshLayout.isRefreshing()
|| DDGControlVar.changedSources) {
mainFeedTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
DDGControlVar.changedSources = false;
}
} else {
cacheTask.execute();
if (DDGControlVar.automaticFeedUpdate || swipeRefreshLayout.isRefreshing()
|| DDGControlVar.changedSources) {
mainFeedTask.execute();
DDGControlVar.changedSources = false;
}
}
}
} else {
// complete the action anyway
swipeRefreshLayout.setRefreshing(false);
}
}
}
/**
* Cleans all views stored in the RecyclerView object
*/
public void cleanImageTasks() {
int count = recyclerView.getChildCount();
for(int i=0;i<count;i++) {
View v = recyclerView.getChildAt(i);
if(v != null) {
AsyncImageView iv = (AsyncImageView) v.findViewById(R.id.feedItemBackground);
if(activity!=null) {
Picasso.with(activity).cancelRequest(iv);
}
}
}
}
/**
* Checks all default and user allowed sources
*
* @return whether the user can update any of the feed sources
*/
public boolean canUpdateFeed() {
if(!DDGControlVar.userAllowedSources.isEmpty()) {
return true;
}
if(DDGControlVar.defaultSources.isEmpty()) {
return true;
}
for(String source : DDGControlVar.defaultSources) {
if(!DDGControlVar.userDisallowedSources.contains(source)) {
return true;
}
}
return false;
}
@Subscribe
public void onFeedRetrieveSuccessEvent(FeedRetrieveSuccessEvent event) {
if(DDGControlVar.targetSource!=null) {
if (event.requestType == REQUEST_TYPE.FROM_NETWORK) {
recyclerAdapter.addSourceData(event.feed);
swipeRefreshLayout.setRefreshing(false);
}
} else {
if (event.requestType == REQUEST_TYPE.FROM_NETWORK) {
synchronized (recyclerAdapter) {
recyclerAdapter.clear();
}
}
recyclerAdapter.addData(event.feed);
// update pull-to-refresh header to reflect task completion
swipeRefreshLayout.setRefreshing(false);
DDGControlVar.hasUpdatedFeed = true;
// do this upon filter completion
if (DDGControlVar.targetSource != null && source_m_objectId != null) {
int pos = -1;
for (int i = 0; pos < 0 && i < recyclerAdapter.getItemCount(); i++) {
if (recyclerAdapter.getItem(i).getId().equals(source_m_objectId)) {
pos = i;
}
}
recyclerView.offsetChildrenVertical(source_m_yOffset);
}
if (DDGControlVar.targetCategory != null) {
recyclerAdapter.filterCategory(DDGControlVar.targetCategory);
}
}
}
@Subscribe
public void onFeedRetrieveErrorEvent(FeedRetrieveErrorEvent event) {
if (activity!=null && !activity.isFinishing() && DDGControlVar.mDuckDuckGoContainer.currentScreen != SCREEN.SCR_FAVORITE && mainFeedTask != null) {
new FeedRequestFailureDialogBuilder(activity).show();
}
swipeRefreshLayout.setRefreshing(false);
}
@Subscribe
public void onFeedItemSelected(FeedItemSelectedEvent event) {
if(event.feedObject!=null) {
feedItemSelected(event.feedObject);
}
}
@Subscribe
public void onSourceFilterEvent(SourceFilterEvent event) {
keepFeedUpdated();
}
@Subscribe
public void onFeedCancelSourceFilterEvent(FeedCancelSourceFilterEvent event) {
cancelSourceFilter();
}
@Subscribe
public void onFeedCancelCategoryFilterEvent(FeedCancelCategoryFilterEvent event) {
cancelCategoryFilter();
}
@Subscribe
public void onRequestKeepFeedUpdatedEvent(RequestKeepFeedUpdatedEvent event) {
keepFeedUpdated();
}
@Subscribe
public void onFeedCleanImageTaskEvent(FeedCleanImageTaskEvent event) {
cleanImageTasks();
}
@Subscribe
public void onOverflowButtonClickEvent(OverflowButtonClickEvent event) {
if(DDGControlVar.mDuckDuckGoContainer.currentFragmentTag.equals(getTag()) && feedMenu!=null) {
feedMenu.findItem(R.id.action_stories).setEnabled(false);
if(overflowMenu!=null && overflowMenu.isShowing()) {
return;
}
overflowMenu = new DDGOverflowMenu(activity);
overflowMenu.setMenu(feedMenu);
overflowMenu.show(event.anchor);
}
}
@Subscribe
public void onOrbotConnectedEvent(OrbotConnectedEvent event) {
keepFeedUpdated();
}
}