From 57acf0585d9dab648c883050555d8e0b026df210 Mon Sep 17 00:00:00 2001 From: seven332 Date: Mon, 29 Jun 2015 15:13:37 +0800 Subject: [PATCH] Update detail scene --- .../com/hippo/drawable/UnikeryDrawable.java | 53 ++++ .../java/com/hippo/drawable/WrapDrawable.java | 123 ++++++++ .../ehviewer/client/GalleryDetailParser.java | 83 ++--- .../java/com/hippo/ehviewer/data/Comment.java | 6 +- .../ehviewer/ui/scene/GalleryDetailScene.java | 152 +++++++++- .../ehviewer/widget/LinkifyTextView.java | 106 +++++++ .../miscellaneous/StartActivityHelper.java | 49 +++ .../java/com/hippo/util/URLImageGetter.java | 26 ++ .../java/com/hippo/widget/AutoWrapLayout.java | 286 ++++++++++++++++++ .../res/layout-v21/scene_gallery_detail.xml | 47 ++- app/src/main/res/layout/item_comment.xml | 46 +++ app/src/main/res/layout/tag.xml | 26 ++ app/src/main/res/layout/tag_group.xml | 26 ++ app/src/main/res/values/attrs.xml | 8 + app/src/main/res/values/strings.xml | 7 +- 15 files changed, 973 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/com/hippo/drawable/UnikeryDrawable.java create mode 100644 app/src/main/java/com/hippo/drawable/WrapDrawable.java create mode 100644 app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java create mode 100644 app/src/main/java/com/hippo/miscellaneous/StartActivityHelper.java create mode 100644 app/src/main/java/com/hippo/util/URLImageGetter.java create mode 100644 app/src/main/java/com/hippo/widget/AutoWrapLayout.java create mode 100644 app/src/main/res/layout/item_comment.xml create mode 100644 app/src/main/res/layout/tag.xml create mode 100644 app/src/main/res/layout/tag_group.xml diff --git a/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java b/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java new file mode 100644 index 000000000..d830116aa --- /dev/null +++ b/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java @@ -0,0 +1,53 @@ +package com.hippo.drawable; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.view.View; + +import com.hippo.conaco.Conaco; +import com.hippo.conaco.Unikery; +import com.hippo.ehviewer.R; + +public class UnikeryDrawable extends WrapDrawable implements Unikery { + + private int mTaskId; + + private View mView; + + public UnikeryDrawable(View view) { + mView = view; + } + + @Override + public void setBitmap(Bitmap bitmap, Conaco.Source source) { + setDrawable(new BitmapDrawable(mView.getResources(), bitmap)); + } + + @Override + public void setDrawable(Drawable drawable) { + super.setDrawable(drawable); + updateBounds(); + mView.requestLayout(); + } + + @Override + public void setTaskId(int id) { + mTaskId = id; + } + + @Override + public int getTaskId() { + return mTaskId; + } + + @Override + public void onFailure() { + setDrawable(mView.getResources().getDrawable(R.drawable.ic_sad)); + } + + @Override + public void onCancel() { + // Empty + } +} diff --git a/app/src/main/java/com/hippo/drawable/WrapDrawable.java b/app/src/main/java/com/hippo/drawable/WrapDrawable.java new file mode 100644 index 000000000..29cc49460 --- /dev/null +++ b/app/src/main/java/com/hippo/drawable/WrapDrawable.java @@ -0,0 +1,123 @@ +package com.hippo.drawable; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +public class WrapDrawable extends Drawable { + + private Drawable mDrawable; + + public void setDrawable(Drawable drawable) { + mDrawable = drawable; + } + + public void updateBounds() { + setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight()); + } + + @Override + public void draw(Canvas canvas) { + if (mDrawable != null) { + mDrawable.draw(canvas); + } + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + + if (mDrawable != null) { + mDrawable.setBounds(left, top, right, bottom); + } + } + + @Override + public void setBounds(Rect bounds) { + super.setBounds(bounds); + + if (mDrawable != null) { + mDrawable.setBounds(bounds); + } + } + + @Override + public void setChangingConfigurations(int configs) { + super.setChangingConfigurations(configs); + + if (mDrawable != null) { + mDrawable.setChangingConfigurations(configs); + } + } + + @Override + public int getChangingConfigurations() { + if (mDrawable != null) { + return mDrawable.getChangingConfigurations(); + } else { + return super.getChangingConfigurations(); + } + } + + @Override + public void setDither(boolean dither) { + super.setDither(dither); + + if (mDrawable != null) { + mDrawable.setDither(dither); + } + } + + @Override + public void setFilterBitmap(boolean filter) { + super.setFilterBitmap(filter); + + if (mDrawable != null) { + mDrawable.setFilterBitmap(filter); + } + } + + + @Override + public void setAlpha(int alpha) { + if (mDrawable != null) { + mDrawable.setAlpha(alpha); + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + if (mDrawable != null) { + mDrawable.setColorFilter(cf); + } + } + + @Override + public int getOpacity() { + if (mDrawable != null) { + return mDrawable.getOpacity(); + } else { + return PixelFormat.UNKNOWN; + } + } + + @Override + public int getIntrinsicWidth() { + if (mDrawable != null) { + return mDrawable.getIntrinsicWidth(); + } else { + return super.getIntrinsicWidth(); + } + } + + @Override + public int getIntrinsicHeight() { + if (mDrawable != null) { + return mDrawable.getIntrinsicHeight(); + } else { + return super.getIntrinsicHeight(); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java b/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java index 510a987ed..9220f68b0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/GalleryDetailParser.java @@ -22,15 +22,28 @@ import com.hippo.ehviewer.util.EhUtils; import com.hippo.util.AssertUtils; import com.hippo.util.Utils; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Locale; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GalleryDetailParser { + private static final DateFormat WEB_COMMENT_DATA_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm z", Locale.US); + private static final DateFormat OUT_COMMENT_DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); + + static { + WEB_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + OUT_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } + 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 = @@ -209,6 +222,7 @@ public class GalleryDetailParser { TagGroup tagGroup = new TagGroup(); tagGroup.groupName = sortOut(m.group(1)); parseTagGroup(tagGroup, m.group(0)); + tags.add(tagGroup); } } @@ -232,7 +246,15 @@ public class GalleryDetailParser { 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))); + String webDateString = sortOut(m.group(1)); + Date date; + try { + date = WEB_COMMENT_DATA_FORMAT.parse(webDateString); + } catch (java.text.ParseException e) { + date = new Date(0l); + } + String outDateString = OUT_COMMENT_DATA_FORMAT.format(date); + comments.add(new Comment(outDateString, date, sortOut(m.group(2)), m.group(3))); } Collections.sort(comments, COMMENT_SORTER); } @@ -257,67 +279,10 @@ public class GalleryDetailParser { } 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; + return c1.date.compareTo(c2.date); } } } diff --git a/app/src/main/java/com/hippo/ehviewer/data/Comment.java b/app/src/main/java/com/hippo/ehviewer/data/Comment.java index 2ebc37243..5de74a7f7 100644 --- a/app/src/main/java/com/hippo/ehviewer/data/Comment.java +++ b/app/src/main/java/com/hippo/ehviewer/data/Comment.java @@ -16,13 +16,17 @@ package com.hippo.ehviewer.data; +import java.util.Date; + public class Comment { public String time; + public Date date; public String user; public String comment; - public Comment (String time, String user, String comment) { + public Comment (String time, Date date, String user, String comment) { this.time = time; + this.date = date; this.user = user; this.comment = comment; } 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 66e7d6129..6621c72f8 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 @@ -16,14 +16,19 @@ package com.hippo.ehviewer.ui.scene; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.Nullable; +import android.text.Html; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.TextSwitcher; import android.widget.TextView; import android.widget.ViewSwitcher; @@ -34,21 +39,31 @@ import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhImageKeyFactory; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhClient; +import com.hippo.ehviewer.data.Comment; import com.hippo.ehviewer.data.GalleryDetail; import com.hippo.ehviewer.data.GalleryInfo; +import com.hippo.ehviewer.data.TagGroup; import com.hippo.ehviewer.util.Config; import com.hippo.ehviewer.util.EhUtils; import com.hippo.ehviewer.widget.LoadImageView; import com.hippo.ehviewer.widget.RatingView; +import com.hippo.miscellaneous.StartActivityHelper; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; import com.hippo.scene.Scene; import com.hippo.util.Log; +import com.hippo.util.URLImageGetter; +import com.hippo.util.UiUtils; +import com.hippo.widget.AutoWrapLayout; + +import java.util.List; public class GalleryDetailScene extends Scene implements View.OnClickListener { public static final String KEY_GALLERY_INFO = "gallery_info"; + private FrameLayout mDetailFrameLayout; + private ViewGroup mHeader; LoadImageView mThumb; TextView mTitle; @@ -72,11 +87,16 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { private TextView mTorrent; private TextView mShare; private TextView mRate; + private LinearLayout mTag; + private LinearLayout mComment; + private TextView mCommentMore; private View mProgressBar; private ViewTransition mViewTransition; + private GalleryDetail mGalleryDetail; + private void handleAnnouncer(Announcer announcer) { GalleryInfo gi; @@ -100,13 +120,7 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { Context context = getStageActivity(); Resources resources = context.getResources(); - mViewTransition.showView(1); - - RippleSalon.addRipple(mRead, false); - RippleSalon.addRipple(mDownload, false); - - mRead.setOnClickListener(this); - mDownload.setOnClickListener(this); + mViewTransition.showView(1, false); mRatingText.setFactory(new RatingTextViewFactory()); @@ -125,6 +139,20 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { mTorrent.setCompoundDrawables(null, torrentDrawable, null, null); mShare.setCompoundDrawables(null, shareDrawable, null, null); mRate.setCompoundDrawables(null, rateDrawable, null, null); + + mRead.setOnClickListener(this); + mDownload.setOnClickListener(this); + mFavorite.setOnClickListener(this); + mTorrent.setOnClickListener(this); + mShare.setOnClickListener(this); + mRate.setOnClickListener(this); + + RippleSalon.addRipple(mRead, false); + RippleSalon.addRipple(mDownload, false); + RippleSalon.addRipple(mFavorite, false); + RippleSalon.addRipple(mTorrent, false); + RippleSalon.addRipple(mShare, false); + RippleSalon.addRipple(mRate, false); } @Override @@ -132,6 +160,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { super.onCreate(savedInstanceState); setContentView(R.layout.scene_gallery_detail); + mDetailFrameLayout = (FrameLayout) findViewById(R.id.detail_frame_layout); + mHeader = (ViewGroup) findViewById(R.id.header); mThumb = (LoadImageView) mHeader.findViewById(R.id.thumb); mTitle = (TextView) mHeader.findViewById(R.id.title); @@ -155,6 +185,9 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { mTorrent = (TextView) mContent.findViewById(R.id.torrent); mShare = (TextView) mContent.findViewById(R.id.share); mRate = (TextView) mContent.findViewById(R.id.rate); + mTag = (LinearLayout) mContent.findViewById(R.id.tag); + mComment = (LinearLayout) mContent.findViewById(R.id.comment); + mCommentMore = (TextView) mContent.findViewById(R.id.comment_more); mProgressBar = findViewById(R.id.progress_bar); @@ -163,18 +196,35 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { handleAnnouncer(getAnnouncer()); } + @Override + protected void onGetFitPaddingBottom(int b) { + mDetailFrameLayout.setPadding(0, 0, 0, b); + } + @Override public void onClick(View v) { + GalleryDetail gd = mGalleryDetail; + if (gd == null) { + return; + } + if (v == mRead) { } else if (v == mDownload) { + } else if (v == mFavorite) { + + } else if (v == mTorrent) { + + } else if (v == mShare) { + StartActivityHelper.share(getStageActivity(), + EhClient.getDetailUrl(Config.getEhSource(), gd.gid, gd.token, 0)); } } private String getRatingText(float rating) { String undefine = "(´_ゝ`)"; - if (rating == Float.NaN) { + if (Float.isNaN(rating)) { return undefine; } @@ -216,6 +266,72 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { " (" + rating + ", " + ratedTimes + ")"; } + private void fillTags(List tags) { + Context context = getStageActivity(); + LayoutInflater inflater = getStageActivity().getLayoutInflater(); + int x = UiUtils.dp2pix(context, 2); + int y = UiUtils.dp2pix(context, 4); + + for (TagGroup tagGroup : tags) { + LinearLayout tagGroupLayout = new LinearLayout(context); + tagGroupLayout.setOrientation(LinearLayout.HORIZONTAL); + AutoWrapLayout tagLayout = new AutoWrapLayout(context); + + // Group name + @SuppressLint("InflateParams") + TextView groupNameView = (TextView) inflater.inflate(R.layout.tag_group, null); + groupNameView.setText(tagGroup.groupName); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + lp.setMargins(x, y, x, y); + tagGroupLayout.addView(groupNameView, lp); + + int count = tagGroup.getTagCount(); + for (int i = 0; i < count; i++) { + @SuppressLint("InflateParams") + TextView tagView = (TextView) inflater.inflate(R.layout.tag, null); + tagView.setText(tagGroup.getTagAt(i)); + /* + tagView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(GalleryDetailActivity.this, + GalleryListActivity.class); + intent.setAction(GalleryListActivity.ACTION_GALLERY_LIST); + intent.putExtra(GalleryListActivity.KEY_MODE, + ListUrls.MODE_TAG); + intent.putExtra(GalleryListActivity.KEY_TAG, groupName + ":" + tag); + intent.setAction(GalleryListActivity.ACTION_GALLERY_LIST); + startActivity(intent); + } + }); + */ + AutoWrapLayout.LayoutParams alp = new AutoWrapLayout.LayoutParams(); + alp.setMargins(x, y, x, y); + tagLayout.addView(tagView, alp); + } + tagGroupLayout.addView(tagLayout); + mTag.addView(tagGroupLayout); + } + } + + private View createCommentView(Comment comment) { + View view = LayoutInflater.from(getStageActivity()).inflate(R.layout.item_comment, null); + ((TextView) view.findViewById(R.id.user)).setText(comment.user); + ((TextView) view.findViewById(R.id.time)).setText(comment.time); + TextView commentText = (TextView) view.findViewById(R.id.comment); + commentText.setMaxLines(3); + commentText.setAutoLinkMask(0); + + + URLImageGetter p = new URLImageGetter( + commentText, EhApplication.getConaco(getStageActivity())); + commentText.setText(Html.fromHtml(comment.comment, p, null)); + + return view; + } + private class RatingTextViewFactory implements ViewSwitcher.ViewFactory { @Override public View makeView() { @@ -229,6 +345,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { @Override public void onSuccess(GalleryDetail galleryDetail) { + mGalleryDetail = galleryDetail; + Resources resources = getStageActivity().getResources(); mLanguage.setText(galleryDetail.language); mPages.setText(String.format(resources.getString(R.string.page_count), @@ -240,12 +358,30 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener { galleryDetail.favoredTimes)); mRating.setRating(galleryDetail.rating); mRatingText.setText(getAllRatingText(galleryDetail.rating, galleryDetail.ratedTimes)); + fillTags(galleryDetail.tags); + + int commentNum = galleryDetail.comments.size(); + if (commentNum == 0) { + mCommentMore.setText("暂无评论"); + } else if (commentNum <= 2){ + mCommentMore.setText("暂无更多评论"); + } else { + mCommentMore.setText("更多评论"); + } + int maxShown = Math.min(2, commentNum); + for (int i = 0; i < maxShown; i++) { + mComment.addView(createCommentView(galleryDetail.comments.get(i)), i, + new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + } + mViewTransition.showView(0); } @Override public void onFailure(Exception e) { e.printStackTrace(); + // TODO } } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java b/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java new file mode 100644 index 000000000..186b8a76d --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/widget/LinkifyTextView.java @@ -0,0 +1,106 @@ +/* + * 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.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.annotation.NonNull; +import android.text.Layout; +import android.text.Spannable; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.TextView; + +public class LinkifyTextView extends TextView { + + private String mUrl; + + public LinkifyTextView(Context context) { + super(context); + init(); + } + + public LinkifyTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public LinkifyTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + public void init() { + // Just make urls striking + // LinkifyTextView itself should not handle touch url event + setAutoLinkMask(Linkify.WEB_URLS); + setLinksClickable(false); + } + + public String getTouchedUrl() { + return mUrl; + } + + /** + * Call it when you do not need the url any more + */ + public void clearTouchedUrl() { + mUrl = null; + } + + @Override + @SuppressLint("ClickableViewAccessibility") + public boolean onTouchEvent(@NonNull MotionEvent event) { + if (getText() instanceof Spannable) { + // Get this code from android.text.method.LinkMovementMethod. + // Work fine ! + int x = (int) event.getX(); + int y = (int) event.getY(); + boolean isGetUrl = false; + + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); + + x += getScrollX(); + y += getScrollY(); + + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] links = ((Spannable)getText()).getSpans(off, off, ClickableSpan.class); + + for (ClickableSpan link : links) { + if (link instanceof URLSpan) { + mUrl = ((URLSpan)link).getURL(); + isGetUrl = true; + break; + } + } + if (!isGetUrl) { + mUrl = null; + } + } else { + mUrl = null; + } + + return super.onTouchEvent(event); + } +} diff --git a/app/src/main/java/com/hippo/miscellaneous/StartActivityHelper.java b/app/src/main/java/com/hippo/miscellaneous/StartActivityHelper.java new file mode 100644 index 000000000..373aee194 --- /dev/null +++ b/app/src/main/java/com/hippo/miscellaneous/StartActivityHelper.java @@ -0,0 +1,49 @@ +package com.hippo.miscellaneous; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.widget.Toast; + +import com.hippo.ehviewer.R; + +public class StartActivityHelper { + + public static boolean sendEmail(@NonNull Activity from, @NonNull String address, + @Nullable String subject, @Nullable String text) { + Intent i = new Intent(Intent.ACTION_SENDTO); + i.setData(Uri.parse("mailto:" + address)); + if (subject != null) { + i.putExtra(Intent.EXTRA_SUBJECT, subject); + } + if (text != null) { + i.putExtra(Intent.EXTRA_TEXT, text); + } + + try { + from.startActivity(i); + return true; + } catch (ActivityNotFoundException e) { + Toast.makeText(from, R.string.em_cant_find_activity, Toast.LENGTH_SHORT).show(); + return false; + } + } + + public static boolean share(@NonNull Activity from, String text) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, text); + sendIntent.setType("text/plain"); + + try { + from.startActivity(sendIntent); + return true; + } catch (ActivityNotFoundException e) { + Toast.makeText(from, R.string.em_cant_find_activity, Toast.LENGTH_SHORT).show(); + return false; + } + } +} diff --git a/app/src/main/java/com/hippo/util/URLImageGetter.java b/app/src/main/java/com/hippo/util/URLImageGetter.java new file mode 100644 index 000000000..cfe74e8c4 --- /dev/null +++ b/app/src/main/java/com/hippo/util/URLImageGetter.java @@ -0,0 +1,26 @@ +package com.hippo.util; + +import android.graphics.drawable.Drawable; +import android.text.Html; +import android.widget.TextView; + +import com.hippo.conaco.Conaco; +import com.hippo.drawable.UnikeryDrawable; + +public class URLImageGetter implements Html.ImageGetter { + + private TextView mTextView; + private Conaco mConaco; + + public URLImageGetter(TextView view, Conaco conaco) { + mTextView = view; + mConaco = conaco; + } + + @Override + public Drawable getDrawable(String source) { + UnikeryDrawable drawable = new UnikeryDrawable(mTextView); + mConaco.load(drawable, source, source); + return drawable; + } +} diff --git a/app/src/main/java/com/hippo/widget/AutoWrapLayout.java b/app/src/main/java/com/hippo/widget/AutoWrapLayout.java new file mode 100644 index 000000000..a4a1fa944 --- /dev/null +++ b/app/src/main/java/com/hippo/widget/AutoWrapLayout.java @@ -0,0 +1,286 @@ +/* + * 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.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.hippo.ehviewer.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A ViewGroup that can layout views in line and auto wrap + * + * @author Hippo + * + */ +public class AutoWrapLayout extends ViewGroup { + + private final static String TAG = "AutoWrapLayout"; + + private final List rectList = new ArrayList<>(); + + private Alignment mAlignment; + + private static final Alignment[] sBaseLineArray = { Alignment.TOP, + Alignment.CENTER, Alignment.BOTTOM }; + + + public enum Alignment { + TOP(0), + CENTER(1), + BOTTOM(2); + + Alignment(int ni) { + nativeInt = ni; + } + + final int nativeInt; + } + + public AutoWrapLayout(Context context) { + super(context); + } + public AutoWrapLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public AutoWrapLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.AutoWrapLayout, defStyle, 0); + + int index = a.getInt(R.styleable.AutoWrapLayout_alignment, -1); + if (index >= 0) { + setAlignment(sBaseLineArray[index]); + } + + a.recycle(); + } + + public void setAlignment(Alignment baseLine) { + if (baseLine == null) { + return; + } + + if (mAlignment != baseLine) { + mAlignment = baseLine; + + requestLayout(); + invalidate(); + } + } + + public Alignment getAlignment() { + return mAlignment; + } + + private void adjustBaseLine(int lineHeight, int startIndex, int endIndex) { + if (mAlignment == Alignment.TOP) + return; + + for (int index = startIndex; index < endIndex; index++) { + final View child = getChildAt(index); + final LayoutParams lp = + (LayoutParams)child.getLayoutParams(); + Rect rect = rectList.get(index); + int offsetRaw = lineHeight - rect.height() - lp.topMargin - lp.bottomMargin; + if (mAlignment == Alignment.CENTER) + rect.offset(0, offsetRaw/2); + else if (mAlignment == Alignment.BOTTOM) + rect.offset(0, offsetRaw); + } + } + + + // TODO Take vertical mode + /** + * each row or line at least show one child + * + * horizontal only show child can show or partly show in parent + */ + @SuppressLint("DrawAllocation") + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + int maxWidth = MeasureSpec.getSize(widthMeasureSpec); + int maxHeight = MeasureSpec.getSize(heightMeasureSpec); + + if (widthMode == MeasureSpec.UNSPECIFIED) + maxWidth = Integer.MAX_VALUE; + if (heightMode == MeasureSpec.UNSPECIFIED) + maxHeight = Integer.MAX_VALUE; + + int paddingLeft = getPaddingLeft(); + int paddingTop = getPaddingTop(); + int paddingRight = getPaddingRight(); + int paddingBottom = getPaddingBottom(); + + int maxRightBound = maxWidth - paddingRight; + int maxBottomBound = maxHeight - paddingBottom; + + int left; + int top; + int right; + int bottom; + int rightBound = paddingLeft; + int maxRightNoPadding = rightBound; + int bottomBound; + int lastMaxBottom = paddingTop; + int maxBottom = lastMaxBottom; + int childWidth; + int childHeight; + + int lineStartIndex = 0; + int lineEndIndex = 0; // endIndex + 1 + + rectList.clear(); + int childCount = getChildCount(); + for (int index = 0; index < childCount; index++) { + final View child = getChildAt(index); + child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + if (child.getVisibility() == View.GONE) + continue; + final LayoutParams lp = + (LayoutParams)child.getLayoutParams(); + childWidth = child.getMeasuredWidth(); + childHeight = child.getMeasuredHeight(); + + left = rightBound + lp.leftMargin; + right = left + childWidth; + rightBound = right + lp.rightMargin; + if (rightBound > maxRightBound) { // Go to next row + lineEndIndex = index; + // Adjust child position base on baseline + adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, lineEndIndex); + + // If child can't show in parent begin this line + if (maxBottom >= maxBottomBound) + break; + + // If it is first item in line, try to show it all + if (lineEndIndex == lineStartIndex) { + child.measure(MeasureSpec.makeMeasureSpec( + maxWidth - paddingLeft - paddingRight + - lp.leftMargin - lp.rightMargin, MeasureSpec.AT_MOST), + MeasureSpec.UNSPECIFIED); + childWidth = child.getMeasuredWidth(); + childHeight = child.getMeasuredHeight(); + } + left = paddingLeft + lp.leftMargin; + right = left + childWidth; + rightBound = right + lp.rightMargin; + + lastMaxBottom = maxBottom; + top = lastMaxBottom + lp.topMargin; + bottom = top + childHeight; + bottomBound = bottom + lp.bottomMargin; + + lineStartIndex = index; + } else { + top = lastMaxBottom + lp.topMargin; + bottom = top + childHeight; + bottomBound = bottom + lp.bottomMargin; + } + // Update max + if (rightBound > maxRightNoPadding) + maxRightNoPadding = rightBound; + if (bottomBound > maxBottom) + maxBottom = bottomBound; + Rect rect = new Rect(); + rect.left = left; + rect.top = top; + rect.right = right; + rect.bottom = bottom; + rectList.add(rect); + } + + // Handle last line baseline + adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, rectList.size()); + + int measuredWidth; + int measuredHeight; + + if (widthMode == MeasureSpec.EXACTLY) + measuredWidth = maxWidth; + else + measuredWidth = maxRightNoPadding + paddingRight; + if (heightMode == MeasureSpec.EXACTLY) + measuredHeight = maxHeight; + else { + measuredHeight = maxBottom + paddingBottom; + if (heightMode == MeasureSpec.AT_MOST) + measuredHeight = measuredHeight > maxHeight ? maxHeight : measuredHeight; + } + + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int count = rectList.size(); + for(int i = 0; i < count; i++){ + final View child = this.getChildAt(i); + if (child.getVisibility() == View.GONE) + continue; + Rect rect = rectList.get(i); + child.layout(rect.left, rect.top, rect.right, rect.bottom); + } + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + public static class LayoutParams extends MarginLayoutParams { + public LayoutParams() { + this(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + public LayoutParams(int width, int height) { + super(width, height); + } + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + public LayoutParams(MarginLayoutParams source) { + super(source); + } + } +} diff --git a/app/src/main/res/layout-v21/scene_gallery_detail.xml b/app/src/main/res/layout-v21/scene_gallery_detail.xml index 80cf7e544..d408b1898 100644 --- a/app/src/main/res/layout-v21/scene_gallery_detail.xml +++ b/app/src/main/res/layout-v21/scene_gallery_detail.xml @@ -26,6 +26,7 @@ android:layout_height="match_parent"> @@ -104,7 +105,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" - android:text="更多信息" + android:text="@string/more_information" android:textColor="@color/theme_accent"/> + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_comment.xml b/app/src/main/res/layout/item_comment.xml new file mode 100644 index 000000000..a7755f226 --- /dev/null +++ b/app/src/main/res/layout/item_comment.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/tag.xml b/app/src/main/res/layout/tag.xml new file mode 100644 index 000000000..75b8838b0 --- /dev/null +++ b/app/src/main/res/layout/tag.xml @@ -0,0 +1,26 @@ + + + diff --git a/app/src/main/res/layout/tag_group.xml b/app/src/main/res/layout/tag_group.xml new file mode 100644 index 000000000..8a3498c3b --- /dev/null +++ b/app/src/main/res/layout/tag_group.xml @@ -0,0 +1,26 @@ + + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index ee3924880..476bd5882 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -63,4 +63,12 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a575cfd44..95f95c2d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,10 +80,11 @@ %d pages %d \u2665 - We feel %s + More information + It is %s - MASTERPIECE + a MASTERPIECE AMAZING GREAT GOOD @@ -104,6 +105,8 @@ Weird Network available ? + Can\'t find target Activity + Display