Handling of dead links, links to other threads and better titles

captchafix
Florens Douwes 11 years ago
parent 78e3fe4763
commit 7b5b451507
  1. 96
      Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java
  2. 161
      Clover/app/src/main/java/org/floens/chan/core/model/Post.java
  3. 22
      Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java
  4. 5
      Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java
  5. 33
      Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java
  6. 2
      Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java
  7. 6
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  8. 5
      Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java
  9. 17
      Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java
  10. 5
      Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java
  11. 1
      Clover/app/src/main/res/values/attrs.xml
  12. 1
      Clover/app/src/main/res/values/strings.xml
  13. 1
      Clover/app/src/main/res/values/styles.xml

@ -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<Post> 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 {

@ -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<String> 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<String> 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;
}
}
}
}

@ -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;
}
}
}

@ -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<Post> posts);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

@ -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<Post> 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);

@ -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);
}
}

@ -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);

@ -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);
}
});

@ -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;

@ -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;
}
}

@ -53,6 +53,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<attr name="capcode_color" format="color"/>
<attr name="id_background_light" format="color"/>
<attr name="id_background_dark" format="color"/>
<attr name="inline_quote_color" format="color"/>
</declare-styleable>
<attr name="board_edit_item_style" format="reference"/>

@ -153,6 +153,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="preference_developer">Developer options</string>
<string name="open_link_confirmation">Open link?</string>
<string name="open_thread_confirmation">Open this thread?</string>
<string name="watch_pause_pins">Stop watching</string>

@ -92,6 +92,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item name="capcode_color">#ffff0000</item>
<item name="id_background_light">#ff636363</item>
<item name="id_background_dark">#00000000</item>
<item name="inline_quote_color">#ff789922</item>
</style>
<style name="BoardEditItem">

Loading…
Cancel
Save