com.normalexception.app.rx8club.fragment.thread.ThreadFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.normalexception.app.rx8club.fragment.thread.ThreadFragment.java

Source

package com.normalexception.app.rx8club.fragment.thread;

/************************************************************************
 * NormalException.net Software, and other contributors
 * http://www.normalexception.net
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ************************************************************************/

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.normalexception.app.rx8club.Log;
import com.normalexception.app.rx8club.MainApplication;
import com.normalexception.app.rx8club.R;
import com.normalexception.app.rx8club.TimeoutFactory;
import com.normalexception.app.rx8club.fragment.AdminFragment;
import com.normalexception.app.rx8club.fragment.FragmentUtils;
import com.normalexception.app.rx8club.fragment.PaginationFragment;
import com.normalexception.app.rx8club.fragment.StylerFragment;
import com.normalexception.app.rx8club.fragment.category.CategoryFragment;
import com.normalexception.app.rx8club.html.HtmlFormUtils;
import com.normalexception.app.rx8club.html.LoginFactory;
import com.normalexception.app.rx8club.html.VBForumFactory;
import com.normalexception.app.rx8club.preferences.PreferenceHelper;
import com.normalexception.app.rx8club.state.AppState;
import com.normalexception.app.rx8club.task.SubmitTask;
import com.normalexception.app.rx8club.user.UserProfile;
import com.normalexception.app.rx8club.utils.Utils;
import com.normalexception.app.rx8club.view.ViewHolder;
import com.normalexception.app.rx8club.view.threadpost.PostModel;
import com.normalexception.app.rx8club.view.threadpost.PostViewArrayAdapter;

/**
 * Activity used to display thread contents.  Within this activity a user can
 * create new posts.
 * 
 * Required Intent Parameters:
 * link - The link to the thread
 * title - The title of the thread
 * page - The page number of the thread
 */
public class ThreadFragment extends Fragment {

    private Logger TAG = LogManager.getLogger(this.getClass());

    private String currentPageLink;
    private String currentPageTitle;

    private String threadNumber;

    private String pageNumber = "1";

    private String securityToken = "none";
    private String postNumber = "none";
    private String finalPage = "1";
    private String thisPage = "1";

    private ArrayList<PostModel> postlist;
    private PostViewArrayAdapter pva;
    private ListView lv;

    private List<String> bmapList;

    private boolean isLocked = false;

    private ProgressDialog loadingDialog;

    private final String MODERATION_TOOLS = "Moderation Tools";

    private ThreadFragmentListener tal = new ThreadFragmentListener(this);

    private View adminContent = null;

    private CategoryFragment parentCategory = null;

    public static ThreadFragment newInstance() {
        ThreadFragment tf = new ThreadFragment();
        return tf;
    }

    /**
     * Report our registered onClickListener
     * @return   The thread's on click listener
     */
    public ThreadFragmentListener getOnClickListener() {
        return tal;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_content, container, false);

        lv = (ListView) rootView.findViewById(R.id.mainlistview);

        //this.parentCategory = (CategoryFragment) getArguments().getSerializable("parent");
        this.parentCategory = (CategoryFragment) getParentFragment();
        adminContent = inflater.inflate(R.layout.view_newreply_header, lv, false);

        // Inflate the header if we are an admin
        //adminContent = inflater.inflate(R.layout.fragment_admin, null);
        getChildFragmentManager().beginTransaction()
                .replace(R.id.fragment_content_admin, AdminFragment.newInstance()).commit();
        lv.addHeaderView(adminContent);

        // Inflate the footer (pagination, styler, reply box)
        View v = inflater.inflate(R.layout.view_newreply_footer, lv, false);

        getChildFragmentManager().beginTransaction().replace(R.id.fragment_content_styler, new StylerFragment())
                .commit();

        getChildFragmentManager().beginTransaction()
                .replace(R.id.nr_pagination, PaginationFragment.newInstance(this.tal)).commit();

        lv.addFooterView(v);
        v.findViewById(R.id.submitButton).setOnClickListener(tal);

        Log.v(TAG, "ThreadFragment view loaded");
        return rootView;
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        try {
            super.onCreate(savedInstanceState);
            MainApplication.setState(AppState.State.THREAD, this);

            //threadId = Utils.randomInt(0, 9999);

            Log.v(TAG, "Thread Activity Started");

            if (TimeoutFactory.getInstance().checkTimeout(this)) {
                postlist = new ArrayList<PostModel>();
                bmapList = new ArrayList<String>();
                if (savedInstanceState == null || (pva == null || pva.getCount() == 0))
                    constructView();
                else
                    updateList();
            }
        } catch (Exception e) {
            Log.e(TAG, "Fatal Error In Thread Activity! " + e.getMessage(), e);
        }
    }

    /**
     * Construct the thread activity view
     */
    private void constructView() {
        AsyncTask<Void, String, Void> updaterTask = new AsyncTask<Void, String, Void>() {
            @Override
            protected void onPreExecute() {

                loadingDialog = ProgressDialog.show(getActivity(), getString(R.string.loading),
                        getString(R.string.pleaseWait), true);
            }

            @Override
            protected Void doInBackground(Void... params) {
                currentPageLink = getArguments().getString("link");
                currentPageTitle = getArguments().getString("title");
                pageNumber = getArguments().getString("page");
                //isPoll = 
                //      getArguments().getBoolean("poll", false);
                isLocked = getArguments().getBoolean("locked", false);

                if (pageNumber == null)
                    pageNumber = "1";

                Log.v(TAG, "Grabbing link: " + currentPageLink);

                Document doc = VBForumFactory.getInstance().get(getActivity(), currentPageLink);

                // First check to see if the title is null, this is possible if the user
                // clicked a link on a thread and got here.
                currentPageTitle = doc.select("title").text();

                // Now check if the page is locked, again, we should have already known 
                // this, but if we got here by other means
                isLocked = doc.select("img[src*=threadclosed]").first() != null;

                // If the user is guest or if the thread is locked, 
                // then hide the items that they generally wont be able to use
                if (LoginFactory.getInstance().isGuestMode() || isLocked) {
                    Log.d(TAG, "Thread Is Locked, Hiding Reply Container");
                    ViewHolder.get(getView(), R.id.nr_replycontainer).setVisibility(View.GONE);
                }

                // Grab the canonical link if it exists.  This is the safest way to
                // make sure that we got the actual URL of the page
                String canonUrl = null;
                try {
                    if ((canonUrl = doc.select("link[rel=canonical]").attr("href")) != null) {
                        currentPageLink = canonUrl;
                        Log.d(TAG, String.format("Grabbed Canonical URL: %s", currentPageLink));
                    }
                } catch (Exception e) {
                }

                if (doc != null) {
                    publishProgress(getString(R.string.asyncDialogGrabThreadContents));

                    try {
                        getThreadContents(doc);
                    } catch (Exception e) {
                        getActivity().runOnUiThread(new Runnable() {
                            public void run() {
                                Toast.makeText(getActivity(), R.string.timeout, Toast.LENGTH_SHORT).show();
                            }
                        });
                        Log.e(TAG, "Exception Grabbing Thread Contents", e);
                    }
                    publishProgress(getString(R.string.asyncDialogPopulating));
                    updateList();
                }
                return null;
            }

            @Override
            protected void onProgressUpdate(String... progress) {
                if (loadingDialog != null)
                    loadingDialog.setMessage(progress[0]);
            }

            @Override
            protected void onPostExecute(Void result) {
                try {
                    loadingDialog.dismiss();
                    loadingDialog = null;
                } catch (Exception e) {
                    Log.w(TAG, e.getMessage());
                }
            }
        };
        updaterTask.execute();
    }

    /**
     * Update our list with the contents
     */
    private void updateList() {
        final Fragment _frag = this;
        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                getView().findViewById(R.id.mainlisttitle).setVisibility(View.VISIBLE);
                ((TextView) getView().findViewById(R.id.mainlisttitle)).setText(String.format("%s [Page %s]",
                        currentPageTitle, pageNumber.equals("last") ? finalPage : pageNumber));
                pva = new PostViewArrayAdapter(_frag, R.layout.view_thread, postlist, tal);
                //pva.setThreadId(threadId);
                lv.setAdapter(pva);
                FragmentUtils.updatePagination(_frag, thisPage, finalPage);
            }
        });
    }

    /**
     * Grab contents from the forum that the user clicked on
     * @param doc   The document parsed from the link
     * @param id   The id number of the link
     * @return      An arraylist of forum contents
     */
    public void getThreadContents(Document doc) {
        // Update pagination
        try {
            Elements pageNumbers = doc.select("div[class=pagenav]");
            if (pageNumbers.first() != null) {
                Elements pageLinks = pageNumbers.first().select("td[class^=vbmenu_control]");
                thisPage = pageLinks.text().split(" ")[1];
                finalPage = pageLinks.text().split(" ")[3];
                Log.d(TAG, String.format("This Page: %s, Final Page: %s", thisPage, finalPage));
            } else {
                Log.d(TAG, "Thread only contains one page");
            }
        } catch (Exception e) {
            Log.e(TAG, "We had an error with pagination", e);
        }

        // Is user thread admin??
        Elements threadTools = doc.select("div[id=threadtools_menu] > form > table");
        if (threadTools.text().contains(MODERATION_TOOLS)) {
            Log.d(TAG, "<><> User has administrative rights here! <><>");
        } else {
            //adminContent.setVisibility(View.GONE);
            lv.removeHeaderView(adminContent);
        }

        // Get the user's actual ID, there is a chance they never got it
        // before
        UserProfile.getInstance().setUserId(HtmlFormUtils.getInputElementValueByName(doc, "loggedinuser"));

        // Get Post Number and security token
        securityToken = HtmlFormUtils.getInputElementValueByName(doc, "securitytoken");

        Elements pNumber = doc.select("a[href^=http://www.rx8club.com/newreply.php?do=newreply&noquote=1&p=]");
        String pNumberHref = pNumber.attr("href");
        postNumber = pNumberHref.substring(pNumberHref.lastIndexOf("=") + 1);
        threadNumber = doc.select("input[name=searchthreadid]").attr("value");

        Elements posts = doc.select("div[id=posts]").select("div[id^=edit]");
        Log.v(TAG, String.format("Parsing through %d posts", posts.size()));
        for (Element post : posts) {
            try {
                Elements innerPost = post.select("table[id^=post]");

                // User Control Panel
                Elements userCp = innerPost.select("td[class=alt2]");
                Elements userDetail = userCp.select("div[class=smallfont]");
                Elements userSubDetail = userDetail.last().select("div");
                Elements userAvatar = userDetail.select("img[alt$=Avatar]");

                // User Information
                PostModel pv = new PostModel();
                pv.setUserName(userCp.select("div[id^=postmenu]").text());
                pv.setIsLoggedInUser(LoginFactory.getInstance().isLoggedIn()
                        ? UserProfile.getInstance().getUsername().equals(pv.getUserName())
                        : false);
                pv.setUserTitle(userDetail.first().text());
                pv.setUserImageUrl(userAvatar.attr("src"));
                pv.setPostDate(innerPost.select("td[class=thead]").first().text());
                pv.setPostId(Utils.parseInts(post.attr("id")));
                pv.setRootThreadUrl(currentPageLink);

                // get Likes if any exist
                Elements eLikes = innerPost.select("div[class*=vbseo_liked] > a");
                List<String> likes = new ArrayList<String>();
                for (Element eLike : eLikes)
                    likes.add(eLike.text());
                pv.setLikes(likes);

                Iterator<Element> itr = userSubDetail.listIterator();
                while (itr.hasNext()) {
                    String txt = itr.next().text();
                    if (txt.contains("Location:"))
                        pv.setUserLocation(txt);
                    else if (txt.contains("Posts:"))
                        pv.setUserPostCount(txt);
                    else if (txt.contains("Join Date:"))
                        pv.setJoinDate(txt);
                }

                // User Post Content
                pv.setUserPost(formatUserPost(innerPost));

                // User signature
                try {
                    Element userSig = innerPost.select("div[class=konafilter]").first();
                    pv.setUserSignature(userSig.html());
                } catch (NullPointerException npe) {
                }

                Elements postAttachments = innerPost.select("a[id^=attachment]");
                if (postAttachments != null && !postAttachments.isEmpty()) {
                    ArrayList<String> attachments = new ArrayList<String>();
                    for (Element postAttachment : postAttachments) {
                        attachments.add(postAttachment.attr("href"));
                    }
                    pv.setAttachments(attachments);
                }

                pv.setSecurityToken(securityToken);

                // Make sure we aren't adding a blank user post
                if (pv.getUserPost() != null)
                    postlist.add(pv);
            } catch (Exception e) {
                Log.w(TAG, "Error Parsing Post...Probably Deleted");
            }
        }
    }

    /**
     * Format the user post by removing the vb style quotes and the 
     * duplicate youtube links
     * @param innerPost   The element that contains the inner post
     * @return         The formatted string
     */
    private String formatUserPost(Elements innerPost) {
        try {
            Element ipost = innerPost.select("td[class=alt1]").select("div[id^=post_message]").first();

            // Only if there is a post to key off of
            if (ipost != null) {
                // Remove the duplicate youtube links (this is caused by a plugin on 
                // the forum that embeds youtube videos automatically)
                for (Element embedded : ipost.select("div[id^=ame_doshow_post_]"))
                    embedded.remove();

                // Remove the vbulletin quotes
                return Utils.reformatQuotes(ipost.html());
            } else {
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "Error Parsing Post", e);
            return null;
        }
    }

    /**
     * Listener that handles the clicks of buttons that are found on the thread
     * posts
     */
    public class ThreadFragmentListener implements OnClickListener {
        private Fragment src = null;

        public ThreadFragmentListener(Fragment _src) {
            src = _src;
        }

        /*
         * (non-Javadoc)
         * @see android.view.View.OnClickListener#onClick(android.view.View)
         */
        @Override
        public void onClick(View arg0) {
            Fragment _fragment = null;
            Bundle args = new Bundle();
            args.putString("title", currentPageTitle);

            // Make sure we aren't manipulating on 'last' as a page
            if (pageNumber.contains("last"))
                pageNumber = finalPage;

            switch (arg0.getId()) {
            case R.id.nr_downButton:
                Log.d(TAG, "Scrolling To Bottom of Screen");
                lv.setSelection(lv.getCount());
                break;
            case R.id.previousButton:
                _fragment = ThreadFragment.newInstance();
                args.putString("link", Utils.decrementPage(currentPageLink, pageNumber));
                args.putString("page", String.valueOf(Integer.parseInt(pageNumber) - 1));
                break;
            case R.id.nextButton:
                _fragment = ThreadFragment.newInstance();
                args.putString("link", Utils.incrementPage(currentPageLink, pageNumber));
                args.putString("page", String.valueOf(Integer.parseInt(pageNumber) + 1));
                break;
            case R.id.submitButton:
                _fragment = null;
                String advert = PreferenceHelper.isAdvertiseEnabled(MainApplication.getAppContext())
                        ? LoginFactory.getInstance().getSignature()
                        : "";
                String thePost = ((TextView) getActivity().findViewById(R.id.postBox)).getText().toString();
                if (thePost != null && !thePost.equals("")) {
                    String toPost = String.format("%s\n\n%s", thePost, advert);
                    SubmitTask sTask = new SubmitTask(src, bmapList, securityToken, threadNumber, postNumber,
                            toPost, currentPageTitle, pageNumber);
                    //sTask.debug();
                    sTask.execute();
                } else {
                    Toast.makeText(getActivity(), R.string.enterPost, Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.paginationText:
                final EditText input = new EditText(getActivity());
                input.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
                new AlertDialog.Builder(getActivity()).setTitle("Go To Page...").setMessage("Enter New Page Number")
                        .setView(input).setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                String value = input.getText().toString();
                                Fragment __fragment = ThreadFragment.newInstance();

                                Bundle _args = new Bundle();
                                _args.putString("link", Utils.getPage(currentPageLink, value));
                                _args.putString("page", value);
                                _args.putString("title", currentPageTitle);

                                FragmentUtils.fragmentTransaction(getActivity(), __fragment, false, false, _args);
                            }
                        }).setNegativeButton("Cancel", null).show();
                break;

            case R.id.firstButton:
                _fragment = ThreadFragment.newInstance();
                args.putString("link", Utils.getPage(currentPageLink, Integer.toString(1)));
                args.putString("page", "1");
                break;

            case R.id.lastButton:
                _fragment = ThreadFragment.newInstance();
                args.putString("link", Utils.getPage(currentPageLink, finalPage));
                args.putString("page", finalPage);
                break;

            default:
                _fragment = null;
                break;
            }

            if (_fragment != null) {
                FragmentUtils.fragmentTransaction(getActivity(), _fragment, false, false, args);
            }
        }
    }

    /**
     * Handle the result of the image attachment dialog
     * @param requestCode   We are expecting the code for the image result
     * @param resultCode   We are expecting an OK result
     * @param data         The image object
     */
    // NOT USED
    /**
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
        
       if (requestCode == StylerFragment.RESULT_LOAD_IMAGE 
        && resultCode == RESULT_OK && null != data) {
     Log.d(TAG, "Image Loaded...");
     Uri selectedImage = data.getData();
     String[] filePathColumn = { MediaStore.Images.Media.DATA };
        
     Cursor cursor = getContentResolver().query(selectedImage,
           filePathColumn, null, null, null);
     cursor.moveToFirst();
        
     int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
     String picturePath = cursor.getString(columnIndex);
     cursor.close();
        
     bmapList.add(picturePath);         
       }     
    }
    */

    /**
     * Report the thread number
     * @return   The thread number
     */
    public String getThreadNumber() {
        return threadNumber;
    }

    /**
     * Report the security token for the user
     * @return   The security token for the user
     */
    public String getSecurityToken() {
        return this.securityToken;
    }

    /**
     * Report the parent category
     * @return   The parent category
     */
    public Fragment getParentCategory() {
        return this.parentCategory;
    }
}