Java tutorial
package com.luorrak.ouroboros.thread; import android.Manifest; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; 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.SearchView; 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.widget.FilterQueryProvider; import android.widget.ProgressBar; import com.google.gson.JsonObject; import com.koushikdutta.async.future.FutureCallback; import com.koushikdutta.ion.Ion; import com.luorrak.ouroboros.R; import com.luorrak.ouroboros.gallery.GalleryFragment; import com.luorrak.ouroboros.reply.ReplyCommentActivity; import com.luorrak.ouroboros.util.ChanUrls; import com.luorrak.ouroboros.util.DbContract; import com.luorrak.ouroboros.util.InfiniteDbHelper; import com.luorrak.ouroboros.util.Media; import com.luorrak.ouroboros.util.NetworkHelper; import com.luorrak.ouroboros.util.SettingsHelper; import com.luorrak.ouroboros.util.Util; import java.util.ArrayList; /** * Ouroboros - An 8chan browser * Copyright (C) 2015 Luorrak * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ public class ThreadFragment extends Fragment implements MenuItemCompat.OnActionExpandListener { // Construction //////////////////////////////////////////////////////////////////////////////// private final String LOG_TAG = ThreadFragment.class.getSimpleName(); private InfiniteDbHelper infiniteDbHelper; private NetworkHelper networkHelper = new NetworkHelper(); private RecyclerView recyclerView; private ThreadAdapter threadAdapter; private LinearLayoutManager layoutManager; private ThreadNetworkFragment networkFragment; private ProgressBar progressBar; private String resto; private String boardName; private int threadPosition; private boolean firstRequest; private Parcelable savedLayoutState; private boolean isStatusCheckIsRunning; private Handler handler; private String oldJsonString; private int pollingInterval; //Get thread number from link somehow public ThreadFragment newInstance(String resto, String boardName) { return newInstance(resto, boardName, 0); } public ThreadFragment newInstance(String resto, String boardName, int threadPosition) { ThreadFragment threadFragment = new ThreadFragment(); Bundle args = new Bundle(); args.putString("resto", resto); args.putString("boardName", boardName); args.putInt("threadPosition", threadPosition); threadFragment.setArguments(args); return threadFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); isStatusCheckIsRunning = false; if (savedInstanceState != null) { savedLayoutState = savedInstanceState.getParcelable("savedLayout"); resto = savedInstanceState.getString("resto"); boardName = getArguments().getString("boardName"); firstRequest = getArguments().getBoolean("firstRequest"); } else { firstRequest = true; } } @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { infiniteDbHelper = new InfiniteDbHelper(getActivity()); networkFragment = (ThreadNetworkFragment) getFragmentManager().findFragmentByTag("Thread_Task"); View view = inflater.inflate(R.layout.fragment_thread, container, false); getActivity().invalidateOptionsMenu(); progressBar = (ProgressBar) view.findViewById(R.id.progress_bar); layoutManager = new LinearLayoutManager(getActivity()) { @Override protected int getExtraLayoutSpace(RecyclerView.State state) { return 300; } }; recyclerView = (RecyclerView) view.findViewById(R.id.postList); recyclerView.setLayoutManager(layoutManager); recyclerView.getLayoutManager().onRestoreInstanceState(savedLayoutState); if (getArguments() != null) { resto = getArguments().getString("resto"); boardName = getArguments().getString("boardName"); threadPosition = getArguments().getInt("threadPosition"); } if (networkFragment == null) { networkFragment = new ThreadNetworkFragment(); getFragmentManager().beginTransaction().add(networkFragment, "Thread_Task").commit(); } if (boardName != null) { handler = new Handler(); pollingInterval = 10000; view.post(new Runnable() { @Override public void run() { int h = recyclerView.getHeight(); int w = recyclerView.getWidth(); threadAdapter = new ThreadAdapter(infiniteDbHelper.getThreadCursor(resto), getFragmentManager(), boardName, getActivity(), infiniteDbHelper, w, h); threadAdapter.setHasStableIds(true); threadAdapter.hasStableIds(); recyclerView.setAdapter(threadAdapter); } }); } return view; } // Life Cycle ////////////////////////////////////////////////////////////////////////////////// @Override public void onPause() { stopStatusCheck(); super.onPause(); } @Override public void onResume() { startStatusCheck(); super.onResume(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (savedLayoutState == null) { savedLayoutState = layoutManager.onSaveInstanceState(); } outState.putParcelable("savedLayout", savedLayoutState); outState.putString("boardName", boardName); outState.putString("resto", resto); outState.putBoolean("firstRequest", firstRequest); } // Options Menu //////////////////////////////////////////////////////////////////////////////// @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { MenuItem goToBottomButton = menu.findItem(R.id.action_scroll_bottom); MenuItem goToTopButton = menu.findItem(R.id.action_scroll_top); MenuItem replyButton = menu.findItem(R.id.action_reply); MenuItem watchlistButton = menu.findItem(R.id.action_add_watchlist); MenuItem refreshButton = menu.findItem(R.id.action_refresh); MenuItem galleryButton = menu.findItem(R.id.action_gallery); MenuItem saveAllImagesButton = menu.findItem(R.id.action_save_all_images); MenuItem openExternalButton = menu.findItem(R.id.action_external_browser); MenuItem shareButton = menu.findItem(R.id.menu_item_share); MenuItem menuLayout = menu.findItem(R.id.action_menu_layout); MenuItem searchButton = menu.findItem(R.id.action_search); searchButton.setVisible(true); final SearchView searchView = (SearchView) searchButton.getActionView(); searchView.setIconifiedByDefault(false); searchView.setSubmitButtonEnabled(false); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { threadAdapter.setFilterQueryProvider(new FilterQueryProvider() { @Override public Cursor runQuery(CharSequence constraint) { return infiniteDbHelper.searchThreadForString(constraint.toString(), resto); } }); threadAdapter.getFilter().filter(newText); return true; } }); MenuItemCompat.setOnActionExpandListener(searchButton, this); refreshButton.setVisible(true); goToBottomButton.setVisible(true); goToTopButton.setVisible(true); replyButton.setVisible(true); galleryButton.setVisible(true); saveAllImagesButton.setVisible(true); openExternalButton.setVisible(true); shareButton.setVisible(true); watchlistButton.setVisible(true); menuLayout.setVisible(true); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onMenuItemActionExpand(MenuItem item) { stopStatusCheck(); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { int backStackCount = getFragmentManager().getBackStackEntryCount(); if (backStackCount > 0) { FragmentManager.BackStackEntry backEntry = getFragmentManager().getBackStackEntryAt(backStackCount - 1); String str = backEntry.getName(); if (str.equals("threadDialog")) { getActivity().onBackPressed(); return false; } } startStatusCheck(); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_refresh: { getThread(resto, boardName); break; } case R.id.action_scroll_bottom: { recyclerView.scrollToPosition(threadAdapter.getItemCount() - 1); break; } case R.id.action_scroll_top: { recyclerView.scrollToPosition(0); break; } case R.id.action_reply: { Intent intent = new Intent(getActivity(), ReplyCommentActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(Util.INTENT_THREAD_NO, resto); intent.putExtra(Util.INTENT_BOARD_NAME, boardName); getActivity().startActivity(intent); break; } case R.id.action_gallery: { GalleryFragment galleryFragment = new GalleryFragment().newInstance(boardName, resto); FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.placeholder_card, galleryFragment).addToBackStack("galleryfragment") .commit(); break; } case R.id.action_save_all_images: { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat .requestPermissions(getActivity(), new String[] { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, Util.REQUEST_STORAGE_PERMISSION); } else { showDownloadAllDialog(); } break; } case R.id.action_external_browser: { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(ChanUrls.getThreadUrlExternal(boardName, resto))); startActivity(browserIntent); break; } case R.id.menu_item_share: { Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); String shareBody = ChanUrls.getThreadUrlExternal(boardName, resto); shareIntent.putExtra(Intent.EXTRA_TEXT, shareBody); startActivity(Intent.createChooser(shareIntent, "Share via")); break; } case R.id.action_add_watchlist: { Cursor cursor = infiniteDbHelper.getWatchlistCursor(); int count = cursor.getCount(); byte[] serializedMediaList; cursor.close(); Cursor threadcursor = infiniteDbHelper.getThreadCursor(resto); serializedMediaList = (threadcursor.getCount() > 0) ? threadcursor .getBlob(threadcursor.getColumnIndex(DbContract.ThreadEntry.COLUMN_THREAD_MEDIA_FILES)) : null; infiniteDbHelper.insertWatchlistEntry(String.valueOf(getActivity().getTitle()), boardName, resto, serializedMediaList, count); ((ThreadActivity) getActivity()).updateWatchlist(); Snackbar.make(getView(), "Thread Added To Watchlist", Snackbar.LENGTH_LONG).show(); threadcursor.close(); break; } case R.id.action_layout_vertical: { SettingsHelper.setThreadView(getActivity(), Util.THREAD_LAYOUT_VERTICAL); ThreadFragment threadFragment = new ThreadFragment().newInstance(resto, boardName); FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.placeholder_card, threadFragment).commit(); break; } case R.id.action_layout_horizontal: { SettingsHelper.setThreadView(getActivity(), Util.THREAD_LAYOUT_HORIZONTAL); ThreadFragment threadFragment = new ThreadFragment().newInstance(resto, boardName); FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.placeholder_card, threadFragment).commit(); break; } } return true; } // Loading Data //////////////////////////////////////////////////////////////////////////////// private void getThread(String threadNo, String boardName) { if (getActivity() != null) { getThreadJson(getActivity(), boardName, threadNo); } } private void getThreadJson(final Context context, final String boardName, final String threadNumber) { progressBar.setVisibility(View.VISIBLE); final String threadJsonUrl = ChanUrls.getThreadUrl(boardName, threadNumber); Ion.with(context).load(threadJsonUrl).setLogging(LOG_TAG, Log.DEBUG).asJsonObject() .setCallback(new FutureCallback<JsonObject>() { @Override public void onCompleted(Exception e, JsonObject jsonObject) { if (getActivity() != null) { if (e == null) { if (jsonObject.toString().equals(oldJsonString)) { pollingInterval = pollingInterval + pollingInterval / 2; progressBar.setVisibility(View.INVISIBLE); } else { restartStatusCheck(); oldJsonString = jsonObject.toString(); networkFragment.beginTask(jsonObject, infiniteDbHelper, boardName, resto, threadPosition, firstRequest, recyclerView, threadAdapter); } } else { progressBar.setVisibility(View.INVISIBLE); Snackbar.make(getView(), "Error retrieving thread", Snackbar.LENGTH_LONG).show(); } } firstRequest = false; } }); } public void showDownloadAllDialog() { new AlertDialog.Builder(getActivity()).setTitle("Download All Images") .setMessage("Are you sure you want to download all images?") .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { startDownload(); } }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); } public void startDownload() { Cursor imageCursor = infiniteDbHelper.getGalleryCursor(resto); do { ArrayList<Media> mediaArrayList = (ArrayList<Media>) Util.deserializeObject(imageCursor .getBlob(imageCursor.getColumnIndex(DbContract.ThreadEntry.COLUMN_THREAD_MEDIA_FILES))); for (Media mediaItem : mediaArrayList) { networkHelper.downloadFile(boardName, mediaItem.fileName, mediaItem.ext, getActivity()); } } while (imageCursor.moveToNext()); imageCursor.close(); } private Runnable statusCheck = new Runnable() { @Override public void run() { getThread(resto, boardName); handler.postDelayed(statusCheck, pollingInterval); } }; private void startStatusCheck() { if (!isStatusCheckIsRunning) { isStatusCheckIsRunning = true; statusCheck.run(); } } private void stopStatusCheck() { isStatusCheckIsRunning = false; handler.removeCallbacks(statusCheck); } private void restartStatusCheck() { stopStatusCheck(); pollingInterval = 10000; startStatusCheck(); } }