Java tutorial
/* Copyright (C) 2014 Prasanna Thirumalai This file is part of StackX. StackX 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. StackX 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 StackX. If not, see <http://www.gnu.org/licenses/>. */ package com.prasanna.android.stacknetwork; import java.util.ArrayList; import java.util.HashMap; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v13.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.Toast; import com.prasanna.android.http.HttpException; import com.prasanna.android.stacknetwork.fragment.AnswerFragment; import com.prasanna.android.stacknetwork.fragment.CommentFragment; import com.prasanna.android.stacknetwork.fragment.CommentFragment.OnCommentChangeListener; import com.prasanna.android.stacknetwork.fragment.CommentFragment.OnShowCommentsListener; import com.prasanna.android.stacknetwork.fragment.PostCommentFragment; import com.prasanna.android.stacknetwork.fragment.QuestionFragment; import com.prasanna.android.stacknetwork.model.Answer; import com.prasanna.android.stacknetwork.model.Comment; import com.prasanna.android.stacknetwork.model.Question; import com.prasanna.android.stacknetwork.model.WritePermission; import com.prasanna.android.stacknetwork.receiver.RestQueryResultReceiver; import com.prasanna.android.stacknetwork.receiver.RestQueryResultReceiver.StackXRestQueryResultReceiver; import com.prasanna.android.stacknetwork.service.QuestionDetailsIntentService; import com.prasanna.android.stacknetwork.service.WriteIntentService; import com.prasanna.android.stacknetwork.utils.AppUtils; import com.prasanna.android.stacknetwork.utils.OperatingSite; import com.prasanna.android.stacknetwork.utils.SharedPreferencesUtil; import com.prasanna.android.stacknetwork.utils.StringConstants; import com.viewpagerindicator.TitlePageIndicator; public class QuestionActivity extends AbstractUserActionBarActivity implements OnPageChangeListener, StackXRestQueryResultReceiver, PageSelectAdapter, OnShowCommentsListener { private boolean serviceRunningForAnswers = false; private boolean serviceRunningForFavorite = false; private Question question; private QuestionViewPageAdapter questionViewPageAdapter; private ViewPager viewPager; private TitlePageIndicator titlePageIndicator; private RestQueryResultReceiver resultReceiver; private QuestionFragment questionFragment; private CommentFragment commentFragment; private PostCommentFragment postCommentFragment; private HashMap<String, String> commentsDraft = new HashMap<String, String>(); private Menu menu; public class QuestionViewPageAdapter extends FragmentPagerAdapter { public QuestionViewPageAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { if (question == null) return 1; else return question.answers == null ? 1 : 1 + question.answers.size(); } @Override public CharSequence getPageTitle(int position) { if (position == 0) return QuestionActivity.this.getString(R.string.question); else { if (question.answers.get(position - 1).accepted) return QuestionActivity.this.getString(R.string.accepted) + " " + QuestionActivity.this.getString(R.string.answer); else return QuestionActivity.this.getString(R.string.answer) + " " + position; } } @Override public Fragment getItem(int position) { if (position == 0) return QuestionActivity.this.questionFragment; else { Fragment fragment = getFragmentManager().findFragmentByTag(getViewPagerFragmentTag(position)); if (fragment == null) return AnswerFragment.newFragment(question.answers.get(position - 1), QuestionActivity.this, question.owner.id == OperatingSite.getSite().userId, question.hasAcceptedAnswer); return fragment; } } } @Override public void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); setContentView(R.layout.viewpager_title_indicator); resultReceiver = new RestQueryResultReceiver(new Handler()); resultReceiver.setReceiver(this); if (questionFragment == null) questionFragment = QuestionFragment.newFragment(); setupViewPager(); if (savedInstanceState != null && savedInstanceState.getSerializable(StringConstants.QUESTION) != null) question = (Question) savedInstanceState.getSerializable(StringConstants.QUESTION); else prepareIntentAndStartService(); } @Override protected boolean shouldSearchViewBeEnabled() { return false; } @Override public boolean onPrepareOptionsMenu(Menu menu) { boolean ret = super.onPrepareOptionsMenu(menu); if (menu != null) { this.menu = menu; setupMenuIfAuthenticatedUser(); } return ret; } private MenuItem setupFavoriteActionBarIcon() { MenuItem favorite = menu.findItem(R.id.menu_favorite); if (question != null && question.favorited) { favorite.setIcon(R.drawable.favorited); } else { favorite.setIcon(R.drawable.favorite); } return favorite; } private void setupMenuIfAuthenticatedUser() { if (AppUtils.inAuthenticatedRealm(this)) { MenuItem favorite = setupFavoriteActionBarIcon(); favorite.setVisible(true); menu.findItem(R.id.menu_refresh).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); setupAddComment(menu); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_favorite: startServiceToFavoriteQuestion(); return true; default: return super.onOptionsItemSelected(item); } } private void startServiceToFavoriteQuestion() { if (!serviceRunningForFavorite) { Intent intent = new Intent(this, QuestionDetailsIntentService.class); if (question.favorited) intent.putExtra(StringConstants.ACTION, QuestionDetailsIntentService.FAVORITE_UNDO); else intent.putExtra(StringConstants.ACTION, QuestionDetailsIntentService.FAVORITE); intent.putExtra(StringConstants.RESULT_RECEIVER, resultReceiver); intent.putExtra(StringConstants.SITE, getIntent().getStringExtra(StringConstants.SITE)); intent.putExtra(StringConstants.ID, question.id); startService(intent); serviceRunningForFavorite = true; } } private void setupAddComment(Menu menu) { MenuItem newCommentMenuItem = menu.findItem(R.id.menu_new_comment); newCommentMenuItem.setVisible(true); newCommentMenuItem.getActionView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setupPostCommentFragment(null); } }); } private void setupPostCommentFragment(String defaultText) { if (viewPager.getCurrentItem() == 0) { displayPostCommentFragment("Comment on question by " + question.owner.displayName, question.id, 0, question.id + "-comment", defaultText); } else { Answer answer = question.answers.get(viewPager.getCurrentItem() - 1); displayPostCommentFragment("Comment on answer by " + answer.owner.getDisplayName(), answer.id, viewPager.getCurrentItem(), answer.id + "-comment", defaultText); } } private void displayPostCommentFragment(String title, long id, int viewPagerPosition, String fragmentTag, String defaultText) { postCommentFragment = new PostCommentFragment(); postCommentFragment.setPostId(id); postCommentFragment.setViewPagerPosition(viewPagerPosition); postCommentFragment.setTitle(title); postCommentFragment.setResultReceiver(resultReceiver); if (commentsDraft.get(fragmentTag) != null) postCommentFragment.setDraftText(commentsDraft.get(fragmentTag)); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.fragmentContainer, postCommentFragment, fragmentTag); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.addToBackStack(fragmentTag); transaction.commit(); } private void setupViewPager() { questionViewPageAdapter = new QuestionViewPageAdapter(getFragmentManager()); viewPager = (ViewPager) findViewById(R.id.viewPager); viewPager.setAdapter(questionViewPageAdapter); titlePageIndicator = (TitlePageIndicator) findViewById(R.id.indicator); titlePageIndicator.setViewPager(viewPager); titlePageIndicator.setOnPageChangeListener(this); } private void prepareIntentAndStartService() { Intent intent = new Intent(this, QuestionDetailsIntentService.class); intent.putExtra(StringConstants.ACTION, QuestionDetailsIntentService.QA); intent.putExtra(StringConstants.RESULT_RECEIVER, resultReceiver); setProgressBarIndeterminateVisibility(true); if (StringConstants.QUESTION_ID.equals(getIntent().getAction())) { long questionId = getIntent().getLongExtra(StringConstants.QUESTION_ID, 0); intent.setAction(StringConstants.QUESTION_ID); intent.putExtra(StringConstants.QUESTION_ID, questionId); intent.putExtra(StringConstants.SITE, getIntent().getStringExtra(StringConstants.SITE)); startService(intent); } else { Question metaDetails = (Question) getIntent().getSerializableExtra(StringConstants.QUESTION); if (metaDetails != null) question = Question.copyMetaDeta(metaDetails); if (question != null) { questionFragment.setQuestion(question); intent.setAction(StringConstants.QUESTION); intent.putExtra(StringConstants.QUESTION_ID, question.id); intent.putExtra(StringConstants.QUESTION, question); intent.putExtra(StringConstants.SITE, getIntent().getStringExtra(StringConstants.SITE)); intent.putExtra(StringConstants.REFRESH, getIntent().getBooleanExtra(StringConstants.REFRESH, false)); startService(intent); } } } @Override public void onBackPressed() { if (commentFragment != null && commentFragment.isVisible()) commentFragment.onBackPressed(); super.onBackPressed(); dismissPostCommentFragmentIfVisible(false); } @Override public void onSaveInstanceState(Bundle outState) { if (question != null) outState.putSerializable(StringConstants.QUESTION, question); super.onSaveInstanceState(outState); } @Override protected void refresh() { Intent intent = getIntent(); finish(); intent.putExtra(StringConstants.REFRESH, true); startActivity(intent); } @Override public void onPageScrollStateChanged(int arg0) { } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageSelected(int position) { int numAnswersDisplayed = questionViewPageAdapter.getCount() - 1; if (commentFragment != null && commentFragment.isVisible()) { getFragmentManager().popBackStackImmediate(); commentFragment = null; } dismissPostCommentFragmentIfVisible(false); if (numAnswersDisplayed < question.answerCount && numAnswersDisplayed - position < 2) { if (question.answers != null && question.answers.size() == numAnswersDisplayed && !serviceRunningForAnswers) { setProgressBarIndeterminateVisibility(true); startServiceForAnswers(); } } } private void dismissPostCommentFragmentIfVisible(boolean finish) { if (postCommentFragment != null) { postCommentFragment.hideSoftKeyboard(); if (!finish) commentsDraft.put(postCommentFragment.getTag(), postCommentFragment.getCurrentText()); getFragmentManager().popBackStackImmediate(); postCommentFragment = null; } } @SuppressWarnings("unchecked") @Override public void onReceiveResult(int resultCode, Bundle resultData) { switch (resultCode) { case QuestionDetailsIntentService.RESULT_CODE_Q_CACHED: setProgressBarIndeterminateVisibility(false); case QuestionDetailsIntentService.RESULT_CODE_Q: question = (Question) resultData.getSerializable(StringConstants.QUESTION); displayQuestion(); if (question.answerCount > 0 && (question.answers == null || question.answers.isEmpty())) startServiceForAnswers(); else setProgressBarIndeterminateVisibility(false); break; case QuestionDetailsIntentService.RESULT_CODE_Q_BODY: questionFragment.displayBody(resultData.getString(StringConstants.BODY)); if (question.answerCount == 0) setProgressBarIndeterminateVisibility(false); break; case QuestionDetailsIntentService.RESULT_CODE_Q_COMMENTS: question.comments = (ArrayList<Comment>) resultData.getSerializable(StringConstants.COMMENTS); questionFragment.setComments(question.comments); break; case QuestionDetailsIntentService.RESULT_CODE_ANSWERS: serviceRunningForAnswers = false; setProgressBarIndeterminateVisibility(false); displayAnswers((ArrayList<Answer>) resultData.getSerializable(StringConstants.ANSWERS), true); break; case WriteIntentService.ACTION_ADD_COMMENT: dismissPostCommentFragmentIfVisible(true); addMyCommentToPost(resultData); break; case QuestionDetailsIntentService.RESULT_CODE_FAVORITE_SUCCESS: serviceRunningForFavorite = false; question.favorited = true; setupFavoriteActionBarIcon(); break; case QuestionDetailsIntentService.RESULT_CODE_FAVORITE_UNDO_SUCCESS: serviceRunningForFavorite = false; question.favorited = false; setupFavoriteActionBarIcon(); break; case QuestionDetailsIntentService.ERROR: if (serviceRunningForAnswers) serviceRunningForAnswers = false; if (serviceRunningForFavorite) serviceRunningForFavorite = false; handleError(resultData); break; default: break; } } private void handleError(Bundle resultData) { setProgressBarIndeterminateVisibility(false); HttpException e = (HttpException) resultData.getSerializable(StringConstants.EXCEPTION); int requestCode = resultData.getInt(StringConstants.REQUEST_CODE, -1); if (requestCode == WriteIntentService.ACTION_ADD_COMMENT) { PostCommentFragment lastPostCommentFragment = findFragmentByTag("postCommentFragment", PostCommentFragment.class); if (lastPostCommentFragment != null) lastPostCommentFragment.setSendError(e.getErrorResponse()); else if (postCommentFragment != null) postCommentFragment.setSendError(e.getErrorResponse()); } else AppUtils.getErrorView(this, e); } protected void addMyCommentToPost(Bundle resultData) { SharedPreferencesUtil.setLong(this, WritePermission.PREF_LAST_COMMENT_WRITE, System.currentTimeMillis()); Comment comment = (Comment) resultData.getSerializable(StringConstants.COMMENT); int viewPagerPosition = resultData.getInt(StringConstants.VIEW_PAGER_POSITION, -1); if (viewPagerPosition == 0) { if (question.comments == null) question.comments = new ArrayList<Comment>(); question.comments.add(comment); questionFragment.setComments(question.comments); } else { AnswerFragment answerFragment = findFragmentByTag(getViewPagerFragmentTag(viewPagerPosition), AnswerFragment.class); answerFragment.onCommentAdd(comment); } } private void startServiceForAnswers() { Intent intent = new Intent(this, QuestionDetailsIntentService.class); intent.setAction(StringConstants.ANSWERS); intent.putExtra(StringConstants.ACTION, QuestionDetailsIntentService.QA); intent.putExtra(StringConstants.RESULT_RECEIVER, resultReceiver); intent.putExtra(StringConstants.SITE, getIntent().getStringExtra(StringConstants.SITE)); intent.putExtra(StringConstants.QUESTION_ID, question.id); intent.putExtra(StringConstants.PAGE, getNextPageNumber()); startService(intent); serviceRunningForAnswers = true; } private void displayQuestion() { if (questionFragment == null) questionFragment = QuestionFragment.newFragment(); questionFragment.setAndDisplay(question); titlePageIndicator.notifyDataSetChanged(); questionViewPageAdapter.notifyDataSetChanged(); } private void displayAnswers(ArrayList<Answer> answers, boolean add) { if (answers != null && !answers.isEmpty()) { if (question.answers == null) question.answers = new ArrayList<Answer>(); if (add && !StringConstants.QUESTION_ID.equals(getIntent().getAction())) question.answers.addAll(answers); titlePageIndicator.notifyDataSetChanged(); questionViewPageAdapter.notifyDataSetChanged(); } } private int getNextPageNumber() { int numAnswersDisplayed = questionViewPageAdapter.getCount() - 1; return (numAnswersDisplayed / 10) + 1; } @Override public void selectQuestionPage() { if (viewPager != null && viewPager.getAdapter().getCount() > 0) { final int currentItem = viewPager.getCurrentItem(); questionFragment.enableNavigationBack(new View.OnClickListener() { @Override public void onClick(View v) { viewPager.setCurrentItem(currentItem); } }); viewPager.setCurrentItem(0); } } private String getViewPagerFragmentTag(int position) { return "android:switcher:" + R.id.viewPager + ":" + position; } @Override public void onShowComments() { if (viewPager.getCurrentItem() == 0) prepareCommentFragment(question.id, question.comments); else { Answer answer = question.answers.get(viewPager.getCurrentItem() - 1); prepareCommentFragment(answer.id, answer.comments); } } private void prepareCommentFragment(long postId, ArrayList<Comment> comments) { if (comments != null && !comments.isEmpty()) showCommentFragment(postId, comments); else Toast.makeText(this, "No comments", Toast.LENGTH_SHORT).show(); } private void showCommentFragment(long postId, ArrayList<Comment> comments) { String fragmentTag = postId + "-" + StringConstants.COMMENTS; commentFragment = findFragmentByTag(fragmentTag, CommentFragment.class); if (commentFragment == null) { commentFragment = new CommentFragment(); commentFragment.setComments(comments); commentFragment.setResultReceiver(resultReceiver); String currentViewPagerFragmentTag = "android:switcher:" + R.id.viewPager + ":" + viewPager.getCurrentItem(); OnCommentChangeListener onCommentChangeListener = (OnCommentChangeListener) getFragmentManager() .findFragmentByTag(currentViewPagerFragmentTag); if (onCommentChangeListener != null) commentFragment.setOnCommentChangeListener(onCommentChangeListener); } FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.fragmentContainer, commentFragment, fragmentTag); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.addToBackStack(fragmentTag); transaction.commit(); } }