diff --git a/Chan/src/org/floens/chan/adapter/PostAdapter.java b/Chan/src/org/floens/chan/adapter/PostAdapter.java index df09c471..f9edfeea 100644 --- a/Chan/src/org/floens/chan/adapter/PostAdapter.java +++ b/Chan/src/org/floens/chan/adapter/PostAdapter.java @@ -27,9 +27,9 @@ public class PostAdapter extends BaseAdapter { private boolean endOfLine; private int count = 0; private final List postList = new ArrayList(); - + public PostAdapter(Context activity, ThreadManager threadManager, ListView listView) { - this.context = activity; + context = activity; this.threadManager = threadManager; this.listView = listView; } @@ -52,33 +52,33 @@ public class PostAdapter extends BaseAdapter { public long getItemId(int position) { return position; } - + @Override public View getView(int position, View convertView, ViewGroup parent) { if (position >= getCount() - 1 && !endOfLine && threadManager.getLoadable().isBoardMode()) { // Try to load more posts threadManager.requestNextData(); } - + if (position >= count) { return createThreadEndView(); } else { PostView postView = null; - + if (position >= 0 && position < postList.size()) { if (convertView != null && convertView instanceof PostView) { postView = (PostView) convertView; } else { postView = new PostView(context); } - + postView.setPost(postList.get(position), threadManager); } - + return postView; } } - + private View createThreadEndView() { if (threadManager.getLoadable().isThreadMode()) { ThreadWatchCounterView view = new ThreadWatchCounterView(context); @@ -102,37 +102,38 @@ public class PostAdapter extends BaseAdapter { } } } - + public void addList(List list) { postList.addAll(list); count = postList.size(); - + notifyDataSetChanged(); } - + public void setList(List list) { postList.clear(); postList.addAll(list); count = postList.size(); - + notifyDataSetChanged(); } - + public List getList() { return postList; } - + public void setEndOfLine(boolean endOfLine) { this.endOfLine = endOfLine; - + notifyDataSetChanged(); } - + public void scrollToPost(Post post) { for (int i = 0; i < postList.size(); i++) { - if (postList.get(i).no == post.no) { - listView.smoothScrollToPosition(i); - + if (postList.get(i).no == post.no) { +// listView.smoothScrollToPosition(i); does not work when a view is taller than the container + listView.setSelection(i); + break; } } diff --git a/Chan/src/org/floens/chan/loader/LoaderPool.java b/Chan/src/org/floens/chan/loader/LoaderPool.java index c90174db..907b02e3 100644 --- a/Chan/src/org/floens/chan/loader/LoaderPool.java +++ b/Chan/src/org/floens/chan/loader/LoaderPool.java @@ -6,37 +6,37 @@ import java.util.Map; import org.floens.chan.model.Loadable; public class LoaderPool { - private static final String TAG = "LoaderPool"; - +// private static final String TAG = "LoaderPool"; + private static LoaderPool instance; - + private static Map loaders = new HashMap(); - + public static LoaderPool getInstance() { if (instance == null) { instance = new LoaderPool(); } - + return instance; } - + public Loader obtain(Loadable loadable, Loader.LoaderListener listener) { Loader loader = loaders.get(loadable); if (loader == null) { loader = new Loader(loadable); loaders.put(loadable, loader); } - + loader.addListener(listener); - + return loader; } - + public void release(Loader loader, Loader.LoaderListener listener) { if (!loaders.containsValue(loader)) { throw new RuntimeException("The released loader does not exist"); } - + if (loader.removeListener(listener)) { loaders.remove(loader.getLoadable()); } diff --git a/Chan/src/org/floens/chan/model/Post.java b/Chan/src/org/floens/chan/model/Post.java index 8caaf7ef..79829b41 100644 --- a/Chan/src/org/floens/chan/model/Post.java +++ b/Chan/src/org/floens/chan/model/Post.java @@ -17,10 +17,12 @@ import org.jsoup.parser.Parser; import android.graphics.Color; import android.text.SpannableString; import android.text.TextUtils; +import android.text.style.AbsoluteSizeSpan; +import android.text.style.BackgroundColorSpan; import android.text.style.ForegroundColorSpan; /** - * Contains all data needer to represent a single post. + * Contains all data needed to represent a single post. */ public class Post { public String board; @@ -50,121 +52,174 @@ public class Post { public long time = 0; public String email = ""; public boolean isSavedReply = false; - + /** * This post replies to the these ids */ public List repliesTo = new ArrayList(); - + /** * These ids replied to this post */ public List repliesFrom = new ArrayList(); - + public final ArrayList linkables = new ArrayList(); /** * The PostView the Post is currently bound to. */ - + + public SpannableString subjectSpan; + public SpannableString nameSpan; + public SpannableString tripcodeSpan; + public SpannableString idSpan; + public SpannableString capcodeSpan; + private PostView linkableListener; private String rawComment; - + public Post() { } - + public void setComment(String e) { rawComment = e; } - + public void setLinkableListener(PostView listener) { linkableListener = listener; } - + public PostView getLinkableListener() { return linkableListener; } - + /** * Finish up the data + * * @return false if this data is invalid */ public boolean finish(Loadable loadable) { - if (board == null) return false; - - if (no < 0 || resto < 0 || date == null) return false; - + if (board == null) + return false; + + if (no < 0 || resto < 0 || date == null) + return false; + isOP = resto == 0; - - if (isOP && replies < 0) return false; - - if (ext != null) hasImage = true; - + + if (isOP && replies < 0) + return false; + + if (ext != null) { + hasImage = true; + } + if (hasImage) { - if (filename == null || tim == null || ext == null || imageWidth <= 0 || imageHeight <= 0) return false; - + if (filename == null || tim == null || ext == null || imageWidth <= 0 || imageHeight <= 0) + return false; + thumbnailUrl = ChanUrls.getThumbnailUrl(board, tim); imageUrl = ChanUrls.getImageUrl(board, tim, ext); } - + if (rawComment != null) { comment = parseComment(rawComment, loadable.simpleMode); } - + try { if (!TextUtils.isEmpty(name)) { name = Parser.unescapeEntities(name, false); } - + if (!TextUtils.isEmpty(subject)) { subject = Parser.unescapeEntities(subject, false); } - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); } - + + parseSpans(); + return true; } - + + private void parseSpans() { + if (!TextUtils.isEmpty(subject)) { + subjectSpan = new SpannableString(subject); + subjectSpan.setSpan(new ForegroundColorSpan(Color.argb(255, 15, 12, 93)), 0, subjectSpan.length(), 0); + } + + if (!TextUtils.isEmpty(name)) { + nameSpan = new SpannableString(name); + nameSpan.setSpan(new ForegroundColorSpan(Color.argb(255, 17, 119, 67)), 0, nameSpan.length(), 0); + } + + if (!TextUtils.isEmpty(tripcode)) { + tripcodeSpan = new SpannableString(tripcode); + tripcodeSpan.setSpan(new ForegroundColorSpan(Color.argb(255, 17, 119, 67)), 0, tripcodeSpan.length(), 0); + tripcodeSpan.setSpan(new AbsoluteSizeSpan(10, true), 0, tripcodeSpan.length(), 0); + } + + if (!TextUtils.isEmpty(id)) { + idSpan = new SpannableString(" ID: " + id + " "); + + // Stolen from the 4chan extension + int hash = id.hashCode(); + + int r = (hash >> 24) & 0xff; + int g = (hash >> 16) & 0xff; + int b = (hash >> 8) & 0xff; + + int idColor = (0xff << 24) + (r << 16) + (g << 8) + b; + int idBgColor = ((r * 0.299f) + (g * 0.587f) + (b * 0.114f)) > 125f ? 0xff636363 : 0x00000000; + + idSpan.setSpan(new ForegroundColorSpan(idColor), 0, idSpan.length(), 0); + idSpan.setSpan(new BackgroundColorSpan(idBgColor), 0, idSpan.length(), 0); + idSpan.setSpan(new AbsoluteSizeSpan(10, true), 0, idSpan.length(), 0); + } + + if (!TextUtils.isEmpty(capcode)) { + capcodeSpan = new SpannableString("Capcode: " + capcode); + capcodeSpan.setSpan(new ForegroundColorSpan(Color.argb(255, 255, 0, 0)), 0, capcodeSpan.length(), 0); + capcodeSpan.setSpan(new AbsoluteSizeSpan(10, true), 0, capcodeSpan.length(), 0); + } + } + private CharSequence parseComment(String commentRaw, boolean simpleMode) { - if (simpleMode) { + if (simpleMode) return ""; - } - + CharSequence total = new SpannableString(""); - + try { String comment = commentRaw.replace("", ""); - + Document document = Jsoup.parseBodyFragment(comment); - + List nodes = document.body().childNodes(); - + for (Node node : nodes) { String nodeName = node.nodeName(); - + if (node instanceof TextNode) { - String text = ((TextNode)node).text(); - - // Find url's in the text node + String text = ((TextNode) node).text(); + + // Find url's in the text node if (text.contains("://")) { String[] parts = text.split("\\s"); - + for (String item : parts) { if (item.contains("://")) { try { URL url = new URL(item); - + SpannableString link = new SpannableString(url.toString()); -// link.setSpan(new ForegroundColorSpan(Color.argb(255, 0, 0, 180)), 0, link.length(), 0); - -// linkables.add(new PostLinkable(this, item, item, PostLinkable.Type.LINK)); - - PostLinkable pl = new PostLinkable(this, item, item, PostLinkable.Type.LINK); + + PostLinkable pl = new PostLinkable(this, item, item, PostLinkable.Type.LINK); link.setSpan(pl, 0, link.length(), 0); linkables.add(pl); - + total = TextUtils.concat(total, link, " "); - } catch(Exception e) { + } catch (Exception e) { total = TextUtils.concat(total, item, " "); } } @@ -174,23 +229,23 @@ public class Post { } } else if (nodeName.equals("br")) { total = TextUtils.concat(total, "\n"); - } else if (nodeName.equals("span")){ - Element span = (Element)node; - + } else if (nodeName.equals("span")) { + Element span = (Element) node; + SpannableString quote = new SpannableString(span.text()); quote.setSpan(new ForegroundColorSpan(Color.argb(255, 120, 153, 34)), 0, quote.length(), 0); - + total = TextUtils.concat(total, quote); } else if (nodeName.equals("a")) { - Element anchor = (Element)node; - + Element anchor = (Element) node; + SpannableString link = new SpannableString(anchor.text()); - + Type t = anchor.text().contains("://") ? Type.LINK : Type.QUOTE; PostLinkable pl = new PostLinkable(this, anchor.text(), anchor.attr("href"), t); link.setSpan(pl, 0, link.length(), 0); linkables.add(pl); - + if (t == Type.QUOTE) { try { // Get post id @@ -199,26 +254,22 @@ public class Post { int id = Integer.parseInt(splitted[1]); repliesTo.add(id); } - } catch(NumberFormatException e) {} + } catch (NumberFormatException e) { + } } - + total = TextUtils.concat(total, link); } else { // Unknown tag, add the inner part if (node instanceof Element) { - total = TextUtils.concat(total, ((Element)node).text()); + total = TextUtils.concat(total, ((Element) node).text()); } } } } catch (Exception e) { e.printStackTrace(); } - + return total; } } - - - - - diff --git a/Chan/src/org/floens/chan/utils/Utils.java b/Chan/src/org/floens/chan/utils/Utils.java index d46f0cc3..06007256 100644 --- a/Chan/src/org/floens/chan/utils/Utils.java +++ b/Chan/src/org/floens/chan/utils/Utils.java @@ -1,5 +1,6 @@ package org.floens.chan.utils; +import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -13,19 +14,24 @@ public class Utils { */ @SuppressWarnings("deprecation") public static void setPressedDrawable(View view) { - TypedArray arr = view.getContext().obtainStyledAttributes( + Drawable drawable = Utils.getSelectableBackgroundDrawable(view.getContext()); + view.setBackgroundDrawable(drawable); + } + + public static Drawable getSelectableBackgroundDrawable(Context context) { + TypedArray arr = context.obtainStyledAttributes( new int[] {android.R.attr.selectableItemBackground}); - + Drawable drawable = arr.getDrawable(0); - - view.setBackgroundDrawable(drawable); - + arr.recycle(); + + return drawable; } - + /** - * Causes the runnable to be added to the message queue. - * The runnable will be run on the ui thread. + * Causes the runnable to be added to the message queue. + * The runnable will be run on the ui thread. * @param runnable */ public static void runOnUiThread(Runnable runnable) { diff --git a/Chan/src/org/floens/chan/view/PostView.java b/Chan/src/org/floens/chan/view/PostView.java index a9285b41..68e70de0 100644 --- a/Chan/src/org/floens/chan/view/PostView.java +++ b/Chan/src/org/floens/chan/view/PostView.java @@ -13,14 +13,18 @@ import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; +import android.text.Layout; +import android.text.Selection; +import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.method.LinkMovementMethod; import android.text.style.AbsoluteSizeSpan; -import android.text.style.BackgroundColorSpan; +import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -33,14 +37,15 @@ public class PostView extends LinearLayout implements View.OnClickListener, View private final static LinearLayout.LayoutParams wrapParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); private final static LinearLayout.LayoutParams matchWrapParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); private final static LinearLayout.LayoutParams wrapMatchParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - + private final Activity context; - + private ThreadManager manager; private Post post; - + private boolean isBuild = false; private LinearLayout full; + private LinearLayout right; private NetworkImageView imageView; private TextView titleView; private TextView commentView; @@ -48,7 +53,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View private LinearLayout iconView; private ImageView stickyView; private NetworkImageView countryView; - + /** * Represents a post. * Use setPost(Post ThreadManager) to fill it with data. @@ -58,36 +63,33 @@ public class PostView extends LinearLayout implements View.OnClickListener, View public PostView(Context activity) { super(activity); context = (Activity) activity; - init(); } - + public PostView(Context activity, AttributeSet attbs) { super(activity, attbs); context = (Activity) activity; - init(); } - + public PostView(Context activity, AttributeSet attbs, int style) { super(activity, attbs, style); context = (Activity) activity; - init(); } - + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - + if (post != null) { post.setLinkableListener(null); } } - + public void setPost(final Post post, final ThreadManager manager) { this.post = post; this.manager = manager; - + buildView(context); - + if (post.hasImage) { imageView.setVisibility(View.VISIBLE); imageView.setImageUrl(post.thumbnailUrl, ChanApplication.getImageLoader()); @@ -95,76 +97,50 @@ public class PostView extends LinearLayout implements View.OnClickListener, View imageView.setVisibility(View.GONE); imageView.setImageUrl(null, null); } - + CharSequence total = new SpannableString(""); - - if (!TextUtils.isEmpty(post.subject)) { - SpannableString subject = new SpannableString(post.subject); - subject.setSpan(new ForegroundColorSpan(Color.argb(255, 15, 12, 93)), 0, subject.length(), 0); - total = TextUtils.concat(total, subject, "\n"); + + if (post.subjectSpan != null) { + total = TextUtils.concat(total, post.subjectSpan, "\n"); } - - if (!TextUtils.isEmpty(post.name)) { - SpannableString name = new SpannableString(post.name); - name.setSpan(new ForegroundColorSpan(Color.argb(255, 17, 119, 67)), 0, name.length(), 0); - total = TextUtils.concat(total, name, " "); + + if (post.nameSpan != null) { + total = TextUtils.concat(total, post.nameSpan, " "); } - - if (!TextUtils.isEmpty(post.tripcode)) { - SpannableString tripcode = new SpannableString(post.tripcode); - tripcode.setSpan(new ForegroundColorSpan(Color.argb(255, 17, 119, 67)), 0, tripcode.length(), 0); - tripcode.setSpan(new AbsoluteSizeSpan(10, true), 0, tripcode.length(), 0); - total = TextUtils.concat(total, tripcode, " "); + + if (post.tripcodeSpan != null) { + total = TextUtils.concat(total, post.tripcodeSpan, " "); } - - if (!TextUtils.isEmpty(post.id)) { - SpannableString id = new SpannableString(" ID: " + post.id + " "); - IDColor c = computeIDColor(post.id); - id.setSpan(new ForegroundColorSpan(c.color), 0, id.length(), 0); - id.setSpan(new BackgroundColorSpan(c.backgroundColor), 0, id.length(), 0); - id.setSpan(new AbsoluteSizeSpan(10, true), 0, id.length(), 0); - total = TextUtils.concat(total, id, " "); + + if (post.idSpan != null) { + total = TextUtils.concat(total, post.idSpan, " "); } - - if (!TextUtils.isEmpty(post.capcode)) { - SpannableString tripcode = new SpannableString("Capcode: " + post.capcode); - tripcode.setSpan(new ForegroundColorSpan(Color.argb(255, 255, 0, 0)), 0, tripcode.length(), 0); - tripcode.setSpan(new AbsoluteSizeSpan(10, true), 0, tripcode.length(), 0); - total = TextUtils.concat(total, tripcode, " "); + + if (post.capcodeSpan != null) { + total = TextUtils.concat(total, post.capcodeSpan, " "); } - - CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(post.time * 1000L, System.currentTimeMillis(), + + CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(post.time * 1000L, System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS, 0); - + SpannableString date = new SpannableString("No." + post.no + " " + relativeTime); date.setSpan(new ForegroundColorSpan(Color.argb(255, 100, 100, 100)), 0, date.length(), 0); date.setSpan(new AbsoluteSizeSpan(10, true), 0, date.length(), 0); total = TextUtils.concat(total, date, " "); - + titleView.setText(total); - + if (!TextUtils.isEmpty(post.comment)) { commentView.setVisibility(View.VISIBLE); commentView.setText(post.comment); - commentView.setMovementMethod(LinkMovementMethod.getInstance()); - commentView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - PostView.this.onClick(v); - } - }); - - commentView.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - return PostView.this.onLongClick(v); - } - }); - + commentView.setMovementMethod(new PostViewMovementMethod()); + commentView.setOnClickListener(this); + commentView.setOnLongClickListener(this); + if (manager.getLoadable().isThreadMode()) { post.setLinkableListener(this); } - + if (manager.getLoadable().isBoardMode()) { int maxHeight = context.getResources().getDimensionPixelSize(R.dimen.post_max_height); commentView.setMaxHeight(maxHeight); @@ -178,20 +154,20 @@ public class PostView extends LinearLayout implements View.OnClickListener, View commentView.setOnLongClickListener(null); post.setLinkableListener(null); } - + if ((post.isOP && manager.getLoadable().isBoardMode() && post.replies > 0) || (post.repliesFrom.size() > 0)) { repliesCountView.setVisibility(View.VISIBLE); - + String text = ""; - + int count = manager.getLoadable().isBoardMode() ? post.replies : post.repliesFrom.size(); - + if (count > 1) { text = count + " " + context.getString(R.string.multiple_replies); } else if (count == 1) { text = count + " " + context.getString(R.string.one_reply); } - + if (manager.getLoadable().isThreadMode()) { repliesCountView.setOnClickListener(new View.OnClickListener() { @Override @@ -200,18 +176,18 @@ public class PostView extends LinearLayout implements View.OnClickListener, View } }); } - + repliesCountView.setText(text); } else { repliesCountView.setVisibility(View.GONE); repliesCountView.setOnClickListener(null); } - + boolean showCountryFlag = !TextUtils.isEmpty(post.country); boolean showStickyIcon = post.sticky; - + iconView.setVisibility((showCountryFlag || showStickyIcon) ? View.VISIBLE : View.GONE); - + stickyView.setVisibility(showStickyIcon ? View.VISIBLE : View.GONE); if (showCountryFlag) { countryView.setVisibility(View.VISIBLE); @@ -220,31 +196,22 @@ public class PostView extends LinearLayout implements View.OnClickListener, View countryView.setVisibility(View.GONE); countryView.setImageUrl(null, null); } - - if (post.isOP && manager.getLoadable().isBoardMode()) { - full.setClickable(true); - full.setFocusable(true); - full.setOnClickListener(this); - - Utils.setPressedDrawable(full); - } - + if (post.isSavedReply) { full.setBackgroundColor(0xFFD6BAD0); } else { full.setBackgroundColor(0x00000000); } + + if (manager.getLoadable().isBoardMode()) { + Utils.setPressedDrawable(right); + } } - - private void init() { - setOnClickListener(this); - setOnLongClickListener(this); - } - + private void buildView(final Context context) { if (isBuild) return; isBuild = true; - + Resources resources = context.getResources(); int postPadding = resources.getDimensionPixelSize(R.dimen.post_padding); int commentPadding = resources.getDimensionPixelSize(R.dimen.post_comment_padding); @@ -252,99 +219,88 @@ public class PostView extends LinearLayout implements View.OnClickListener, View int iconWidth = resources.getDimensionPixelSize(R.dimen.post_icon_width); int iconHeight = resources.getDimensionPixelSize(R.dimen.post_icon_height); int imageSize = resources.getDimensionPixelSize(R.dimen.thumbnail_size); - + full = new LinearLayout(context); full.setLayoutParams(matchParams); full.setOrientation(HORIZONTAL); - + // Create thumbnail imageView = new NetworkImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setFadeIn(100); - + imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { manager.onThumbnailClicked(post); } }); - + LinearLayout left = new LinearLayout(context); left.setOrientation(VERTICAL); left.setBackgroundColor(0xffdddddd); - + left.addView(imageView, new LinearLayout.LayoutParams(imageSize, imageSize)); - + full.addView(left, wrapMatchParams); full.setMinimumHeight(imageSize); - - LinearLayout right = new LinearLayout(context); + + right = new LinearLayout(context); right.setOrientation(VERTICAL); - + LinearLayout header = new LinearLayout(context); header.setOrientation(HORIZONTAL); - + titleView = new TextView(context); titleView.setTextSize(14); titleView.setPadding(postPadding, postPadding, postPadding, 0); header.addView(titleView, wrapParams); - + right.addView(header, matchWrapParams); - + iconView = new LinearLayout(context); iconView.setOrientation(HORIZONTAL); iconView.setPadding(postPadding, iconPadding, postPadding, 0); - + stickyView = new ImageView(context); stickyView.setImageBitmap(IconCache.stickyIcon); iconView.addView(stickyView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - + countryView = new NetworkImageView(context); countryView.setScaleType(ImageView.ScaleType.FIT_CENTER); iconView.addView(countryView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - + right.addView(iconView, matchWrapParams); - + commentView = new TextView(context); commentView.setTextSize(15); commentView.setPadding(postPadding, commentPadding, postPadding, commentPadding); right.addView(commentView, matchWrapParams); - + repliesCountView = new TextView(context); - + // Set the drawable before the padding, because setting the background resets the padding // This behavior differs with 4.4 / 4.1 Utils.setPressedDrawable(repliesCountView); - + repliesCountView.setTextColor(Color.argb(255, 100, 100, 100)); repliesCountView.setPadding(postPadding, postPadding, postPadding, postPadding); repliesCountView.setTextSize(14); - + right.addView(repliesCountView, wrapParams); - + full.addView(right, matchWrapParams); - + addView(full, matchParams); + + full.setOnClickListener(this); + full.setOnLongClickListener(this); } - - private IDColor computeIDColor(String id) { - // Stolen from the 4chan extension - int hash = post.id.hashCode(); - - int r = (hash >> 24) & 0xff; - int g = (hash >> 16) & 0xff; - int b = (hash >> 8) & 0xff; - - IDColor c = new IDColor(); - c.color = (0xff << 24) + (r << 16) + (g << 8) + b; - c.backgroundColor = ((r * 0.299f) + (g * 0.587f) + (b * 0.114f)) > 125 ? 0xff636363 : 0x00000000; - return c; - } - + public void onLinkableClick(PostLinkable linkable) { manager.onPostLinkableClicked(linkable); } - + @Override public void onClick(View v) { manager.onPostClicked(post); @@ -353,13 +309,56 @@ public class PostView extends LinearLayout implements View.OnClickListener, View @Override public boolean onLongClick(View v) { manager.onPostLongClicked(post); - + return true; } - - private static class IDColor { - public int color; - public int backgroundColor; + + private class PostViewMovementMethod extends LinkMovementMethod { + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP || + action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + + if (link.length != 0) { + if (action == MotionEvent.ACTION_UP) { + link[0].onClick(widget); + } else if (action == MotionEvent.ACTION_DOWN) { + Selection.setSelection(buffer, + buffer.getSpanStart(link[0]), + buffer.getSpanEnd(link[0])); + } + + return true; + } else { + Selection.removeSelection(buffer); + + // Changed this to propagate events + PostView.this.onTouchEvent(event); + return true; + } + } else { + PostView.this.onTouchEvent(event); + return true; + } + +// return Touch.onTouchEvent(widget, buffer, event); + } } }