Add vote comment
This commit is contained in:
1 parent
0bc4569084
commit
0aad8ca1e8
11 files changed
+331
-15
No files matched your search
@@ -56,6 +56,7 @@ public class EhClient {
|
||||
public static final int METHOD_GET_TORRENT_LIST = 12;
|
||||
public static final int METHOD_GET_WHATS_HOT = 13;
|
||||
public static final int METHOD_GET_PROFILE = 14;
|
||||
public static final int METHOD_VOTE_COMMENT = 15;
|
||||
|
||||
private final ThreadPoolExecutor mRequestThreadPool;
|
||||
private final OkHttpClient mOkHttpClient;
|
||||
@@ -177,6 +178,8 @@ public class EhClient {
|
||||
return EhEngine.getWhatsHot(this, mOkHttpClient);
|
||||
case METHOD_GET_PROFILE:
|
||||
return EhEngine.getProfile(this, mOkHttpClient);
|
||||
case METHOD_VOTE_COMMENT:
|
||||
return EhEngine.voteComment(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (Integer) params[3]);
|
||||
default:
|
||||
return new IllegalStateException("Can't detect method " + mMethod);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.hippo.ehviewer.client.parser.ProfileParser;
|
||||
import com.hippo.ehviewer.client.parser.RateGalleryParser;
|
||||
import com.hippo.ehviewer.client.parser.SignInParser;
|
||||
import com.hippo.ehviewer.client.parser.TorrentParser;
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser;
|
||||
import com.hippo.ehviewer.client.parser.WhatsHotParser;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
import com.hippo.util.ReadableTime;
|
||||
@@ -672,4 +673,40 @@ public class EhEngine {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static VoteCommentParser.Result voteComment(EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long gid, String token, long commentId, int commentVote) throws Exception {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put("method", "votecomment");
|
||||
json.put("apiuid", APIUID);
|
||||
json.put("apikey", APIKEY);
|
||||
json.put("gid", gid);
|
||||
json.put("token", token);
|
||||
json.put("comment_id", commentId);
|
||||
json.put("comment_vote", commentVote);
|
||||
final RequestBody requestBody = RequestBody.create(JSON, json.toString());
|
||||
String url = EhUrl.getApiUrl();
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, task.getEhConfig())
|
||||
.post(requestBody)
|
||||
.build();
|
||||
Call call = okHttpClient.newCall(request);
|
||||
|
||||
// Put call
|
||||
task.setCall(call);
|
||||
|
||||
String body = null;
|
||||
Headers headers = null;
|
||||
int code = -1;
|
||||
try {
|
||||
Response response = call.execute();
|
||||
code = response.code();
|
||||
headers = response.headers();
|
||||
body = response.body().string();
|
||||
return VoteCommentParser.parse(body, commentVote);
|
||||
} catch (Exception e) {
|
||||
throwException(call, code, headers, body, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ import android.os.Parcelable;
|
||||
|
||||
public class GalleryComment implements Parcelable {
|
||||
|
||||
// 0 for uploader comment. can't vote
|
||||
public long id;
|
||||
public int score;
|
||||
public boolean voteUp;
|
||||
public boolean voteDown;
|
||||
public long time;
|
||||
public String user;
|
||||
public String comment;
|
||||
@@ -32,6 +37,10 @@ public class GalleryComment implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(this.id);
|
||||
dest.writeInt(this.score);
|
||||
dest.writeByte(voteUp ? (byte) 1 : (byte) 0);
|
||||
dest.writeByte(voteDown ? (byte) 1 : (byte) 0);
|
||||
dest.writeLong(this.time);
|
||||
dest.writeString(this.user);
|
||||
dest.writeString(this.comment);
|
||||
@@ -41,16 +50,22 @@ public class GalleryComment implements Parcelable {
|
||||
}
|
||||
|
||||
protected GalleryComment(Parcel in) {
|
||||
this.id = in.readLong();
|
||||
this.score = in.readInt();
|
||||
this.voteUp = in.readByte() != 0;
|
||||
this.voteDown = in.readByte() != 0;
|
||||
this.time = in.readLong();
|
||||
this.user = in.readString();
|
||||
this.comment = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<GalleryComment> CREATOR = new Creator<GalleryComment>() {
|
||||
@Override
|
||||
public GalleryComment createFromParcel(Parcel source) {
|
||||
return new GalleryComment(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryComment[] newArray(int size) {
|
||||
return new GalleryComment[size];
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
@@ -342,11 +343,35 @@ public class GalleryDetailParser {
|
||||
public static GalleryComment parseComment(Element element) {
|
||||
try {
|
||||
GalleryComment comment = new GalleryComment();
|
||||
// Id
|
||||
Element a = element.previousElementSibling();
|
||||
String name = a.attr("name");
|
||||
comment.id = Integer.parseInt(StringUtils.trim(name).substring(1));
|
||||
// Vote up and vote down
|
||||
Element c4 = JsoupUtils.getElementByClass(element, "c4");
|
||||
if (null != c4) {
|
||||
Elements es = c4.children();
|
||||
if (2 == es.size()) {
|
||||
comment.voteUp = !TextUtils.isEmpty(StringUtils.trim(es.get(0).attr("style")));
|
||||
comment.voteDown = !TextUtils.isEmpty(StringUtils.trim(es.get(1).attr("style")));
|
||||
}
|
||||
}
|
||||
// Score
|
||||
Element c5 = JsoupUtils.getElementByClass(element, "c5");
|
||||
if (null != c5) {
|
||||
Elements es = c5.children();
|
||||
if (!es.isEmpty()) {
|
||||
comment.score = NumberUtils.parseIntSafely(StringUtils.trim(es.get(0).text()), 0);
|
||||
}
|
||||
}
|
||||
// time
|
||||
Element c3 = JsoupUtils.getElementByClass(element, "c3");
|
||||
String temp = c3.ownText();
|
||||
temp = temp.substring("Posted on ".length(), temp.length() - " by: ".length());
|
||||
comment.time = WEB_COMMENT_DATE_FORMAT.parse(temp).getTime();
|
||||
// user
|
||||
comment.user = c3.child(0).text();
|
||||
// comment
|
||||
comment.comment = JsoupUtils.getElementByClass(element, "c6").html();
|
||||
return comment;
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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.hippo.ehviewer.client.parser;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class VoteCommentParser {
|
||||
|
||||
public static class Result {
|
||||
public long id;
|
||||
public int score;
|
||||
public int vote;
|
||||
public int expectVote;
|
||||
}
|
||||
|
||||
// {"comment_id":1253922,"comment_score":-19,"comment_vote":0}
|
||||
public static Result parse(String body, int vote) throws JSONException {
|
||||
Result result = new Result();
|
||||
JSONObject jo = new JSONObject(body);
|
||||
result.id = jo.getLong("comment_id");
|
||||
result.score = jo.getInt("comment_score");
|
||||
result.vote = jo.getInt("comment_vote");
|
||||
result.expectVote = vote;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -18,19 +18,32 @@ package com.hippo.ehviewer.ui.scene;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.DefaultItemAnimator;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -38,7 +51,6 @@ import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.reveal.ViewAnimationUtils;
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.easyrecyclerview.LinearDividerItemDecoration;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
@@ -48,7 +60,9 @@ import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.reveal.ViewAnimationUtils;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.text.Html;
|
||||
@@ -66,7 +80,9 @@ import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
public final class GalleryCommentsScene extends ToolbarScene
|
||||
implements EasyRecyclerView.OnItemClickListener,
|
||||
EasyRecyclerView.OnItemLongClickListener, View.OnClickListener {
|
||||
View.OnClickListener {
|
||||
|
||||
public static final String TAG = GalleryCommentsScene.class.getSimpleName();
|
||||
|
||||
public static final String KEY_GID = "gid";
|
||||
public static final String KEY_TOKEN = "token";
|
||||
@@ -172,9 +188,13 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setOnItemClickListener(this);
|
||||
mRecyclerView.setOnItemLongClickListener(this);
|
||||
mRecyclerView.setPadding(mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
|
||||
mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingBottom() + paddingBottomFab);
|
||||
// Cancel change animator
|
||||
RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
|
||||
if (itemAnimator instanceof DefaultItemAnimator) {
|
||||
((DefaultItemAnimator) itemAnimator).setSupportsChangeAnimations(false);
|
||||
}
|
||||
|
||||
mSendImage.setOnClickListener(this);
|
||||
mFab.setOnClickListener(this);
|
||||
@@ -215,6 +235,62 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
private void voteComment(long id, int vote) {
|
||||
Context context = getContext2();
|
||||
MainActivity activity = getActivity2();
|
||||
if (null == context || null == activity) {
|
||||
return;
|
||||
}
|
||||
|
||||
EhRequest request = new EhRequest()
|
||||
.setMethod(EhClient.METHOD_VOTE_COMMENT)
|
||||
.setArgs(mGid, mToken, id, vote)
|
||||
.setCallback(new VoteCommentListener(context,
|
||||
activity.getStageId(), getTag()));
|
||||
EhApplication.getEhClient(context).execute(request);
|
||||
}
|
||||
|
||||
private void showCommentDialog(final int position) {
|
||||
final Context context = getContext2();
|
||||
if (null == context || null == mComments || position >= mComments.length || position < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GalleryComment comment = mComments[position];
|
||||
String[] menuArray;
|
||||
Resources resources = context.getResources();
|
||||
if (0 == comment.id) {
|
||||
// 0 id is uploader comment, can't vote
|
||||
menuArray = new String[1];
|
||||
menuArray[0] = resources.getString(R.string.copy_comment_text);
|
||||
} else {
|
||||
menuArray = new String[3];
|
||||
menuArray[0] = resources.getString(R.string.copy_comment_text);
|
||||
menuArray[1] = resources.getString(comment.voteUp ? R.string.cancel_vote_up : R.string.vote_up);
|
||||
menuArray[2] = resources.getString(comment.voteDown ? R.string.cancel_vote_down : R.string.vote_down);
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setItems(menuArray, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case 0: // Copy
|
||||
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setPrimaryClip(ClipData.newPlainText(null, comment.comment));
|
||||
showTip(R.string.copied_to_clipboard, LENGTH_SHORT);
|
||||
break;
|
||||
case 1: // Vote up
|
||||
voteComment(comment.id, 1);
|
||||
break;
|
||||
case 2: // Vote down
|
||||
voteComment(comment.id, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
|
||||
Activity activity = getActivity2();
|
||||
@@ -233,12 +309,10 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(EasyRecyclerView parent, View view, int position, long id) {
|
||||
return false;
|
||||
showCommentDialog(position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateView(boolean animation) {
|
||||
@@ -406,9 +480,9 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
|
||||
private class CommentHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView user;
|
||||
public TextView time;
|
||||
public LinkifyTextView comment;
|
||||
public final TextView user;
|
||||
public final TextView time;
|
||||
public final LinkifyTextView comment;
|
||||
|
||||
public CommentHolder(View itemView) {
|
||||
super(itemView);
|
||||
@@ -432,6 +506,24 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
return new CommentHolder(mInflater.inflate(R.layout.item_gallery_comment, parent, false));
|
||||
}
|
||||
|
||||
public CharSequence generateComment(Context context, TextView textView, GalleryComment comment) {
|
||||
SpannableStringBuilder ssb = Html.fromHtml(comment.comment, new URLImageGetter(textView,
|
||||
EhApplication.getConaco(context)), null);
|
||||
|
||||
if (0 != comment.id && 0 != comment.score) {
|
||||
int score = comment.score;
|
||||
String scoreString = score > 0 ? "+" + score : Integer.toString(score);
|
||||
SpannableString ss = new SpannableString(scoreString);
|
||||
ss.setSpan(new RelativeSizeSpan(0.8f), 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ss.setSpan(new ForegroundColorSpan(context.getResources().getColor(R.color.secondary_text_default_light))
|
||||
, 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append(" ").append(ss);
|
||||
}
|
||||
|
||||
return TextUrl.handleTextUrl(ssb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(CommentHolder holder, int position) {
|
||||
Context context = getContext2();
|
||||
@@ -442,8 +534,7 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
GalleryComment comment = mComments[position];
|
||||
holder.user.setText(comment.user);
|
||||
holder.time.setText(ReadableTime.getTimeAgo(comment.time));
|
||||
holder.comment.setText(TextUrl.handleTextUrl(Html.fromHtml(comment.comment,
|
||||
new URLImageGetter(holder.comment, EhApplication.getConaco(context)), null)));
|
||||
holder.comment.setText(generateComment(context, holder.comment, comment));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -471,6 +562,37 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
updateView(true);
|
||||
}
|
||||
|
||||
private void onVoteCommentSuccess(VoteCommentParser.Result result) {
|
||||
if (null == mAdapter || null == mComments) {
|
||||
return;
|
||||
}
|
||||
|
||||
int position = -1;
|
||||
for (int i = 0, n = mComments.length; i < n; i++) {
|
||||
GalleryComment comment = mComments[i];
|
||||
if (comment.id == result.id) {
|
||||
position = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == position) {
|
||||
Log.d(TAG, "Can't find comment with id " + result.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update comment
|
||||
GalleryComment comment = mComments[position];
|
||||
comment.score = result.score;
|
||||
if (result.expectVote > 0) {
|
||||
comment.voteUp = 0 != result.vote;
|
||||
} else {
|
||||
comment.voteDown = 0 != result.vote;
|
||||
}
|
||||
|
||||
mAdapter.notifyItemChanged(position);
|
||||
}
|
||||
|
||||
private static class CommentGalleryListener extends EhCallback<GalleryCommentsScene, GalleryComment[]> {
|
||||
|
||||
public CommentGalleryListener(Context context, int stageId, String sceneTag) {
|
||||
@@ -501,4 +623,37 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
return scene instanceof GalleryCommentsScene;
|
||||
}
|
||||
}
|
||||
|
||||
private static class VoteCommentListener extends EhCallback<GalleryCommentsScene, VoteCommentParser.Result> {
|
||||
|
||||
public VoteCommentListener(Context context, int stageId, String sceneTag) {
|
||||
super(context, stageId, sceneTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(VoteCommentParser.Result result) {
|
||||
showTip(result.expectVote > 0 ?
|
||||
(0 != result.vote ? R.string.vote_up_successfully : R.string.cancel_vote_up_successfully) :
|
||||
(0 != result.vote ? R.string.vote_down_successfully : R.string.cancel_vote_down_successfully),
|
||||
LENGTH_SHORT);
|
||||
|
||||
GalleryCommentsScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onVoteCommentSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
showTip(R.string.vote_failed, LENGTH_SHORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {}
|
||||
|
||||
@Override
|
||||
public boolean isInstance(SceneFragment scene) {
|
||||
return scene instanceof GalleryCommentsScene;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public class Html {
|
||||
*
|
||||
* <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
|
||||
*/
|
||||
public static Spanned fromHtml(String source, ImageGetter imageGetter,
|
||||
public static SpannableStringBuilder fromHtml(String source, ImageGetter imageGetter,
|
||||
TagHandler tagHandler) {
|
||||
Parser parser = new Parser();
|
||||
try {
|
||||
@@ -462,7 +462,7 @@ class HtmlToSpannedConverter implements ContentHandler {
|
||||
mReader = parser;
|
||||
}
|
||||
|
||||
public Spanned convert() {
|
||||
public SpannableStringBuilder convert() {
|
||||
|
||||
mReader.setContentHandler(this);
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user