From 7b5b4515072cd3c12b9b08cee8b5dfbb30a0ae7d Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Sat, 21 Jun 2014 21:01:04 +0200 Subject: [PATCH] Handling of dead links, links to other threads and better titles --- .../chan/core/manager/ThreadManager.java | 96 +++++------ .../java/org/floens/chan/core/model/Post.java | 161 ++++++++++++------ .../floens/chan/core/model/PostLinkable.java | 22 ++- .../floens/chan/ui/activity/BaseActivity.java | 5 + .../chan/ui/activity/BoardActivity.java | 33 +++- .../chan/ui/activity/ImageViewActivity.java | 2 +- .../floens/chan/ui/adapter/PostAdapter.java | 6 +- .../chan/ui/fragment/PostRepliesFragment.java | 5 +- .../chan/ui/fragment/ThreadFragment.java | 17 +- .../org/floens/chan/utils/ThemeHelper.java | 5 + Clover/app/src/main/res/values/attrs.xml | 1 + Clover/app/src/main/res/values/strings.xml | 1 + Clover/app/src/main/res/values/styles.xml | 1 + 13 files changed, 232 insertions(+), 123 deletions(-) diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java index 42aeb294..dce167c4 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java @@ -290,12 +290,12 @@ public class ThreadManager implements Loader.LoaderListener { handleLinkableSelected(linkable); } - public void scrollToPost(Post post) { + public void scrollToPost(int post) { threadManagerListener.onScrollTo(post); } - public void highlightPost(Post post) { - highlightedPost = post.no; + public void highlightPost(int post) { + highlightedPost = post; } public boolean isPostHightlighted(Post post) { @@ -402,69 +402,48 @@ public class ThreadManager implements Loader.LoaderListener { */ private void handleLinkableSelected(final PostLinkable linkable) { if (linkable.type == PostLinkable.Type.QUOTE) { - showPostReply(linkable); + Post post = findPostById((Integer) linkable.value); + if (post != null) { + RepliesPopup l = new RepliesPopup(); + l.posts.add(post); + showPostsRepliesFragment(l); + } } else if (linkable.type == PostLinkable.Type.LINK) { if (ChanPreferences.getOpenLinkConfirmation()) { - AlertDialog dialog = new AlertDialog.Builder(activity) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + new AlertDialog.Builder(activity) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - openLink(linkable); + Utils.openLink(activity, (String) linkable.value); } - }).setTitle(R.string.open_link_confirmation).setMessage(linkable.value).create(); - - dialog.show(); + }) + .setTitle(R.string.open_link_confirmation) + .setMessage((String) linkable.value) + .show(); } else { - openLink(linkable); + Utils.openLink(activity, (String) linkable.value); } } else if (linkable.type == PostLinkable.Type.SPOILER) { - new AlertDialog.Builder(activity).setMessage(linkable.value).show(); - } - } - - /** - * When a linkable to a post has been clicked, show a dialog with the - * referenced post in it. - * - * @param linkable the clicked linkable. - */ - private void showPostReply(PostLinkable linkable) { - String value = linkable.value; - - Post post; - - try { - // Get post id - String[] splitted = value.split("#p"); - if (splitted.length == 2) { - int id = Integer.parseInt(splitted[1]); - - post = findPostById(id); + new AlertDialog.Builder(activity).setMessage((String) linkable.value).show(); + } else if (linkable.type == PostLinkable.Type.THREAD) { + final PostLinkable.ThreadLink link = (PostLinkable.ThreadLink) linkable.value; + final Loadable thread = new Loadable(link.board, link.threadId); - if (post != null) { - RepliesPopup l = new RepliesPopup(); - l.posts.add(post); - showPostsRepliesFragment(l); - } - } - } catch (NumberFormatException e) { - e.printStackTrace(); + new AlertDialog.Builder(activity) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + threadManagerListener.onOpenThread(thread, link.postId); + } + }) + .setTitle(R.string.open_thread_confirmation) + .setMessage("/" + thread.board + "/" + thread.no) + .show(); } } - /** - * Open an url. - * - * @param linkable Linkable with an url. - */ - private void openLink(PostLinkable linkable) { - Utils.openLink(activity, linkable.value); - } - private void showPostsRepliesFragment(RepliesPopup repliesPopup) { // Post popups are now queued up, more than 32 popups on top of each // other makes the system crash! @@ -568,11 +547,18 @@ public class ThreadManager implements Loader.LoaderListener { public interface ThreadManagerListener { public void onThreadLoaded(List result, boolean append); + public void onThreadLoadError(VolleyError error); + public void onOPClicked(Post post); + public void onThumbnailClicked(Post post); - public void onScrollTo(Post post); + + public void onScrollTo(int post); + public void onRefreshView(); + + public void onOpenThread(Loadable thread, int highlightedPost); } public static class RepliesPopup { diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java index 686b7999..b864533d 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java @@ -17,14 +17,15 @@ */ package org.floens.chan.core.model; -import android.graphics.Color; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; +import android.text.style.StrikethroughSpan; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.model.PostLinkable.Type; import org.floens.chan.ui.view.PostView; +import org.floens.chan.utils.ThemeHelper; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -34,6 +35,7 @@ import org.jsoup.parser.Parser; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Contains all data needed to represent a single post. @@ -179,60 +181,123 @@ public class Post { detectLinks(text, spannable); total = TextUtils.concat(total, spannable); - } else if (nodeName.equals("br")) { - total = TextUtils.concat(total, "\n"); - } 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); - - detectLinks(span.text(), quote); - - total = TextUtils.concat(total, quote); - } else if (nodeName.equals("a")) { - Element anchor = (Element) node; - - // is this a link or a quote - Type t = anchor.text().contains("://") ? Type.LINK : Type.QUOTE; - if (t == Type.QUOTE) { - try { - // Get post id - String[] splitted = anchor.attr("href").split("#p"); - if (splitted.length == 2) { - int id = Integer.parseInt(splitted[1]); - repliesTo.add(id); - - // Append OP when its a reply to OP - if (id == resto) { - anchor.appendText(" (OP)"); - } - } - } catch (NumberFormatException e) { + } else { + switch (nodeName) { + case "br": { + total = TextUtils.concat(total, "\n"); + break; } - } + case "span": { + Element span = (Element) node; + + SpannableString quote = new SpannableString(span.text()); + + Set classes = span.classNames(); + if (classes.contains("deadlink")) { + quote.setSpan(new ForegroundColorSpan(ThemeHelper.getInstance().getQuoteColor()), 0, quote.length(), 0); + quote.setSpan(new StrikethroughSpan(), 0, quote.length(), 0); + } else { + quote.setSpan(new ForegroundColorSpan(ThemeHelper.getInstance().getInlineQuoteColor()), 0, quote.length(), 0); + detectLinks(span.text(), quote); + } - SpannableString link = new SpannableString(anchor.text()); + total = TextUtils.concat(total, quote); + break; + } + case "a": { + Element anchor = (Element) node; + + String href = anchor.attr("href"); + Set classes = anchor.classNames(); + + Type t = null; + String key = null; + Object value = null; + if (classes.contains("quotelink")) { + if (href.contains("/thread/")) { + // link to another thread + PostLinkable.ThreadLink threadLink = null; + + String[] slashSplit = href.split("/"); + if (slashSplit.length == 4) { + String board = slashSplit[1]; + String nums = slashSplit[3]; + String[] numsSplitted = nums.split("#p"); + if (numsSplitted.length == 2) { + try { + int tId = Integer.parseInt(numsSplitted[0]); + int pId = Integer.parseInt(numsSplitted[1]); + threadLink = new PostLinkable.ThreadLink(board, tId, pId); + } catch (NumberFormatException e) { + } + } + } + + if (threadLink != null) { + t = Type.THREAD; + key = anchor.text() + " \u2192"; // arrow to the right + value = threadLink; + } + } else { + // normal quote + int id = -1; + + String[] splitted = href.split("#p"); + if (splitted.length == 2) { + try { + id = Integer.parseInt(splitted[1]); + } catch (NumberFormatException e) { + } + } + + if (id >= 0) { + t = Type.QUOTE; + key = anchor.text(); + value = id; + repliesTo.add(id); + + // Append OP when its a reply to OP + if (id == resto) { + key += " (OP)"; + } + } + } + } else { + // normal link + t = Type.LINK; + key = anchor.text(); + value = href; + } - PostLinkable pl = new PostLinkable(this, anchor.text(), anchor.attr("href"), t); - link.setSpan(pl, 0, link.length(), 0); - linkables.add(pl); + if (t != null && key != null && value != null) { + SpannableString link = new SpannableString(key); + PostLinkable pl = new PostLinkable(this, key, value, t); + link.setSpan(pl, 0, link.length(), 0); + linkables.add(pl); - total = TextUtils.concat(total, link); - } else if (nodeName.equals("s")) { - Element spoiler = (Element) node; + total = TextUtils.concat(total, link); + } + break; + } + case "s": { + Element spoiler = (Element) node; - SpannableString link = new SpannableString(spoiler.text()); + SpannableString link = new SpannableString(spoiler.text()); - PostLinkable pl = new PostLinkable(this, spoiler.text(), spoiler.text(), Type.SPOILER); - link.setSpan(pl, 0, link.length(), 0); - linkables.add(pl); + PostLinkable pl = new PostLinkable(this, spoiler.text(), spoiler.text(), Type.SPOILER); + link.setSpan(pl, 0, link.length(), 0); + linkables.add(pl); - 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, link); + break; + } + default: { + // Unknown tag, add the inner part + if (node instanceof Element) { + total = TextUtils.concat(total, ((Element) node).text()); + } + break; + } } } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java b/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java index 01581113..928681e3 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java @@ -28,15 +28,15 @@ import org.floens.chan.utils.ThemeHelper; */ public class PostLinkable extends ClickableSpan { public static enum Type { - QUOTE, LINK, SPOILER + QUOTE, LINK, SPOILER, THREAD } public final Post post; public final String key; - public final String value; + public final Object value; public final Type type; - public PostLinkable(Post post, String key, String value, Type type) { + public PostLinkable(Post post, String key, Object value, Type type) { this.post = post; this.key = key; this.value = value; @@ -52,8 +52,8 @@ public class PostLinkable extends ClickableSpan { @Override public void updateDrawState(TextPaint ds) { - if (type == Type.QUOTE || type == Type.LINK) { - ds.setColor(type == Type.QUOTE ? ThemeHelper.getInstance().getQuoteColor() : ThemeHelper.getInstance().getLinkColor()); + if (type == Type.QUOTE || type == Type.LINK || type == Type.THREAD) { + ds.setColor(type == Type.LINK ? ThemeHelper.getInstance().getLinkColor() : ThemeHelper.getInstance().getQuoteColor()); ds.setUnderlineText(true); } else if (type == Type.SPOILER) { ds.setColor(ThemeHelper.getInstance().getSpoilerColor()); @@ -61,4 +61,16 @@ public class PostLinkable extends ClickableSpan { ds.setUnderlineText(false); } } + + public static class ThreadLink { + public String board; + public int threadId; + public int postId; + + public ThreadLink(String board, int threadId, int postId) { + this.board = board; + this.threadId = threadId; + this.postId = postId; + } + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java index 207337b4..2f6f699e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java @@ -47,6 +47,7 @@ import android.widget.ShareActionProvider; import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.core.manager.WatchManager; +import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Post; import org.floens.chan.ui.BadgeDrawable; @@ -87,6 +88,10 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene */ abstract public void onOPClicked(Post post); + abstract public void onOpenThread(Loadable thread); + + abstract public void onThreadLoaded(Loadable loadable, List posts); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java index 93d41fe9..34d8c2d9 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java @@ -210,7 +210,20 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel @Override public void onOPClicked(Post post) { - startLoadingThread(new Loadable(post.board, post.no, post.subject)); + startLoadingThread(new Loadable(post.board, post.no, generateTitle(post))); + } + + @Override + public void onOpenThread(Loadable thread) { + startLoadingThread(thread); + } + + @Override + public void onThreadLoaded(Loadable loadable, List posts) { + if (loadable.isThreadMode() && TextUtils.isEmpty(threadLoadable.title) && posts.size() > 0) { + threadLoadable.title = generateTitle(posts.get(0)); + updateActionBarState(); + } } @Override @@ -482,10 +495,6 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel threadFragment.bindLoadable(loadable); threadFragment.requestData(); - if (TextUtils.isEmpty(loadable.title)) { - loadable.title = "/" + loadable.board + "/" + loadable.no; - } - threadPane.closePane(); updateActionBarState(); @@ -544,6 +553,16 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel }).setCancelable(false).create().show(); } + private String generateTitle(Post post) { + if (!TextUtils.isEmpty(post.subject)) { + return post.subject; + } else if (!TextUtils.isEmpty(post.comment)) { + return post.comment.subSequence(0, Math.min(post.comment.length(), 100)).toString(); + } else { + return "/" + post.board + "/" + post.no; + } + } + private class BoardSpinnerAdapter extends BaseAdapter { private static final int VIEW_TYPE_ITEM = 0; private static final int VIEW_TYPE_ADD = 1; @@ -610,7 +629,7 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel @Override public String getItem(final int position) { - switch(getItemViewType(position)) { + switch (getItemViewType(position)) { case VIEW_TYPE_ITEM: return keys.get(position); case VIEW_TYPE_ADD: @@ -622,7 +641,7 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel @Override public View getView(final int position, View convertView, final ViewGroup parent) { - switch(getItemViewType(position)) { + switch (getItemViewType(position)) { case VIEW_TYPE_ITEM: { if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.board_select_spinner, null); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java index e7c3e1bc..fd701753 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java @@ -145,7 +145,7 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang Post post = adapter.getPost(position); if (postAdapter != null) { - postAdapter.scrollToPost(post); + postAdapter.scrollToPost(post.no); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java index f810da41..7b9003d0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java @@ -169,12 +169,12 @@ public class PostAdapter extends BaseAdapter { notifyDataSetChanged(); } - public void scrollToPost(Post post) { + public void scrollToPost(int no) { notifyDataSetChanged(); for (int i = 0; i < postList.size(); i++) { - if (postList.get(i).no == post.no) { - if (Math.abs(i - listView.getFirstVisiblePosition()) > 20) { + if (postList.get(i).no == no) { + if (Math.abs(i - listView.getFirstVisiblePosition()) > 20 || listView.getChildCount() == 0) { listView.setSelection(i); } else { ScrollerRunnable r = new ScrollerRunnable(listView); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java index 9a17aa0a..ba2a273f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java @@ -32,7 +32,6 @@ import org.floens.chan.R; import org.floens.chan.core.manager.ThreadManager; import org.floens.chan.core.model.Post; import org.floens.chan.ui.view.PostView; -import org.floens.chan.utils.Logger; import org.floens.chan.utils.ThemeHelper; /** @@ -130,8 +129,8 @@ public class PostRepliesFragment extends DialogFragment { public void onClick(View v) { manager.closeAllPostFragments(); dismiss(); - manager.highlightPost(p); - manager.scrollToPost(p); + manager.highlightPost(p.no); + manager.scrollToPost(p.no); } }); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java index 4cade546..00ef91f2 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java @@ -59,6 +59,7 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana private ListView listView; private ImageView skip; private SkipLogic skipLogic; + private int highlightedPost = -1; public static ThreadFragment newInstance(BaseActivity activity) { ThreadFragment fragment = new ThreadFragment(); @@ -196,6 +197,12 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana } else { postAdapter.setList(posts); } + + threadManager.highlightPost(highlightedPost); + postAdapter.scrollToPost(highlightedPost); + highlightedPost = -1; + + baseActivity.onThreadLoaded(loadable, posts); } private void setEmpty() { @@ -227,6 +234,8 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana postAdapter.setErrorMessage(getLoadErrorText(error)); } } + + highlightedPost = -1; } /** @@ -278,7 +287,7 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana } @Override - public void onScrollTo(Post post) { + public void onScrollTo(int post) { if (postAdapter != null) { postAdapter.scrollToPost(post); } @@ -291,6 +300,12 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana } } + @Override + public void onOpenThread(final Loadable thread, int highlightedPost) { + baseActivity.onOpenThread(thread); + this.highlightedPost = highlightedPost; + } + private static class SkipLogic { private final ImageView skip; private int lastFirstVisibleItem; diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java b/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java index 606ef74a..6394deae 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java @@ -45,6 +45,7 @@ public class ThemeHelper { private int quoteColor; private int linkColor; private int spoilerColor; + private int inlineQuoteColor; public static ThemeHelper getInstance() { if (instance == null) { @@ -85,6 +86,7 @@ public class ThemeHelper { quoteColor = ta.getColor(R.styleable.PostView_quote_color, 0); linkColor = ta.getColor(R.styleable.PostView_link_color, 0); spoilerColor = ta.getColor(R.styleable.PostView_spoiler_color, 0); + inlineQuoteColor = ta.getColor(R.styleable.PostView_inline_quote_color, 0); ta.recycle(); } @@ -99,4 +101,7 @@ public class ThemeHelper { public int getSpoilerColor() { return spoilerColor; } + public int getInlineQuoteColor() { + return inlineQuoteColor; + } } diff --git a/Clover/app/src/main/res/values/attrs.xml b/Clover/app/src/main/res/values/attrs.xml index d2844aca..8a17befd 100644 --- a/Clover/app/src/main/res/values/attrs.xml +++ b/Clover/app/src/main/res/values/attrs.xml @@ -53,6 +53,7 @@ along with this program. If not, see . + diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 36b0076f..4520d1fb 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -153,6 +153,7 @@ along with this program. If not, see . Developer options Open link? + Open this thread? Stop watching diff --git a/Clover/app/src/main/res/values/styles.xml b/Clover/app/src/main/res/values/styles.xml index 3d0fd3af..8a50ff84 100644 --- a/Clover/app/src/main/res/values/styles.xml +++ b/Clover/app/src/main/res/values/styles.xml @@ -92,6 +92,7 @@ along with this program. If not, see . #ffff0000 #ff636363 #00000000 + #ff789922