com.luorrak.ouroboros.thread.ThreadFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.luorrak.ouroboros.thread.ThreadFragment.java

Source

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();
    }
}