package de.tap.easy_xkcd.fragments.whatIf; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; 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 android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.SearchView; import android.widget.Toast; import com.tap.xkcd_reader.R; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Random; import butterknife.Bind; import butterknife.ButterKnife; import de.tap.easy_xkcd.Activities.MainActivity; import de.tap.easy_xkcd.Activities.WhatIfActivity; import de.tap.easy_xkcd.utils.PrefHelper; import de.tap.easy_xkcd.utils.ThemePrefs; import jp.wasabeef.recyclerview.adapters.AlphaInAnimationAdapter; import jp.wasabeef.recyclerview.adapters.SlideInBottomAnimationAdapter; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okio.BufferedSink; import okio.Okio; public class WhatIfFragment extends android.support.v4.app.Fragment { public static ArrayList<String> mTitles = new ArrayList<>(); private static ArrayList<String> mImgs = new ArrayList<>(); @Bind(R.id.rv) RecyclerView rv; private MenuItem searchMenuItem; public static WhatIfRVAdapter adapter; private static WhatIfFragment instance; public static boolean newIntent; private boolean offlineMode; private static final String OFFLINE_WHATIF_OVERVIEW_PATH = "/easy xkcd/what if/overview"; private static final String OFFLINE_WHATIF_PATH = "/easy xkcd/what if/"; private PrefHelper prefHelper; private ThemePrefs themePrefs; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.recycler_layout, container, false); ButterKnife.bind(this, v); setHasOptionsMenu(true); prefHelper = ((MainActivity) getActivity()).getPrefHelper(); themePrefs = ((MainActivity) getActivity()).getThemePrefs(); LinearLayoutManager llm = new LinearLayoutManager(getActivity()); rv.setLayoutManager(llm); rv.setHasFixedSize(false); offlineMode = prefHelper.fullOfflineWhatIf(); instance = this; ((MainActivity) getActivity()).getFab().setVisibility(View.GONE); if (prefHelper.isOnline(getActivity()) && (prefHelper.isWifi(getActivity()) | prefHelper.mobileEnabled()) && offlineMode) { new UpdateArticles().execute(); Log.d("info", "update started"); } else { new DisplayOverview().execute(); } return v; } private class UpdateArticles extends AsyncTask<Void, Void, Void> { private ProgressDialog progress; private boolean showProgress; private OkHttpClient client; @Override protected void onPreExecute() { showProgress = ((MainActivity) getActivity()).getCurrentFragment() == R.id.nav_whatif; if (showProgress) { progress = new ProgressDialog(getActivity()); progress.setMessage(getResources().getString(R.string.loading_articles)); progress.setIndeterminate(true); progress.setCancelable(false); progress.show(); } client = new OkHttpClient(); } @SuppressWarnings("ResultOfMethodCallIgnored") @Override protected Void doInBackground(Void... dummy) { int highestOffline = prefHelper.getNewestWhatIf(); try { Document doc = Jsoup.connect("https://what-if.xkcd.com/archive/") .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.19 Safari/537.36") .get(); Elements titles = doc.select("h1"); Elements img = doc.select("img.archive-image"); if (titles.size() > prefHelper.getNewestWhatIf()) { Log.d("what if", "updating overview"); prefHelper.setNewestWhatif(titles.size()); StringBuilder sb = new StringBuilder(); sb.append(titles.first().text()); titles.remove(0); for (Element title : titles) { sb.append("&&"); sb.append(title.text()); } prefHelper.setWhatIfTitles(sb.toString()); File sdCard = prefHelper.getOfflinePath(); File dir = new File(sdCard.getAbsolutePath() + OFFLINE_WHATIF_OVERVIEW_PATH); if (!dir.exists()) dir.mkdirs(); for (int i = prefHelper.getNewestWhatIf(); i < titles.size() + 1; i++) { String url = img.get(i).absUrl("src"); try { File file = new File(dir, String.valueOf(i + 1) + ".png"); Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); BufferedSink sink = Okio.buffer(Okio.sink(file)); sink.writeAll(response.body().source()); sink.close(); response.body().close(); } catch (Exception e) { e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } for (int i = highestOffline + 1; i <= prefHelper.getNewestWhatIf(); i++) { downloadArticle(i); } //this What If failed downloading when it first came out if (!prefHelper.sunBeamDownloaded()) downloadArticle(141); if (!prefHelper.nomediaCreated()) { File sdCard = prefHelper.getOfflinePath(); File dir = new File(sdCard.getAbsolutePath() + "/easy xkcd"); File nomedia = new File(dir, ".nomedia"); try { boolean created = nomedia.createNewFile(); Log.d("created", String.valueOf(created)); } catch (IOException e) { e.printStackTrace(); } } return null; } private void downloadArticle(int i) { Log.d("what if", "downloading " + i); if (i == 141) prefHelper.setSunbeamLoaded(); Document doc; File sdCard = prefHelper.getOfflinePath(); File dir; try { doc = Jsoup.connect("https://what-if.xkcd.com/" + String.valueOf(i)).get(); dir = new File(sdCard.getAbsolutePath() + OFFLINE_WHATIF_PATH + String.valueOf(i)); if (!dir.exists()) dir.mkdirs(); File file = new File(dir, String.valueOf(i) + ".html"); BufferedWriter writer = new BufferedWriter(new FileWriter(file)); writer.write(doc.outerHtml()); writer.close(); //download images int count = 1; for (Element e : doc.select(".illustration")) { try { String url = "http://what-if.xkcd.com" + e.attr("src"); Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); file = new File(dir, String.valueOf(count) + ".png"); BufferedSink sink = Okio.buffer(Okio.sink(file)); sink.writeAll(response.body().source()); sink.close(); response.body().close(); count++; } catch (Exception e2) { Log.e("article" + i, e2.getMessage()); } } } catch (Exception e) { e.printStackTrace(); } } @Override protected void onPostExecute(Void dummy) { if (showProgress) progress.dismiss(); new DisplayOverview().execute(); } } private class DisplayOverview extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... dummy) { mTitles.clear(); mImgs.clear(); if (offlineMode) { mTitles = prefHelper.getWhatIfTitles(); Collections.reverse(mTitles); } else { Document doc = WhatIfOverviewFragment.doc; Elements titles = doc.select("h1"); Elements imagelinks = doc.select("img.archive-image"); boolean bowlingFixed = false; //This title appears twice, so add " " to one of the titles to make everything work later for (Element title : titles) { if (!bowlingFixed && title.text().equals("Bowling Ball")) { mTitles.add(title.text() + " "); bowlingFixed = true; } else mTitles.add(title.text()); } for (Element image : imagelinks) mImgs.add(image.absUrl("src")); Collections.reverse(mTitles); Collections.reverse(mImgs); } return null; } @Override protected void onPostExecute(Void dummy) { prefHelper.setNewestWhatif(mTitles.size()); setupAdapter(prefHelper.hideReadWhatIf()); Toolbar toolbar = ((MainActivity) getActivity()).getToolbar(); if (toolbar.getAlpha() == 0) { toolbar.setTranslationY(-300); toolbar.animate().setDuration(300).translationY(0).alpha(1); View view; for (int i = 0; i < toolbar.getChildCount(); i++) { view = toolbar.getChildAt(i); view.setTranslationY(-300); view.animate().setStartDelay(50 * (i + 1)).setDuration(70 * (i + 1)).translationY(0); } } if (newIntent) { Intent intent = new Intent(getActivity(), WhatIfActivity.class); startActivity(intent); newIntent = false; } } } public class WhatIfRVAdapter extends WhatIfOverviewFragment.RVAdapter { public WhatIfRVAdapter(ArrayList<String> t, ArrayList<String> i, MainActivity activity) { super(t, i, activity); } @Override public ComicViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.whatif_overview, viewGroup, false); v.setOnClickListener(new CustomOnClickListener()); v.setOnLongClickListener(new CustomOnLongClickListener()); return new ComicViewHolder(v); } @Override public void onBindViewHolder(final ComicViewHolder comicViewHolder, int i) { if (prefHelper.checkRead(titles.size() - titles.indexOf(titles.get(i)))) { if (themePrefs.nightThemeEnabled()) comicViewHolder.articleTitle.setTextColor(ContextCompat.getColor(getActivity(), android.R.color.tertiary_text_light)); else comicViewHolder.articleTitle.setTextColor(ContextCompat.getColor(getActivity(), R.color.Read)); } else { if (themePrefs.nightThemeEnabled()) comicViewHolder.articleTitle.setTextColor(ContextCompat.getColor(getActivity(), R.color.Read)); else comicViewHolder.articleTitle.setTextColor(ContextCompat.getColor(getActivity(), android.R.color.tertiary_text_light)); } super.onBindViewHolder(comicViewHolder, i); } } private class CustomOnClickListener implements View.OnClickListener { @Override public void onClick(View v) { if (!prefHelper.isOnline(getActivity()) && !offlineMode) { Toast.makeText(getActivity(), R.string.no_connection, Toast.LENGTH_SHORT).show(); return; } int pos = rv.getChildAdapterPosition(v); Intent intent = new Intent(getActivity(), WhatIfActivity.class); String title = adapter.titles.get(pos); int n = mTitles.size() - mTitles.indexOf(title); WhatIfActivity.WhatIfIndex = n; startActivity(intent); prefHelper.setLastWhatIf(n); prefHelper.setWhatifRead(String.valueOf(n)); if (searchMenuItem.isActionViewExpanded()) { searchMenuItem.collapseActionView(); rv.scrollToPosition(mTitles.size() - n); } } } public void getRandom() { if (prefHelper.isOnline(getActivity()) | offlineMode) { Random mRand = new Random(); int number = mRand.nextInt(adapter.titles.size()); Intent intent = new Intent(getActivity(), WhatIfActivity.class); String title = adapter.titles.get(number); int n = mTitles.size() - mTitles.indexOf(title); WhatIfActivity.WhatIfIndex = n; startActivity(intent); prefHelper.setLastWhatIf(n); prefHelper.setWhatifRead(String.valueOf(n)); } else { Toast.makeText(getActivity(), R.string.no_connection, Toast.LENGTH_SHORT).show(); } } class CustomOnLongClickListener implements View.OnLongClickListener { @Override public boolean onLongClick(final View v) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); int pos = rv.getChildAdapterPosition(v); final String title = adapter.titles.get(pos); final int n = mTitles.size() - mTitles.indexOf(title); int array = prefHelper.checkWhatIfFav(n) ? R.array.whatif_card_long_click_remove : R.array.whatif_card_long_click; builder.setItems(array, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { int pos; int n; String title; switch (which) { case 0: pos = rv.getChildAdapterPosition(v); title = adapter.titles.get(pos); n = mTitles.size() - mTitles.indexOf(title); Intent share = new Intent(Intent.ACTION_SEND); share.setType("text/plain"); share.putExtra(Intent.EXTRA_SUBJECT, "What if: " + title); share.putExtra(Intent.EXTRA_TEXT, "http://what-if.xkcd.com/" + String.valueOf(n)); startActivity(share); break; case 1: pos = rv.getChildAdapterPosition(v); title = adapter.titles.get(pos); n = mTitles.size() - mTitles.indexOf(title); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://what-if.xkcd.com/" + String.valueOf(n))); startActivity(intent); break; case 2: pos = rv.getChildAdapterPosition(v); title = adapter.titles.get(pos); n = mTitles.size() - mTitles.indexOf(title); if (!prefHelper.checkWhatIfFav(n)) { prefHelper.setWhatIfFavorite(String.valueOf(n)); } else { prefHelper.removeWhatifFav(n); } WhatIfFavoritesFragment.getInstance().updateFavorites(); break; } } }); AlertDialog alert = builder.create(); alert.show(); return true; } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_unread: prefHelper.setAllUnread(); setupAdapter(prefHelper.hideReadWhatIf()); return true; case R.id.action_all_read: prefHelper.setAllWhatIfRead(); setupAdapter(prefHelper.hideReadWhatIf()); case R.id.action_hide_read: item.setChecked(!item.isChecked()); prefHelper.setHideReadWhatIf(item.isChecked()); setupAdapter(item.isChecked()); return true; } return super.onOptionsItemSelected(item); } private void setupAdapter(boolean hideRead) { if (hideRead) { ArrayList<String> titleUnread = new ArrayList<>(); ArrayList<String> imgUnread = new ArrayList<>(); for (int i = 0; i < mTitles.size(); i++) { if (!prefHelper.checkRead(mTitles.size() - i)) { titleUnread.add(mTitles.get(i)); if (!offlineMode) imgUnread.add(mImgs.get(i)); } } adapter = new WhatIfRVAdapter(titleUnread, imgUnread, (MainActivity) getActivity()); SlideInBottomAnimationAdapter slideAdapter = new SlideInBottomAnimationAdapter(adapter); slideAdapter.setInterpolator(new DecelerateInterpolator()); rv.setAdapter(slideAdapter); rv.scrollToPosition(titleUnread.size() - prefHelper.getLastWhatIf()); } else { adapter = new WhatIfRVAdapter(mTitles, mImgs, (MainActivity) getActivity()); SlideInBottomAnimationAdapter slideAdapter = new SlideInBottomAnimationAdapter(adapter); slideAdapter.setInterpolator(new DecelerateInterpolator()); rv.setAdapter(slideAdapter); rv.scrollToPosition(mTitles.size() - prefHelper.getLastWhatIf()); } } public void updateRv() { setupAdapter(prefHelper.hideReadWhatIf()); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.findItem(R.id.action_hide_read).setChecked(prefHelper.hideReadWhatIf()); final SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); searchView.setIconifiedByDefault(false); searchView.setQueryHint(getResources().getString(R.string.search_hint_whatif)); searchMenuItem = menu.findItem(R.id.action_search); searchMenuItem.setVisible(true); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return true; } @Override public boolean onQueryTextChange(String newText) { ArrayList<String> titleResults = new ArrayList<>(); ArrayList<String> imgResults = new ArrayList<>(); for (int i = 0; i < mTitles.size(); i++) { if (mTitles.get(i).toLowerCase().contains(newText.toLowerCase().trim())) { titleResults.add(mTitles.get(i)); if (!offlineMode) imgResults.add(mImgs.get(i)); } } adapter = new WhatIfRVAdapter(titleResults, imgResults, (MainActivity) getActivity()); SlideInBottomAnimationAdapter slideAdapter = new SlideInBottomAnimationAdapter(adapter); slideAdapter.setInterpolator(new DecelerateInterpolator()); rv.setAdapter(slideAdapter); return false; } }); MenuItemCompat.setOnActionExpandListener(searchMenuItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { View view = getActivity().getCurrentFocus(); if (view != null) { InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(view, 0); } searchView.requestFocus(); ((WhatIfOverviewFragment) getParentFragment()).fab.hide(); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { View view = getActivity().getCurrentFocus(); if (view != null) { InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } adapter = new WhatIfRVAdapter(mTitles, mImgs, (MainActivity) getActivity()); SlideInBottomAnimationAdapter slideAdapter = new SlideInBottomAnimationAdapter(adapter); slideAdapter.setInterpolator(new DecelerateInterpolator()); rv.setAdapter(slideAdapter); searchView.setQuery("", false); ((WhatIfOverviewFragment) getParentFragment()).fab.show(); return true; } }); super.onCreateOptionsMenu(menu, inflater); } public RecyclerView getRv() { return rv; } public static WhatIfFragment getInstance() { return instance; } @Override public void onDestroyView() { super.onDestroyView(); ButterKnife.unbind(this); } }