diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c030c0ac2..2c1403f2e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,6 +41,14 @@
+
+
+
+
diff --git a/app/src/main/java/android/support/v7/widget/RecyclerView.java b/app/src/main/java/android/support/v7/widget/RecyclerView.java
index d856323b6..a1c1ce4b5 100644
--- a/app/src/main/java/android/support/v7/widget/RecyclerView.java
+++ b/app/src/main/java/android/support/v7/widget/RecyclerView.java
@@ -3570,7 +3570,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
}
}
- Rect getItemDecorInsetsForChild(View child) {
+ public Rect getItemDecorInsetsForChild(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.mInsetsDirty) {
return lp.mDecorInsets;
@@ -8332,7 +8332,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
ViewHolder mViewHolder;
final Rect mDecorInsets = new Rect();
- boolean mInsetsDirty = true;
+ public boolean mInsetsDirty = true;
// Flag is set to true if the view is bound while it is detached from RV.
// In this case, we need to manually call invalidate after view is added to guarantee that
// invalidation is populated through the View hierarchy
diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/widget/GalleryView.java b/app/src/main/java/com/hippo/ehviewer/gallery/widget/GalleryView.java
new file mode 100644
index 000000000..7c7368d49
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/gallery/widget/GalleryView.java
@@ -0,0 +1,341 @@
+/*
+ * 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.gallery.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.hippo.ehviewer.R;
+import com.hippo.yorozuya.LayoutUtils;
+import com.hippo.yorozuya.MathUtils;
+import com.hippo.yorozuya.Say;
+
+public class GalleryView extends RecyclerView {
+
+ private static final String TAG = GalleryView.class.getSimpleName();
+
+ private static int INTERVAL_DP = 32;
+
+ private Mode mMode;
+ private int mHalfInterval;
+ private int mSize = 20;
+
+ private GalleryAdapter mAdapter;
+ private GalleryLayoutManager mLayoutManager;
+
+ public enum Mode {
+ LEFT_TO_RIGHT,
+ RIGHT_TO_LEFT,
+ TOP_TO_BOTTOM
+ }
+
+ public GalleryView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public GalleryView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public GalleryView(Context context, @Nullable AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ mHalfInterval = LayoutUtils.dp2pix(context, INTERVAL_DP) / 2;
+
+ mLayoutManager = new GalleryLayoutManager(context);
+ setLayoutManager(mLayoutManager);
+ mAdapter = new GalleryAdapter();
+ setAdapter(mAdapter);
+ addItemDecoration(new IntervalDecoration());
+ addOnScrollListener(new ScrollListener());
+ setHasFixedSize(true);
+ setMode(Mode.RIGHT_TO_LEFT);
+ }
+
+ public void setMode(Mode mode) {
+ mMode = mode;
+ if (mMode == Mode.LEFT_TO_RIGHT) {
+ mLayoutManager.setOrientation(HORIZONTAL);
+ mLayoutManager.setReverseLayout(false);
+ } else if (mMode == Mode.RIGHT_TO_LEFT) {
+ mLayoutManager.setOrientation(HORIZONTAL);
+ mLayoutManager.setReverseLayout(true);
+ } else if (mMode == Mode.TOP_TO_BOTTOM) {
+ mLayoutManager.setOrientation(VERTICAL);
+ mLayoutManager.setReverseLayout(false);
+ }
+ // Not all DecorInset will be updated at once, so do it by myself
+ int length = getChildCount();
+ for (int i = 0; i < length; i++) {
+ ((RecyclerView.LayoutParams) getChildAt(i).getLayoutParams()).mInsetsDirty = true;
+ }
+ }
+
+ @Override
+ public boolean fling(int velocityX, int velocityY) {
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ return super.fling(velocityX, velocityY);
+ } else {
+ return false;
+ }
+ }
+
+ // Only work for Mode.LEFT_TO_RIGHT and Mode.RIGHT_TO_LEFT
+ private View findCenterXView() {
+ int centerX = getWidth() / 2;
+ int length = getChildCount();
+ for (int i = 0; i < length; i++) {
+ View v = getChildAt(i);
+ if (v.getLeft() < centerX && v.getRight() >= centerX) {
+ return v;
+ }
+ }
+ return null;
+ }
+
+ // Only work for Mode.LEFT_TO_RIGHT and Mode.RIGHT_TO_LEFT
+ private int getOffsetToPage(int page) throws Exception {
+ View v = findCenterXView();
+ if (v == null) {
+ throw new Exception("Can't find center view");
+ }
+ int currentPage = getChildAdapterPosition(v);
+ if (currentPage == RecyclerView.NO_POSITION) {
+ throw new Exception("Can't find current page");
+ }
+
+ int d = page - currentPage;
+ if (mMode == Mode.RIGHT_TO_LEFT) {
+ d = -d;
+ }
+ return d * (mHalfInterval * 2 + getWidth()) + v.getLeft();
+ }
+
+ public void smoothScrollToPage(int page) {
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ smoothScrollToPosition(page);
+ } else {
+ page = MathUtils.clamp(page, 0, mSize - 1);
+ try {
+ int offsetX = getOffsetToPage(page);
+ smoothScrollBy(offsetX, 0);
+ } catch (Exception e) {
+ Say.d(TAG, e.getMessage());
+ smoothScrollToPosition(page);
+ }
+ }
+ }
+
+ public void scrollToPage(int page) {
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ scrollToPosition(page);
+ } else {
+ page = MathUtils.clamp(page, 0, mSize - 1);
+ try {
+ int offsetX = getOffsetToPage(page);
+ scrollBy(offsetX, 0);
+ } catch (Exception e) {
+ Say.d(TAG, e.getMessage());
+ scrollToPosition(page);
+ }
+ }
+ }
+
+ public int getCurrentPage() {
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ return mLayoutManager.findFirstVisibleItemPosition();
+ } else {
+ View v = findCenterXView();
+ if (v == null) {
+ return RecyclerView.NO_POSITION;
+ } else {
+ return getChildLayoutPosition(v);
+ }
+ }
+ }
+
+ class GalleryHolder extends RecyclerView.ViewHolder {
+
+ private ImageView imageView;
+
+ public GalleryHolder(View itemView) {
+ super(itemView);
+
+ imageView = (ImageView) itemView;
+ }
+ }
+
+ class GalleryAdapter extends RecyclerView.Adapter {
+
+ @Override
+ public GalleryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new GalleryHolder(new ImageView(getContext()));
+ }
+
+ @Override
+ public void onBindViewHolder(GalleryHolder holder, int position) {
+ holder.imageView.setImageDrawable(getResources().getDrawable(position == 0 ?
+ R.drawable.ic_heart : R.drawable.ic_sad));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSize;
+ }
+ }
+
+ class GalleryLayoutManager extends LinearLayoutManager {
+
+ public GalleryLayoutManager(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
+ // Call it to update decor
+ getItemDecorInsetsForChild(child);
+
+ int widthSpec;
+ int heightSpec;
+
+ // LayoutParams is useless
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ widthSpec = View.MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
+ heightSpec = View.MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST);
+ } else {
+ widthSpec = View.MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
+ heightSpec = View.MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY);
+ }
+ child.measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ super.onLayoutChildren(recycler, state);
+
+ // Ensure page is in center
+ if (mMode == Mode.LEFT_TO_RIGHT || mMode == Mode.RIGHT_TO_LEFT) {
+ scrollToPage(getCurrentPage());
+ }
+ }
+ }
+
+ class ScrollListener extends OnScrollListener {
+
+ private int mCurrentPage = RecyclerView.NO_POSITION;
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ if (mMode == Mode.TOP_TO_BOTTOM) {
+ // Nothing to do
+ return;
+ }
+
+ if (newState == SCROLL_STATE_DRAGGING) {
+ mCurrentPage = getCurrentPage();
+ } else if (newState == SCROLL_STATE_IDLE) {
+ if (mCurrentPage != RecyclerView.NO_POSITION) {
+ ViewHolder viewHolder = findViewHolderForLayoutPosition(mCurrentPage);
+ if (viewHolder == null) {
+ // WTF?
+ Say.w(TAG, "Can't find ViewHolder when adjustPosition");
+ smoothScrollToPage(mCurrentPage);
+ } else {
+ View v = viewHolder.itemView;
+ int scrollX;
+ int interval = mHalfInterval * 2;
+ int left = v.getLeft();
+ int width = getWidth();
+ if (left > interval &&
+ ((mMode == Mode.LEFT_TO_RIGHT && mCurrentPage > 0) ||
+ (mMode == Mode.RIGHT_TO_LEFT && mCurrentPage < mSize - 1))) { // Scroll left
+ scrollX = -width - interval + left;
+ } else if (left < -interval &&
+ ((mMode == Mode.LEFT_TO_RIGHT && mCurrentPage < mSize - 1) ||
+ (mMode == Mode.RIGHT_TO_LEFT && mCurrentPage > 0))) { // Scroll right
+ scrollX = width + interval + left;
+ } else {
+ scrollX = left;
+ }
+ smoothScrollBy(scrollX, 0);
+ }
+ mCurrentPage = RecyclerView.NO_POSITION;
+ }
+ }
+ }
+ }
+
+ class IntervalDecoration extends RecyclerView.ItemDecoration {
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
+ int index = getChildLayoutPosition(view);
+ if (mMode == Mode.LEFT_TO_RIGHT) {
+ if (index == 0) {
+ outRect.left = 0;
+ } else {
+ outRect.left = mHalfInterval;
+ }
+ if (index == mSize - 1) {
+ outRect.right = 0;
+ } else {
+ outRect.right = mHalfInterval;
+ }
+ outRect.top = 0;
+ outRect.bottom = 0;
+ } else if (mMode == Mode.RIGHT_TO_LEFT) {
+ if (index == 0) {
+ outRect.right = 0;
+ } else {
+ outRect.right = mHalfInterval;
+ }
+ if (index == mSize - 1) {
+ outRect.left = 0;
+ } else {
+ outRect.left = mHalfInterval;
+ }
+ outRect.top = 0;
+ outRect.bottom = 0;
+ } else if (mMode == Mode.TOP_TO_BOTTOM) {
+ if (index == 0) {
+ outRect.top = 0;
+ } else {
+ outRect.top = mHalfInterval;
+ }
+ if (index == mSize - 1) {
+ outRect.bottom = 0;
+ } else {
+ outRect.bottom = mHalfInterval;
+ }
+ outRect.left = 0;
+ outRect.right = 0;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java
index 5cf3a3321..0580fa33d 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java
@@ -16,7 +16,17 @@
package com.hippo.ehviewer.ui;
+import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
+import com.hippo.ehviewer.R;
+
public class GalleryActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_gallery);
+ }
}
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 3d9bbef10..ae88ef875 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
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.ui.scene;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.text.Html;
@@ -47,6 +48,7 @@ import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.PreviewSet;
import com.hippo.ehviewer.client.data.TagGroup;
+import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.ehviewer.util.Settings;
import com.hippo.ehviewer.widget.LoadImageView;
@@ -278,9 +280,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
}
if (v == mRead) {
- Announcer announcer = new Announcer();
- announcer.putExtra(GalleryScene.KEY_GALLERY_BASE, mGalleryDetail);
- startScene(GalleryScene.class, announcer);
+ Intent intent = new Intent(getStageActivity(), GalleryActivity.class);
+ getStageActivity().startActivity(intent);
} else if (v == mDownload) {
} else if (v == mFavorite) {
diff --git a/app/src/main/res/layout/activity_gallery.xml b/app/src/main/res/layout/activity_gallery.xml
new file mode 100644
index 000000000..6cf2a661e
--- /dev/null
+++ b/app/src/main/res/layout/activity_gallery.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+