Improve preference, use leakcanary, start GalleryDetailScene

This commit is contained in:
seven332 committed 2015-06-02 23:23:34 +08:00
1 parent 9b6815c60c
commit 2130e7a8dd
42 files changed
+1289 -386

No files matched your search

@@ -20,11 +20,12 @@ import android.content.Context;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.backends.okhttp.OkHttpImagePipelineConfigFactory;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.hippo.vectorold.content.VectorContext;
import com.hippo.ehviewer.fresco.EhCacheKeyFactory;
import com.hippo.ehviewer.network.EhOkHttpClient;
import com.hippo.ehviewer.util.Config;
import com.hippo.scene.SceneApplication;
import com.hippo.util.Log;
import com.hippo.vectorold.content.VectorContext;
public class EhApplication extends SceneApplication {
@@ -34,6 +35,7 @@ public class EhApplication extends SceneApplication {
ImagePipelineConfig config = OkHttpImagePipelineConfigFactory
.newBuilder(this, EhOkHttpClient.getInstance())
.setCacheKeyFactory(EhCacheKeyFactory.getInstance())
.build();
Fresco.initialize(this, config);
Config.initialize(this);
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.client;
import android.os.AsyncTask;
import android.os.Process;
import com.hippo.ehviewer.data.GalleryDetail;
import com.hippo.ehviewer.data.GalleryInfo;
import com.hippo.ehviewer.network.EhOkHttpClient;
import com.hippo.network.ResponseCodeException;
@@ -120,6 +121,19 @@ public final class EhClient {
}
}
/**
* Get gellary detail url in target source
*/
public static String getDetailUrl(int source, int gid, String token, int pageIndex) {
StringBuilder sb = new StringBuilder(getUrlHeader(source));
sb.append("g/").append(gid).append('/').append(token).append('/');
if (pageIndex > 0) {
sb.append("?p=").append(pageIndex);
}
return sb.toString();
}
public interface SimpleListener {
void onFailure(Exception e);
}
@@ -371,4 +385,84 @@ public final class EhClient {
public void signIn(String username, String password, OnSignInListener listener) {
doBgJob(new SignInHelper(username, password, listener));
}
private GalleryDetail doGetGalleryDetail(int source, String url) throws Exception {
Request request = new Request.Builder()
.url(url)
.build();
Response response = mHttpClient.newCall(request).execute();
checkResponse(response);
GalleryDetailParser parser = new GalleryDetailParser();
parser.parse(response.body().string(),
GalleryDetailParser.REQUEST_DETAIL | GalleryDetailParser.REQUEST_TAG |
GalleryDetailParser.REQUEST_PREVIEW_INFO |
GalleryDetailParser.REQUEST_COMMENT,
source);
GalleryDetail gd = new GalleryDetail();
gd.gid = parser.gid;
gd.token = parser.token;
gd.thumb = parser.thumb;
gd.title = parser.title;
gd.titleJpn = parser.titleJpn;
gd.category = parser.category;
gd.uploader = parser.uploader;
gd.torrentCount = parser.torrentCount;
gd.torrentUrl = parser.torrentUrl;
gd.posted = parser.posted;
gd.parent = parser.parent;
gd.visible = parser.visible;
gd.language = parser.language;
gd.size = parser.size;
gd.resize = parser.resize;
gd.pageCount = parser.pageCount;
gd.favoredTimes = parser.favoredTimes;
gd.isFavored = parser.isFavored;
gd.rating = parser.rating;
gd.ratedTimes = parser.ratedTimes;
gd.tags = parser.tags;
gd.comments = parser.comments;
gd.previewPageCount = parser.previewPageCount;
return gd;
}
public abstract static class OnGetGalleryDetailListener implements SimpleListener {
public abstract void onSuccess(GalleryDetail galleryDetail);
}
private final class GetGalleryDetailHelper extends SimpleBgJobHelper {
private int mSource;
private String mUrl;
private OnGetGalleryDetailListener mListener;
private GalleryDetail mGalleryDetail;
public GetGalleryDetailHelper(int source, String url, OnGetGalleryDetailListener listener) {
super(listener);
mSource = source;
mUrl = url;
mListener = listener;
}
@Override
public void doBgJob() throws Exception {
mGalleryDetail = doGetGalleryDetail(mSource, mUrl);
}
@Override
public void doSuccessCallback() {
mListener.onSuccess(mGalleryDetail);
}
}
/**
* Get gallery detail
*/
public void getGalleryDetail(int source, String url, OnGetGalleryDetailListener listener) {
doBgJob(new GetGalleryDetailHelper(source, url, listener));
}
}
@@ -0,0 +1,318 @@
/*
* Copyright (C) 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
* 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;
import com.hippo.ehviewer.data.Comment;
import com.hippo.ehviewer.data.TagGroup;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.util.AssertUtils;
import com.hippo.util.Utils;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GalleryDetailParser {
private static final String OFFENSIVE_STRING =
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>";
private static final String PINING_STRING =
"<p>This gallery is pining for the fjords.</p>";
public static final int REQUEST_DETAIL = 0x1;
public static final int REQUEST_TAG = 0x2;
public static final int REQUEST_PREVIEW_INFO = 0x4;
public static final int REQUEST_COMMENT = 0x10;
private static final CommentSort COMMENT_SORTER = new CommentSort();
public int gid;
public String token;
public String thumb;
public String title;
public String titleJpn;
public int category;
public String uploader;
public int torrentCount;
public String torrentUrl;
public String posted;
public String parent;
public String visible;
public String language;
public String size;
public String resize;
public int pageCount;
public int favoredTimes;
public boolean isFavored;
public float rating;
public int ratedTimes;
public List<TagGroup> tags;
public List<Comment> comments;
public int previewPageCount;
private String sortOut(String str) {
return Utils.unescapeXml(str).trim();
}
private int sortOutInteger(String str, int defaultValue) {
return Utils.parseIntSafely(sortOut(str).replace(",", ""), defaultValue);
}
private void parseTagGroup(TagGroup tagGroup, String body) {
Pattern p = Pattern.compile("<div[^<>]+><a[^<>]+>([\\w\\s]+)</a></div>");
Matcher m = p.matcher(body);
while (m.find()) {
tagGroup.addTag(sortOut(m.group(1)));
}
}
private void parse(String body, int request) throws EhException {
Pattern p;
Matcher m;
if (!body.startsWith("<")) {
throw new EhException(body);
}
if (body.contains(OFFENSIVE_STRING)) {
throw new OffensiveException();
}
if (body.contains(PINING_STRING)) {
throw new PiningException();
}
// TODO I can't remember what's this for
p = Pattern.compile("<div class=\"d\">\n<p>([^<]+)</p>");
m = p.matcher(body);
if (m.find()) {
throw new EhException(m.group(1));
}
if ((request & REQUEST_DETAIL) != 0) {
p = Pattern.compile("var gid = (\\d+)" // 1 gid
+ ".+?"
+ "var token = \"([a-z0-9A]+)\"" // 2 token
+ ".+?"
+ "<div id=\"gd1\"><img src=\"([^\"]+)\"[^<>]+></div>" // 3 thumb
+ "</div>"
+ "<div id=\"gd2\">"
+ "<h1 id=\"gn\">([^<>]+)</h1>" // 4 title
+ "<h1 id=\"gj\">([^<>]*)</h1>" // 5 title_jpn might be empty string
+ "</div>"
+ ".+?"
+ "<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download \\( (\\d+) \\)</a>" // 6 torrentUrl, 7 torrentCount
+ ".+?"
+ "<div id=\"gdc\"><a[^<>]+><[^<>]*alt=\"([\\w|\\-]+)\"[^<>]*></a></div>" // 8 category
+ "<div id=\"gdn\"><a[^<>]+>([^<>]+)</a>" // 9 uploader
+ ".+?"
+ "<tr><td[^<>]*>Posted:</td><td[^<>]*>([\\w|\\-|\\s|:]+)</td></tr>" // 10 posted
+ "<tr><td[^<>]*>Parent:</td><td[^<>]*>(?:<a[^<>]*>)?([^<>]+)(?:</a>)?</td></tr>" // 11 parent
+ "<tr><td[^<>]*>Visible:</td><td[^<>]*>([^<>]+)</td></tr>" // 12 visible
+ "<tr><td[^<>]*>Language:</td><td[^<>]*>([^<>]+)(?:<span[^<>]*>[^<>]*</span>)?</td></tr>" // 13 language
+ "<tr><td[^<>]*>File Size:</td><td[^<>]*>([^<>]+)(?:<span[^<>]*>([^<>]+)</span>)?</td></tr>" // 14 File size, 15 resize
+ "<tr><td[^<>]*>Length:</td><td[^<>]*>([\\d|,]+) pages</td></tr>" // 16 pageCount
+ "<tr><td[^<>]*>Favorited:</td><[^<>]*>([^<>]+)</td></tr>" // 17 Favorite times "([\d|,]+) times" or "Once" or "Never"
+ ".+?"
+ "<td id=\"grt3\"><span id=\"rating_count\">([\\d|,]+)</span></td>" // 18 ratedTimes
+ "</tr>"
+ "<tr><td[^<>]*>Average: ([^<>]+)</td>" // 19 rating "Average: x.xx" or "Not Yet Rated"
+ ".+?"
+ "<a id=\"favoritelink\"[^<>]*>(.+?)</a>", Pattern.DOTALL); // 20 isFavored "Favorite Gallery" for favorite
m = p.matcher(body);
if (m.find()) {
gid = sortOutInteger(m.group(1), 0);
token = sortOut(m.group(2));
thumb = sortOut(m.group(3));
title = sortOut(m.group(4));
titleJpn = sortOut(m.group(5));
torrentUrl = sortOut(m.group(6));
torrentCount = sortOutInteger(m.group(7), 0);
category = EhUtils.getCategory(sortOut(m.group(8)));
uploader = sortOut(m.group(9));
posted = sortOut(m.group(10));
parent = sortOut(m.group(11));
visible = sortOut(m.group(12));
language = sortOut(m.group(13));
size = sortOut(m.group(14));
resize = sortOut(m.group(15));
pageCount = sortOutInteger(m.group(16), 0);
String favTimeStr = sortOut(m.group(17));
switch (favTimeStr) {
case "Never":
favoredTimes = 0;
break;
case "Once":
favoredTimes = 1;
break;
default:
int index = favTimeStr.indexOf(' ');
if (index == -1) {
favoredTimes = 0;
} else {
favoredTimes = Utils.parseIntSafely(favTimeStr.substring(0, index), 0);
}
break;
}
ratedTimes = sortOutInteger(m.group(18), 0);
String ratingStr = sortOut(m.group(19));
if ("Not Yet Rated".equals(ratingStr)) {
rating = Float.NaN;
} else {
int index = ratingStr.indexOf(' ');
if (index == -1 || index >= ratingStr.length()) {
rating = 0f;
} else {
rating = Utils.parseFloatSafely(ratingStr.substring(index + 1), 0f);
}
}
isFavored = "Favorite Gallery".equals(sortOut(m.group(20)));
} else {
throw new ParseException("Parse gallery body error", body);
}
}
if ((request & REQUEST_TAG) != 0) {
tags = new LinkedList<>();
p = Pattern.compile("<tr><td[^<>]+>([\\w\\s]+):</td><td>(?:<div[^<>]+><a[^<>]+>[\\w\\s]+</a></div>)+</td></tr>");
m = p.matcher(body);
while (m.find()) {
TagGroup tagGroup = new TagGroup();
tagGroup.groupName = sortOut(m.group(1));
parseTagGroup(tagGroup, m.group(0));
}
}
// Get preview info
if ((request & REQUEST_PREVIEW_INFO) != 0) {
p = Pattern.compile("<td[^>]+><a[^>]+>([\\d,]+)</a></td><td[^>]+>(?:<a[^>]+>)?&gt;(?:</a>)?</td>");
m = p.matcher(body);
if (m.find()) {
previewPageCount = sortOutInteger(m.group(1), 0);
} else {
previewPageCount = 0;
}
if (previewPageCount <= 0) {
throw new ParseException("Parse preview page count error", body);
}
}
// Get comment
if ((request & REQUEST_COMMENT) != 0) {
p = Pattern.compile("<div class=\"c3\">Posted on ([^<>]+) by: &nbsp; <a[^<>]+>([^<>]+)</a>.+?<div class=\"c6\"[^>]*>(.+?)</div><div class=\"c[78]\"");
m = p.matcher(body);
comments = new LinkedList<>();
while (m.find()) {
comments.add(new Comment(sortOut(m.group(1)), sortOut(m.group(2)), m.group(3)));
}
Collections.sort(comments, COMMENT_SORTER);
}
}
public void parse(String body, int request, int source) throws Exception {
AssertUtils.assertNotNull("Body is null when parse gallery list", body);
switch (source) {
default:
case EhClient.SOURCE_G:
case EhClient.SOURCE_EX: {
parse(body, request);
break;
}
case EhClient.SOURCE_LOFI: {
//parseLofi(body, request);
//break;
throw new EhException("Not support lofi now");
}
}
}
static class CommentSort implements Comparator<Comment> {
private int compareNum(String n1, String n2, int median) {
int re = 0;
for (int i = 0; i < median; i++) {
re *= 10;
re += n1.charAt(i) - n2.charAt(i);
}
return re;
}
private int getMonth(String m) {
if (m.equals("January"))
return 1;
else if (m.equals("February"))
return 2;
else if (m.equals("March"))
return 3;
else if (m.equals("April"))
return 4;
else if (m.equals("May"))
return 5;
else if (m.equals("June"))
return 6;
else if (m.equals("July"))
return 7;
else if (m.equals("August"))
return 8;
else if (m.equals("September"))
return 9;
else if (m.equals("October"))
return 10;
else if (m.equals("November"))
return 11;
else if (m.equals("December"))
return 12;
else
return 0;
}
@Override
public int compare(Comment c1, Comment c2) {
int re = 0;
Pattern p = Pattern.compile("(\\d{2}) (\\w+) (\\d{4}), (\\d{2}):(\\d{2})");
Matcher m1 = p.matcher(c1.time);
Matcher m2 = p.matcher(c2.time);
if (!m1.find() || !m2.find())
return 0;
// year
re += compareNum(m1.group(3), m2.group(3), 4);
re <<= 4;
// Month
re += getMonth(m1.group(2)) - getMonth(m2.group(2));
re <<= 5;
// Day
re += compareNum(m1.group(1), m2.group(1), 2);
re <<= 5;
// Hour
re += compareNum(m1.group(4), m2.group(4), 2);
re <<= 6;
// Minute
re += compareNum(m1.group(5), m2.group(5), 2);
return re;
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 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
* 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;
/**
* It is an exception for get offensive tip for g.e-hentai.org
*/
public class OffensiveException extends EhException {
public OffensiveException() {
super("OFFENSIVE");
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 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
* 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;
/**
* It is an exception for parse error,
*/
public class ParseException extends EhException {
private String mBody;
public ParseException(String detailMessage, String body) {
super(detailMessage);
mBody = body;
}
public String getBody() {
return mBody;
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 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
* 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 PiningException extends EhException {
public PiningException() {
super("pining for the fjords");
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2014 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.data;
public class Comment {
public String time;
public String user;
public String comment;
public Comment (String time, String user, String comment) {
this.time = time;
this.user = user;
this.comment = comment;
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 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
* 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.data;
import java.util.List;
public class GalleryDetail extends GalleryInfo {
public String thumb;
public String title;
public String titleJpn;
public int category;
public String uploader;
public int torrentCount;
public String torrentUrl;
public String posted;
public String parent;
public String visible;
public String language;
public String size;
public String resize;
public int pageCount;
public int favoredTimes;
public boolean isFavored;
public float rating;
public int ratedTimes;
public List<TagGroup> tags;
public List<Comment> comments;
public int previewPageCount;
}
@@ -156,10 +156,7 @@ public class GalleryInfo implements Parcelable {
@Override
public boolean equals(Object o) {
if (o instanceof GalleryInfo && ((GalleryInfo) o).gid == gid)
return true;
else
return false;
return o instanceof GalleryInfo && ((GalleryInfo) o).gid == gid;
}
public final void generateSLang() {
@@ -0,0 +1,37 @@
/*
* Copyright (C) 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
* 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.data;
import java.util.ArrayList;
import java.util.List;
public class TagGroup {
public String groupName;
private List<String> mTagList = new ArrayList<>();
public void addTag(String tag) {
mTagList.add(tag);
}
public int getTagCount() {
return mTagList.size();
}
public String getTagAt(int position) {
return mTagList.get(position);
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 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
* 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.fresco;
import android.net.Uri;
import com.facebook.cache.common.CacheKey;
import com.facebook.cache.common.SimpleCacheKey;
import com.facebook.imagepipeline.cache.BitmapMemoryCacheKey;
import com.facebook.imagepipeline.cache.CacheKeyFactory;
import com.facebook.imagepipeline.request.ImageRequest;
public class EhCacheKeyFactory implements CacheKeyFactory {
private static EhCacheKeyFactory sInstance = null;
private EhCacheKeyFactory() {
}
public static synchronized EhCacheKeyFactory getInstance() {
if(sInstance == null) {
sInstance = new EhCacheKeyFactory();
}
return sInstance;
}
private String getCommonKey(ImageRequest imageRequest) {
if (imageRequest instanceof KeyImageRequest) {
return ((KeyImageRequest) imageRequest).getKey();
} else {
return getCacheKeySourceUri(imageRequest.getSourceUri()).toString();
}
}
@Override
public CacheKey getBitmapCacheKey(ImageRequest imageRequest) {
return new BitmapMemoryCacheKey(
getCommonKey(imageRequest),
imageRequest.getResizeOptions(),
imageRequest.getAutoRotateEnabled(),
imageRequest.getImageDecodeOptions());
}
@Override
public CacheKey getEncodedCacheKey(ImageRequest imageRequest) {
return new SimpleCacheKey(getCommonKey(imageRequest));
}
@Override
public Uri getCacheKeySourceUri(Uri uri) {
return uri;
}
public static String getThumbKey(int gid) {
return "thumb:" + gid;
}
public static String getGalleryKey(int gid, int index) {
return "gallery:" + gid + "-" + index;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 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
* 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.fresco;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
public class KeyImageRequest extends ImageRequest {
private String mKey;
// TODO can't do ImageRequestBuilder.validate()
public KeyImageRequest(ImageRequestBuilder builder) {
super(builder);
}
public String getKey() {
return mKey;
}
public void setKey(String key) {
mKey = key;
}
}
@@ -121,10 +121,6 @@ public final class ContentActivity extends StageActivity
lockDrawer();
}
}
@Override
public void onReplace(Class clazz) {
}
};
@Override
@@ -15,8 +15,72 @@
package com.hippo.ehviewer.ui.scene;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.TextView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.data.GalleryInfo;
import com.hippo.ehviewer.fresco.EhCacheKeyFactory;
import com.hippo.ehviewer.fresco.KeyImageRequest;
import com.hippo.scene.Scene;
public class GalleryDetailScene extends Scene {
public static final String KEY_GALLERY_INFO = "gallery_info";
private SimpleDraweeView mThumb;
private TextView mTitle;
private TextView mUploader;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scene_gallery_detail);
GalleryInfo gi = getAnnouncer().getParcelableExtra(KEY_GALLERY_INFO);
mThumb = (SimpleDraweeView) findViewById(R.id.thumb);
mThumb.bringToFront();
ViewGroup a;
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(gi.thumb));
KeyImageRequest keyImageRequest = new KeyImageRequest(builder);
keyImageRequest.setKey(EhCacheKeyFactory.getThumbKey(gi.gid));
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(keyImageRequest)
.setTapToRetryEnabled(true)
.setOldController(mThumb.getController()).build();
mThumb.setController(controller);
mTitle = (TextView) findViewById(R.id.title);
mTitle.setText(gi.title);
mUploader = (TextView) findViewById(R.id.uploader);
mUploader.setText(gi.uploader);
/*
int source = Config.getEhSource();
EhClient.getInstance().getGalleryDetail(source, EhClient.getDetailUrl(source, gi.gid, gi.token, 0), new EhClient.OnGetGalleryDetailListener() {
@Override
public void onSuccess(GalleryDetail galleryDetail) {
Log.d(galleryDetail.torrentUrl);
}
@Override
public void onFailure(Exception e) {
Log.d("", e);
}
});
*/
}
}
@@ -30,7 +30,6 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -42,6 +41,7 @@ import android.widget.Toast;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.effect.ViewTransition;
@@ -51,6 +51,8 @@ import com.hippo.ehviewer.client.GalleryListParser;
import com.hippo.ehviewer.data.GalleryInfo;
import com.hippo.ehviewer.data.ListUrlBuilder;
import com.hippo.ehviewer.data.UnsupportedSearchException;
import com.hippo.ehviewer.fresco.EhCacheKeyFactory;
import com.hippo.ehviewer.fresco.KeyImageRequest;
import com.hippo.ehviewer.ui.ContentActivity;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.EhUtils;
@@ -61,8 +63,10 @@ import com.hippo.ehviewer.widget.SearchBar;
import com.hippo.ehviewer.widget.SearchDatabase;
import com.hippo.ehviewer.widget.SearchLayout;
import com.hippo.scene.Announcer;
import com.hippo.scene.Curtain;
import com.hippo.scene.Scene;
import com.hippo.scene.SimpleDialog;
import com.hippo.scene.TransitionCurtain;
import com.hippo.util.AnimationUtils;
import com.hippo.util.AppHandler;
import com.hippo.util.AssertUtils;
@@ -70,20 +74,21 @@ import com.hippo.util.MathUtils;
import com.hippo.util.ViewUtils;
import com.hippo.widget.FabLayout;
import com.hippo.widget.FloatingActionButton;
import com.hippo.widget.recyclerview.EasyRecyclerView;
import java.util.List;
import java.util.Map;
// TODO remeber the data in ContentHelper after screen dirction change
// TODO Must refresh when change source
// TODO disable click action when animating
public final class GalleryListScene extends Scene implements SearchBar.Helper,
View.OnClickListener, FabLayout.OnCancelListener,
SearchLayout.SearhLayoutHelper {
SearchLayout.SearhLayoutHelper, EasyRecyclerView.OnItemClickListener {
public static final String KEY_MODE = "mode";
private static final String KEY_STATE = "state";
private static final String KEY_FAB_STATE = "fab_state";
public static final int MODE_HOMEPAGE = 0;
public static final int MODE_POPULAR = 1;
@@ -104,7 +109,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
private SearchBar mSearchBar;
private ContentLayout mContentLayout;
private RecyclerView mContentRecyclerView;
private EasyRecyclerView mContentRecyclerView;
private SearchLayout mSearchLayout;
private FabLayout mFabLayout;
private FloatingActionButton mCornerFab;
@@ -133,7 +138,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
@Override
public void onCreateCustomView(final SimpleDialog dialog, View view) {
int currentPage = mGalleryListHelper.getCurrentPage();
int pageSize = mGalleryListHelper.getPageSize();
int pageSize = mGalleryListHelper.getPageCount();
TextView pageInfo = (TextView) view.findViewById(R.id.go_to_page_info);
pageInfo.setText(String.format(mResources.getString(R.string.go_to_page_info),
currentPage + 1, pageSize == Integer.MAX_VALUE ?
@@ -168,7 +173,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
return false;
}
targetPage = Integer.parseInt(text);
int pageSize = mGalleryListHelper.getPageSize();
int pageSize = mGalleryListHelper.getPageCount();
if (targetPage <= 0 || (targetPage > pageSize)) {
Toast.makeText(mActivity, R.string.go_to_error_invaild,
Toast.LENGTH_SHORT).show();
@@ -193,13 +198,25 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
return LAUNCH_MODE_SINGLE_TOP;
}
@Override
protected void onReplace(@NonNull Scene oldScene) {
super.onReplace(oldScene);
GalleryListScene oldGalleryListScene = (GalleryListScene) oldScene;
mGalleryListHelper = oldGalleryListScene.mGalleryListHelper
.newInstance(getStageActivity());
@Override
protected void onSave(Map<String, Object> transferStop) {
super.onSave(transferStop);
transferStop.put("gallery_list_helper_data", mGalleryListHelper.copyData());
transferStop.put("gallery_list_helper_save", mGalleryListHelper.save());
transferStop.put("gallery_list_helper_listener", mGalleryListHelper.mListener);
}
@SuppressWarnings("unchecked")
@Override
protected void onRestore(Map<String, Object> transferStop) {
super.onRestore(transferStop);
mGalleryListHelper = new GalleryListHelper(getStageActivity(),
(List<GalleryInfo>) transferStop.get("gallery_list_helper_data"),
(int[]) transferStop.get("gallery_list_helper_save"),
(GalleryListHelperSettable) transferStop.get("gallery_list_helper_listener"));
}
private void handleAnnouncer(Announcer announcer) {
@@ -280,12 +297,15 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
mSearchLayout.setHelper(this);
// Content Layout
// OnRestore may create new GalleryListHelper
if (mGalleryListHelper == null) {
mGalleryListHelper = new GalleryListHelper(mActivity);
}
mContentLayout.setHelper(mGalleryListHelper);
mContentRecyclerView.setOnItemClickListener(this);
// Fab Layout
mFabLayout.setOnCancelListener(this);
@@ -307,6 +327,16 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
}
}
@Override
protected void onDestroy(boolean willSave) {
super.onDestroy(willSave);
// Avoid memory leak
if (mGalleryListHelper.mListener != null) {
mGalleryListHelper.mListener.setGalleryListHelper(null);
}
}
@Override
protected void saveInstanceState(Bundle outState) {
super.saveInstanceState(outState);
@@ -712,6 +742,52 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
// TODO
}
@Override
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
GalleryInfo gi = mGalleryListHelper.getDataAt(position);
Announcer announcer = new Announcer();
announcer.putExtra(GalleryDetailScene.KEY_GALLERY_INFO, gi);
Curtain curtain = new TransitionCurtain(new TransitonHelper(position));
startScene(GalleryDetailScene.class, announcer, curtain);
return true;
}
private class TransitonHelper implements TransitionCurtain.ViewPairSet {
private int mPosition;
public TransitonHelper(int position) {
mPosition = position;
}
@Override
public View[] getFromViewSet(@NonNull Scene fromScene) {
View[] views = new View[3];
GalleryListScene scene = (GalleryListScene) fromScene;
RecyclerView.ViewHolder viewHolder = scene.mContentRecyclerView.findViewHolderForAdapterPosition(mPosition);
if (viewHolder != null) {
GalleryHolder gh = (GalleryHolder) viewHolder;
views[0] = gh.thumb;
views[1] = gh.title;
views[2] = gh.uploader;
}
return views;
}
@Override
public View[] getToViewSet(@NonNull Scene toScene) {
View[] views = new View[3];
View sceneView = toScene.getSceneView();
views[0] = sceneView.findViewById(R.id.thumb);
views[1] = sceneView.findViewById(R.id.title);
views[2] = sceneView.findViewById(R.id.uploader);
return views;
}
}
private class SearchBarMoveHelper extends RecyclerView.OnScrollListener {
@Override
@@ -771,8 +847,6 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
private class GalleryListHelper extends ContentLayout.ContentHelper<GalleryInfo, GalleryHolder> {
private LayoutInflater mInflater;
private GalleryListHelperSettable mListener;
private Runnable mSearchBarPositionTask = new Runnable() {
@@ -784,25 +858,16 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
public GalleryListHelper(Context context) {
super(context);
init();
}
private GalleryListHelper(Context context, GalleryListHelper oldContentHelper) {
super(context, oldContentHelper);
public GalleryListHelper(Context context, List<GalleryInfo> data,
int[] save, GalleryListHelperSettable listener) {
super(context, data, save);
// Update gallery listener
mListener = oldContentHelper.mListener;
mListener = listener;
if (mListener != null) {
mListener.setGalleryListHelper(this);
}
init();
}
public GalleryListHelper newInstance(Context context) {
return new GalleryListHelper(context, this);
}
private void init() {
mInflater = mActivity.getLayoutInflater();
}
private void clearLastGalleryListHelperSettable(GalleryListHelperSettable listener) {
@@ -859,19 +924,23 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
@Override
public GalleryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_gallery_list_detail, parent, false);
View view = mActivity.getLayoutInflater().inflate(R.layout.item_gallery_list_detail, parent, false);
return new GalleryHolder(view);
}
@Override
public void onBindViewHolder(GalleryHolder holder, int position) {
GalleryInfo gi = getDataAt(position);
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(gi.thumb));
KeyImageRequest keyImageRequest = new KeyImageRequest(builder);
keyImageRequest.setKey(EhCacheKeyFactory.getThumbKey(gi.gid));
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(Uri.parse(gi.thumb))
.setImageRequest(keyImageRequest)
.setTapToRetryEnabled(true)
.setOldController(holder.thumb.getController())
.build();
.setOldController(holder.thumb.getController()).build();
holder.thumb.setController(controller);
holder.title.setText(gi.title);
holder.uploader.setText(gi.uploader);
holder.rating.setRating(gi.rating);
@@ -913,12 +982,12 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
if (mHelper != null) {
if (mSource == EhClient.SOURCE_LOFI) {
if (pageNum == GalleryListParser.CURRENT_PAGE_IS_LAST) {
mHelper.setPageSize(mTargetPage);
mHelper.setPageCount(mTargetPage);
} else if (mTaskType == ContentLayout.ContentHelper.TYPE_REFRESH) {
mHelper.setPageSize(Integer.MAX_VALUE);
mHelper.setPageCount(Integer.MAX_VALUE);
}
} else {
mHelper.setPageSize(pageNum);
mHelper.setPageCount(pageNum);
}
mHelper.onGetPageData(mTaskId, glList);
@@ -954,7 +1023,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
@Override
public void onSuccess(List<GalleryInfo> glList, long timeStamp) {
if (mHelper != null) {
mHelper.setPageSize(1);
mHelper.setPageCount(1);
mHelper.onGetPageData(mTaskId, glList);
mHelper.clearLastGalleryListHelperSettable(this);
@@ -35,15 +35,15 @@ public final class MainSettingsScene extends PreferenceHeaderScene {
PreferenceHeader[] phs = new PreferenceHeader[] {
newPreferenceHeader(
R.drawable.ic_cellphone_android_theme_primary,
R.drawable.ic_cellphone_android_theme_accent,
R.string.settings_display,
DisplaySettingsScene.class),
newPreferenceHeader(
R.drawable.ic_eh_theme_primary,
R.drawable.ic_eh_theme_accent,
R.string.settings_eh,
EHSettingsScene.class),
newPreferenceHeader(
R.drawable.ic_lambda_theme_primary,
R.drawable.ic_lambda_theme_accent,
R.string.settings_advance,
AdvanceSettingsScene.class)
};
@@ -0,0 +1,57 @@
/*
* Copyright (C) 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
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
public class CircularRevealLayout extends FrameLayout {
public CircularRevealLayout(Context context) {
super(context);
}
public CircularRevealLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircularRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if(mRunning && child == mRevealInfo.getTarget()){
final int state = canvas.save();
mRevealPath.reset();
mRevealPath.addCircle(mRevealInfo.centerX, mRevealInfo.centerY, mRadius, Path.Direction.CW);
canvas.clipPath(mRevealPath);
boolean isInvalided = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(state);
return isInvalided;
}
return super.drawChild(canvas, child, drawingTime);
}
*/
}
@@ -187,7 +187,7 @@ public class ContentLayout extends FrameLayout {
/**
* The size of page
*/
private int mPageSize;
private int mPageCount;
private int mCurrentPage;
private int mPageVolume;
@@ -246,20 +246,35 @@ public class ContentLayout extends FrameLayout {
}
@SuppressWarnings("unchecked")
protected ContentHelper(Context context, ContentHelper oldContentHelper) {
protected ContentHelper(Context context, List<E> data, int[] save) {
this(context);
mData.addAll(oldContentHelper.mData);
mData.addAll(data);
mFirstPage = oldContentHelper.mFirstPage;
mLastPage = oldContentHelper.mLastPage;
mFirstIndex = oldContentHelper.mFirstIndex;
mLastIndex = oldContentHelper.mLastIndex;
mPageSize = oldContentHelper.mPageSize;
mCurrentPage = oldContentHelper.mCurrentPage;
mPageVolume = oldContentHelper.mPageVolume;
mCurrentTaskId = oldContentHelper.mCurrentTaskId;
mCurrentTaskType = oldContentHelper.mCurrentTaskType;
mCurrentTaskPage = oldContentHelper.mCurrentTaskPage;
mFirstPage = save[0];
mLastPage = save[1];
mFirstIndex = save[2];
mLastIndex = save[3];
mPageCount = save[4];
mCurrentPage = save[5];
mPageVolume = save[6];
mCurrentTaskId = save[7];
mCurrentTaskType = save[8];
mCurrentTaskPage = save[9];
}
public int[] save() {
int[] save = new int[10];
save[0] = mFirstPage;
save[1] = mLastPage;
save[2] = mFirstIndex;
save[3] = mLastIndex;
save[4] = mPageCount;
save[5] = mCurrentPage;
save[6] = mPageVolume;
save[7] = mCurrentTaskId;
save[8] = mCurrentTaskType;
save[9] = mCurrentTaskPage;
return save;
}
private void init(ContentLayout contentLayout) {
@@ -312,6 +327,12 @@ public class ContentLayout extends FrameLayout {
}
}
public List<E> copyData() {
List<E> data = new ArrayList<>(mData.size());
data.addAll(mData);
return data;
}
/**
* @throws IndexOutOfBoundsException
* if {@code location < 0 || location >= size()}
@@ -328,12 +349,12 @@ public class ContentLayout extends FrameLayout {
*/
protected abstract void getPageData(int taskId, int type, int page);
public void setPageSize(int pageSize) {
mPageSize = pageSize;
public void setPageCount(int pageSize) {
mPageCount = pageSize;
}
public int getPageSize() {
return mPageSize;
public int getPageCount() {
return mPageCount;
}
public int getCurrentPage() {
@@ -341,7 +362,7 @@ public class ContentLayout extends FrameLayout {
}
public void resetPageSize() {
mPageSize = Integer.MAX_VALUE;
mPageCount = Integer.MAX_VALUE;
}
public void onGetPageData(int taskId, List<E> data) {
@@ -446,7 +467,7 @@ public class ContentLayout extends FrameLayout {
@Override
public boolean onFooterRefresh() {
if (mLastPage >= mPageSize) {
if (mLastPage >= mPageCount) {
return false;
} else {
mCurrentTaskId = mIdGenerator.nextId();
@@ -525,8 +546,8 @@ public class ContentLayout extends FrameLayout {
* Only work when data is loaded
*/
public void goTo(int page) {
if (page < 0 || page >= mPageSize) {
throw new IndexOutOfBoundsException("Page size is " + mPageSize + ", page is " + page);
if (page < 0 || page >= mPageCount) {
throw new IndexOutOfBoundsException("Page count is " + mPageCount + ", page is " + page);
} else if (page >= mFirstPage && page < mLastPage) {
cancelCurrentTask();
+24 -9
View File
@@ -32,6 +32,8 @@ import android.widget.FrameLayout;
import com.hippo.util.AssertUtils;
import com.hippo.util.ViewUtils;
import java.util.Map;
/**
* {@link com.hippo.scene.Scene} is a {@code Activity} of {@link android.app.Activity}.
* <p>
@@ -164,8 +166,11 @@ public abstract class Scene {
}
}
void destroy() {
onDestroy();
// TODO Tell destory is it need close
void destroy(boolean willSave) {
onDestroy(willSave);
SceneApplication.getRefWatcher(getStageActivity()).watch(this);
}
void open() {
@@ -184,8 +189,12 @@ public abstract class Scene {
onResume();
}
void replace(@NonNull Scene oldScene) {
onReplace(oldScene);
void save(Map<String, Object> transferStop) {
onSave(transferStop);
}
void restore(Map<String, Object> transferStop) {
onRestore(transferStop);
}
void setFitPaddingBottom(int b) {
@@ -239,7 +248,7 @@ public abstract class Scene {
}
@SuppressWarnings("ConstantConditions")
protected void onDestroy() {
protected void onDestroy(boolean willSave) {
// Make sure soft key broad is hidden
InputMethodManager imm = (InputMethodManager) getStageActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSceneView.getWindowToken(), 0);
@@ -259,10 +268,16 @@ public abstract class Scene {
getSceneView().setEnableTouch(true);
}
protected void onReplace(@NonNull Scene oldScene) {
mId = oldScene.mId;
mAnnouncer = oldScene.mAnnouncer;
mCurtain = oldScene.mCurtain;
protected void onSave(Map<String, Object> transferStop) {
transferStop.put("scene_id", mId);
transferStop.put("scene_announcer", mAnnouncer);
transferStop.put("scene_curtain", mCurtain);
}
protected void onRestore(Map<String, Object> transferStop) {
mId = (Integer) transferStop.get("scene_id");
mAnnouncer = (Announcer) transferStop.get("scene_announcer");
mCurtain = (Curtain) transferStop.get("scene_curtain");
if (mCurtain != null) {
mCurtain.onReplace();
}
@@ -16,15 +16,23 @@
package com.hippo.scene;
import android.app.Application;
import android.content.Context;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
public class SceneApplication extends Application {
private SceneManager mSceneManager;
private RefWatcher mRefWatcher;
@Override
public void onCreate() {
super.onCreate();
mRefWatcher = LeakCanary.install(this);
mSceneManager = SceneManager.getInstance();
StageActivity.setSceneManager(mSceneManager);
Scene.setSceneManager(mSceneManager);
@@ -33,4 +41,9 @@ public class SceneApplication extends Application {
public SceneManager getSceneManager() {
return mSceneManager;
}
public static RefWatcher getRefWatcher(Context context) {
SceneApplication application = (SceneApplication) context.getApplicationContext();
return application.mRefWatcher;
}
}
@@ -23,7 +23,9 @@ import com.hippo.util.IntIdGenerator;
import com.hippo.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class SceneManager {
@@ -39,6 +41,8 @@ public class SceneManager {
private List<SceneStateListener> mSceneStateListenerList = new ArrayList<>();
private Map<String, Object> mTransferStop = new HashMap<>();
private static SceneManager sSceneManger;
public static SceneManager getInstance() {
@@ -100,12 +104,6 @@ public class SceneManager {
}
}
private void notifyReplace(Class clazz) {
for (SceneStateListener l : mSceneStateListenerList) {
l.onReplace(clazz);
}
}
Scene createSceneByClass(@NonNull Class sceneClass) {
try {
return (Scene) sceneClass.newInstance();
@@ -210,7 +208,7 @@ public class SceneManager {
// It is the last scene, just finish the activity
mSceneStack.remove(index);
scene.destroy();
scene.destroy(false);
scene.setState(Scene.SCENE_STATE_DESTROY);
notifyDestroy(scene.getClass());
@@ -229,7 +227,7 @@ public class SceneManager {
notifyResume(previousState.getClass());
}
scene.destroy();
scene.destroy(false);
scene.setState(Scene.SCENE_STATE_DESTROY);
notifyDestroy(scene.getClass());
@@ -298,15 +296,18 @@ public class SceneManager {
tempStack.addAll(mSceneStack);
mSceneStack.clear();
Map<String, Object> transferStop = mTransferStop;
for (Scene oldScene : tempStack) {
Scene newScene = createSceneByClass(oldScene.getClass());
newScene.replace(oldScene);
notifyReplace(newScene.getClass());
oldScene.destroy();
oldScene.destroy(true);
oldScene.setState(Scene.SCENE_STATE_DESTROY);
notifyDestroy(oldScene.getClass());
oldScene.save(transferStop);
Scene newScene = createSceneByClass(oldScene.getClass());
newScene.restore(transferStop);
newScene.create(savedInstanceState);
newScene.setState(Scene.SCENE_STATE_CREATE);
notifyCreate(newScene.getClass());
@@ -315,6 +316,8 @@ public class SceneManager {
newScene.setState(Scene.SCENE_STATE_RUN);
mSceneStack.push(newScene);
transferStop.clear();
}
}
@@ -323,6 +326,5 @@ public class SceneManager {
void onDestroy(Class clazz);
void onPause(Class clazz);
void onResume(Class clazz);
void onReplace(Class clazz);
}
}
@@ -37,6 +37,8 @@ import com.hippo.effect.ripple.RippleSalon;
import com.hippo.ehviewer.R;
import com.hippo.util.ViewUtils;
import java.util.Map;
public class SimpleDialog extends SceneDialog implements View.OnClickListener,
SceneDialogView.OnClickOutOfDialogListener, AdapterView.OnItemClickListener {
@@ -103,18 +105,22 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener,
}
@Override
protected void onReplace(@NonNull Scene oldScene) {
super.onReplace(oldScene);
protected void onSave(Map<String, Object> transferStop) {
super.onSave(transferStop);
SimpleDialog oldDialog = (SimpleDialog) oldScene;
mBuilder = oldDialog.mBuilder;
// Avoid memory leak
mBuilder.mContext = getStageActivity();
transferStop.put("builder", mBuilder);
}
@Override
protected void onDestroy() {
super.onDestroy();
protected void onRestore(Map<String, Object> transferStop) {
super.onRestore(transferStop);
mBuilder = (Builder) transferStop.get("builder");
}
@Override
protected void onDestroy(boolean willSave) {
super.onDestroy(willSave);
OnCloseListener listener = mBuilder.mOnCloseListener;
if (listener != null) {
@@ -446,6 +452,9 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener,
}
public @NonNull SimpleDialog build() {
// Avoid memory leak
mContext = null;
SimpleDialog dialog = new SimpleDialog();
dialog.setBuilder(this);
return dialog;
@@ -29,6 +29,7 @@ import android.widget.AbsoluteLayout;
import com.hippo.animation.ArgbEvaluator;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.util.AnimationUtils;
import com.hippo.util.AssertUtils;
import com.hippo.util.Log;
import com.hippo.util.ViewUtils;
import com.hippo.widget.GlobalLayoutSet;
@@ -42,12 +43,12 @@ public class TransitionCurtain extends Curtain {
private static long ANIMATE_TIME = 300L;
private ViewPair[] mViewPairArray;
private ViewPairSet mViewPairSet;
private AnimatorSet mAnimatorSet;
public TransitionCurtain(ViewPair[] viewPairArray) {
mViewPairArray = viewPairArray;
public TransitionCurtain(ViewPairSet viewPairSet) {
mViewPairSet = viewPairSet;
}
// TODO what if back key press before move animator start
@@ -82,9 +83,13 @@ public class TransitionCurtain extends Curtain {
Set<TransitionItem> transitionItemSet = new HashSet<>();
// Handle transit part
for (ViewPair pair : mViewPairArray) {
final View enterView = pair.getToView(enter);
final View exitView = pair.getFromView(exit);
View[] enterViews = mViewPairSet.getToViewSet(enter);
View[] exitViews = mViewPairSet.getFromViewSet(exit);
AssertUtils.assertEquals("From view size and to view size must be the same", enterViews.length, exitViews.length);
int length = enterViews.length;
for (int i = 0; i < length; i++) {
final View enterView = enterViews[i];
final View exitView = exitViews[i];
if (enterView == null) {
Log.w(TAG, "Can't get enterView when open.");
continue;
@@ -266,9 +271,13 @@ public class TransitionCurtain extends Curtain {
Set<TransitionItem> transitionItemSet = new HashSet<>();
// Handle transit part
for (ViewPair pair : mViewPairArray) {
final View enterView = pair.getFromView(enter);
final View exitView = pair.getToView(exit);
View[] enterViews = mViewPairSet.getFromViewSet(enter);
View[] exitViews = mViewPairSet.getToViewSet(exit);
AssertUtils.assertEquals("From view size and to view size must be the same", enterViews.length, exitViews.length);
int length = enterViews.length;
for (int i = 0; i < length; i++) {
final View enterView = enterViews[i];
final View exitView = exitViews[i];
if (enterView == null) {
Log.w(TAG, "Can't get enterView when close");
continue;
@@ -447,28 +456,8 @@ public class TransitionCurtain extends Curtain {
/**
* the interface help {@link TransitionCurtain} to get the views to do transfer
*/
public interface ViewPair {
View getFromView(@NonNull Scene fromScene);
View getToView(@NonNull Scene toScene);
}
public static class IdViewPair implements ViewPair {
private int mFromId;
private int mToId;
public IdViewPair(int fromId, int toId) {
mFromId = fromId;
mToId = toId;
}
@Override
public View getFromView(@NonNull Scene fromScene) {
return fromScene.findViewById(mFromId);
}
@Override
public View getToView(@NonNull Scene toScene) {
return toScene.findViewById(mToId);
}
public interface ViewPairSet {
View[] getFromViewSet(@NonNull Scene fromScene);
View[] getToViewSet(@NonNull Scene toScene);
}
}
@@ -50,11 +50,11 @@ public class PreferenceHeaderScene extends AppbarScene {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scene_preference_header);
mActivity = getStageActivity();
mRecyclerView = new EasyRecyclerView(getStageActivity());
setContentView(mRecyclerView);
mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view);
mHeaderAdapter = new HeaderAdapter();
mRecyclerView.setAdapter(mHeaderAdapter);
@@ -46,9 +46,9 @@ public class PreferenceScene extends AppbarScene {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scene_preference);
mRecyclerView = new EasyRecyclerView(getStageActivity());
setContentView(mRecyclerView);
mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view);
mAdapter = new PreferenceAdapter();
mRecyclerView.setAdapter(mAdapter);
@@ -1,230 +0,0 @@
/*
* Copyright (C) 2014-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
* 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.util;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* It is the same as {@link java.io.ByteArrayOutputStream}, but buffer can be got
* directly. So it is not safe.
*/
public class FastByteArrayOutputStream extends OutputStream {
/**
* The byte array containing the bytes written.
*/
protected byte[] buf;
/**
* The number of bytes written.
*/
protected int count;
/**
* Constructs a new ByteArrayOutputStream with a default size of 32 bytes.
* If more than 32 bytes are written to this instance, the underlying byte
* array will expand.
*/
public FastByteArrayOutputStream() {
buf = new byte[32];
}
/**
* Constructs a new {@code ByteArrayOutputStream} with a default size of
* {@code size} bytes. If more than {@code size} bytes are written to this
* instance, the underlying byte array will expand.
*
* @param size
* initial size for the underlying byte array, must be
* non-negative.
* @throws IllegalArgumentException
* if {@code size} < 0.
*/
public FastByteArrayOutputStream(int size) {
if (size >= 0) {
buf = new byte[size];
} else {
throw new IllegalArgumentException("size < 0");
}
}
/**
* Closes this stream. This releases system resources used for this stream.
*
* @throws java.io.IOException
* if an error occurs while attempting to close this stream.
*/
@Override
public void close() throws IOException {
/**
* Although the spec claims "A closed stream cannot perform output
* operations and cannot be reopened.", this implementation must do
* nothing.
*/
super.close();
}
private void expand(int i) {
/* Can the buffer handle @i more bytes, if not expand it */
if (count + i <= buf.length) {
return;
}
byte[] newbuf = new byte[(count + i) * 2];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
/**
* Resets this stream to the beginning of the underlying byte array. All
* subsequent writes will overwrite any bytes previously stored in this
* stream.
*/
public synchronized void reset() {
count = 0;
}
/**
* Returns the total number of bytes written to this stream so far.
*
* @return the number of bytes written to this stream.
*/
public int size() {
return count;
}
/**
* Returns the contents of this ByteArrayOutputStream as a byte array. Any
* changes made to the receiver after returning will not be reflected in the
* byte array returned to the caller.
*
* @return this stream's current contents as a byte array.
*/
public synchronized byte[] getBuffer() {
return buf;
}
/**
* Returns the contents of this ByteArrayOutputStream as a string. Any
* changes made to the receiver after returning will not be reflected in the
* string returned to the caller.
*
* @return this stream's current contents as a string.
*/
@Override
public String toString() {
return new String(buf, 0, count);
}
/**
* Returns the contents of this ByteArrayOutputStream as a string. Each byte
* {@code b} in this stream is converted to a character {@code c} using the
* following function:
* {@code c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))}. This method is
* deprecated and either {@link #toString()} or {@link #toString(String)}
* should be used.
*
* @param hibyte
* the high byte of each resulting Unicode character.
* @return this stream's current contents as a string with the high byte set
* to {@code hibyte}.
* @deprecated Use {@link #toString()} instead.
*/
@Deprecated
public String toString(int hibyte) {
char[] newBuf = new char[size()];
for (int i = 0; i < newBuf.length; i++) {
newBuf[i] = (char) (((hibyte & 0xff) << 8) | (buf[i] & 0xff));
}
return new String(newBuf);
}
/**
* Returns the contents of this ByteArrayOutputStream as a string converted
* according to the encoding declared in {@code charsetName}.
*
* @param charsetName
* a string representing the encoding to use when translating
* this stream to a string.
* @return this stream's current contents as an encoded string.
* @throws java.io.UnsupportedEncodingException
* if the provided encoding is not supported.
*/
public String toString(String charsetName) throws UnsupportedEncodingException {
return new String(buf, 0, count, charsetName);
}
/**
* Writes {@code count} bytes from the byte array {@code buffer} starting at
* offset {@code index} to this stream.
*
* @param buffer
* the buffer to be written.
* @param offset
* the initial position in {@code buffer} to retrieve bytes.
* @param len
* the number of bytes of {@code buffer} to write.
* @throws NullPointerException
* if {@code buffer} is {@code null}.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code len < 0}, or if
* {@code offset + len} is greater than the length of
* {@code buffer}.
*/
@Override
public synchronized void write(byte[] buffer, int offset, int len) {
if ((offset | count) < 0 || offset > buffer.length || buffer.length - offset < count) {
throw new ArrayIndexOutOfBoundsException("length=" + buffer.length + "; regionStart=" + offset
+ "; regionLength=" + count);
}
if (len == 0) {
return;
}
expand(len);
System.arraycopy(buffer, offset, buf, this.count, len);
this.count += len;
}
/**
* Writes the specified byte {@code oneByte} to the OutputStream. Only the
* low order byte of {@code oneByte} is written.
*
* @param oneByte
* the byte to be written.
*/
@Override
public synchronized void write(int oneByte) {
if (count == buf.length) {
expand(1);
}
buf[count++] = (byte) oneByte;
}
/**
* Takes the contents of this stream and writes it to the output stream
* {@code out}.
*
* @param out
* an OutputStream on which to write the contents of this stream.
* @throws java.io.IOException
* if an error occurs while writing to {@code out}.
*/
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
}
@@ -35,6 +35,10 @@ public class GlobalLayoutSet {
mSize++;
}
public int getSize() {
return mSize;
}
private class LayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private ViewTreeObserver mObserver;