mItemList = new ArrayList<>();
+
+ @Override
+ public int size() {
+ return mItemList.size();
+ }
+
+ public void addItem(String imageUrl, String pageUrl) {
+ mItemList.add(new Item(imageUrl, pageUrl));
+ }
+
+ @Override
+ public void bindView(SimpleGridLayout simpleGridLayout, LayoutInflater inflater, Conaco conaco) {
+ ensureViewGroup(simpleGridLayout, inflater);
+
+ int size = mItemList.size();
+ for (int i = 0; i < size; i++) {
+ final Item item = mItemList.get(i);
+ final int index = i + getStartIndex();
+ final ViewGroup viewGroup = (ViewGroup) simpleGridLayout.getChildAt(i);
+ conaco.load((LoadImageView) viewGroup.getChildAt(0),
+ EhImageKeyFactory.getLargePreviewKey(getGid(), index), item.imageUrl);
+ ((TextView) viewGroup.getChildAt(1)).setText(Integer.toString(index + 1));
+ }
+ }
+
+ @Override
+ public void cancelLoadTask(SimpleGridLayout simpleGridLayout, Conaco conaco) {
+ int count = simpleGridLayout.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final ViewGroup viewGroup = (ViewGroup) simpleGridLayout.getChildAt(i);
+ conaco.cancel((LoadImageView) viewGroup.getChildAt(0));
+ }
+ }
+
+ private class Item {
+
+ public String imageUrl;
+ public String pageUrl;
+
+ public Item(String imageUrl, String pageUrl) {
+ this.imageUrl = imageUrl;
+ this.pageUrl = pageUrl;
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/data/ListUrlBuilder.java b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java
similarity index 99%
rename from app/src/main/java/com/hippo/ehviewer/data/ListUrlBuilder.java
rename to app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java
index b182020a6..8c2b60b05 100644
--- a/app/src/main/java/com/hippo/ehviewer/data/ListUrlBuilder.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.hippo.ehviewer.data;
+package com.hippo.ehviewer.client.data;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.util.EhUtils;
diff --git a/app/src/main/java/com/hippo/ehviewer/data/LofiGalleryInfo.java b/app/src/main/java/com/hippo/ehviewer/client/data/LofiGalleryInfo.java
similarity index 98%
rename from app/src/main/java/com/hippo/ehviewer/data/LofiGalleryInfo.java
rename to app/src/main/java/com/hippo/ehviewer/client/data/LofiGalleryInfo.java
index f490d280c..80c79da86 100644
--- a/app/src/main/java/com/hippo/ehviewer/data/LofiGalleryInfo.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/LofiGalleryInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.hippo.ehviewer.data;
+package com.hippo.ehviewer.client.data;
import android.os.Parcel;
diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java b/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java
new file mode 100644
index 000000000..c04f8d713
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java
@@ -0,0 +1,230 @@
+/*
+ * 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.data;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.hippo.conaco.BitmapHolder;
+import com.hippo.conaco.Conaco;
+import com.hippo.conaco.Unikery;
+import com.hippo.ehviewer.EhImageKeyFactory;
+import com.hippo.util.Log;
+import com.hippo.widget.SimpleGridLayout;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class NormalPreviewSet extends PreviewSet {
+
+ private List
mRowArray = new ArrayList<>();
+
+ private Set mLoadImageTasks = new HashSet<>();
+
+ @Override
+ public int size() {
+ int size = 0;
+ for (Row row : mRowArray) {
+ size += row.itemArray.size();
+ }
+ return size;
+ }
+
+ public void addItem(String imageUrl, int xOffset, int yOffset, int width,
+ int height, String url) {
+ Row row;
+ int size = mRowArray.size();
+ if (size == 0 || ! (row = mRowArray.get(size - 1)).imageUrl.equals(imageUrl)) {
+ row = new Row(imageUrl);
+ mRowArray.add(row);
+ }
+ row.addItem(xOffset, yOffset, width, height, url);
+ }
+
+ @Override
+ public void bindView(SimpleGridLayout simpleGridLayout,
+ LayoutInflater inflater, Conaco conaco) {
+ ensureViewGroup(simpleGridLayout, inflater);
+
+ int i = 0;
+ for (Row row : mRowArray) {
+ int rowSize = row.itemArray.size();
+ ImageView[] imageViews = new ImageView[rowSize];
+ for (int j = 0; j < rowSize; j++) {
+ ViewGroup viewGroup = (ViewGroup) simpleGridLayout.getChildAt(i);
+ imageViews[j] = (ImageView) viewGroup.getChildAt(0);
+ ((TextView) viewGroup.getChildAt(1)).setText(Integer.toString(i + getStartIndex() + 1));
+ i++;
+ }
+
+ LoadImageTask loadImageTask = new LoadImageTask(row, imageViews);
+ mLoadImageTasks.add(loadImageTask);
+
+ String key;
+ if (row.imageIndex != -1) {
+ key = EhImageKeyFactory.getNormalPreviewKey(getGid(), row.imageIndex);
+ } else {
+ key = row.imageUrl;
+ }
+ conaco.load(loadImageTask, key, row.imageUrl);
+ }
+ }
+
+ @Override
+ public void cancelLoadTask(SimpleGridLayout simpleGridLayout, Conaco conaco) {
+ for (LoadImageTask loadImageTask : mLoadImageTasks) {
+ conaco.cancel(loadImageTask);
+ }
+ mLoadImageTasks.clear();
+ }
+
+ class LoadImageTask implements Unikery {
+
+ private int mTaskId;
+ private Row mRow;
+ private ImageView[] mImageViews;
+
+ public LoadImageTask(Row row, ImageView[] imageViews) {
+ mRow = row;
+ mImageViews = imageViews;
+ }
+
+ @Override
+ public void setBitmap(BitmapHolder bitmapHolder, Conaco.Source source) {
+ bitmapHolder.obtain();
+ Bitmap bitmap = bitmapHolder.getBitmap();
+ int maxWidth = bitmap.getWidth();
+ int maxHeight = bitmap.getHeight();
+
+ int size = mRow.itemArray.size();
+ for(int i = 0; i < size; i++) {
+ Item item = mRow.itemArray.get(i);
+ if (item.xOffset + item.width > maxWidth) {
+ item.width = maxWidth - item.xOffset;
+ if (item.width <= 0) {
+ // Bad area
+ continue;
+ }
+ }
+ if (item.yOffset + item.height > maxHeight) {
+ item.height = maxHeight - item.yOffset;
+ if (item.height <= 0) {
+ // Bad area
+ continue;
+ }
+ }
+
+ Bitmap previewBitmap = Bitmap.createBitmap(bitmap, item.xOffset,
+ item.yOffset, item.width, item.height);
+
+ if (source != Conaco.Source.MEMORY) {
+ Drawable[] layers = new Drawable[2];
+ layers[0] = new ColorDrawable(Color.TRANSPARENT);
+ layers[1] = new BitmapDrawable(mImageViews[i].getResources(), previewBitmap);
+ TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
+ mImageViews[i].setImageDrawable(transitionDrawable);
+ transitionDrawable.startTransition(300);
+ } else {
+ mImageViews[i].setImageBitmap(previewBitmap);
+ }
+ }
+
+ bitmapHolder.release();
+
+ mLoadImageTasks.remove(this);
+ }
+
+ @Override
+ public void setDrawable(Drawable drawable) {
+ // Empty
+ }
+
+ @Override
+ public void setTaskId(int id) {
+ mTaskId = id;
+ }
+
+ @Override
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ @Override
+ public void onFailure() {
+ // TODO
+ mLoadImageTasks.remove(this);
+ }
+
+ @Override
+ public void onCancel() {
+ // Empty
+ }
+ }
+
+ class Row {
+
+ public String imageUrl;
+ public List- itemArray = new ArrayList<>();
+ public int imageIndex;
+
+ public Row(String imageUrl) {
+ this.imageUrl = imageUrl;
+
+ int index1 = imageUrl.lastIndexOf('-');
+ int index2 = imageUrl.lastIndexOf('.');
+
+ try {
+ imageIndex = Integer.parseInt(imageUrl.substring(index1 + 1, index2)) - 1;
+ } catch (Exception e) {
+ Log.w("Can't get normal preview index", e);
+ imageIndex = -1;
+ }
+ }
+
+ public void addItem(int xOffset, int yOffset, int width, int height,
+ String url) {
+ itemArray.add(new Item(xOffset, yOffset, width, height, url));
+ }
+ }
+
+ class Item {
+
+ public int xOffset;
+ public int yOffset;
+ public int width;
+ public int height;
+ public String url;
+
+ public Item(int xOffset, int yOffset, int width, int height, String url) {
+ this.xOffset = xOffset;
+ this.yOffset = yOffset;
+ this.width = width;
+ this.height = height;
+ this.url = url;
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/PreviewSet.java b/app/src/main/java/com/hippo/ehviewer/client/data/PreviewSet.java
new file mode 100644
index 000000000..0318fea5d
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/PreviewSet.java
@@ -0,0 +1,68 @@
+/*
+ * 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.data;
+
+import android.view.LayoutInflater;
+
+import com.hippo.conaco.Conaco;
+import com.hippo.ehviewer.R;
+import com.hippo.widget.SimpleGridLayout;
+
+public abstract class PreviewSet {
+
+ private int mStartIndex;
+ private int mGid;
+
+ public void setStartIndex(int startIndex) {
+ mStartIndex = startIndex;
+ }
+
+ public int getStartIndex() {
+ return mStartIndex;
+ }
+
+ public abstract int size();
+
+ public void setGid(int gid) {
+ mGid = gid;
+ }
+
+ public int getGid() {
+ return mGid;
+ }
+
+ public abstract void bindView(SimpleGridLayout simpleGridLayout,
+ LayoutInflater inflater, Conaco conaco);
+
+ protected void ensureViewGroup(SimpleGridLayout simpleGridLayout, LayoutInflater inflater) {
+ int size = size();
+ int diff = size - simpleGridLayout.getChildCount();
+
+ if (diff > 0) {
+ for (int i = 0; i < diff; i++) {
+ inflater.inflate(R.layout.item_preview, simpleGridLayout, true);
+ }
+ } else if (diff < 0) {
+ diff = -diff;
+ for (int i = 0; i < diff; i++) {
+ simpleGridLayout.removeViews(size, diff);
+ }
+ }
+ }
+
+ public abstract void cancelLoadTask(SimpleGridLayout simpleGridLayout, Conaco conaco);
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/data/TagGroup.java b/app/src/main/java/com/hippo/ehviewer/client/data/TagGroup.java
similarity index 96%
rename from app/src/main/java/com/hippo/ehviewer/data/TagGroup.java
rename to app/src/main/java/com/hippo/ehviewer/client/data/TagGroup.java
index ef49c543e..20e8de1c0 100644
--- a/app/src/main/java/com/hippo/ehviewer/data/TagGroup.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/TagGroup.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.hippo.ehviewer.data;
+package com.hippo.ehviewer.client.data;
import java.util.ArrayList;
import java.util.List;
diff --git a/app/src/main/java/com/hippo/ehviewer/data/UnsupportedSearchException.java b/app/src/main/java/com/hippo/ehviewer/client/data/UnsupportedSearchException.java
similarity index 95%
rename from app/src/main/java/com/hippo/ehviewer/data/UnsupportedSearchException.java
rename to app/src/main/java/com/hippo/ehviewer/client/data/UnsupportedSearchException.java
index 6bae76ea3..a99b5092c 100644
--- a/app/src/main/java/com/hippo/ehviewer/data/UnsupportedSearchException.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/UnsupportedSearchException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.hippo.ehviewer.data;
+package com.hippo.ehviewer.client.data;
public class UnsupportedSearchException extends Exception {
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java
index 808c02d0e..c945ba393 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/CommentScene.java
@@ -13,7 +13,7 @@ import android.widget.TextView;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
-import com.hippo.ehviewer.data.Comment;
+import com.hippo.ehviewer.client.data.Comment;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
import com.hippo.scene.AppbarScene;
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 d65660fdf..01c0851c1 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
@@ -39,26 +39,36 @@ import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.EhImageKeyFactory;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhClient;
-import com.hippo.ehviewer.data.Comment;
-import com.hippo.ehviewer.data.GalleryDetail;
-import com.hippo.ehviewer.data.GalleryInfo;
-import com.hippo.ehviewer.data.TagGroup;
+import com.hippo.ehviewer.client.EhException;
+import com.hippo.ehviewer.client.OffensiveException;
+import com.hippo.ehviewer.client.data.Comment;
+import com.hippo.ehviewer.client.data.GalleryDetail;
+import com.hippo.ehviewer.client.data.GalleryInfo;
+import com.hippo.ehviewer.client.data.TagGroup;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.ehviewer.widget.LoadImageView;
+import com.hippo.ehviewer.widget.PreviewLayout;
import com.hippo.ehviewer.widget.RatingView;
import com.hippo.miscellaneous.StartActivityHelper;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
+import com.hippo.scene.RippleCurtain;
import com.hippo.scene.Scene;
+import com.hippo.scene.SimpleDialog;
import com.hippo.util.Log;
import com.hippo.util.URLImageGetter;
import com.hippo.util.UiUtils;
+import com.hippo.util.ViewUtils;
+import com.hippo.widget.AccurateClick;
import com.hippo.widget.AutoWrapLayout;
import java.util.List;
-public class GalleryDetailScene extends Scene implements View.OnClickListener {
+import static com.hippo.ehviewer.R.id.no_tags;
+
+public class GalleryDetailScene extends Scene implements View.OnClickListener,
+ AccurateClick.OnAccurateClickListener {
public static final String KEY_GALLERY_INFO = "gallery_info";
@@ -88,8 +98,10 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
private TextView mShare;
private TextView mRate;
private LinearLayout mTag;
+ private TextView mNoTags;
private LinearLayout mComment;
private TextView mCommentMore;
+ private PreviewLayout mPreviewLayout;
private View mProgressBar;
@@ -97,6 +109,12 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
private GalleryDetail mGalleryDetail;
+ private void requestGalleryDetail(GalleryInfo gi) {
+ EhClient.getInstance().getGalleryDetail(Config.getEhSource(),
+ EhClient.getDetailUrl(Config.getEhSource(), gi.gid, gi.token, 0),
+ new EhDetailListener());
+ }
+
private void handleAnnouncer(Announcer announcer) {
GalleryInfo gi;
@@ -112,9 +130,7 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
mCategory.setText(EhUtils.getCategory(gi.category));
mCategory.setTextColor(EhUtils.getCategoryColor(gi.category));
- EhClient.getInstance().getGalleryDetail(Config.getEhSource(),
- EhClient.getDetailUrl(Config.getEhSource(), gi.gid, gi.token, 0),
- new EhDetailListener());
+ requestGalleryDetail(gi);
}
Context context = getStageActivity();
@@ -146,7 +162,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
mTorrent.setOnClickListener(this);
mShare.setOnClickListener(this);
mRate.setOnClickListener(this);
- mCommentMore.setOnClickListener(this);
+
+ AccurateClick.setOnAccurateClickListener(mComment, this);
RippleSalon.addRipple(mRead, false);
RippleSalon.addRipple(mDownload, false);
@@ -187,8 +204,10 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
mShare = (TextView) mContent.findViewById(R.id.share);
mRate = (TextView) mContent.findViewById(R.id.rate);
mTag = (LinearLayout) mContent.findViewById(R.id.tag);
+ mNoTags = (TextView) mTag.findViewById(no_tags);
mComment = (LinearLayout) mContent.findViewById(R.id.comment);
mCommentMore = (TextView) mContent.findViewById(R.id.comment_more);
+ mPreviewLayout = (PreviewLayout) mContent.findViewById(R.id.preview);
mProgressBar = findViewById(R.id.progress_bar);
@@ -220,10 +239,23 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
} else if (v == mShare) {
StartActivityHelper.share(getStageActivity(),
EhClient.getDetailUrl(Config.getEhSource(), gd.gid, gd.token, 0));
- } else if (v == mCommentMore) {
+ }
+ }
+
+ @Override
+ public void onAccurateClick(View v, float x, float y) {
+ GalleryDetail gd = mGalleryDetail;
+ if (gd == null) {
+ return;
+ }
+
+ if (v == mComment) {
+ int[] position = new int[2];
+ ViewUtils.getLocationInAncestor(mComment, position, getSceneView());
Announcer announcer = new Announcer();
announcer.setObject(mGalleryDetail.comments);
- startScene(CommentScene.class, announcer);
+ startScene(CommentScene.class, announcer,
+ new RippleCurtain((int) x + position[0], (int) y + position[1]));
}
}
@@ -272,31 +304,34 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
}
private void fillTags(List tags) {
- Context context = getStageActivity();
- LayoutInflater inflater = getStageActivity().getLayoutInflater();
- int x = UiUtils.dp2pix(context, 2);
- int y = UiUtils.dp2pix(context, 4);
+ if (tags.size() != 0) {
+ mNoTags.setVisibility(View.GONE);
- for (TagGroup tagGroup : tags) {
- LinearLayout tagGroupLayout = new LinearLayout(context);
- tagGroupLayout.setOrientation(LinearLayout.HORIZONTAL);
- AutoWrapLayout tagLayout = new AutoWrapLayout(context);
+ Context context = getStageActivity();
+ LayoutInflater inflater = getStageActivity().getLayoutInflater();
+ int x = UiUtils.dp2pix(context, 2);
+ int y = UiUtils.dp2pix(context, 4);
- // Group name
- @SuppressLint("InflateParams")
- TextView groupNameView = (TextView) inflater.inflate(R.layout.tag_group, null);
- groupNameView.setText(tagGroup.groupName);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT);
- lp.setMargins(x, y, x, y);
- tagGroupLayout.addView(groupNameView, lp);
+ for (TagGroup tagGroup : tags) {
+ LinearLayout tagGroupLayout = new LinearLayout(context);
+ tagGroupLayout.setOrientation(LinearLayout.HORIZONTAL);
+ AutoWrapLayout tagLayout = new AutoWrapLayout(context);
- int count = tagGroup.getTagCount();
- for (int i = 0; i < count; i++) {
+ // Group name
@SuppressLint("InflateParams")
- TextView tagView = (TextView) inflater.inflate(R.layout.tag, null);
- tagView.setText(tagGroup.getTagAt(i));
+ TextView groupNameView = (TextView) inflater.inflate(R.layout.tag_group, null);
+ groupNameView.setText(tagGroup.groupName);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(x, y, x, y);
+ tagGroupLayout.addView(groupNameView, lp);
+
+ int count = tagGroup.getTagCount();
+ for (int i = 0; i < count; i++) {
+ @SuppressLint("InflateParams")
+ TextView tagView = (TextView) inflater.inflate(R.layout.tag, null);
+ tagView.setText(tagGroup.getTagAt(i));
/*
tagView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -312,17 +347,21 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
}
});
*/
- AutoWrapLayout.LayoutParams alp = new AutoWrapLayout.LayoutParams();
- alp.setMargins(x, y, x, y);
- tagLayout.addView(tagView, alp);
+ AutoWrapLayout.LayoutParams alp = new AutoWrapLayout.LayoutParams();
+ alp.setMargins(x, y, x, y);
+ tagLayout.addView(tagView, alp);
+ }
+ tagGroupLayout.addView(tagLayout);
+ mTag.addView(tagGroupLayout);
}
- tagGroupLayout.addView(tagLayout);
- mTag.addView(tagGroupLayout);
+ } else {
+ mNoTags.setVisibility(View.VISIBLE);
}
}
+ @SuppressLint("InflateParams")
private View createCommentView(Comment comment) {
- View view = LayoutInflater.from(getStageActivity()).inflate(R.layout.item_comment, null);
+ View view = LayoutInflater.from(getStageActivity()).inflate(R.layout.item_comment, null);
((TextView) view.findViewById(R.id.user)).setText(comment.user);
((TextView) view.findViewById(R.id.time)).setText(comment.time);
TextView commentText = (TextView) view.findViewById(R.id.comment);
@@ -364,11 +403,13 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
int commentNum = galleryDetail.comments.size();
if (commentNum == 0) {
- mCommentMore.setText("暂无评论");
+ mCommentMore.setText(R.string.no_comments);
+ // Remove padding interval
+ mCommentMore.setPadding(0, 0, 0, 0);
} else if (commentNum <= 2){
- mCommentMore.setText("暂无更多评论");
+ mCommentMore.setText(R.string.no_more_comments);
} else {
- mCommentMore.setText("更多评论");
+ mCommentMore.setText(R.string.more_comment);
}
int maxShown = Math.min(2, commentNum);
for (int i = 0; i < maxShown; i++) {
@@ -377,13 +418,30 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
LinearLayout.LayoutParams.WRAP_CONTENT));
}
+ mPreviewLayout.setData(galleryDetail.previewSetArray,
+ getStageActivity().getLayoutInflater(),
+ EhApplication.getConaco(getStageActivity()));
+ mPreviewLayout.selectPreviewAt(0);
+
mViewTransition.showView(0);
}
@Override
public void onFailure(Exception e) {
e.printStackTrace();
- // TODO
+ if (e instanceof OffensiveException) {
+
+ } else if (e instanceof EhException) {
+ new SimpleDialog.Builder(getStageActivity()).setTitle("Error")
+ .setMessage(e.getMessage())
+ .setNegativeButton(android.R.string.cancel)
+ .setOnCloseListener(new SimpleDialog.OnCloseListener() {
+ @Override
+ public void onClose(SimpleDialog dialog, boolean cancel) {
+ finish();
+ }
+ }).show();
+ }
}
}
}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
index e76e39fcb..5bb10b54c 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
@@ -47,9 +47,9 @@ import com.hippo.ehviewer.EhImageKeyFactory;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.client.GalleryListParser;
-import com.hippo.ehviewer.data.GalleryInfo;
-import com.hippo.ehviewer.data.ListUrlBuilder;
-import com.hippo.ehviewer.data.UnsupportedSearchException;
+import com.hippo.ehviewer.client.data.GalleryInfo;
+import com.hippo.ehviewer.client.data.ListUrlBuilder;
+import com.hippo.ehviewer.client.data.UnsupportedSearchException;
import com.hippo.ehviewer.ui.ContentActivity;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.EhUtils;
diff --git a/app/src/main/java/com/hippo/ehviewer/widget/LoadImageView.java b/app/src/main/java/com/hippo/ehviewer/widget/LoadImageView.java
index 35af0832d..2df81bb8f 100644
--- a/app/src/main/java/com/hippo/ehviewer/widget/LoadImageView.java
+++ b/app/src/main/java/com/hippo/ehviewer/widget/LoadImageView.java
@@ -7,13 +7,13 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
-import android.widget.ImageView;
import com.hippo.conaco.BitmapHolder;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.Unikery;
+import com.hippo.widget.FixedAspectImageView;
-public class LoadImageView extends ImageView implements Unikery {
+public class LoadImageView extends FixedAspectImageView implements Unikery {
private int mTaskId = Unikery.INVAILD_ID;
diff --git a/app/src/main/java/com/hippo/ehviewer/widget/PreviewLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/PreviewLayout.java
new file mode 100644
index 000000000..40e761484
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/widget/PreviewLayout.java
@@ -0,0 +1,220 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.hippo.conaco.Conaco;
+import com.hippo.ehviewer.R;
+import com.hippo.ehviewer.client.data.LargePreviewSet;
+import com.hippo.ehviewer.client.data.PreviewSet;
+import com.hippo.rippleold.RippleSalon;
+import com.hippo.widget.SimpleGridLayout;
+
+public final class PreviewLayout extends LinearLayout implements View.OnClickListener {
+
+ private GestureDetector mGestureDetector;
+ private PreviewSet[] mPreviewSets;
+ private Conaco mConaco;
+ private LayoutInflater mInflater;
+ private int mPreviewSetCount;
+ private int mCurrentPreviewIndex = -1;
+
+ private SimpleGridLayout mSimpleGridLayout;
+ private View mTopPrevious;
+ private TextView mTopSelection;
+ private View mTopNext;
+ private View mBottomPrevious;
+ private TextView mBottomSelection;
+ private View mBottomNext;
+
+ private int mWidth;
+
+ public PreviewLayout(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public PreviewLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public PreviewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setOrientation(LinearLayout.VERTICAL);
+
+ LayoutInflater.from(context).inflate(R.layout.widget_preview_layout, this);
+
+ ViewGroup viewGroup = (ViewGroup) getChildAt(0);
+ mTopPrevious = viewGroup.getChildAt(0);
+ mTopSelection = (TextView) viewGroup.getChildAt(2);
+ mTopNext = viewGroup.getChildAt(4);
+ mSimpleGridLayout = (SimpleGridLayout) getChildAt(1);
+ viewGroup = (ViewGroup) getChildAt(2);
+ mBottomPrevious = viewGroup.getChildAt(0);
+ mBottomSelection = (TextView) viewGroup.getChildAt(2);
+ mBottomNext = viewGroup.getChildAt(4);
+
+ RippleSalon.addRipple(mTopPrevious, false);
+ RippleSalon.addRipple(mTopSelection, false);
+ RippleSalon.addRipple(mTopNext, false);
+ RippleSalon.addRipple(mBottomPrevious, false);
+ RippleSalon.addRipple(mBottomSelection, false);
+ RippleSalon.addRipple(mBottomNext, false);
+
+ mTopPrevious.setOnClickListener(this);
+ mTopSelection.setOnClickListener(this);
+ mTopNext.setOnClickListener(this);
+ mBottomPrevious.setOnClickListener(this);
+ mBottomSelection.setOnClickListener(this);
+ mBottomNext.setOnClickListener(this);
+
+ GestureListener gestureListener = new GestureListener();
+ mGestureDetector = new GestureDetector(context, gestureListener);
+ mGestureDetector.setIsLongpressEnabled(false);
+ }
+
+ public void setData(PreviewSet[] previewSets, LayoutInflater inflater, Conaco conaco) {
+ mPreviewSets = previewSets;
+ mPreviewSetCount = previewSets.length;
+ mInflater = inflater;
+ mConaco = conaco;
+ }
+
+ private boolean isLargePreview() {
+ for (PreviewSet previewSet : mPreviewSets) {
+ if (previewSet != null) {
+ return previewSet instanceof LargePreviewSet;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ int width = r - l;
+ if (mWidth != width) {
+ mWidth = width;
+ int itemWidth = getResources().getDimensionPixelOffset(
+ isLargePreview() ? R.dimen.gallery_large_preview_item_width :
+ R.dimen.gallery_normal_preview_item_width);
+ int columnCount = Math.round(
+ (width - getPaddingLeft() - getPaddingRight()) / (float) itemWidth);
+ columnCount = Math.max(columnCount, 1);
+ mSimpleGridLayout.setColumnCount(columnCount);
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return handleTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
+ return handleTouchEvent(event);
+ }
+
+ private boolean handleTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+
+ public int getPreviewPageCount() {
+ return mPreviewSetCount;
+ }
+
+ public void selectPreviewAt(int index) {
+ if (index < 0 || index >= mPreviewSetCount) {
+ throw new IndexOutOfBoundsException("Invalid index " + index + ", size is " + mPreviewSetCount);
+ }
+
+ mCurrentPreviewIndex = index;
+
+ PreviewSet previewSet = mPreviewSets[index];
+ if (previewSet != null) {
+ previewSet.bindView(mSimpleGridLayout, mInflater, mConaco);
+ } else {
+ // TODO
+ }
+
+ String showText = (index + 1) + "/" + mPreviewSetCount;
+ mTopSelection.setText(showText);
+ mBottomSelection.setText(showText);
+ }
+
+ public void tryNextPreview() {
+ if (mCurrentPreviewIndex < mPreviewSetCount - 1) {
+ selectPreviewAt(mCurrentPreviewIndex + 1);
+ }
+ }
+
+ public void tryPreviousPreview() {
+ if (mCurrentPreviewIndex > 0) {
+ selectPreviewAt(mCurrentPreviewIndex - 1);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+
+ }
+
+ class GestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ private boolean mHasScrollX = false;
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ mHasScrollX = false;
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ if (Math.abs(distanceX) > Math.abs(distanceY)) {
+ if (!mHasScrollX) {
+ mHasScrollX = true;
+ if (distanceX > 0) {
+ tryNextPreview();
+ } else {
+ tryPreviousPreview();
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java
index f1cd0c92e..6a46f40e8 100644
--- a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java
+++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java
@@ -36,7 +36,7 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import com.hippo.ehviewer.R;
-import com.hippo.ehviewer.data.ListUrlBuilder;
+import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.SimpleDialog;
import com.hippo.util.UiUtils;
diff --git a/app/src/main/java/com/hippo/scene/RippleCurtain.java b/app/src/main/java/com/hippo/scene/RippleCurtain.java
new file mode 100644
index 000000000..0121c53e8
--- /dev/null
+++ b/app/src/main/java/com/hippo/scene/RippleCurtain.java
@@ -0,0 +1,155 @@
+package com.hippo.scene;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.hippo.animation.DurationInterpolator;
+import com.hippo.animation.SimpleAnimatorListener;
+import com.hippo.util.Log;
+import com.hippo.util.ViewUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class RippleCurtain extends Curtain {
+
+ private static final String TAG = RippleCurtain.class.getSimpleName();
+
+ private static long ANIMATE_TIME = 300L;
+
+ private int mStartX;
+ private int mStartY;
+
+ private AnimatorSet mAnimatorSet;
+
+ public RippleCurtain(int startX, int startY) {
+ mStartX = startX;
+ mStartY = startY;
+ }
+
+ @Override
+ protected void onRebirth() {
+ mStartX = 0;
+ mStartY = 0;
+ }
+
+ @Override
+ public void open(@NonNull final Scene enter, @NonNull final Scene exit) {
+ final Set animatorCollection = new HashSet<>();
+
+ final SceneView enterView = enter.getSceneView();
+ enterView.setStartPoint(mStartX, mStartY);
+ ViewUtils.setVisibility(enterView, View.INVISIBLE);
+
+ // Canvas.clipPath() not work for pre-JELLY_BEAN_MR2 when hardware accelerated
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ ViewUtils.removeHardwareAccelerationSupport(enterView);
+ }
+
+ enterView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ ViewUtils.removeOnGlobalLayoutListener(enterView.getViewTreeObserver(), this);
+
+ final View sceneContentView = enter.getSceneView().getChildAt(0);
+ if (sceneContentView != null) {
+ sceneContentView.setVisibility(View.INVISIBLE);
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(sceneContentView, "alpha", 0f, 1f);
+ alphaAnim.setDuration(ANIMATE_TIME * 2);
+ alphaAnim.setInterpolator(new DurationInterpolator(0.3f));
+ alphaAnim.addListener(new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ sceneContentView.setVisibility(View.VISIBLE);
+ }
+ });
+ animatorCollection.add(alphaAnim);
+ } else {
+ Log.w(TAG, "Can't get enter scene content view");
+ }
+
+ PropertyValuesHolder circlePercentPvh = PropertyValuesHolder.ofFloat("circlePercent", 0f, 1f);
+ ObjectAnimator animFrame = ObjectAnimator.ofPropertyValuesHolder(enterView, circlePercentPvh);
+ animFrame.setDuration(ANIMATE_TIME);
+ animatorCollection.add(animFrame);
+
+ mAnimatorSet = new AnimatorSet();
+ mAnimatorSet.playTogether(animatorCollection);
+ mAnimatorSet.addListener(new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ ViewUtils.setVisibility(enterView, View.VISIBLE);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchOpenFinished(enter, exit);
+ hideSceneOnOpen(exit);
+ mAnimatorSet = null;
+
+ // Canvas.clipPath() not work for pre-JELLY_BEAN_MR2 when hardware accelerated
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ ViewUtils.addHardwareAccelerationSupport(enterView);
+ }
+ }
+ });
+ mAnimatorSet.start();
+ }
+ });
+ }
+
+ @Override
+ public void close(@NonNull final Scene enter, @NonNull final Scene exit) {
+ showSceneOnClose(enter);
+
+ final Set animatorCollection = new HashSet<>();
+
+ final SceneView exitView = exit.getSceneView();
+ exitView.setStartPoint(mStartX, mStartY);
+
+ // Canvas.clipPath() not work for pre-JELLY_BEAN_MR2 when hardware accelerated
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ ViewUtils.removeHardwareAccelerationSupport(exitView);
+ }
+
+ PropertyValuesHolder circlePercentPvh = PropertyValuesHolder.ofFloat("circlePercent", 1f, 0f);
+ ObjectAnimator animFrame = ObjectAnimator.ofPropertyValuesHolder(exitView, circlePercentPvh);
+ animFrame.setDuration(ANIMATE_TIME);
+ animatorCollection.add(animFrame);
+
+ mAnimatorSet = new AnimatorSet();
+ mAnimatorSet.playTogether(animatorCollection);
+ mAnimatorSet.addListener(new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animation, boolean canceled) {
+ super.onAnimationEnd(animation, canceled);
+ dispatchCloseFinished(enter, exit);
+ mAnimatorSet = null;
+
+ // Canvas.clipPath() not work for pre-JELLY_BEAN_MR2 when hardware accelerated
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ ViewUtils.addHardwareAccelerationSupport(exitView);
+ }
+ }
+ });
+ mAnimatorSet.start();
+ }
+
+ @Override
+ public void endAnimation() {
+ if (mAnimatorSet != null) {
+ mAnimatorSet.end();
+ }
+ }
+
+ @Override
+ public boolean isInAnimation() {
+ return mAnimatorSet != null;
+ }
+}
diff --git a/app/src/main/java/com/hippo/scene/SceneView.java b/app/src/main/java/com/hippo/scene/SceneView.java
index 600a53d95..d09a4ba23 100644
--- a/app/src/main/java/com/hippo/scene/SceneView.java
+++ b/app/src/main/java/com/hippo/scene/SceneView.java
@@ -17,6 +17,8 @@
package com.hippo.scene;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -27,6 +29,12 @@ public class SceneView extends AbsoluteLayout {
private boolean mEnableTouch = true;
+ private Path mPath;
+ private float mRadius;
+ private float mCirclePercent = 1f;
+ private float mStartX = -1f;
+ private float mStartY = -1f;
+
public SceneView(Context context) {
super(context);
init();
@@ -44,6 +52,7 @@ public class SceneView extends AbsoluteLayout {
private void init() {
setClickable(true);
+ mPath = new Path();
}
public void setEnableTouch(boolean enableTouch) {
@@ -64,4 +73,65 @@ public class SceneView extends AbsoluteLayout {
public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) {
return mEnableTouch && super.dispatchGenericMotionEvent(event);
}
+
+ private void updateRadius() {
+ int width = getWidth();
+ int height = getHeight();
+ if ((mStartX != -1f || mStartY != -1f) && width != 0 && height != 0) {
+ float h = Math.max(mStartX, width - mStartX);
+ float v = Math.max(mStartY, height - mStartY);
+ mRadius = (float) Math.hypot(h, v);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t,
+ int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ updateRadius();
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ float circlePercent = mCirclePercent;
+ int saved = 0;
+
+ if (circlePercent != 1f && mRadius != 0f) {
+ mPath.rewind();
+ mPath.addCircle(mStartX, mStartY, circlePercent * mRadius, Path.Direction.CW);
+ saved = canvas.save();
+ canvas.clipPath(mPath);
+ }
+
+ super.draw(canvas);
+
+ if (circlePercent != 1f && mRadius != 0f && saved != 0) {
+ canvas.restoreToCount(saved);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public void setCirclePercent(float circlePercent) {
+ float largeCirclePercent = Math.max(mCirclePercent, circlePercent);
+ mCirclePercent = circlePercent;
+
+ if (largeCirclePercent == 1f) {
+ invalidate();
+ } else {
+ float radius = mRadius * largeCirclePercent;
+ invalidate((int) (mStartX - radius), (int) (mStartY - radius),
+ (int) (mStartX + radius), (int) (mStartY + radius));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public float getCirclePercent() {
+ return mCirclePercent;
+ }
+
+ public void setStartPoint(float x, float y) {
+ mStartX = x;
+ mStartY = y;
+ updateRadius();
+ }
}
diff --git a/app/src/main/java/com/hippo/scene/SimpleDialogCurtain.java b/app/src/main/java/com/hippo/scene/SimpleDialogCurtain.java
index d453eeb99..8c3c40e65 100644
--- a/app/src/main/java/com/hippo/scene/SimpleDialogCurtain.java
+++ b/app/src/main/java/com/hippo/scene/SimpleDialogCurtain.java
@@ -157,7 +157,7 @@ public class SimpleDialogCurtain extends Curtain {
final Set animatorCollection = new HashSet<>();
// Handle background
- int bgColor = enter.getBackgroundColor();
+ int bgColor = exitDialog.getBackgroundColor();
int endBgColor = bgColor & 0xffffff;
ObjectAnimator colorAnim = ObjectAnimator.ofInt(exit, "backgroundColor", endBgColor);
colorAnim.setEvaluator(ArgbEvaluator.getInstance());
diff --git a/app/src/main/java/com/hippo/util/MathUtils.java b/app/src/main/java/com/hippo/util/MathUtils.java
index 569337072..49c1a241b 100644
--- a/app/src/main/java/com/hippo/util/MathUtils.java
+++ b/app/src/main/java/com/hippo/util/MathUtils.java
@@ -274,6 +274,16 @@ public final class MathUtils {
return n + 1;
}
+ /**
+ *
+ * @param a
+ * @param b
+ * @return
+ */
+ public static int ceilDivide(int a, int b) {
+ return (a + b - 1) / b;
+ }
+
/**
* Return is a and b is in the same side to standard.
* If one of a and b equals standard, return true.
diff --git a/app/src/main/java/com/hippo/util/ViewUtils.java b/app/src/main/java/com/hippo/util/ViewUtils.java
index 46cf42b5f..babdc5470 100644
--- a/app/src/main/java/com/hippo/util/ViewUtils.java
+++ b/app/src/main/java/com/hippo/util/ViewUtils.java
@@ -28,6 +28,8 @@ import android.view.ViewTreeObserver;
public final class ViewUtils {
+ public static final int MAX_SIZE = Integer.MAX_VALUE & ~(0x3 << 30);
+
/**
* Get view center location in window
*
diff --git a/app/src/main/java/com/hippo/widget/AccurateClick.java b/app/src/main/java/com/hippo/widget/AccurateClick.java
new file mode 100644
index 000000000..a980b150c
--- /dev/null
+++ b/app/src/main/java/com/hippo/widget/AccurateClick.java
@@ -0,0 +1,64 @@
+/*
+ * 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.widget;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+
+import com.hippo.ehviewer.R;
+import com.hippo.hotspot.HotspotTouchHelper;
+import com.hippo.hotspot.Hotspotable;
+
+public final class AccurateClick {
+
+ public static void setOnAccurateClickListener(@NonNull View view, @NonNull OnAccurateClickListener listener) {
+ AccurateClickHelper accurateClickHelper = new AccurateClickHelper();
+ view.setTag(R.id.view_tag_accurate_click_helper, accurateClickHelper);
+ view.setTag(R.id.view_tag_on_accurate_click_listener, listener);
+ view.setOnTouchListener(new HotspotTouchHelper(accurateClickHelper));
+ view.setOnClickListener(new DelegateClickListener());
+ }
+
+ static class DelegateClickListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(View v) {
+ Object accurateClickHelper = v.getTag(R.id.view_tag_accurate_click_helper);
+ Object listener = v.getTag(R.id.view_tag_on_accurate_click_listener);
+ if (accurateClickHelper instanceof AccurateClickHelper && listener instanceof OnAccurateClickListener) {
+ AccurateClickHelper ach = (AccurateClickHelper) accurateClickHelper;
+ ((OnAccurateClickListener) listener).onAccurateClick(v, ach.x, ach.y);
+ }
+ }
+ }
+
+ static class AccurateClickHelper implements Hotspotable {
+
+ public float x;
+ public float y;
+
+ @Override
+ public void setHotspot(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ public interface OnAccurateClickListener {
+ void onAccurateClick(View v, float x, float y);
+ }
+}
diff --git a/app/src/main/java/com/hippo/widget/CardButton.java b/app/src/main/java/com/hippo/widget/CardButton.java
index 7e8b87c91..f81c3a0ac 100644
--- a/app/src/main/java/com/hippo/widget/CardButton.java
+++ b/app/src/main/java/com/hippo/widget/CardButton.java
@@ -43,6 +43,13 @@ public class CardButton extends Button implements CardViewDelegate {
// Empty
}
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ if (mCardHelper != null) {
+ mCardHelper.setPadding(left, top, right, bottom);
+ }
+ }
+
@Override
public void setRawPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
diff --git a/app/src/main/java/com/hippo/widget/CardLinearLayout.java b/app/src/main/java/com/hippo/widget/CardLinearLayout.java
index 93978f29e..78eb1342b 100644
--- a/app/src/main/java/com/hippo/widget/CardLinearLayout.java
+++ b/app/src/main/java/com/hippo/widget/CardLinearLayout.java
@@ -45,7 +45,9 @@ public class CardLinearLayout extends LinearLayout implements CardViewDelegate {
@Override
public void setPadding(int left, int top, int right, int bottom) {
- mCardHelper.setPadding(left, top, right, bottom);
+ if (mCardHelper != null) {
+ mCardHelper.setPadding(left, top, right, bottom);
+ }
}
@Override
diff --git a/app/src/main/java/com/hippo/widget/SimpleGridLayout.java b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java
new file mode 100644
index 000000000..661deb3cf
--- /dev/null
+++ b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java
@@ -0,0 +1,174 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.hippo.ehviewer.R;
+import com.hippo.util.MathUtils;
+import com.hippo.util.ViewUtils;
+
+/**
+ * not scrollable
+ *
+ * @author Hippo
+ *
+ */
+public class SimpleGridLayout extends ViewGroup {
+
+ private static final int DEFAULT_COLUMN_COUNT = 3;
+
+ private int mColumnCount;
+ private int mItemMargin;
+
+ private int[] mRowHeights;
+ private int mItemWidth;
+
+ public SimpleGridLayout(Context context) {
+ super(context);
+ init(context, null);
+ }
+
+ public SimpleGridLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public SimpleGridLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs);
+ }
+
+ public void init(Context context, AttributeSet attrs) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleGridLayout);
+ mColumnCount = a.getInteger(R.styleable.SimpleGridLayout_columnCount, DEFAULT_COLUMN_COUNT);
+ mItemMargin = a.getDimensionPixelOffset(R.styleable.SimpleGridLayout_itemMargin, 0);
+ a.recycle();
+ }
+
+ public void setItemMargin(int itemMargin) {
+ if (mItemMargin != itemMargin) {
+ mItemMargin = itemMargin;
+ requestLayout();
+ }
+ }
+
+ public void setColumnCount(int columnCount) {
+ if (columnCount <= 0) {
+ throw new IllegalStateException("Column count can't be " + columnCount);
+ }
+
+ if (mColumnCount != columnCount) {
+ mColumnCount = columnCount;
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxRowCount = MathUtils.ceilDivide(getChildCount(), mColumnCount);
+ if (mRowHeights == null || mRowHeights.length != maxRowCount) {
+ mRowHeights = new int[maxRowCount];
+ }
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthMode == MeasureSpec.UNSPECIFIED) {
+ maxWidth = 300;
+ }
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ maxHeight = ViewUtils.MAX_SIZE;
+ }
+
+ // Get item width MeasureSpec
+ mItemWidth = Math.max(
+ (maxWidth - getPaddingLeft() - getPaddingRight() - ((mColumnCount - 1) * mItemMargin)) / mColumnCount, 1);
+ int itemWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY);
+ int itemHeightMeasureSpec = MeasureSpec.UNSPECIFIED;
+
+ int measuredWidth = maxWidth;
+ int measuredHeight = 0;
+ int rowHeight = 0;
+ int row = 0;
+ int count = getChildCount();
+ for (int index = 0, indexInRow = 0; index < count; index++, indexInRow++) {
+ final View child = getChildAt(index);
+ if (child.getVisibility() == View.GONE) {
+ indexInRow--;
+ continue;
+ }
+
+ child.measure(itemWidthMeasureSpec, itemHeightMeasureSpec);
+
+ if (indexInRow == mColumnCount) {
+ // New row
+ indexInRow = 0;
+ rowHeight = 0;
+ row++;
+ }
+
+ rowHeight = Math.max(rowHeight, child.getMeasuredHeight());
+
+ if (indexInRow == mColumnCount - 1 || index == count - 1) {
+ mRowHeights[row] = rowHeight;
+ measuredHeight += rowHeight + mItemMargin;
+ }
+ }
+ measuredHeight -= mItemMargin;
+ measuredHeight = Math.min(measuredHeight + getPaddingTop() + getPaddingBottom(), maxHeight);
+
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int itemWidth = mItemWidth;
+ int itemMargin = mItemMargin;
+ int paddingLeft = getPaddingLeft();
+ int left = paddingLeft;
+ int top = getPaddingTop();
+ int row = 0;
+ int count = getChildCount();
+ for (int index = 0, indexInRow = 0; index < count; index++, indexInRow++) {
+ final View child = getChildAt(index);
+ if (child.getVisibility() == View.GONE) {
+ indexInRow--;
+ continue;
+ }
+
+ if (indexInRow == mColumnCount) {
+ // New row
+ left = paddingLeft;
+ top += mRowHeights[row] + itemMargin;
+
+ indexInRow = 0;
+ row++;
+ }
+
+ child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+
+ left += itemWidth + itemMargin;
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml
new file mode 100644
index 000000000..b8969b2ff
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml
new file mode 100644
index 000000000..2fc4d8d2b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chevron_right.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-v21/scene_gallery_detail.xml b/app/src/main/res/layout-v21/scene_gallery_detail.xml
index 4654b0f5a..089bf0011 100644
--- a/app/src/main/res/layout-v21/scene_gallery_detail.xml
+++ b/app/src/main/res/layout-v21/scene_gallery_detail.xml
@@ -205,43 +205,43 @@
android:orientation="vertical"
android:paddingLeft="32dp"
android:paddingRight="32dp"
- android:paddingTop="16dp"/>
-
-
-
-
+ android:paddingBottom="16dp">
+ android:textColor="@color/theme_accent"
+ android:text="@string/no_tags"/>
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/gallery_detail_comment.xml b/app/src/main/res/layout/gallery_detail_comment.xml
new file mode 100644
index 000000000..ba729b847
--- /dev/null
+++ b/app/src/main/res/layout/gallery_detail_comment.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_preview.xml b/app/src/main/res/layout/item_preview.xml
new file mode 100644
index 000000000..a29fc42d5
--- /dev/null
+++ b/app/src/main/res/layout/item_preview.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/preview_controller.xml b/app/src/main/res/layout/preview_controller.xml
new file mode 100644
index 000000000..6db78cf6f
--- /dev/null
+++ b/app/src/main/res/layout/preview_controller.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/scene_comment.xml b/app/src/main/res/layout/scene_comment.xml
index 5fdf8874f..790ed7e4a 100644
--- a/app/src/main/res/layout/scene_comment.xml
+++ b/app/src/main/res/layout/scene_comment.xml
@@ -27,6 +27,9 @@
android:id="@+id/reply"
style="@style/FloatingActionButton"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|bottom"
+ android:layout_marginRight="@dimen/keyline_margin"
+ android:layout_marginBottom="16dp"/>
diff --git a/app/src/main/res/layout/widget_preview_layout.xml b/app/src/main/res/layout/widget_preview_layout.xml
new file mode 100644
index 000000000..8605173ed
--- /dev/null
+++ b/app/src/main/res/layout/widget_preview_layout.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 3bcd4b3df..0ed6fa36e 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -60,6 +60,30 @@
请输入数字
当前页面无效
+
+ %d 页
+ %d \u2665
+ 更多信息
+ 大家感觉 %s
+ 暂无评论
+ 暂无更多评论
+ 更多评论
+
+
+ 根本把持不住
+ 好极了
+ 很棒
+ 不错
+ 还行
+ 一般般
+ 不行
+ 糟糕
+ 瞎眼
+ 快要窒息了
+ …
+
+ 无法找到指定 Activity
+
超时
找不到主机
@@ -101,4 +125,10 @@
用户名
密码
+ 阅读
+ 种子
+ 分享
+
+ 评论
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 476bd5882..9a77babc5 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -71,4 +71,9 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index cc661e167..f6249ee22 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -130,6 +130,9 @@
20dp
2dp
+ 96dp
+ 128dp
+
24sp
22sp
diff --git a/app/src/main/res/values/id.xml b/app/src/main/res/values/id.xml
new file mode 100644
index 000000000..b29a98451
--- /dev/null
+++ b/app/src/main/res/values/id.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml
index 3ffa905fe..c26b0e3b8 100644
--- a/app/src/main/res/values/pathdata.xml
+++ b/app/src/main/res/values/pathdata.xml
@@ -38,5 +38,7 @@
M19,14.5C19,14.5 21,16.67 21,18A2,2 0 0,1 19,20A2,2 0 0,1 17,18C17,16.67 19,14.5 19,14.5M5,18V9A2,2 0 0,1 3,7A2,2 0 0,1 5,5V4A2,2 0 0,1 7,2H9A2,2 0 0,1 11,4V5H19A2,2 0 0,1 21,7V9L21,11A1,1 0 0,1 22,12A1,1 0 0,1 21,13H17A1,1 0 0,1 16,12A1,1 0 0,1 17,11V9H11V18H12A2,2 0 0,1 14,20V22H2V20A2,2 0 0,1 4,18H5Z
M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z
M22.5,10.5H15.75C15.13,10.5 14.6,10.88 14.37,11.41L12.11,16.7C12.04,16.87 12,17.06 12,17.25V18.5A1,1 0 0,0 13,19.5H18.18L17.5,22.68V22.92C17.5,23.23 17.63,23.5 17.83,23.72L18.62,24.5L23.56,19.56C23.83,19.29 24,18.91 24,18.5V12A1.5,1.5 0 0,0 22.5,10.5M12,6.5A1,1 0 0,0 11,5.5H5.82L6.5,2.32V2.09C6.5,1.78 6.37,1.5 6.17,1.29L5.38,0.5L0.44,5.44C0.17,5.71 0,6.09 0,6.5V13A1.5,1.5 0 0,0 1.5,14.5H8.25C8.87,14.5 9.4,14.12 9.63,13.59L11.89,8.3C11.96,8.13 12,7.94 12,7.75V6.5Z
+ M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z
+ M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ccd1e10f6..06b3e74ff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -82,6 +82,10 @@
%d \u2665
More information
It is %s
+ No tags
+ No comments
+ No more comments
+ More comments
a MASTERPIECE
diff --git a/cardsalon b/cardsalon
index 80600fba8..834b8482b 160000
--- a/cardsalon
+++ b/cardsalon
@@ -1 +1 @@
-Subproject commit 80600fba814f4e9f20cf59e39d3070f507591243
+Subproject commit 834b8482bd21f93d5008220b7bffc9ca5bc49751
diff --git a/hotspot b/hotspot
index 1c2b4d616..0cddb7f7c 160000
--- a/hotspot
+++ b/hotspot
@@ -1 +1 @@
-Subproject commit 1c2b4d616029a128ea5300b26ff85425ee055ee5
+Subproject commit 0cddb7f7cad043dd6745b3e0013abdba147198a5