diff --git a/app/app.iml b/app/app.iml index d1293d4db..b00ce6b8d 100644 --- a/app/app.iml +++ b/app/app.iml @@ -78,6 +78,7 @@ + @@ -95,9 +96,12 @@ + + + @@ -106,7 +110,8 @@ - + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8bd28d233..7e109f8e7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,5 +30,7 @@ dependencies { compile 'com.facebook.fresco:fresco:0.5.0' compile 'com.facebook.fresco:imagepipeline-okhttp:0.5.0' compile 'com.squareup.okhttp:okhttp:2.4.0-RC1' + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' compile fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index 33dcc2b76..b4d2557a8 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -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); diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java index bc0cf6488..c5361c9a7 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java @@ -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)); + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java b/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java new file mode 100644 index 000000000..70b0b2d8e --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java @@ -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 = + "

(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)

"; + private static final String PINING_STRING = + "

This gallery is pining for the fjords.

"; + + 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 tags; + public List 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("]+>]+>([\\w\\s]+)"); + 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("
\n

([^<]+)

"); + 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 + + ".+?" + + "
]+>
" // 3 thumb + + "
" + + "
" + + "

([^<>]+)

" // 4 title + + "

([^<>]*)

" // 5 title_jpn might be empty string + + "
" + + ".+?" + + "]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download \\( (\\d+) \\)" // 6 torrentUrl, 7 torrentCount + + ".+?" + + "
]+><[^<>]*alt=\"([\\w|\\-]+)\"[^<>]*>
" // 8 category + + "
]+>([^<>]+)" // 9 uploader + + ".+?" + + "]*>Posted:]*>([\\w|\\-|\\s|:]+)" // 10 posted + + "]*>Parent:]*>(?:]*>)?([^<>]+)(?:)?" // 11 parent + + "]*>Visible:]*>([^<>]+)" // 12 visible + + "]*>Language:]*>([^<>]+)(?:]*>[^<>]*)?" // 13 language + + "]*>File Size:]*>([^<>]+)(?:]*>([^<>]+))?" // 14 File size, 15 resize + + "]*>Length:]*>([\\d|,]+) pages" // 16 pageCount + + "]*>Favorited:<[^<>]*>([^<>]+)" // 17 Favorite times "([\d|,]+) times" or "Once" or "Never" + + ".+?" + + "([\\d|,]+)" // 18 ratedTimes + + "" + + "]*>Average: ([^<>]+)" // 19 rating "Average: x.xx" or "Not Yet Rated" + + ".+?" + + "]*>(.+?)", 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("]+>([\\w\\s]+):(?:]+>]+>[\\w\\s]+
)+"); + 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("]+>]+>([\\d,]+)]+>(?:]+>)?>(?:)?"); + 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("
Posted on ([^<>]+) by:   ]+>([^<>]+).+?
]*>(.+?)
(); + 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 { + 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; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/client/OffensiveException.java b/app/src/main/java/com/hippo/ehviewer/client/OffensiveException.java new file mode 100644 index 000000000..bf4a671ec --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/OffensiveException.java @@ -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"); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/client/ParseException.java b/app/src/main/java/com/hippo/ehviewer/client/ParseException.java new file mode 100644 index 000000000..2e49301c0 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/ParseException.java @@ -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; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/client/PiningException.java b/app/src/main/java/com/hippo/ehviewer/client/PiningException.java new file mode 100644 index 000000000..d58d18c96 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/PiningException.java @@ -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"); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/data/Comment.java b/app/src/main/java/com/hippo/ehviewer/data/Comment.java new file mode 100644 index 000000000..ba872c418 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/data/Comment.java @@ -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; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/data/GalleryDetail.java b/app/src/main/java/com/hippo/ehviewer/data/GalleryDetail.java new file mode 100644 index 000000000..b997c254d --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/data/GalleryDetail.java @@ -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 tags; + public List comments; + + public int previewPageCount; + +} diff --git a/app/src/main/java/com/hippo/ehviewer/data/GalleryInfo.java b/app/src/main/java/com/hippo/ehviewer/data/GalleryInfo.java index adc518dae..d7f0c98dd 100644 --- a/app/src/main/java/com/hippo/ehviewer/data/GalleryInfo.java +++ b/app/src/main/java/com/hippo/ehviewer/data/GalleryInfo.java @@ -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() { diff --git a/app/src/main/java/com/hippo/ehviewer/data/TagGroup.java b/app/src/main/java/com/hippo/ehviewer/data/TagGroup.java new file mode 100644 index 000000000..fd1b3d62b --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/data/TagGroup.java @@ -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 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); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/fresco/EhCacheKeyFactory.java b/app/src/main/java/com/hippo/ehviewer/fresco/EhCacheKeyFactory.java new file mode 100644 index 000000000..517fe9d12 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/fresco/EhCacheKeyFactory.java @@ -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; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/fresco/KeyImageRequest.java b/app/src/main/java/com/hippo/ehviewer/fresco/KeyImageRequest.java new file mode 100644 index 000000000..68805c2a1 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/fresco/KeyImageRequest.java @@ -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; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java index 077ae4377..30f6a4fb5 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java @@ -121,10 +121,6 @@ public final class ContentActivity extends StageActivity lockDrawer(); } } - - @Override - public void onReplace(Class clazz) { - } }; @Override diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index bda94d6cd..ae57bddad 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -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); + } + }); + */ + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java index 42579b2b3..59be22fb6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java @@ -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 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 transferStop) { + super.onRestore(transferStop); + + mGalleryListHelper = new GalleryListHelper(getStageActivity(), + (List) 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 { - 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 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 glList, long timeStamp) { if (mHelper != null) { - mHelper.setPageSize(1); + mHelper.setPageCount(1); mHelper.onGetPageData(mTaskId, glList); mHelper.clearLastGalleryListHelperSettable(this); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/MainSettingsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/MainSettingsScene.java index 8a86f488a..3699add85 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/MainSettingsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/MainSettingsScene.java @@ -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) }; diff --git a/app/src/main/java/com/hippo/ehviewer/widget/CircularRevealLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/CircularRevealLayout.java new file mode 100644 index 000000000..bac28bd3d --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/widget/CircularRevealLayout.java @@ -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); + } + */ +} diff --git a/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java index d3668c967..85065912e 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java @@ -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 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 copyData() { + List 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 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(); diff --git a/app/src/main/java/com/hippo/scene/Scene.java b/app/src/main/java/com/hippo/scene/Scene.java index 5d17a3144..ef6dff8ae 100644 --- a/app/src/main/java/com/hippo/scene/Scene.java +++ b/app/src/main/java/com/hippo/scene/Scene.java @@ -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}. *

@@ -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 transferStop) { + onSave(transferStop); + } + + void restore(Map 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 transferStop) { + transferStop.put("scene_id", mId); + transferStop.put("scene_announcer", mAnnouncer); + transferStop.put("scene_curtain", mCurtain); + } + + protected void onRestore(Map transferStop) { + mId = (Integer) transferStop.get("scene_id"); + mAnnouncer = (Announcer) transferStop.get("scene_announcer"); + mCurtain = (Curtain) transferStop.get("scene_curtain"); if (mCurtain != null) { mCurtain.onReplace(); } diff --git a/app/src/main/java/com/hippo/scene/SceneApplication.java b/app/src/main/java/com/hippo/scene/SceneApplication.java index 235603d14..feba98893 100644 --- a/app/src/main/java/com/hippo/scene/SceneApplication.java +++ b/app/src/main/java/com/hippo/scene/SceneApplication.java @@ -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; + } } diff --git a/app/src/main/java/com/hippo/scene/SceneManager.java b/app/src/main/java/com/hippo/scene/SceneManager.java index 068f0673d..63836e71d 100644 --- a/app/src/main/java/com/hippo/scene/SceneManager.java +++ b/app/src/main/java/com/hippo/scene/SceneManager.java @@ -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 mSceneStateListenerList = new ArrayList<>(); + private Map 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 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); } } diff --git a/app/src/main/java/com/hippo/scene/SimpleDialog.java b/app/src/main/java/com/hippo/scene/SimpleDialog.java index 6fa906ebd..489f44b90 100644 --- a/app/src/main/java/com/hippo/scene/SimpleDialog.java +++ b/app/src/main/java/com/hippo/scene/SimpleDialog.java @@ -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 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 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; diff --git a/app/src/main/java/com/hippo/scene/TransitionCurtain.java b/app/src/main/java/com/hippo/scene/TransitionCurtain.java index 8112ba5dc..88950fcf0 100644 --- a/app/src/main/java/com/hippo/scene/TransitionCurtain.java +++ b/app/src/main/java/com/hippo/scene/TransitionCurtain.java @@ -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 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 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); } } diff --git a/app/src/main/java/com/hippo/scene/preference/PreferenceHeaderScene.java b/app/src/main/java/com/hippo/scene/preference/PreferenceHeaderScene.java index b8e3eddec..0d45687d8 100644 --- a/app/src/main/java/com/hippo/scene/preference/PreferenceHeaderScene.java +++ b/app/src/main/java/com/hippo/scene/preference/PreferenceHeaderScene.java @@ -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); diff --git a/app/src/main/java/com/hippo/scene/preference/PreferenceScene.java b/app/src/main/java/com/hippo/scene/preference/PreferenceScene.java index 4f1a31426..1c2e325d6 100644 --- a/app/src/main/java/com/hippo/scene/preference/PreferenceScene.java +++ b/app/src/main/java/com/hippo/scene/preference/PreferenceScene.java @@ -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); diff --git a/app/src/main/java/com/hippo/util/FastByteArrayOutputStream.java b/app/src/main/java/com/hippo/util/FastByteArrayOutputStream.java deleted file mode 100644 index 6a81082ed..000000000 --- a/app/src/main/java/com/hippo/util/FastByteArrayOutputStream.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/main/java/com/hippo/widget/GlobalLayoutSet.java b/app/src/main/java/com/hippo/widget/GlobalLayoutSet.java index 74a2f0174..ecbde2341 100644 --- a/app/src/main/java/com/hippo/widget/GlobalLayoutSet.java +++ b/app/src/main/java/com/hippo/widget/GlobalLayoutSet.java @@ -35,6 +35,10 @@ public class GlobalLayoutSet { mSize++; } + public int getSize() { + return mSize; + } + private class LayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { private ViewTreeObserver mObserver; diff --git a/app/src/main/res/drawable/ic_cellphone_android_theme_primary.xml b/app/src/main/res/drawable/ic_cellphone_android_theme_accent.xml similarity index 92% rename from app/src/main/res/drawable/ic_cellphone_android_theme_primary.xml rename to app/src/main/res/drawable/ic_cellphone_android_theme_accent.xml index 191f8b6e7..bfee3f683 100644 --- a/app/src/main/res/drawable/ic_cellphone_android_theme_primary.xml +++ b/app/src/main/res/drawable/ic_cellphone_android_theme_accent.xml @@ -31,9 +31,9 @@ + android:fillColor="@color/theme_accent"/> diff --git a/app/src/main/res/drawable/ic_eh_theme_primary.xml b/app/src/main/res/drawable/ic_eh_theme_accent.xml similarity index 91% rename from app/src/main/res/drawable/ic_eh_theme_primary.xml rename to app/src/main/res/drawable/ic_eh_theme_accent.xml index 3295ac609..0dcc3a039 100644 --- a/app/src/main/res/drawable/ic_eh_theme_primary.xml +++ b/app/src/main/res/drawable/ic_eh_theme_accent.xml @@ -31,9 +31,9 @@ + android:fillColor="@color/theme_accent"/> diff --git a/app/src/main/res/drawable/ic_lambda_theme_primary.xml b/app/src/main/res/drawable/ic_lambda_theme_accent.xml similarity index 92% rename from app/src/main/res/drawable/ic_lambda_theme_primary.xml rename to app/src/main/res/drawable/ic_lambda_theme_accent.xml index c0d0aeb1a..79ff4c3b8 100644 --- a/app/src/main/res/drawable/ic_lambda_theme_primary.xml +++ b/app/src/main/res/drawable/ic_lambda_theme_accent.xml @@ -31,9 +31,9 @@ + android:fillColor="@color/theme_accent"/> diff --git a/app/src/main/res/layout/scene_gallery_detail.xml b/app/src/main/res/layout/scene_gallery_detail.xml index 42797ff94..9d863d757 100644 --- a/app/src/main/res/layout/scene_gallery_detail.xml +++ b/app/src/main/res/layout/scene_gallery_detail.xml @@ -13,10 +13,76 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/scene_gallery_list.xml b/app/src/main/res/layout/scene_gallery_list.xml index 9e0e782c4..db3dd741d 100644 --- a/app/src/main/res/layout/scene_gallery_list.xml +++ b/app/src/main/res/layout/scene_gallery_list.xml @@ -31,8 +31,8 @@ android:layout_height="match_parent" android:clipToPadding="false" auto:space="@dimen/search_layout_space" - auto:spaceLeft="@dimen/search_layout_space_vertical" - auto:spaceRight="@dimen/search_layout_space_vertical"/> + auto:spaceLeft="@dimen/search_layout_space_horizontal" + auto:spaceRight="@dimen/search_layout_space_horizontal"/> + + + diff --git a/app/src/main/res/layout/scene_preference_header.xml b/app/src/main/res/layout/scene_preference_header.xml new file mode 100644 index 000000000..1e2cd2c3d --- /dev/null +++ b/app/src/main/res/layout/scene_preference_header.xml @@ -0,0 +1,25 @@ + + + diff --git a/app/src/main/res/layout/widget_content_layout.xml b/app/src/main/res/layout/widget_content_layout.xml index b64f6a33f..d122c3be3 100644 --- a/app/src/main/res/layout/widget_content_layout.xml +++ b/app/src/main/res/layout/widget_content_layout.xml @@ -58,8 +58,8 @@ android:layout_height="match_parent" android:clipToPadding="false" auto:space="@dimen/search_layout_space" - auto:spaceLeft="@dimen/search_layout_space_vertical" - auto:spaceRight="@dimen/search_layout_space_vertical" + auto:spaceLeft="@dimen/search_layout_space_horizontal" + auto:spaceRight="@dimen/search_layout_space_horizontal" tools:ignore="RtlHardcoded"/> diff --git a/app/src/main/res/values-sw600dp-land/dimens.xml b/app/src/main/res/values-sw600dp-land/dimens.xml new file mode 100644 index 000000000..f32e01f71 --- /dev/null +++ b/app/src/main/res/values-sw600dp-land/dimens.xml @@ -0,0 +1,25 @@ + + + + + + + 128dp + + + 128dp + + diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index dace243cf..8e957c3f7 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -15,13 +15,23 @@ --> - + 8dp 32dp + 64dp + + + 64dp 48dp + + 32dp + + + 32dp + 52dp 52dp diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c7788ad8d..bbd8b1047 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -76,7 +76,7 @@ @color/material_teal_500 @color/material_teal_700 - #ffff6977 + #ffe91e63 @android:color/white diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a354bacb3..57231265e 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -15,19 +15,20 @@ --> - + 72dp 24dp 16dp 32dp + 0dp + + 64dip + 0dp 56dp - - - 24dp @@ -70,7 +71,7 @@ 8dp - 8dp + 8dp 16dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a4597f125..2bffea35b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -135,4 +135,20 @@ @color/text_button_text_accent + + + +