Improve Gallery Detail
This commit is contained in:
1 parent
dd1daa96d2
commit
507c7e7879
42 files changed
+2174
-142
No files matched your search
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.drawable;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.conaco.Conaco;
|
||||
import com.hippo.conaco.Unikery;
|
||||
import com.hippo.conaco.ValueHolder;
|
||||
|
||||
public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageWrapper> {
|
||||
|
||||
private int mTaskId = Unikery.INVALID_ID;
|
||||
|
||||
private TextView mTextView;
|
||||
|
||||
private ValueHolder<ImageWrapper> mHolder;
|
||||
|
||||
public UnikeryDrawable(TextView textView) {
|
||||
mTextView = textView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawable(Drawable drawable) {
|
||||
// Remove old callback
|
||||
Drawable oldDrawable = getDrawable();
|
||||
if (oldDrawable != null) {
|
||||
oldDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
super.setDrawable(drawable);
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(mTextView);
|
||||
}
|
||||
|
||||
updateBounds();
|
||||
if (drawable != null) {
|
||||
// mTextView.requestLayout() not work!
|
||||
CharSequence cs = mTextView.getText();
|
||||
mTextView.setText(cs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskId(int id) {
|
||||
mTaskId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskId() {
|
||||
return mTaskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMiss(Conaco.Source source) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long singleReceivedSize, long receivedSize, long totalSize) {
|
||||
}
|
||||
|
||||
private void removeDrawableAndHolder() {
|
||||
// Remove drawable
|
||||
Drawable drawable = getDrawable();
|
||||
if (drawable instanceof ImageDrawable) {
|
||||
((ImageDrawable) drawable).recycle();
|
||||
}
|
||||
setDrawable(null);
|
||||
|
||||
// Remove holder
|
||||
if (mHolder != null) {
|
||||
mHolder.release(this);
|
||||
|
||||
ImageWrapper imageWrapper = mHolder.getValue();
|
||||
if (mHolder.isFree()) {
|
||||
// ImageWrapper is free, stop animate
|
||||
imageWrapper.stop();
|
||||
if (!mHolder.isInMemoryCache()) {
|
||||
// ImageWrapper is not needed any more, recycle it
|
||||
imageWrapper.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
mHolder = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGetObject(@NonNull ValueHolder<ImageWrapper> holder, Conaco.Source source) {
|
||||
holder.obtain(this);
|
||||
|
||||
removeDrawableAndHolder();
|
||||
|
||||
mHolder = holder;
|
||||
ImageWrapper imageWrapper = holder.getValue();
|
||||
Drawable drawable = new ImageDrawable(imageWrapper);
|
||||
imageWrapper.start();
|
||||
|
||||
setDrawable(drawable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetDrawable(Drawable drawable) {
|
||||
removeDrawableAndHolder();
|
||||
|
||||
setDrawable(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
// Empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.drawable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class WrapDrawable extends Drawable {
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
public void setDrawable(Drawable drawable) {
|
||||
mDrawable = drawable;
|
||||
}
|
||||
|
||||
public Drawable getDrawable() {
|
||||
return mDrawable;
|
||||
}
|
||||
|
||||
public void updateBounds() {
|
||||
setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int left, int top, int right, int bottom) {
|
||||
super.setBounds(left, top, right, bottom);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setBounds(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Rect bounds) {
|
||||
super.setBounds(bounds);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangingConfigurations(int configs) {
|
||||
super.setChangingConfigurations(configs);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getChangingConfigurations();
|
||||
} else {
|
||||
return super.getChangingConfigurations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDither(boolean dither) {
|
||||
super.setDither(dither);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setDither(dither);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
super.setFilterBitmap(filter);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setColorFilter(cf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getOpacity();
|
||||
} else {
|
||||
return PixelFormat.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getIntrinsicWidth();
|
||||
} else {
|
||||
return super.getIntrinsicWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getIntrinsicHeight();
|
||||
} else {
|
||||
return super.getIntrinsicHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.hippo.beerbelly.LruMap;
|
||||
import com.hippo.beerbelly.LruCacheEx;
|
||||
import com.hippo.conaco.Conaco;
|
||||
import com.hippo.drawable.ImageWrapper;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
@@ -30,6 +30,7 @@ import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
import com.hippo.okhttp.CookieDB;
|
||||
import com.hippo.scene.SceneApplication;
|
||||
import com.hippo.text.Html;
|
||||
import com.hippo.utils.ReadableTime;
|
||||
import com.hippo.yorozuya.IntIdGenerator;
|
||||
|
||||
@@ -41,6 +42,8 @@ import okhttp3.OkHttpClient;
|
||||
|
||||
public class EhApplication extends SceneApplication {
|
||||
|
||||
private static final boolean DEBUG_CONACO = false;
|
||||
|
||||
private IntIdGenerator mIdGenerator = new IntIdGenerator();
|
||||
private SparseArray<Object> mGlobalStuffMap = new SparseArray<>();
|
||||
private EhCookieStore mEhCookieStore;
|
||||
@@ -48,7 +51,7 @@ public class EhApplication extends SceneApplication {
|
||||
private OkHttpClient mOkHttpClient;
|
||||
private ImageWrapperHelper mImageWrapperHelper;
|
||||
private Conaco<ImageWrapper> mConaco;
|
||||
private LruMap<Integer, GalleryDetail> mGalleryDetailCache;
|
||||
private LruCacheEx<Integer, GalleryDetail> mGalleryDetailCache;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -59,6 +62,7 @@ public class EhApplication extends SceneApplication {
|
||||
StatusCodeException.initialize(this);
|
||||
Settings.initialize(this);
|
||||
ReadableTime.initialize(this);
|
||||
Html.initialize(this);
|
||||
}
|
||||
|
||||
public int putGlobalStuff(@NonNull Object o) {
|
||||
@@ -155,22 +159,23 @@ public class EhApplication extends SceneApplication {
|
||||
builder.diskCacheMaxSize = 80 * 1024 * 1024; // 80MB
|
||||
builder.okHttpClient = getOkHttpClient(context);
|
||||
builder.objectHelper = getImageWrapperHelper(context);
|
||||
builder.debug = BuildConfig.DEBUG;
|
||||
builder.debug = DEBUG_CONACO;
|
||||
application.mConaco = builder.build();
|
||||
}
|
||||
return application.mConaco;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static LruMap<Integer, GalleryDetail> getGalleryDetailCache(@NonNull Context context) {
|
||||
public static LruCacheEx<Integer, GalleryDetail> getGalleryDetailCache(@NonNull Context context) {
|
||||
EhApplication application = ((EhApplication) context.getApplicationContext());
|
||||
if (application.mGalleryDetailCache == null) {
|
||||
application.mGalleryDetailCache = new LruMap<>(new Comparator<Integer>() {
|
||||
// Max size 25, 3 min timeout
|
||||
application.mGalleryDetailCache = new LruCacheEx<>(25, 3 * 60 * 1000, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer lhs, Integer rhs) {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}, 3 * 60 * 1000); // 3 min timeout
|
||||
});
|
||||
}
|
||||
return application.mGalleryDetailCache;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
public class EhCacheKeyFactory {
|
||||
|
||||
public static String getThumbKey(int gid) {
|
||||
return "thumb:" + gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getNormalPreviewKey(int gid, int index) {
|
||||
return "preview:normal:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getLargePreviewKey(int gid, int index) {
|
||||
return "preview:large:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getImageKey(int gid, int index) {
|
||||
return "image:" + gid + ":" + index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class EhCacheKeyFactory {
|
||||
|
||||
public static String getThumbKey(int gid) {
|
||||
return "thumb:" + gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getNormalPreviewKey(int gid, int index) {
|
||||
return "preview:normal:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getLargePreviewKey(int gid, int index) {
|
||||
return "preview:large:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getImageKey(int gid, int index) {
|
||||
return "image:" + gid + ":" + index;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.hippo.ehviewer.client;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.exception.CancelledException;
|
||||
import com.hippo.ehviewer.client.exception.EhException;
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser;
|
||||
@@ -126,7 +127,7 @@ public class EhEngine {
|
||||
return okHttpClient.newCall(request);
|
||||
}
|
||||
|
||||
public static GalleryDetailParser.Result doGetGalleryDetail(Call call) throws Exception {
|
||||
public static GalleryDetail doGetGalleryDetail(Call call) throws Exception {
|
||||
String body = null;
|
||||
Headers headers = null;
|
||||
int code = -1;
|
||||
|
||||
@@ -34,6 +34,7 @@ public class GalleryDetail extends GalleryInfo {
|
||||
public GalleryTagGroup[] tags;
|
||||
public GalleryComment[] comments;
|
||||
public int previewPages;
|
||||
public LargePreviewSet previewSet;
|
||||
|
||||
|
||||
@Override
|
||||
@@ -58,6 +59,7 @@ public class GalleryDetail extends GalleryInfo {
|
||||
dest.writeParcelableArray(this.tags, 0);
|
||||
dest.writeParcelableArray(this.comments, 0);
|
||||
dest.writeInt(this.previewPages);
|
||||
dest.writeParcelable(previewSet, flags);
|
||||
}
|
||||
|
||||
public GalleryDetail() {
|
||||
@@ -79,6 +81,7 @@ public class GalleryDetail extends GalleryInfo {
|
||||
this.tags = in.createTypedArray(GalleryTagGroup.CREATOR);
|
||||
this.comments = in.createTypedArray(GalleryComment.CREATOR);
|
||||
this.previewPages = in.readInt();
|
||||
this.previewSet = in.readParcelable(LargePreviewSet.class.getClassLoader());
|
||||
}
|
||||
|
||||
public static final Creator<GalleryDetail> CREATOR = new Creator<GalleryDetail>() {
|
||||
|
||||
@@ -16,10 +16,16 @@
|
||||
|
||||
package com.hippo.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.hippo.yorozuya.IntList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LargePreviewSet {
|
||||
public class LargePreviewSet implements Parcelable {
|
||||
|
||||
private IntList mIndexList = new IntList();
|
||||
private ArrayList<String> mImageUrlList = new ArrayList<>();
|
||||
private ArrayList<String> mPageUrlList = new ArrayList<>();
|
||||
|
||||
@@ -27,11 +33,16 @@ public class LargePreviewSet {
|
||||
return mImageUrlList.size();
|
||||
}
|
||||
|
||||
public void addItem(String imageUrl, String pageUrl) {
|
||||
public void addItem(int index, String imageUrl, String pageUrl) {
|
||||
mIndexList.add(index);
|
||||
mImageUrlList.add(imageUrl);
|
||||
mPageUrlList.add(pageUrl);
|
||||
}
|
||||
|
||||
public int getIndexAt(int index) {
|
||||
return mIndexList.get(index);
|
||||
}
|
||||
|
||||
public String getImageUrlAt(int index) {
|
||||
return mImageUrlList.get(index);
|
||||
}
|
||||
@@ -39,4 +50,36 @@ public class LargePreviewSet {
|
||||
public String getPageUrlAt(int index) {
|
||||
return mPageUrlList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
this.mIndexList.writeToParcel(dest, flags);
|
||||
dest.writeParcelable(this.mIndexList, 0);
|
||||
dest.writeStringList(this.mImageUrlList);
|
||||
dest.writeStringList(this.mPageUrlList);
|
||||
}
|
||||
|
||||
public LargePreviewSet() {
|
||||
}
|
||||
|
||||
protected LargePreviewSet(Parcel in) {
|
||||
this.mIndexList = IntList.CREATOR.createFromParcel(in);
|
||||
this.mImageUrlList = in.createStringArrayList();
|
||||
this.mPageUrlList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LargePreviewSet> CREATOR = new Parcelable.Creator<LargePreviewSet>() {
|
||||
public LargePreviewSet createFromParcel(Parcel source) {
|
||||
return new LargePreviewSet(source);
|
||||
}
|
||||
|
||||
public LargePreviewSet[] newArray(int size) {
|
||||
return new LargePreviewSet[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class GalleryDetailParser {
|
||||
public static final Pattern PAGES_PATTERN = Pattern.compile("<tr><td[^<>]*>Length:</td><td[^<>]*>([\\d,]+) pages</td></tr>");
|
||||
public static final Pattern PREVIEW_PAGES_PATTERN = Pattern.compile("<td[^>]+><a[^>]+>([\\d,]+)</a></td><td[^>]+>(?:<a[^>]+>)?>(?:</a>)?</td>");
|
||||
private static final Pattern NORMAL_PREVIEW_PATTERN = Pattern.compile("<div[^<>]*class=\"gdtm\"[^<>]*><div[^<>]*width:(\\d+)[^<>]*height:(\\d+)[^<>]*\\((.+?)\\)[^<>]*-(\\d+)px[^<>]*><a[^<>]*href=\"(.+?)\"[^<>]*>");
|
||||
private static final Pattern LARGE_PREVIEW_PATTERN = Pattern.compile("<div class=\"gdtl\".+?<a href=\"(.+?)\"><img.+?src=\"(.+?)\"");
|
||||
private static final Pattern LARGE_PREVIEW_PATTERN = Pattern.compile("<div class=\"gdtl\".+?<a href=\"(.+?)\"><img alt=\"([\\d,]+)\".+?src=\"(.+?)\"");
|
||||
|
||||
static {
|
||||
WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
@@ -92,12 +92,7 @@ public class GalleryDetailParser {
|
||||
private static final String PINING_STRING =
|
||||
"<p>This gallery is pining for the fjords.</p>";
|
||||
|
||||
public static class Result {
|
||||
public GalleryDetail galleryDetail;
|
||||
public LargePreviewSet previewSet;
|
||||
}
|
||||
|
||||
public static GalleryDetailParser.Result parse(String body) throws EhException {
|
||||
public static GalleryDetail parse(String body) throws EhException {
|
||||
if (body.contains(OFFENSIVE_STRING)) {
|
||||
throw new OffensiveException();
|
||||
}
|
||||
@@ -117,16 +112,10 @@ public class GalleryDetailParser {
|
||||
galleryDetail.tags = parseTagGroup(body);
|
||||
galleryDetail.comments = parseComment(body);
|
||||
galleryDetail.previewPages = parsePreviewPages(body);
|
||||
|
||||
LargePreviewSet previewSet = parsePreview(body);
|
||||
|
||||
Result result = new Result();
|
||||
result.galleryDetail = galleryDetail;
|
||||
result.previewSet = previewSet;
|
||||
return result;
|
||||
galleryDetail.previewSet = parsePreview(body);
|
||||
return galleryDetail;
|
||||
}
|
||||
|
||||
|
||||
public static void parseDetail(String body, GalleryDetail gd) throws EhException {
|
||||
Matcher m = DETAIL_PATTERN.matcher(body);
|
||||
if (!m.find()) {
|
||||
@@ -253,18 +242,23 @@ public class GalleryDetailParser {
|
||||
}
|
||||
|
||||
public static LargePreviewSet parsePreview(String body) throws EhException {
|
||||
return parseLargePreview(body);
|
||||
/*
|
||||
if (body.contains("<div class=\"gdtm\"")) {
|
||||
throw new EhException("Not support normal preview now");
|
||||
} else {
|
||||
return parseLargePreview(body);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private static LargePreviewSet parseLargePreview(String body) {
|
||||
Matcher m = LARGE_PREVIEW_PATTERN.matcher(body);
|
||||
LargePreviewSet largePreviewSet = new LargePreviewSet();
|
||||
|
||||
while (m.find()) {
|
||||
largePreviewSet.addItem(m.group(2), m.group(1));
|
||||
largePreviewSet.addItem(ParserUtils.parseInt(m.group(2)),
|
||||
ParserUtils.trim(m.group(3)), ParserUtils.trim(m.group(1)));
|
||||
}
|
||||
return largePreviewSet;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.ui.scene;
|
||||
|
||||
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.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.easyrecyclerview.LinearDividerItemDecoration;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.text.Html;
|
||||
import com.hippo.text.URLImageGetter;
|
||||
import com.hippo.utils.OpenUrlHelper;
|
||||
import com.hippo.utils.ReadableTime;
|
||||
import com.hippo.utils.TextUrl;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.widget.LinkifyTextView;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
|
||||
public final class GalleryCommentsScene extends ToolbarScene
|
||||
implements EasyRecyclerView.OnItemClickListener,
|
||||
EasyRecyclerView.OnItemLongClickListener {
|
||||
|
||||
public static final String KEY_COMMENTS = "comments";
|
||||
|
||||
private GalleryComment[] mComments;
|
||||
private CommentAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
onInit();
|
||||
} else {
|
||||
onRestore(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleArgs(Bundle args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Parcelable[] parcelables = args.getParcelableArray(KEY_COMMENTS);
|
||||
if (parcelables instanceof GalleryComment[]) {
|
||||
mComments = (GalleryComment[]) parcelables;
|
||||
}
|
||||
}
|
||||
|
||||
private void onInit() {
|
||||
handleArgs(getArguments());
|
||||
}
|
||||
|
||||
private void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
Parcelable[] parcelables = savedInstanceState.getParcelableArray(KEY_COMMENTS);
|
||||
if (parcelables instanceof GalleryComment[]) {
|
||||
mComments = (GalleryComment[]) parcelables;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelableArray(KEY_COMMENTS, mComments);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView2(LayoutInflater inflater,
|
||||
@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);
|
||||
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fab);
|
||||
|
||||
mAdapter = new CommentAdapter();
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(),
|
||||
LinearLayoutManager.VERTICAL, false));
|
||||
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
|
||||
LinearDividerItemDecoration.VERTICAL, getResources().getColor(R.color.divider),
|
||||
LayoutUtils.dp2pix(getContext(), 1));
|
||||
decoration.setShowLastDivider(true);
|
||||
recyclerView.addItemDecoration(decoration);
|
||||
recyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setOnItemClickListener(this);
|
||||
recyclerView.setOnItemLongClickListener(this);
|
||||
|
||||
fab.setImageDrawable(VectorDrawable.create(getContext(), R.drawable.ic_reply));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setTitle(R.string.gallery_comments);
|
||||
setNavigationIcon(VectorDrawable.create(getContext(), R.drawable.ic_arrow_left));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationClick() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
|
||||
RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
|
||||
if (holder instanceof CommentHolder) {
|
||||
CommentHolder commentHolder = (CommentHolder) holder;
|
||||
ClickableSpan span = commentHolder.comment.getCurrentSpan();
|
||||
commentHolder.comment.clearCurrentSpan();
|
||||
|
||||
if (span instanceof URLSpan) {
|
||||
OpenUrlHelper.openUrl(getContext(), ((URLSpan) span).getURL(), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(EasyRecyclerView parent, View view, int position, long id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private class CommentHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView user;
|
||||
public TextView time;
|
||||
public LinkifyTextView comment;
|
||||
|
||||
public CommentHolder(View itemView) {
|
||||
super(itemView);
|
||||
user = (TextView) itemView.findViewById(R.id.user);
|
||||
time = (TextView) itemView.findViewById(R.id.time);
|
||||
comment = (LinkifyTextView) itemView.findViewById(R.id.comment);
|
||||
}
|
||||
}
|
||||
|
||||
private class CommentAdapter extends RecyclerView.Adapter<CommentHolder> {
|
||||
|
||||
@Override
|
||||
public CommentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new CommentHolder(getActivity().getLayoutInflater()
|
||||
.inflate(R.layout.item_gallery_comment, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(CommentHolder holder, int position) {
|
||||
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(getContext())), null)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mComments == null ? 0 : mComments.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -34,8 +35,8 @@ import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
@@ -44,19 +45,25 @@ import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.client.data.GalleryTagGroup;
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser;
|
||||
import com.hippo.ehviewer.client.data.LargePreviewSet;
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.text.Html;
|
||||
import com.hippo.text.URLImageGetter;
|
||||
import com.hippo.utils.ApiHelper;
|
||||
import com.hippo.utils.ExceptionUtils;
|
||||
import com.hippo.utils.ReadableTime;
|
||||
import com.hippo.vector.AnimatedVectorDrawable;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.widget.AutoWrapLayout;
|
||||
import com.hippo.widget.LoadImageView;
|
||||
import com.hippo.widget.SimpleGridLayout;
|
||||
import com.hippo.widget.SimpleImageView;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
// TODO Update drawer checked item
|
||||
public class GalleryDetailScene extends BaseScene implements View.OnClickListener {
|
||||
|
||||
@@ -111,8 +118,10 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
// Comments
|
||||
private LinearLayout mComments;
|
||||
private TextView mCommentsText;
|
||||
|
||||
|
||||
// Previews
|
||||
private View mPreviews;
|
||||
private SimpleGridLayout mGridLayout;
|
||||
private TextView mPreviewText;
|
||||
// Progress
|
||||
private View mProgress;
|
||||
|
||||
@@ -175,6 +184,27 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
}
|
||||
|
||||
private String getUploader() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.uploader;
|
||||
} else if (mGalleryInfo != null) {
|
||||
return mGalleryInfo.uploader;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// -1 for error
|
||||
private int getCategory() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.category;
|
||||
} else if (mGalleryInfo != null) {
|
||||
return mGalleryInfo.category;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -199,6 +229,26 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mRequestId = savedInstanceState.getInt(KEY_REQUEST_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
if (mAction != null) {
|
||||
outState.putString(KEY_ACTION, mAction);
|
||||
}
|
||||
if (mGalleryInfo != null) {
|
||||
outState.putParcelable(KEY_GALLERY_INFO, mGalleryInfo);
|
||||
}
|
||||
outState.putInt(KEY_GID, mGid);
|
||||
if (mToken != null) {
|
||||
outState.putString(KEY_TOKEN, mAction);
|
||||
}
|
||||
if (mGalleryDetail != null) {
|
||||
outState.putParcelable(KEY_GALLERY_DETAIL, mGalleryDetail);
|
||||
}
|
||||
outState.putInt(KEY_REQUEST_ID, mRequestId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@@ -210,6 +260,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mProgressView = main.findViewById(R.id.progress_view);
|
||||
mFailedView = (ViewGroup) main.findViewById(R.id.tip);
|
||||
mFailedText = (TextView) mFailedView.getChildAt(1);
|
||||
mFailedView.setOnClickListener(this);
|
||||
|
||||
View mainView = mMainView;
|
||||
mHeader = mainView.findViewById(R.id.header);
|
||||
@@ -225,6 +276,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
RippleSalon.addRipple(mOtherActions, false);
|
||||
RippleSalon.addRipple(mDownload, false);
|
||||
RippleSalon.addRipple(mRead, false);
|
||||
mUploader.setOnClickListener(this);
|
||||
mCategory.setOnClickListener(this);
|
||||
mOtherActions.setOnClickListener(this);
|
||||
mDownload.setOnClickListener(this);
|
||||
mRead.setOnClickListener(this);
|
||||
@@ -265,16 +318,24 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mComments = (LinearLayout) belowHeader.findViewById(R.id.comments);
|
||||
mCommentsText = (TextView) mComments.findViewById(R.id.comments_text);
|
||||
|
||||
mPreviews = belowHeader.findViewById(R.id.previews);
|
||||
mGridLayout = (SimpleGridLayout) mPreviews.findViewById(R.id.grid_layout);
|
||||
mPreviewText = (TextView) mPreviews.findViewById(R.id.preview_text);
|
||||
|
||||
mProgress = mainView.findViewById(R.id.progress);
|
||||
|
||||
fetchGalleryDetail();
|
||||
adjustViewVisibility();
|
||||
if (mGalleryDetail == null) {
|
||||
bindViewFirst();
|
||||
if (prepareData()) {
|
||||
if (mGalleryDetail != null) {
|
||||
bindViewSecond();
|
||||
setTransitionName();
|
||||
} else if (mGalleryInfo != null) {
|
||||
bindViewFirst();
|
||||
setTransitionName();
|
||||
}
|
||||
} else {
|
||||
bindViewSecond();
|
||||
mFailedText.setText(R.string.cannot_find_gallery);
|
||||
}
|
||||
setTransitionName();
|
||||
adjustViewVisibility();
|
||||
|
||||
return view;
|
||||
}
|
||||
@@ -316,6 +377,16 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mShare = null;
|
||||
mRate = null;
|
||||
|
||||
mTags = null;
|
||||
mNoTags = null;
|
||||
|
||||
mComments = null;
|
||||
mCommentsText = null;
|
||||
|
||||
mPreviews = null;
|
||||
mGridLayout = null;
|
||||
mPreviewText = null;
|
||||
|
||||
mProgress = null;
|
||||
|
||||
mHeartDrawable = null;
|
||||
@@ -323,32 +394,36 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mPopupMenu = null;
|
||||
}
|
||||
|
||||
private void fetchGalleryDetail() {
|
||||
private boolean prepareData() {
|
||||
if (mGalleryDetail != null) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
int gid = getGid();
|
||||
if (gid == -1) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get from cache
|
||||
mGalleryDetail = EhApplication.getGalleryDetailCache(getContext()).get(gid);
|
||||
if (mGalleryDetail != null) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
EhApplication application = (EhApplication) getContext().getApplicationContext();
|
||||
if (application.containGlobalStuff(mRequestId)) {
|
||||
// request exist
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do request
|
||||
return request();
|
||||
}
|
||||
|
||||
private boolean request() {
|
||||
String url = getGalleryDetailUrl();
|
||||
if (url == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
EhClient.Callback callback = new GetGalleryDetailListener(getContext(),
|
||||
@@ -359,6 +434,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
.setArgs(url)
|
||||
.setCallback(callback);
|
||||
EhApplication.getEhClient(getContext()).execute(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void adjustViewVisibility() {
|
||||
@@ -400,9 +477,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mUploader.setText(gi.uploader);
|
||||
mCategory.setText(EhUtils.getCategory(gi.category));
|
||||
mCategory.setTextColor(EhUtils.getCategoryColor(gi.category));
|
||||
|
||||
// Hide actions
|
||||
mActionGroup.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,10 +509,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActionGroup.getVisibility() == View.GONE) {
|
||||
mActionGroup.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
|
||||
if (TextUtils.isEmpty(mCategory.getText())) {
|
||||
@@ -471,8 +541,10 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
|
||||
bindTags(gd.tags);
|
||||
bindComments(gd.comments);
|
||||
bindPreviews(gd);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void bindTags(GalleryTagGroup[] tagGroups) {
|
||||
mTags.removeViews(1, mTags.getChildCount() - 1);
|
||||
if (tagGroups == null || tagGroups.length == 0) {
|
||||
@@ -502,13 +574,19 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
for (int j = 0, z = tg.size(); j < z; j++) {
|
||||
TextView tag = (TextView) inflater.inflate(R.layout.item_gallery_tag, awl, false);
|
||||
awl.addView(tag);
|
||||
tag.setText(tg.getTagAt(j));
|
||||
String tagStr = tg.getTagAt(j);
|
||||
tag.setText(tagStr);
|
||||
tag.setBackgroundColor(colorTag);
|
||||
tag.setTag(R.id.tag, tagStr);
|
||||
tag.setOnClickListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindComments(GalleryComment[] comments) {
|
||||
RippleSalon.addRipple(mComments, false);
|
||||
mComments.setOnClickListener(this);
|
||||
|
||||
mComments.removeViews(0, mComments.getChildCount() - 1);
|
||||
|
||||
final int maxShowCount = 2;
|
||||
@@ -527,9 +605,40 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
GalleryComment comment = comments[i];
|
||||
View v = inflater.inflate(R.layout.item_gallery_comment, mComments, false);
|
||||
mComments.addView(v, i);
|
||||
((TextView) v.findViewById(R.id.user)).setText(comment.user);
|
||||
((TextView) v.findViewById(R.id.time)).setText(ReadableTime.getTimeAgo(comment.time));
|
||||
((TextView) v.findViewById(R.id.comment)).setText(Html.fromHtml(comment.comment));
|
||||
TextView user = (TextView) v.findViewById(R.id.user);
|
||||
user.setText(comment.user);
|
||||
TextView time = (TextView) v.findViewById(R.id.time);
|
||||
time.setText(ReadableTime.getTimeAgo(comment.time));
|
||||
TextView c = (TextView) v.findViewById(R.id.comment);
|
||||
c.setMaxLines(5);
|
||||
c.setText(Html.fromHtml(comment.comment,
|
||||
new URLImageGetter(c, EhApplication.getConaco(getContext())), null));
|
||||
}
|
||||
}
|
||||
|
||||
private void bindPreviews(GalleryDetail gd) {
|
||||
mGridLayout.removeAllViews();
|
||||
LargePreviewSet previewSet = gd.previewSet;
|
||||
if (gd.previewPages <= 0 || previewSet == null || previewSet.size() == 0) {
|
||||
mPreviewText.setText(R.string.no_previews);
|
||||
return;
|
||||
} else if (gd.previewPages == 1) {
|
||||
mPreviewText.setText(R.string.no_more_previews);
|
||||
} else {
|
||||
mPreviewText.setText(R.string.more_previews);
|
||||
}
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
for (int i = 0, size = previewSet.size(); i < size; i++) {
|
||||
View view = inflater.inflate(R.layout.item_preview, mGridLayout, false);
|
||||
mGridLayout.addView(view);
|
||||
|
||||
int index = previewSet.getIndexAt(i);
|
||||
String imageUrl = previewSet.getImageUrlAt(i);
|
||||
LoadImageView image = (LoadImageView) view.findViewById(R.id.image);
|
||||
image.load(EhCacheKeyFactory.getLargePreviewKey(gd.gid, index), imageUrl, true);
|
||||
TextView text = (TextView) view.findViewById(R.id.text);
|
||||
text.setText(String.format(Locale.US, "%d", index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,32 +698,12 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
if (mAction != null) {
|
||||
outState.putString(KEY_ACTION, mAction);
|
||||
}
|
||||
if (mGalleryInfo != null) {
|
||||
outState.putParcelable(KEY_GALLERY_INFO, mGalleryInfo);
|
||||
}
|
||||
outState.putInt(KEY_GID, mGid);
|
||||
if (mToken != null) {
|
||||
outState.putString(KEY_TOKEN, mAction);
|
||||
}
|
||||
if (mGalleryDetail != null) {
|
||||
outState.putParcelable(KEY_GALLERY_DETAIL, mGalleryDetail);
|
||||
}
|
||||
outState.putInt(KEY_REQUEST_ID, mRequestId);
|
||||
}
|
||||
|
||||
private void ensurePopMenu() {
|
||||
if (mPopupMenu != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PopupMenu popup = new PopupMenu(getContext(), mOtherActions);
|
||||
PopupMenu popup = new PopupMenu(getContext(), mOtherActions, Gravity.TOP);
|
||||
mPopupMenu = popup;
|
||||
popup.getMenuInflater().inflate(R.menu.gallery_detail_other_action, popup.getMenu());
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@@ -632,23 +721,65 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mOtherActions == v) {
|
||||
if (mFailedView == v) {
|
||||
if (request()) {
|
||||
mFailedText.setText("");
|
||||
adjustViewVisibility();
|
||||
}
|
||||
} else if (mOtherActions == v) {
|
||||
ensurePopMenu();
|
||||
mPopupMenu.show();
|
||||
} else if (mUploader == v) {
|
||||
String uploader = getUploader();
|
||||
if (TextUtils.isEmpty(uploader)) {
|
||||
return;
|
||||
}
|
||||
ListUrlBuilder lub = new ListUrlBuilder();
|
||||
lub.setMode(ListUrlBuilder.MODE_UPLOADER);
|
||||
lub.setKeyword(EhUtils.getSpecifyUploaderKeyword(uploader));
|
||||
GalleryListScene.startScene(this, lub);
|
||||
} else if (mCategory == v) {
|
||||
int category = getCategory();
|
||||
if (category == -1) {
|
||||
return;
|
||||
}
|
||||
ListUrlBuilder lub = new ListUrlBuilder();
|
||||
lub.setCategory(category);
|
||||
GalleryListScene.startScene(this, lub);
|
||||
} else if (mInfo == v) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(GalleryInfoScene.KEY_GALLERY_DETAIL, mGalleryDetail);
|
||||
startScene(GalleryInfoScene.class, args);
|
||||
} else if (mComments == v) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelableArray(GalleryCommentsScene.KEY_COMMENTS, mGalleryDetail.comments);
|
||||
startScene(GalleryCommentsScene.class, args);
|
||||
} else {
|
||||
Object tag;
|
||||
tag = v.getTag(R.id.tag);
|
||||
if (tag instanceof String) {
|
||||
String tagStr = (String) tag;
|
||||
ListUrlBuilder lub = new ListUrlBuilder();
|
||||
lub.setMode(ListUrlBuilder.MODE_TAG);
|
||||
lub.setKeyword(tagStr);
|
||||
GalleryListScene.startScene(this, lub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailSuccess(GalleryDetailParser.Result result) {
|
||||
mGalleryDetail = result.galleryDetail;
|
||||
private void onGetGalleryDetailSuccess(GalleryDetail result) {
|
||||
mGalleryDetail = result;
|
||||
adjustViewVisibility();
|
||||
bindViewSecond();
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailFailure(Exception e) {
|
||||
|
||||
String error = ExceptionUtils.getReadableString(getContext(), e);
|
||||
mFailedText.setText(error);
|
||||
adjustViewVisibility();
|
||||
}
|
||||
|
||||
private static class GetGalleryDetailListener implements EhClient.Callback<GalleryDetailParser.Result> {
|
||||
private static class GetGalleryDetailListener implements EhClient.Callback<GalleryDetail> {
|
||||
|
||||
private EhApplication mApplication;
|
||||
private int mStageId;
|
||||
@@ -674,12 +805,11 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(GalleryDetailParser.Result result) {
|
||||
public void onSuccess(GalleryDetail result) {
|
||||
mApplication.removeGlobalStuff(this);
|
||||
|
||||
// Put gallery detail to cache
|
||||
GalleryDetail gd = result.galleryDetail;
|
||||
EhApplication.getGalleryDetailCache(mApplication).put(gd.gid, gd);
|
||||
EhApplication.getGalleryDetailCache(mApplication).put(result.gid, result);
|
||||
|
||||
// Notify success
|
||||
GalleryDetailScene scene = getScene();
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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.ui.scene;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.easyrecyclerview.LinearDividerItemDecoration;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class GalleryInfoScene extends ToolbarScene implements EasyRecyclerView.OnItemClickListener {
|
||||
|
||||
public static final String KEY_GALLERY_DETAIL = "gallery_detail";
|
||||
public static final String KEY_KEYS = "keys";
|
||||
public static final String KEY_VALUES = "values";
|
||||
|
||||
private ArrayList<String> mKeys;
|
||||
private ArrayList<String> mValues;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
onInit();
|
||||
} else {
|
||||
onRestore(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlerArgs(Bundle args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
GalleryDetail gd = args.getParcelable(KEY_GALLERY_DETAIL);
|
||||
if (gd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Resources resources = getResources();
|
||||
mKeys.add(resources.getString(R.string.header_key));
|
||||
mValues.add(resources.getString(R.string.header_value));
|
||||
mKeys.add(resources.getString(R.string.key_gid));
|
||||
mValues.add(Integer.toString(gd.gid));
|
||||
mKeys.add(resources.getString(R.string.key_token));
|
||||
mValues.add(gd.token);
|
||||
mKeys.add(resources.getString(R.string.key_url));
|
||||
mValues.add(EhUrl.getGalleryDetailUrl(gd.gid, gd.token));
|
||||
mKeys.add(resources.getString(R.string.key_title));
|
||||
mValues.add(gd.title);
|
||||
mKeys.add(resources.getString(R.string.key_title_jpn));
|
||||
mValues.add(gd.titleJpn);
|
||||
mKeys.add(resources.getString(R.string.key_thumb));
|
||||
mValues.add(gd.thumb);
|
||||
mKeys.add(resources.getString(R.string.key_category));
|
||||
mValues.add(EhUtils.getCategory(gd.category));
|
||||
mKeys.add(resources.getString(R.string.key_uploader));
|
||||
mValues.add(gd.uploader);
|
||||
mKeys.add(resources.getString(R.string.key_posted));
|
||||
mValues.add(gd.posted);
|
||||
mKeys.add(resources.getString(R.string.key_parent));
|
||||
mValues.add(gd.parent);
|
||||
mKeys.add(resources.getString(R.string.key_visible));
|
||||
mValues.add(gd.visible);
|
||||
mKeys.add(resources.getString(R.string.key_language));
|
||||
mValues.add(gd.language);
|
||||
mKeys.add(resources.getString(R.string.key_pages));
|
||||
mValues.add(Integer.toString(gd.pages));
|
||||
mKeys.add(resources.getString(R.string.key_size));
|
||||
mValues.add(gd.size);
|
||||
mKeys.add(resources.getString(R.string.key_resize));
|
||||
mValues.add(gd.resize);
|
||||
mKeys.add(resources.getString(R.string.key_favourites));
|
||||
mValues.add(Integer.toString(gd.favoredTimes));
|
||||
mKeys.add(resources.getString(R.string.key_favourited));
|
||||
mValues.add(Boolean.toString(gd.isFavored));
|
||||
mKeys.add(resources.getString(R.string.key_rates));
|
||||
mValues.add(Integer.toString(gd.ratedTimes));
|
||||
mKeys.add(resources.getString(R.string.key_rating));
|
||||
mValues.add(Float.toString(gd.rating));
|
||||
mKeys.add(resources.getString(R.string.key_torrents));
|
||||
mValues.add(Integer.toString(gd.torrentCount));
|
||||
mKeys.add(resources.getString(R.string.key_torrent_url));
|
||||
mValues.add(gd.torrentUrl);
|
||||
}
|
||||
|
||||
protected void onInit() {
|
||||
mKeys = new ArrayList<>();
|
||||
mValues = new ArrayList<>();
|
||||
handlerArgs(getArguments());
|
||||
}
|
||||
|
||||
protected void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
mKeys = savedInstanceState.getStringArrayList(KEY_KEYS);
|
||||
mValues = savedInstanceState.getStringArrayList(KEY_VALUES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putStringArrayList(KEY_KEYS, mKeys);
|
||||
outState.putStringArrayList(KEY_VALUES, mValues);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView2(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.scene_gallery_info, container, false);
|
||||
|
||||
EasyRecyclerView recyclerView = (EasyRecyclerView) view.findViewById(R.id.recycler_view);
|
||||
InfoAdapter adapter = new InfoAdapter();
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
|
||||
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
|
||||
LinearDividerItemDecoration.VERTICAL, getResources().getColor(R.color.divider),
|
||||
LayoutUtils.dp2pix(getContext(), 1));
|
||||
decoration.setPadding(getResources().getDimensionPixelOffset(R.dimen.keyline_margin));
|
||||
recyclerView.addItemDecoration(decoration);
|
||||
recyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
|
||||
recyclerView.setClipToPadding(false);
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setOnItemClickListener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setTitle(R.string.gallery_info);
|
||||
setNavigationIcon(VectorDrawable.create(getContext(), R.drawable.ic_arrow_left));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
|
||||
if (position != 0) {
|
||||
ClipboardManager cmb = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setPrimaryClip(ClipData.newPlainText(null, mValues.get(position)));
|
||||
Toast.makeText(getContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationClick() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private static class InfoHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private TextView key;
|
||||
private TextView value;
|
||||
|
||||
public InfoHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
key = (TextView) itemView.findViewById(R.id.key);
|
||||
value = (TextView) itemView.findViewById(R.id.value);
|
||||
}
|
||||
}
|
||||
|
||||
private class InfoAdapter extends RecyclerView.Adapter<InfoHolder> {
|
||||
|
||||
private static final int TYPE_HEADER = 0;
|
||||
private static final int TYPE_DATA = 1;
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return TYPE_HEADER;
|
||||
} else {
|
||||
return TYPE_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new InfoHolder(getActivity().getLayoutInflater().inflate(viewType == TYPE_HEADER ?
|
||||
R.layout.item_gallery_info_header : R.layout.item_gallery_info_data, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InfoHolder holder, int position) {
|
||||
holder.key.setText(mKeys.get(position));
|
||||
holder.value.setText(mValues.get(position));
|
||||
holder.itemView.setEnabled(position != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mKeys == null || mValues == null ? 0 : Math.min(mKeys.size(), mValues.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,14 +45,14 @@ import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.anani.AnimationUtils;
|
||||
import com.hippo.anani.SimpleAnimatorListener;
|
||||
import com.hippo.drawable.AddDeleteDrawable;
|
||||
import com.hippo.drawable.DrawerArrowDrawable;
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.easyrecyclerview.FastScroller;
|
||||
import com.hippo.easyrecyclerview.MarginItemDecoration;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.client.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
@@ -94,7 +94,7 @@ public final class GalleryListScene extends BaseScene
|
||||
public final static String KEY_ACTION = "action";
|
||||
public final static String ACTION_HOMEPAGE = "action_homepage";
|
||||
public final static String ACTION_WHATS_HOT = "action_whats_hot";
|
||||
public final static String ACTION_SEARCH = "action_search";
|
||||
public final static String ACTION_LIST_URL_BUILDER = "action_list_url_builder";
|
||||
|
||||
public final static String KEY_LIST_URL_BUILDER = "list_url_builder";
|
||||
public final static String KEY_HAS_FIRST_REFRESH = "has_first_refresh";
|
||||
@@ -157,9 +157,11 @@ public final class GalleryListScene extends BaseScene
|
||||
mUrlBuilder.reset();
|
||||
} else if (ACTION_WHATS_HOT.equals(action)) {
|
||||
mUrlBuilder.setMode(ListUrlBuilder.MODE_WHATS_HOT);
|
||||
} else if (ACTION_SEARCH.equals(action)) {
|
||||
mUrlBuilder.reset();
|
||||
// TODO
|
||||
} else if (ACTION_LIST_URL_BUILDER.equals(action)) {
|
||||
ListUrlBuilder builder = args.getParcelable(KEY_LIST_URL_BUILDER);
|
||||
if (builder != null) {
|
||||
mUrlBuilder.set(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +277,6 @@ public final class GalleryListScene extends BaseScene
|
||||
mRecyclerView.hasFixedSize();
|
||||
mRecyclerView.setClipToPadding(false);
|
||||
mRecyclerView.setOnItemClickListener(this);
|
||||
mRecyclerView.addItemDecoration(new MarginItemDecoration(
|
||||
getContext().getResources().getDimensionPixelOffset(R.dimen.list_item_margin)));
|
||||
int paddingH = resources.getDimensionPixelOffset(R.dimen.list_content_margin_h);
|
||||
int paddingV = resources.getDimensionPixelOffset(R.dimen.list_content_margin_v);
|
||||
mRecyclerView.setPadding(paddingV, paddingH, paddingV, paddingH);
|
||||
@@ -434,7 +434,8 @@ public final class GalleryListScene extends BaseScene
|
||||
mFab.setVisibility(View.VISIBLE);
|
||||
mFab.setScaleX(0.0f);
|
||||
mFab.setScaleY(0.0f);
|
||||
mFab.animate().scaleX(1.0f).scaleY(1.0f).setListener(null).setDuration(ANIMATE_TIME).start();
|
||||
mFab.animate().scaleX(1.0f).scaleY(1.0f).setListener(null).setDuration(ANIMATE_TIME)
|
||||
.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start();
|
||||
}
|
||||
|
||||
private void hideFab() {
|
||||
@@ -446,7 +447,8 @@ public final class GalleryListScene extends BaseScene
|
||||
}
|
||||
};
|
||||
}
|
||||
mFab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mFabAnimatorListener).setDuration(ANIMATE_TIME).start();
|
||||
mFab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mFabAnimatorListener)
|
||||
.setDuration(ANIMATE_TIME).setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start();
|
||||
}
|
||||
|
||||
private void setState(@State int state) {
|
||||
@@ -584,6 +586,7 @@ public final class GalleryListScene extends BaseScene
|
||||
mSearchLayout.formatListUrlBuilder(mUrlBuilder, query);
|
||||
} else {
|
||||
mUrlBuilder.reset();
|
||||
mUrlBuilder.setKeyword(query);
|
||||
}
|
||||
onUpdateUrlBuilder();
|
||||
mHelper.refresh();
|
||||
@@ -649,14 +652,14 @@ public final class GalleryListScene extends BaseScene
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState){
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE && isVaildView(recyclerView)) {
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE && isValidView(recyclerView)) {
|
||||
returnSearchBarPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
if (isVaildView(recyclerView)) {
|
||||
if (isValidView(recyclerView)) {
|
||||
|
||||
int oldBottom = mSearchBar.getBottom();
|
||||
OffsetLayout.LayoutParams sbLayoutParams = (OffsetLayout.LayoutParams) mSearchBar.getLayoutParams();
|
||||
@@ -673,12 +676,12 @@ public final class GalleryListScene extends BaseScene
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isVaildView(RecyclerView view) {
|
||||
private boolean isValidView(RecyclerView view) {
|
||||
return (mState == STATE_NORMAL && view == mRecyclerView) ||
|
||||
(mState == STATE_SEARCH && view == mSearchLayout);
|
||||
}
|
||||
|
||||
private RecyclerView getVaildRecyclerView() {
|
||||
private RecyclerView getValidRecyclerView() {
|
||||
if (mState == STATE_NORMAL || mState == STATE_SIMPLE_SEARCH) {
|
||||
return mRecyclerView;
|
||||
} else {
|
||||
@@ -700,7 +703,7 @@ public final class GalleryListScene extends BaseScene
|
||||
if (mState == STATE_SIMPLE_SEARCH || mState == STATE_SEARCH_SHOW_LIST) {
|
||||
show = true;
|
||||
} else {
|
||||
RecyclerView recyclerView = getVaildRecyclerView();
|
||||
RecyclerView recyclerView = getValidRecyclerView();
|
||||
if (!recyclerView.isShown()) {
|
||||
show = true;
|
||||
} else if (recyclerView.computeVerticalScrollOffset() < getSearchBarOriginalBottom()){
|
||||
@@ -812,6 +815,13 @@ public final class GalleryListScene extends BaseScene
|
||||
}
|
||||
}
|
||||
|
||||
public static void startScene(SceneFragment scene, ListUrlBuilder lub) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_ACTION, ACTION_LIST_URL_BUILDER);
|
||||
args.putParcelable(KEY_LIST_URL_BUILDER, lub);
|
||||
scene.startScene(GalleryListScene.class, args);
|
||||
}
|
||||
|
||||
private class GalleryListHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private LoadImageView thumb;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.ui.scene;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public abstract class ToolbarScene extends BaseScene {
|
||||
|
||||
private Toolbar mToolbar;
|
||||
|
||||
@Nullable
|
||||
public View onCreateView2(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.scene_toolbar, container, false);
|
||||
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
|
||||
FrameLayout contentPanel = (FrameLayout) view.findViewById(R.id.content_panel);
|
||||
|
||||
View contentView = onCreateView2(inflater, contentPanel, savedInstanceState);
|
||||
if (contentView == null) {
|
||||
return null;
|
||||
} else {
|
||||
mToolbar = toolbar;
|
||||
contentPanel.addView(contentView, 0);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mToolbar = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (mToolbar != null) {
|
||||
int menuResId = getMenuResId();
|
||||
if (menuResId != 0) {
|
||||
mToolbar.inflateMenu(menuResId);
|
||||
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return ToolbarScene.this.onMenuItemClick(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onNavigationClick();
|
||||
}
|
||||
});
|
||||
onMenuCreated(mToolbar.getMenu());
|
||||
}
|
||||
}
|
||||
|
||||
public int getMenuResId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void onMenuCreated(Menu menu) {
|
||||
}
|
||||
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onNavigationClick() {
|
||||
}
|
||||
|
||||
public void setNavigationIcon(@DrawableRes int resId) {
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setNavigationIcon(resId);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNavigationIcon(@Nullable Drawable icon) {
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setNavigationIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitle(@StringRes int resId) {
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setTitle(resId);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setTitle(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class SearchBar extends CardView implements View.OnClickListener,
|
||||
String suggestion = mSuggestionList.get(MathUtils.clamp(
|
||||
position - mList.getHeaderViewsCount(), 0, mSuggestionList.size() - 1));
|
||||
mEditText.setText(suggestion);
|
||||
mEditText.setSelection(suggestion.length());
|
||||
mEditText.setSelection(mEditText.getText().length());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.text;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.conaco.Conaco;
|
||||
import com.hippo.conaco.ConacoTask;
|
||||
import com.hippo.drawable.ImageWrapper;
|
||||
import com.hippo.drawable.UnikeryDrawable;
|
||||
|
||||
public class URLImageGetter implements Html.ImageGetter {
|
||||
|
||||
private TextView mTextView;
|
||||
private Conaco<ImageWrapper> mConaco;
|
||||
|
||||
public URLImageGetter(TextView textView, Conaco<ImageWrapper> conaco) {
|
||||
mTextView = textView;
|
||||
mConaco = conaco;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
UnikeryDrawable drawable = new UnikeryDrawable(mTextView);
|
||||
mConaco.load(new ConacoTask.Builder<ImageWrapper>().setUnikery(drawable).setUrl(source).setKey(source));
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class ActivityHelper {
|
||||
|
||||
public static boolean sendEmail(@NonNull Activity from, @NonNull String address,
|
||||
@Nullable String subject, @Nullable String text) {
|
||||
Intent i = new Intent(Intent.ACTION_SENDTO);
|
||||
i.setData(Uri.parse("mailto:" + address));
|
||||
if (subject != null) {
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
}
|
||||
if (text != null) {
|
||||
i.putExtra(Intent.EXTRA_TEXT, text);
|
||||
}
|
||||
|
||||
try {
|
||||
from.startActivity(i);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean share(@NonNull Activity from, String text) {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
sendIntent.setType("text/plain");
|
||||
|
||||
try {
|
||||
from.startActivity(sendIntent);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class for Custom Tabs.
|
||||
*/
|
||||
public class CustomTabsHelper {
|
||||
private static final String TAG = "CustomTabsHelper";
|
||||
static final String STABLE_PACKAGE = "com.android.chrome";
|
||||
static final String BETA_PACKAGE = "com.chrome.beta";
|
||||
static final String DEV_PACKAGE = "com.chrome.dev";
|
||||
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
|
||||
private static final String ACTION_CUSTOM_TABS_CONNECTION =
|
||||
"android.support.customtabs.action.CustomTabsService";
|
||||
private static final String UNINITIALIZED = "UNINITIALIZED";
|
||||
|
||||
private static String sPackageNameToUse = UNINITIALIZED;
|
||||
|
||||
private CustomTabsHelper() {}
|
||||
|
||||
/**
|
||||
* The browser of smartisan is terrible
|
||||
*/
|
||||
public static String getPackageNameToUseFixed(Context context) {
|
||||
if (!UNINITIALIZED.equals(sPackageNameToUse)) return sPackageNameToUse;
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
// Get all apps that can handle VIEW intents.
|
||||
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
|
||||
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
|
||||
List<String> packagesSupportingCustomTabs = new ArrayList<>();
|
||||
for (ResolveInfo info : resolvedActivityList) {
|
||||
Intent serviceIntent = new Intent();
|
||||
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
|
||||
serviceIntent.setPackage(info.activityInfo.packageName);
|
||||
if (pm.resolveService(serviceIntent, 0) != null) {
|
||||
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
|
||||
// and service calls.
|
||||
if (packagesSupportingCustomTabs.isEmpty()) {
|
||||
sPackageNameToUse = null;
|
||||
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
|
||||
sPackageNameToUse = STABLE_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
|
||||
sPackageNameToUse = BETA_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
|
||||
sPackageNameToUse = DEV_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
|
||||
sPackageNameToUse = LOCAL_PACKAGE;
|
||||
} else {
|
||||
sPackageNameToUse = null;
|
||||
}
|
||||
return sPackageNameToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
|
||||
* the one chosen by the user if there is one, otherwise makes a best effort to return a
|
||||
* valid package name.
|
||||
*
|
||||
* This is <strong>not</strong> threadsafe.
|
||||
*
|
||||
* @param context {@link Context} to use for accessing {@link PackageManager}.
|
||||
* @return The package name recommended to use for connecting to custom tabs related components.
|
||||
*/
|
||||
public static String getPackageNameToUse(Context context) {
|
||||
if (sPackageNameToUse != null) return sPackageNameToUse;
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
// Get default VIEW intent handler.
|
||||
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
|
||||
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
|
||||
String defaultViewHandlerPackageName = null;
|
||||
if (defaultViewHandlerInfo != null) {
|
||||
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
|
||||
}
|
||||
|
||||
// Get all apps that can handle VIEW intents.
|
||||
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
|
||||
List<String> packagesSupportingCustomTabs = new ArrayList<>();
|
||||
for (ResolveInfo info : resolvedActivityList) {
|
||||
Intent serviceIntent = new Intent();
|
||||
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
|
||||
serviceIntent.setPackage(info.activityInfo.packageName);
|
||||
if (pm.resolveService(serviceIntent, 0) != null) {
|
||||
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
|
||||
// and service calls.
|
||||
if (packagesSupportingCustomTabs.isEmpty()) {
|
||||
sPackageNameToUse = null;
|
||||
} else if (packagesSupportingCustomTabs.size() == 1) {
|
||||
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
|
||||
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
|
||||
&& !hasSpecializedHandlerIntents(context, activityIntent)
|
||||
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
|
||||
sPackageNameToUse = defaultViewHandlerPackageName;
|
||||
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
|
||||
sPackageNameToUse = STABLE_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
|
||||
sPackageNameToUse = BETA_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
|
||||
sPackageNameToUse = DEV_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
|
||||
sPackageNameToUse = LOCAL_PACKAGE;
|
||||
}
|
||||
return sPackageNameToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check whether there is a specialized handler for a given intent.
|
||||
* @param intent The intent to check with.
|
||||
* @return Whether there is a specialized handler for the given intent.
|
||||
*/
|
||||
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> handlers = pm.queryIntentActivities(
|
||||
intent,
|
||||
PackageManager.GET_RESOLVED_FILTER);
|
||||
if (handlers == null || handlers.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (ResolveInfo resolveInfo : handlers) {
|
||||
IntentFilter filter = resolveInfo.filter;
|
||||
if (filter == null) continue;
|
||||
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
|
||||
if (resolveInfo.activityInfo == null) continue;
|
||||
return true;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Runtime exception while getting specialized handlers");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public final class OpenUrlHelper {
|
||||
|
||||
private OpenUrlHelper() {
|
||||
}
|
||||
|
||||
public static void openUrl(Context context, String url, boolean checkNMB) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent;
|
||||
Uri uri = Uri.parse(url);
|
||||
|
||||
/*
|
||||
// Is nmb url
|
||||
if (checkNMB) {
|
||||
NMBUriParser.PostResult result = NMBUriParser.parsePostUri(uri);
|
||||
if (result.site != null && result.id != null) {
|
||||
intent = new Intent(context, PostActivity.class);
|
||||
intent.setAction(PostActivity.ACTION_SITE_ID);
|
||||
intent.putExtra(PostActivity.KEY_SITE, result.site.getId());
|
||||
intent.putExtra(PostActivity.KEY_ID, result.id);
|
||||
if (!(context instanceof Activity)) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// CustomTabs
|
||||
if (context instanceof Activity) {
|
||||
String packageName = CustomTabsHelper.getPackageNameToUseFixed(context);
|
||||
if (packageName != null) {
|
||||
new CustomTabsIntent.Builder()
|
||||
.setToolbarColor(context.getResources().getColor(R.color.colorPrimary))
|
||||
.setShowTitle(true)
|
||||
.build()
|
||||
.launchUrl((Activity) context, uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Intent.ACTION_VIEW
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(uri);
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ResolveInfo ri = pm.resolveActivity(intent, 0);
|
||||
if (ri != null) {
|
||||
if (!(context instanceof Activity)) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// Use WebViewActivity
|
||||
intent = new Intent(context, WebViewActivity.class);
|
||||
intent.putExtra(WebViewActivity.KEY_URL, url);
|
||||
if (!(context instanceof Activity)) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.URLSpan;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class TextUrl {
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile("(http|https)://[a-z0-9A-Z%-]+(\\.[a-z0-9A-Z%-]+)+(:\\d{1,5})?(/[a-zA-Z0-9-_~:#@!&',;=%/\\*\\.\\?\\+\\$\\[\\]\\(\\)]+)?/?");
|
||||
|
||||
public static CharSequence handleTextUrl(CharSequence content) {
|
||||
Matcher m = URL_PATTERN.matcher(content);
|
||||
|
||||
Spannable spannable = null;
|
||||
while (m.find()) {
|
||||
// Ensure spannable
|
||||
if (spannable == null) {
|
||||
if (content instanceof Spannable) {
|
||||
spannable = (Spannable) content;
|
||||
} else {
|
||||
spannable = new SpannableString(content);
|
||||
}
|
||||
}
|
||||
|
||||
int start = m.start();
|
||||
int end = m.end();
|
||||
|
||||
URLSpan[] links = spannable.getSpans(start, end, URLSpan.class);
|
||||
if (links.length > 0) {
|
||||
// There has been URLSpan already, leave it alone
|
||||
continue;
|
||||
}
|
||||
|
||||
URLSpan urlSpan = new URLSpan(m.group(0));
|
||||
spannable.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
return spannable == null ? content : spannable;
|
||||
}
|
||||
}
|
||||
@@ -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.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.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.max(0, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user