diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java index 84c47b199..64804e5d5 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java @@ -41,6 +41,7 @@ public class EhClient { public static final int METHOD_GET_GALLERY_LIST = 1; public static final int METHOD_GET_GALLERY_DETAIL = 2; public static final int METHOD_GET_LARGE_PREVIEW_SET = 3; + public static final int METHOD_GET_RATE_GALLERY = 4; private final ThreadPoolExecutor mRequestThreadPool; private final OkHttpClient mOkHttpClient; @@ -149,6 +150,17 @@ public class EhClient { } } + private Object rateGallery(EhConfig ehConfig, Object... params) throws Exception { + Call call = EhEngine.prepareRateGallery(mOkHttpClient, ehConfig, + (Integer) params[0], (String) params[1], (Float) params[2]); + if (!mStop) { + mCall = call; + return EhEngine.doRateGallery(call); + } else { + throw new CancelledException(); + } + } + @Override protected Object doInBackground(Object... params) { try { @@ -161,6 +173,8 @@ public class EhClient { return getGalleryDetail(mEhConfig, params); case METHOD_GET_LARGE_PREVIEW_SET: return getLargePreviewSet(mEhConfig, params); + case METHOD_GET_RATE_GALLERY: + return rateGallery(mEhConfig, params); default: return new IllegalStateException("Can't detect method " + mMethod); } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java index 3bb0a05a2..713acc735 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java @@ -26,14 +26,20 @@ import com.hippo.ehviewer.client.exception.CancelledException; import com.hippo.ehviewer.client.exception.EhException; import com.hippo.ehviewer.client.parser.GalleryDetailParser; import com.hippo.ehviewer.client.parser.GalleryListParser; +import com.hippo.ehviewer.client.parser.RateGalleryParser; import com.hippo.ehviewer.client.parser.SignInParser; import com.hippo.network.StatusCodeException; +import org.json.JSONException; +import org.json.JSONObject; + import okhttp3.Call; import okhttp3.FormBody; import okhttp3.Headers; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; public class EhEngine { @@ -44,6 +50,11 @@ public class EhEngine { private static final String SAD_PANDA_TYPE = "image/gif"; private static final String SAD_PANDA_LENGTH = "9615"; + public static final long APIUID = 1363542; + public static final String APIKEY = "f4b5407ab1727b9d08d7"; + + public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static void throwException(Call call, int code, @Nullable Headers headers, @Nullable String body, Exception e) throws Exception { if (call.isCanceled()) { @@ -167,4 +178,42 @@ public class EhEngine { throw e; } } + + // rating 0.0 - 0.5 + public static Call prepareRateGallery(OkHttpClient okHttpClient, + EhConfig ehConfig, int gid, String token, float rating) throws JSONException { + final JSONObject json = new JSONObject(); + json.put("method", "rategallery"); + json.put("apiuid", APIUID); + json.put("apikey", APIKEY); + json.put("gid", gid); + json.put("token", token); + json.put("rating", (int) Math.ceil(rating * 2)); + + final RequestBody body = RequestBody.create(JSON, json.toString()); + + String url = EhUrl.API_EX; + Log.d(TAG, url); + + Request request = new EhRequestBuilder(url, ehConfig) + .post(body) + .build(); + return okHttpClient.newCall(request); + } + + public static RateGalleryParser.Result doRateGallery(Call call) throws Exception { + 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 RateGalleryParser.parse(body); + } catch (Exception e) { + throwException(call, code, headers, body, e); + throw e; + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java b/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java index bc0c779df..9e1a22ad0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java @@ -27,6 +27,8 @@ public class EhUrl { public static final String API_SIGN_IN = "http://forums.e-hentai.org/index.php?act=Login&CODE=01"; + public static final String API_EX = HOST_EX + "api.php"; + public static String getGalleryDetailUrl(int gid, String token) { return getGalleryDetailUrl(gid, token, 0); } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java new file mode 100644 index 000000000..7574c9798 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 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 com.hippo.ehviewer.client.exception.ParseException; + +import org.json.JSONException; +import org.json.JSONObject; + +public class RateGalleryParser { + + public static class Result { + public float rating; + public int ratedTimes; + } + + public static Result parse(String body) throws Exception { + try { + JSONObject jsonObject = new JSONObject(body); + Result result = new Result(); + result.rating = (float)jsonObject.getDouble("rating_avg"); + result.ratedTimes = jsonObject.getInt("rating_cnt"); + return result; + } catch (JSONException e) { + Exception exception = new ParseException("Can't parse rate gallery", body); + exception.initCause(e); + throw exception; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java index 7e799ab47..21cd36624 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java @@ -42,7 +42,9 @@ import com.hippo.ehviewer.OpenUrlHelper; import com.hippo.util.ReadableTime; import com.hippo.util.TextUrl; import com.hippo.vector.VectorDrawable; +import com.hippo.view.ViewTransition; import com.hippo.widget.LinkifyTextView; +import com.hippo.widget.SimpleImageView; import com.hippo.yorozuya.LayoutUtils; public final class GalleryCommentsScene extends ToolbarScene @@ -52,7 +54,9 @@ public final class GalleryCommentsScene extends ToolbarScene public static final String KEY_COMMENTS = "comments"; private GalleryComment[] mComments; + private CommentAdapter mAdapter; + private ViewTransition mViewTransition; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -99,6 +103,8 @@ public final class GalleryCommentsScene extends ToolbarScene @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_gallery_comments, container, false); EasyRecyclerView recyclerView = (EasyRecyclerView) view.findViewById(R.id.recycler_view); + View tip = view.findViewById(R.id.tip); + SimpleImageView tipImage = (SimpleImageView) tip.findViewById(R.id.tip_image); FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fab); mAdapter = new CommentAdapter(); @@ -115,11 +121,28 @@ public final class GalleryCommentsScene extends ToolbarScene recyclerView.setOnItemClickListener(this); recyclerView.setOnItemLongClickListener(this); + tipImage.setDrawable(VectorDrawable.create(getContext(), R.drawable.sadpanda_head)); + fab.setImageDrawable(VectorDrawable.create(getContext(), R.drawable.ic_reply)); + mViewTransition = new ViewTransition(recyclerView, tip); + + if (mComments == null || mComments.length <= 0) { + mViewTransition.showView(1); + } else { + mViewTransition.showView(0); + } + return view; } + @Override + public void onDestroyView() { + super.onDestroyView(); + mAdapter = null; + mViewTransition = null; + } + @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index 53203fdcf..86a727cc2 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -16,8 +16,10 @@ package com.hippo.ehviewer.ui.scene; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; +import android.content.DialogInterface; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; @@ -25,6 +27,7 @@ import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.PopupMenu; import android.text.TextUtils; import android.transition.TransitionInflater; @@ -37,6 +40,7 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RatingBar; import android.widget.TextView; +import android.widget.Toast; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.OpenUrlHelper; @@ -52,6 +56,7 @@ import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.client.data.GalleryTagGroup; import com.hippo.ehviewer.client.data.LargePreviewSet; import com.hippo.ehviewer.client.data.ListUrlBuilder; +import com.hippo.ehviewer.client.parser.RateGalleryParser; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.SceneFragment; import com.hippo.scene.StageActivity; @@ -66,6 +71,7 @@ import com.hippo.vector.VectorDrawable; import com.hippo.view.ViewTransition; import com.hippo.widget.AutoWrapLayout; import com.hippo.widget.LoadImageView; +import com.hippo.widget.ProgressiveRatingBar; import com.hippo.widget.SimpleGridLayout; import com.hippo.widget.SimpleImageView; import com.hippo.yorozuya.SimpleHandler; @@ -152,11 +158,14 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene private AnimatedVectorDrawable mHeartOutlineDrawable; private PopupMenu mPopupMenu; + @Nullable private String mAction; + @Nullable private GalleryInfo mGalleryInfo; private int mGid; private String mToken; + @Nullable private GalleryDetail mGalleryDetail; private int mRequestId; @@ -767,7 +776,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene private String getAllRatingText(float rating, int ratedTimes) { Resources resources = getResources(); - return resources.getString(R.string.rating_text, getRatingText(rating, resources), ratedTimes); + return resources.getString(R.string.rating_text, getRatingText(rating, resources), rating, ratedTimes); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -836,7 +845,21 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene Bundle args = new Bundle(); args.putParcelable(GalleryInfoScene.KEY_GALLERY_DETAIL, mGalleryDetail); startScene(GalleryInfoScene.class, args); + } else if (mRate == v) { + if (mGalleryDetail == null) { + return; + } + RateDialogHelper helper = new RateDialogHelper(mGalleryDetail.rating); + new AlertDialog.Builder(getContext()) + .setTitle(R.string.rate) + .setView(helper.view) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, helper) + .show(); } else if (mComments == v) { + if (mGalleryDetail == null) { + return; + } Bundle args = new Bundle(); args.putParcelableArray(GalleryCommentsScene.KEY_COMMENTS, mGalleryDetail.comments); startScene(GalleryCommentsScene.class, args); @@ -929,6 +952,17 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene adjustViewVisibility(STATE_FAILED, true); } + private void onRateGallerySuccess(RateGalleryParser.Result result) { + if (mGalleryDetail != null) { + mGalleryDetail.rating = result.rating; + mGalleryDetail.ratedTimes = result.ratedTimes; + } + + // Update UI + mRatingText.setText(getAllRatingText(result.rating, result.ratedTimes)); + mRating.setRating(result.rating); + } + private static class GetGalleryDetailListener implements EhClient.Callback { private EhApplication mApplication; @@ -985,4 +1019,98 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene mApplication.removeGlobalStuff(this); } } + + private class RateDialogHelper implements ProgressiveRatingBar.OnUserRateListener, + DialogInterface.OnClickListener { + + public View view; + private ProgressiveRatingBar mRatingBar; + private TextView mRatingText; + + @SuppressLint("InflateParams") + private RateDialogHelper(float rating) { + view = getActivity().getLayoutInflater().inflate(R.layout.dialog_rate, null); + mRatingText = (TextView) view.findViewById(R.id.rating_text); + mRatingBar = (ProgressiveRatingBar) view.findViewById(R.id.rating_view); + mRatingText.setText(getRatingText(rating, getResources())); + mRatingBar.setRating(rating); + mRatingBar.setOnUserRateListener(this); + } + + @Override + public void onUserRate(float rating) { + mRatingText.setText(getRatingText(rating, getResources())); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which != DialogInterface.BUTTON_POSITIVE || mGalleryDetail == null) { + return; + } + + EhRequest request = new EhRequest() + .setMethod(EhClient.METHOD_GET_RATE_GALLERY) + .setArgs(mGalleryDetail.gid, mGalleryDetail.token, mRatingBar.getRating()) + .setCallback(new RateGalleryListener(getContext(), + ((StageActivity) getActivity()).getStageId(), getTag(), mGalleryDetail.gid)); + EhApplication.getEhClient(getContext()).execute(request); + } + } + + private static class RateGalleryListener implements EhClient.Callback { + + private EhApplication mApplication; + private int mStageId; + private String mSceneTag; + private int mGid; + + public RateGalleryListener(Context context, int stageId, String sceneTag, int gid) { + mApplication = (EhApplication) context.getApplicationContext(); + mStageId = stageId; + mSceneTag = sceneTag; + mGid = gid; + } + + private GalleryDetailScene getScene() { + StageActivity stage = mApplication.findStageActivityById(mStageId); + if (stage == null) { + return null; + } + SceneFragment scene = stage.findSceneByTag(mSceneTag); + if (scene instanceof GalleryDetailScene) { + return (GalleryDetailScene) scene; + } else { + return null; + } + } + + @Override + public void onSuccess(RateGalleryParser.Result result) { + // Show toast + Toast.makeText(mApplication, R.string.rate_successfully, Toast.LENGTH_SHORT).show(); + + GalleryDetailScene scene = getScene(); + if (scene != null) { + scene.onRateGallerySuccess(result); + } else { + // Update rating in cache + GalleryDetail gd = EhApplication.getGalleryDetailCache(mApplication).get(mGid); + if (gd != null) { + gd.rating = result.rating; + gd.ratedTimes = result.ratedTimes; + } + } + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + // Show toast + Toast.makeText(mApplication, R.string.rate_failed, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCancel() { + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java index 51c9bbda8..b5fa593e4 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java @@ -138,6 +138,13 @@ public class GalleryPreviewsScene extends ToolbarScene { return contentLayout; } + @Override + public void onDestroyView() { + super.onDestroyView(); + mAdapter = null; + mHelper = null; + } + @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); diff --git a/app/src/main/java/com/hippo/widget/ProgressiveRatingBar.java b/app/src/main/java/com/hippo/widget/ProgressiveRatingBar.java new file mode 100644 index 000000000..e873d2d38 --- /dev/null +++ b/app/src/main/java/com/hippo/widget/ProgressiveRatingBar.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 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.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.support.v7.widget.AppCompatRatingBar; +import android.util.AttributeSet; + +public class ProgressiveRatingBar extends AppCompatRatingBar { + + private OnUserRateListener mListener; + + public ProgressiveRatingBar(Context context) { + super(context); + } + public ProgressiveRatingBar(Context context, AttributeSet attrs) { + super(context, attrs); + } + public ProgressiveRatingBar(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isPressed() && mListener != null) { + mListener.onUserRate(getRating()); + } + } + + public void setOnUserRateListener(OnUserRateListener l) { + mListener = l; + } + + public interface OnUserRateListener { + void onUserRate(float rating); + } +} diff --git a/app/src/main/res/layout/dialog_rate.xml b/app/src/main/res/layout/dialog_rate.xml new file mode 100644 index 000000000..74d54f174 --- /dev/null +++ b/app/src/main/res/layout/dialog_rate.xml @@ -0,0 +1,40 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/scene_gallery_comments.xml b/app/src/main/res/layout/scene_gallery_comments.xml index 050e4803b..12072a1ba 100644 --- a/app/src/main/res/layout/scene_gallery_comments.xml +++ b/app/src/main/res/layout/scene_gallery_comments.xml @@ -32,6 +32,31 @@ android:paddingBottom="@dimen/corner_fab_margin" android:clipToPadding="false"/> + + + + + + + + + android:scrollbars="none" + android:overScrollMode="never"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62a2cb62f..60621b5c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -133,7 +133,7 @@ \u2665 %d - %1$s (%2$d) + %1$s (%2$.2f, %3$d) Favorite Torrent (%d) @@ -149,6 +149,9 @@ Refresh Open in other app + Rate successfully + Rate failed + MASTERPIECE AMAZING @@ -191,6 +194,7 @@ Gallery Comments + No one comments on this gallery Gallery Previews