/* * Copyright 2011 Azwan Adli Abdullah * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gh4a.fragment; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.RecyclerView; import android.text.SpannableString; import android.text.style.StyleSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.gh4a.BaseActivity; import com.gh4a.Gh4Application; import com.gh4a.ProgressDialogTask; import com.gh4a.R; import com.gh4a.activities.UserActivity; import com.gh4a.adapter.IssueEventAdapter; import com.gh4a.adapter.RootAdapter; import com.gh4a.loader.IssueEventHolder; import com.gh4a.utils.ApiHelpers; import com.gh4a.utils.AvatarHandler; import com.gh4a.utils.HttpImageGetter; import com.gh4a.utils.IntentUtils; import com.gh4a.utils.StringUtils; import com.gh4a.utils.UiUtils; import org.eclipse.egit.github.core.Comment; import org.eclipse.egit.github.core.Issue; import org.eclipse.egit.github.core.Label; import org.eclipse.egit.github.core.RepositoryId; import org.eclipse.egit.github.core.User; import org.eclipse.egit.github.core.service.IssueService; import java.io.IOException; import java.util.List; import java.util.Set; public abstract class IssueFragmentBase extends ListDataBaseFragment<IssueEventHolder> implements View.OnClickListener, IssueEventAdapter.OnCommentAction<IssueEventHolder>, CommentBoxFragment.Callback { protected static final int REQUEST_EDIT = 1000; protected View mListHeaderView; protected Issue mIssue; protected String mRepoOwner; protected String mRepoName; private IntentUtils.InitialCommentMarker mInitialComment; private boolean mIsCollaborator; private boolean mListShown; private CommentBoxFragment mCommentFragment; private IssueEventAdapter mAdapter; private HttpImageGetter mImageGetter; protected static Bundle buildArgs(String repoOwner, String repoName, Issue issue, boolean isCollaborator, IntentUtils.InitialCommentMarker initialComment) { Bundle args = new Bundle(); args.putString("owner", repoOwner); args.putString("repo", repoName); args.putSerializable("issue", issue); args.putSerializable("collaborator", isCollaborator); args.putParcelable("initial_comment", initialComment); return args; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); mRepoOwner = args.getString("owner"); mRepoName = args.getString("repo"); mIssue = (Issue) args.getSerializable("issue"); mIsCollaborator = args.getBoolean("collaborator"); mInitialComment = args.getParcelable("initial_comment"); args.remove("initial_comment"); updateCommentLockState(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View listContent = super.onCreateView(inflater, container, savedInstanceState); View v = inflater.inflate(R.layout.issue, container, false); FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container); listContainer.addView(listContent); mImageGetter = new HttpImageGetter(inflater.getContext()); updateCommentSectionVisibility(v); return v; } @Override public void onDestroyView() { super.onDestroyView(); mImageGetter.destroy(); mImageGetter = null; if (mAdapter != null) { mAdapter.destroy(); mAdapter = null; } } @Override protected void onRecyclerViewInflated(RecyclerView view, LayoutInflater inflater) { super.onRecyclerViewInflated(view, inflater); mListHeaderView = inflater.inflate(R.layout.issue_comment_list_header, view, false); mAdapter.setHeaderView(mListHeaderView); } @Override public void onActivityCreated(Bundle savedInstanceState) { FragmentManager fm = getChildFragmentManager(); mCommentFragment = (CommentBoxFragment) fm.findFragmentById(R.id.comment_box); fillData(); fillLabels(mIssue.getLabels()); updateCommentLockState(); super.onActivityCreated(savedInstanceState); } @Override public void onRefresh() { if (mListHeaderView != null) { getActivity().supportInvalidateOptionsMenu(); fillLabels(null); } if (mImageGetter != null) { mImageGetter.clearHtmlCache(); } super.onRefresh(); } @Override public void onResume() { super.onResume(); mImageGetter.resume(); mAdapter.resume(); } @Override public void onPause() { super.onPause(); mImageGetter.pause(); mAdapter.pause(); } @Override public boolean canChildScrollUp() { if (mCommentFragment != null && mCommentFragment.canChildScrollUp()) { return true; } return super.canChildScrollUp(); } public void reloadEvents(boolean alsoClearCaches) { if (mAdapter != null && !alsoClearCaches) { // Don't clear adapter's cache, we're only interested in the new event mAdapter.suppressCacheClearOnNextClear(); } super.onRefresh(); } @Override protected RootAdapter<IssueEventHolder, ? extends RecyclerView.ViewHolder> onCreateAdapter() { mAdapter = new IssueEventAdapter(getActivity(), mRepoOwner, mRepoName, mIssue.getNumber(), this); mAdapter.setLocked(isLocked()); return mAdapter; } @Override protected void onAddData(RootAdapter<IssueEventHolder, ?> adapter, List<IssueEventHolder> data) { super.onAddData(adapter, data); if (mInitialComment != null) { for (int i = 0; i < data.size(); i++) { IssueEventHolder event = data.get(i); if (event.comment == null) { continue; } if (mInitialComment.matches(event.comment.getId(), event.getCreatedAt())) { scrollToAndHighlightPosition(i + 1 /* adjust for header view */); break; } } mInitialComment = null; } updateMentionUsers(); } @Override protected int getEmptyTextResId() { return 0; } @Override protected void updateEmptyState() { // we're never empty -> don't call super } @Override protected void setContentShown(boolean shown) { super.setContentShown(shown); mListShown = shown; updateCommentSectionVisibility(getView()); } private void updateCommentSectionVisibility(View v) { if (v == null) { return; } int commentVisibility = mListShown && Gh4Application.get().isAuthorized() ? View.VISIBLE : View.GONE; v.findViewById(R.id.comment_box).setVisibility(commentVisibility); } private boolean isLocked() { return mIssue.isLocked() && !mIsCollaborator; } private void updateMentionUsers() { Set<User> users = mAdapter.getUsers(); if (mIssue.getUser() != null) { users.add(mIssue.getUser()); } mCommentFragment.setMentionUsers(users); } private void updateCommentLockState() { if (mCommentFragment != null) { mCommentFragment.setLocked(isLocked()); } } private void fillData() { ImageView ivGravatar = (ImageView) mListHeaderView.findViewById(R.id.iv_gravatar); AvatarHandler.assignAvatar(ivGravatar, mIssue.getUser()); ivGravatar.setTag(mIssue.getUser()); ivGravatar.setOnClickListener(this); TextView tvExtra = (TextView) mListHeaderView.findViewById(R.id.tv_extra); tvExtra.setText(ApiHelpers.getUserLogin(getActivity(), mIssue.getUser())); TextView tvTimestamp = (TextView) mListHeaderView.findViewById(R.id.tv_timestamp); tvTimestamp.setText(StringUtils.formatRelativeTime(getActivity(), mIssue.getCreatedAt(), true)); String body = mIssue.getBodyHtml(); TextView descriptionView = (TextView) mListHeaderView.findViewById(R.id.tv_desc); descriptionView.setMovementMethod(UiUtils.CHECKING_LINK_METHOD); if (!StringUtils.isBlank(body)) { mImageGetter.bind(descriptionView, body, mIssue.getId()); if (!isLocked()) { descriptionView.setCustomSelectionActionModeCallback( new UiUtils.QuoteActionModeCallback(descriptionView) { @Override public void onTextQuoted(CharSequence text) { quoteText(text); } }); } else { descriptionView.setCustomSelectionActionModeCallback(null); } } else { SpannableString noDescriptionString = new SpannableString(getString(R.string.issue_no_description)); noDescriptionString.setSpan(new StyleSpan(Typeface.ITALIC), 0, noDescriptionString.length(), 0); descriptionView.setText(noDescriptionString); } View milestoneGroup = mListHeaderView.findViewById(R.id.milestone_container); if (mIssue.getMilestone() != null) { TextView tvMilestone = (TextView) mListHeaderView.findViewById(R.id.tv_milestone); tvMilestone.setText(mIssue.getMilestone().getTitle()); milestoneGroup.setVisibility(View.VISIBLE); } else { milestoneGroup.setVisibility(View.GONE); } View assigneeGroup = mListHeaderView.findViewById(R.id.assignee_container); List<User> assignees = mIssue.getAssignees(); if (assignees != null && !assignees.isEmpty()) { ViewGroup assigneeContainer = (ViewGroup) mListHeaderView.findViewById(R.id.assignee_list); LayoutInflater inflater = getLayoutInflater(null); assigneeContainer.removeAllViews(); for (User assignee : assignees) { View row = inflater.inflate(R.layout.row_assignee, assigneeContainer, false); TextView tvAssignee = (TextView) row.findViewById(R.id.tv_assignee); tvAssignee.setText(ApiHelpers.getUserLogin(getActivity(), assignee)); ImageView ivAssignee = (ImageView) row.findViewById(R.id.iv_assignee); AvatarHandler.assignAvatar(ivAssignee, assignee); ivAssignee.setTag(assignee); ivAssignee.setOnClickListener(this); assigneeContainer.addView(row); } assigneeGroup.setVisibility(View.VISIBLE); } else { assigneeGroup.setVisibility(View.GONE); } assignHighlightColor(); bindSpecialViews(mListHeaderView); } private void fillLabels(List<Label> labels) { View labelGroup = mListHeaderView.findViewById(R.id.label_container); if (labels != null && !labels.isEmpty()) { TextView labelView = (TextView) mListHeaderView.findViewById(R.id.labels); labelView.setText(UiUtils.formatLabelList(getActivity(), labels)); labelGroup.setVisibility(View.VISIBLE); } else { labelGroup.setVisibility(View.GONE); } } @Override public void onClick(View v) { Intent intent = UserActivity.makeIntent(getActivity(), (User) v.getTag()); if (intent != null) { startActivity(intent); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_EDIT) { if (resultCode == Activity.RESULT_OK) { reloadEvents(true); getActivity().setResult(Activity.RESULT_OK); } } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void quoteText(CharSequence text) { mCommentFragment.addQuote(text); } @Override public void onSendCommentInBackground(String comment) throws IOException { IssueService issueService = (IssueService) Gh4Application.get().getService(Gh4Application.ISSUE_SERVICE); issueService.createComment(mRepoOwner, mRepoName, mIssue.getNumber(), comment); } @Override public void onCommentSent() { // reload comments if (isAdded()) { reloadEvents(false); } } @Override public void deleteComment(final IssueEventHolder comment) { new AlertDialog.Builder(getActivity()) .setMessage(R.string.delete_comment_message) .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new DeleteCommentTask(getBaseActivity(), comment.comment).schedule(); } }) .setNegativeButton(R.string.cancel, null) .show(); } protected abstract void bindSpecialViews(View headerView); protected abstract void assignHighlightColor(); protected abstract void deleteCommentInBackground(RepositoryId repoId, Comment comment) throws Exception; private class DeleteCommentTask extends ProgressDialogTask<Void> { private final Comment mComment; public DeleteCommentTask(BaseActivity activity, Comment comment) { super(activity, R.string.deleting_msg); mComment = comment; } @Override protected ProgressDialogTask<Void> clone() { return new DeleteCommentTask(getBaseActivity(), mComment); } @Override protected Void run() throws Exception { RepositoryId repoId = new RepositoryId(mRepoOwner, mRepoName); deleteCommentInBackground(repoId, mComment); return null; } @Override protected void onSuccess(Void result) { reloadEvents(false); getActivity().setResult(Activity.RESULT_OK); } @Override protected String getErrorMessage() { return getContext().getString(R.string.error_delete_comment); } } }