Format code

captchafix
Florens Douwes 11 years ago
parent 4293a41830
commit 30724d2148
  1. 21
      Chan/src/org/floens/chan/chan/ChanUrls.java
  2. 13
      Chan/src/org/floens/chan/core/ChanPreferences.java
  3. 2
      Chan/src/org/floens/chan/core/loader/EndOfLineException.java
  4. 14
      Chan/src/org/floens/chan/core/loader/Loader.java
  5. 2
      Chan/src/org/floens/chan/core/loader/LoaderPool.java
  6. 42
      Chan/src/org/floens/chan/core/manager/BoardManager.java
  7. 8
      Chan/src/org/floens/chan/core/manager/PinnedManager.java
  8. 65
      Chan/src/org/floens/chan/core/manager/ReplyManager.java
  9. 11
      Chan/src/org/floens/chan/core/manager/ThreadManager.java
  10. 16
      Chan/src/org/floens/chan/core/model/Board.java
  11. 71
      Chan/src/org/floens/chan/core/model/Loadable.java
  12. 19
      Chan/src/org/floens/chan/core/model/Pin.java
  13. 2
      Chan/src/org/floens/chan/core/model/Post.java
  14. 4
      Chan/src/org/floens/chan/core/model/PostLinkable.java
  15. 6
      Chan/src/org/floens/chan/core/model/SavedReply.java
  16. 8
      Chan/src/org/floens/chan/core/net/BitmapLruImageCache.java
  17. 38
      Chan/src/org/floens/chan/core/net/BoardsRequest.java
  18. 12
      Chan/src/org/floens/chan/core/net/ByteArrayRequest.java
  19. 51
      Chan/src/org/floens/chan/core/net/ChanReaderRequest.java
  20. 2
      Chan/src/org/floens/chan/core/net/FileRequest.java
  21. 14
      Chan/src/org/floens/chan/core/net/GIFRequest.java
  22. 4
      Chan/src/org/floens/chan/core/net/JsonReaderRequest.java
  23. 7
      Chan/src/org/floens/chan/database/DatabaseHelper.java
  24. 15
      Chan/src/org/floens/chan/database/DatabaseManager.java
  25. 2
      Chan/src/org/floens/chan/service/WatchService.java
  26. 14
      Chan/src/org/floens/chan/ui/ScrollerRunnable.java
  27. 349
      Chan/src/org/floens/chan/ui/SwipeDismissListViewTouchListener.java
  28. 36
      Chan/src/org/floens/chan/ui/ViewFlipperAnimations.java
  29. 4
      Chan/src/org/floens/chan/ui/activity/AboutActivity.java
  30. 91
      Chan/src/org/floens/chan/ui/activity/BoardEditor.java
  31. 23
      Chan/src/org/floens/chan/ui/activity/DeveloperActivity.java
  32. 5
      Chan/src/org/floens/chan/ui/activity/ImagePickActivity.java
  33. 10
      Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java
  34. 20
      Chan/src/org/floens/chan/ui/activity/ReplyActivity.java
  35. 2
      Chan/src/org/floens/chan/ui/activity/SettingsActivity.java
  36. 23
      Chan/src/org/floens/chan/ui/adapter/BoardEditAdapter.java
  37. 29
      Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java
  38. 2
      Chan/src/org/floens/chan/ui/adapter/PinnedAdapter.java
  39. 9
      Chan/src/org/floens/chan/ui/adapter/PostAdapter.java
  40. 8
      Chan/src/org/floens/chan/ui/fragment/PostRepliesFragment.java
  41. 209
      Chan/src/org/floens/chan/ui/fragment/ReplyFragment.java
  42. 7
      Chan/src/org/floens/chan/ui/fragment/SettingsFragment.java
  43. 84
      Chan/src/org/floens/chan/ui/fragment/ThreadFragment.java
  44. 246
      Chan/src/org/floens/chan/ui/view/DynamicListView.java
  45. 57
      Chan/src/org/floens/chan/ui/view/GIFView.java
  46. 2
      Chan/src/org/floens/chan/ui/view/HackyViewPager.java
  47. 10
      Chan/src/org/floens/chan/ui/view/NetworkPhotoView.java
  48. 2
      Chan/src/org/floens/chan/ui/view/PostView.java
  49. 37
      Chan/src/org/floens/chan/ui/view/ThreadWatchCounterView.java
  50. 11
      Chan/src/org/floens/chan/utils/IOUtils.java
  51. 1
      Chan/src/org/floens/chan/utils/IconCache.java
  52. 45
      Chan/src/org/floens/chan/utils/ImageDecoder.java
  53. 54
      Chan/src/org/floens/chan/utils/ImageSaver.java
  54. 27
      Chan/src/org/floens/chan/utils/Logger.java
  55. 32
      Chan/src/org/floens/chan/utils/Utils.java

@ -6,23 +6,23 @@ public class ChanUrls {
public static String getCatalogUrl(String board) { public static String getCatalogUrl(String board) {
return "https://a.4cdn.org/" + board + "/catalog.json"; return "https://a.4cdn.org/" + board + "/catalog.json";
} }
public static String getPageUrl(String board, int pageNumber) { public static String getPageUrl(String board, int pageNumber) {
return "https://a.4cdn.org/" + board + "/" + pageNumber + ".json"; return "https://a.4cdn.org/" + board + "/" + pageNumber + ".json";
} }
public static String getThreadUrl(String board, int no) { public static String getThreadUrl(String board, int no) {
return "https://a.4cdn.org/" + board + "/res/" + no + ".json"; return "https://a.4cdn.org/" + board + "/res/" + no + ".json";
} }
public static String getCaptchaChallengeUrl() { public static String getCaptchaChallengeUrl() {
return "https://www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"; return "https://www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc";
} }
public static String getCaptchaImageUrl(String challenge) { public static String getCaptchaImageUrl(String challenge) {
return "https://www.google.com/recaptcha/api/image?c=" + challenge; return "https://www.google.com/recaptcha/api/image?c=" + challenge;
} }
public static String getImageUrl(String board, String code, String extension) { public static String getImageUrl(String board, String code, String extension) {
return "https://i.4cdn.org/" + board + "/src/" + code + "." + extension; return "https://i.4cdn.org/" + board + "/src/" + code + "." + extension;
} }
@ -41,12 +41,12 @@ public class ChanUrls {
public static String getPostUrl(String board) { public static String getPostUrl(String board) {
return "https://sys.4chan.org/" + board + "/post"; return "https://sys.4chan.org/" + board + "/post";
// return "http://192.168.6.214/Testing/PostEchoer/post.php"; // return "http://192.168.6.214/Testing/PostEchoer/post.php";
} }
public static String getDeleteUrl(String board) { public static String getDeleteUrl(String board) {
return "https://sys.4chan.org/" + board + "/imgboard.php"; return "https://sys.4chan.org/" + board + "/imgboard.php";
// return "http://192.168.6.214/Testing/PostEchoer/post.php"; // return "http://192.168.6.214/Testing/PostEchoer/post.php";
} }
public static String getBoardUrlDesktop(String board) { public static String getBoardUrlDesktop(String board) {
@ -61,8 +61,3 @@ public class ChanUrls {
return "https://boards.4chan.org/" + board + "/catalog"; return "https://boards.4chan.org/" + board + "/catalog";
} }
} }

@ -33,9 +33,9 @@ public class ChanPreferences {
} }
/** /**
* This also calls updateRunningState on the PinnedService to * This also calls updateRunningState on the PinnedService to start/stop the
* start/stop the service as needed. * service as needed.
* *
* @param enabled * @param enabled
*/ */
public static void setWatchEnabled(boolean enabled) { public static void setWatchEnabled(boolean enabled) {
@ -54,13 +54,8 @@ public class ChanPreferences {
String number = ChanApplication.getPreferences().getString("preference_watch_background_timeout", "0"); String number = ChanApplication.getPreferences().getString("preference_watch_background_timeout", "0");
return Integer.parseInt(number) * 1000L; return Integer.parseInt(number) * 1000L;
} }
public static boolean getVideoAutoPlay() { public static boolean getVideoAutoPlay() {
return ChanApplication.getPreferences().getBoolean("preference_autoplay", false); return ChanApplication.getPreferences().getBoolean("preference_autoplay", false);
} }
} }

@ -12,7 +12,7 @@ public class EndOfLineException extends VolleyError {
public EndOfLineException() { public EndOfLineException() {
super(); super();
} }
@Override @Override
public String getMessage() { public String getMessage() {
return "End of the line"; return "End of the line";

@ -43,7 +43,7 @@ public class Loader {
/** /**
* Add a LoaderListener * Add a LoaderListener
* *
* @param l * @param l
* the listener to add * the listener to add
*/ */
@ -53,7 +53,7 @@ public class Loader {
/** /**
* Remove a LoaderListener * Remove a LoaderListener
* *
* @param l * @param l
* the listener to remove * the listener to remove
* @return true if there are no more listeners, false otherwise * @return true if there are no more listeners, false otherwise
@ -85,9 +85,9 @@ public class Loader {
} }
/** /**
* Request more data if the time left is below 0 * Request more data if the time left is below 0 If auto load more is
* If auto load more is disabled, this needs to be called manually. * disabled, this needs to be called manually. Otherwise this is called
* Otherwise this is called automatically when the timer hits 0. * automatically when the timer hits 0.
*/ */
public void loadMoreIfTime() { public void loadMoreIfTime() {
if (getTimeUntilLoadMore() < 0L) { if (getTimeUntilLoadMore() < 0L) {
@ -165,6 +165,7 @@ public class Loader {
/** /**
* Get the time in milliseconds until another loadMore is recommended * Get the time in milliseconds until another loadMore is recommended
*
* @return * @return
*/ */
public long getTimeUntilLoadMore() { public long getTimeUntilLoadMore() {
@ -175,7 +176,7 @@ public class Loader {
return lastLoadTime + waitTime - System.currentTimeMillis(); return lastLoadTime + waitTime - System.currentTimeMillis();
} }
} }
public List<Post> getCachedPosts() { public List<Post> getCachedPosts() {
return cachedPosts; return cachedPosts;
} }
@ -291,6 +292,7 @@ public class Loader {
public static interface LoaderListener { public static interface LoaderListener {
public void onData(List<Post> result, boolean append); public void onData(List<Post> result, boolean append);
public void onError(VolleyError error); public void onError(VolleyError error);
} }
} }

@ -6,7 +6,7 @@ import java.util.Map;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
public class LoaderPool { public class LoaderPool {
// private static final String TAG = "LoaderPool"; // private static final String TAG = "LoaderPool";
private static LoaderPool instance; private static LoaderPool instance;

@ -37,6 +37,7 @@ public class BoardManager {
/** /**
* Avoid having 0 boards, which causes graphical problems * Avoid having 0 boards, which causes graphical problems
*
* @param list * @param list
*/ */
private ArrayList<Board> getDefaultBoards() { private ArrayList<Board> getDefaultBoards() {
@ -125,6 +126,7 @@ public class BoardManager {
/** /**
* Try to add value to the supplied list. * Try to add value to the supplied list.
*
* @param list * @param list
* @param value * @param value
*/ */
@ -174,7 +176,8 @@ public class BoardManager {
private ArrayList<Board> getBoardListFromDatabase(String key) { private ArrayList<Board> getBoardListFromDatabase(String key) {
String total = ChanApplication.getPreferences().getString(key, null); String total = ChanApplication.getPreferences().getString(key, null);
if (total == null) return null; if (total == null)
return null;
ArrayList<Board> list = new ArrayList<Board>(); ArrayList<Board> list = new ArrayList<Board>();
@ -184,7 +187,8 @@ public class BoardManager {
String line = scanner.nextLine(); String line = scanner.nextLine();
String[] splitted = line.split("\\|"); String[] splitted = line.split("\\|");
if (splitted.length < 2) continue; if (splitted.length < 2)
continue;
Board board = new Board(); Board board = new Board();
board.key = splitted[0]; board.key = splitted[0];
@ -207,24 +211,20 @@ public class BoardManager {
allBoards = temp; allBoards = temp;
} }
ChanApplication.getVolleyRequestQueue().add(new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener<ArrayList<Board>>() { ChanApplication.getVolleyRequestQueue().add(
@Override new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener<ArrayList<Board>>() {
public void onResponse(ArrayList<Board> data) { @Override
storeBoardListInDatabase("allBoards", data); public void onResponse(ArrayList<Board> data) {
allBoards = data; storeBoardListInDatabase("allBoards", data);
allBoards = data;
Logger.i(TAG, "Got boards from server");
} Logger.i(TAG, "Got boards from server");
}, new Response.ErrorListener() { }
@Override }, new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) { @Override
Logger.e(TAG, "Failed to get boards from server"); public void onErrorResponse(VolleyError error) {
} Logger.e(TAG, "Failed to get boards from server");
})); }
}));
} }
} }

@ -28,7 +28,7 @@ public class PinnedManager {
/** /**
* Look for a pin that has an loadable that is equal to the supplied * Look for a pin that has an loadable that is equal to the supplied
* loadable. * loadable.
* *
* @param other * @param other
* @return The pin whose loadable is equal to the supplied loadable, or null * @return The pin whose loadable is equal to the supplied loadable, or null
* if no pin was found. * if no pin was found.
@ -60,7 +60,7 @@ public class PinnedManager {
/** /**
* Add a pin * Add a pin
* *
* @param pin * @param pin
* @return true if it was added, false if it wasn't (duplicated) * @return true if it was added, false if it wasn't (duplicated)
*/ */
@ -82,7 +82,7 @@ public class PinnedManager {
/** /**
* Remove a pin * Remove a pin
* *
* @param pin * @param pin
*/ */
public void remove(Pin pin) { public void remove(Pin pin) {
@ -95,7 +95,7 @@ public class PinnedManager {
/** /**
* Update the pin in the database * Update the pin in the database
* *
* @param pin * @param pin
*/ */
public void update(Pin pin) { public void update(Pin pin) {

@ -59,7 +59,9 @@ public class ReplyManager {
/** /**
* Set an reply draft. * Set an reply draft.
* @param value the draft to save. *
* @param value
* the draft to save.
*/ */
public void setReplyDraft(Reply value) { public void setReplyDraft(Reply value) {
draft = value; draft = value;
@ -67,6 +69,7 @@ public class ReplyManager {
/** /**
* Gets the saved reply draft. * Gets the saved reply draft.
*
* @return the saved draft or an empty draft. * @return the saved draft or an empty draft.
*/ */
public Reply getReplyDraft() { public Reply getReplyDraft() {
@ -75,7 +78,9 @@ public class ReplyManager {
/** /**
* Add an quote to the comment field. Looks like >>123456789\n * Add an quote to the comment field. Looks like >>123456789\n
* @param no the raw no to quote to. *
* @param no
* the raw no to quote to.
*/ */
public void quote(int no) { public void quote(int no) {
draft.comment = draft.comment + ">>" + no + "\n"; draft.comment = draft.comment + ">>" + no + "\n";
@ -83,7 +88,9 @@ public class ReplyManager {
/** /**
* Pick an file. Starts up the ImagePickActivity. * Pick an file. Starts up the ImagePickActivity.
* @param listener FileListener to listen on. *
* @param listener
* FileListener to listen on.
*/ */
public void pickFile(FileListener listener) { public void pickFile(FileListener listener) {
fileListener = listener; fileListener = listener;
@ -103,7 +110,8 @@ public class ReplyManager {
} }
/** /**
* Called from ImagePickActivity. Sends the file to the listening fileListener, and deletes the fileListener. * Called from ImagePickActivity. Sends the file to the listening
* fileListener, and deletes the fileListener.
*/ */
public void _onPickedFile(File file) { public void _onPickedFile(File file) {
if (fileListener != null) { if (fileListener != null) {
@ -121,7 +129,9 @@ public class ReplyManager {
/** /**
* Get the CAPTCHA challenge hash from an JSON response. * Get the CAPTCHA challenge hash from an JSON response.
* @param total The total response from the server *
* @param total
* The total response from the server
* @return The pattern, or null when none was found. * @return The pattern, or null when none was found.
*/ */
public static String getChallenge(String total) { public static String getChallenge(String total) {
@ -137,8 +147,12 @@ public class ReplyManager {
/** /**
* Send an reply off to the server. * Send an reply off to the server.
* @param reply The reply object with all data needed, like captcha and the file. *
* @param listener The listener, after server response. * @param reply
* The reply object with all data needed, like captcha and the
* file.
* @param listener
* The listener, after server response.
*/ */
public void sendDelete(final SavedReply reply, boolean onlyImageDelete, final DeleteListener listener) { public void sendDelete(final SavedReply reply, boolean onlyImageDelete, final DeleteListener listener) {
Logger.i(TAG, "Sending delete request: " + reply.board + ", " + reply.no); Logger.i(TAG, "Sending delete request: " + reply.board + ", " + reply.no);
@ -196,8 +210,12 @@ public class ReplyManager {
/** /**
* Send an reply off to the server. * Send an reply off to the server.
* @param reply The reply object with all data needed, like captcha and the file. *
* @param listener The listener, after server response. * @param reply
* The reply object with all data needed, like captcha and the
* file.
* @param listener
* The listener, after server response.
*/ */
public void sendReply(final Reply reply, final ReplyListener listener) { public void sendReply(final Reply reply, final ReplyListener listener) {
Logger.i(TAG, "Sending reply request: " + reply.board + ", " + reply.resto); Logger.i(TAG, "Sending reply request: " + reply.board + ", " + reply.resto);
@ -248,8 +266,8 @@ public class ReplyManager {
if (responseString.contains("No file selected")) { if (responseString.contains("No file selected")) {
e.isUserError = true; e.isUserError = true;
e.isFileError = true; e.isFileError = true;
} else if (responseString.contains("You forgot to solve the CAPTCHA") || } else if (responseString.contains("You forgot to solve the CAPTCHA")
responseString.contains("You seem to have mistyped the CAPTCHA")) { || responseString.contains("You seem to have mistyped the CAPTCHA")) {
e.isUserError = true; e.isUserError = true;
e.isCaptchaError = true; e.isCaptchaError = true;
} else if (responseString.toLowerCase(Locale.ENGLISH).contains("post successful")) { } else if (responseString.toLowerCase(Locale.ENGLISH).contains("post successful")) {
@ -282,11 +300,12 @@ public class ReplyManager {
} }
/** /**
* Async task to send an reply to the server. * Async task to send an reply to the server. Uses HttpClient. Since Android
* Uses HttpClient. Since Android 4.4 there is an updated version of HttpClient, 4.2, given with Android. * 4.4 there is an updated version of HttpClient, 4.2, given with Android.
* However, that version causes problems with file uploading. Version 4.3 of HttpClient has been given with a library, * However, that version causes problems with file uploading. Version 4.3 of
* that has another namespace: ch.boye.httpclientandroidlib * HttpClient has been given with a library, that has another namespace:
* This lib also has some fixes/improvements of HttpClient for Android. * ch.boye.httpclientandroidlib This lib also has some fixes/improvements of
* HttpClient for Android.
*/ */
private void sendHttpPost(final HttpPost post, final HttpPostSendListener listener) { private void sendHttpPost(final HttpPost post, final HttpPostSendListener listener) {
new Thread(new Runnable() { new Thread(new Runnable() {
@ -328,9 +347,12 @@ public class ReplyManager {
public static abstract class FileListener { public static abstract class FileListener {
/** /**
* When a file is picked. * When a file is picked.
* @param the picked file *
* @param the
* picked file
*/ */
public abstract void onFile(File file); public abstract void onFile(File file);
/** /**
* When the file has started loading. * When the file has started loading.
*/ */
@ -382,14 +404,9 @@ public class ReplyManager {
public boolean isSuccessful = false; public boolean isSuccessful = false;
/** /**
* Raw html from the response. Used to set html in an WebView to the client, when the error was not * Raw html from the response. Used to set html in an WebView to the
* recognized by Chan. * client, when the error was not recognized by Chan.
*/ */
public String responseData = ""; public String responseData = "";
} }
} }

@ -53,7 +53,7 @@ public class ThreadManager implements Loader.LoaderListener {
private int highlightedPost = -1; private int highlightedPost = -1;
private int lastSeenPost = -1; private int lastSeenPost = -1;
private int lastPost = -1; private int lastPost = -1;
private Loader loader; private Loader loader;
public ThreadManager(Activity activity, final ThreadManagerListener listener) { public ThreadManager(Activity activity, final ThreadManagerListener listener) {
@ -111,7 +111,7 @@ public class ThreadManager implements Loader.LoaderListener {
if (pin != null) { if (pin != null) {
ChanApplication.getPinnedManager().onPinViewed(pin); ChanApplication.getPinnedManager().onPinViewed(pin);
} }
updateLastSeen(); updateLastSeen();
} }
} }
@ -150,7 +150,7 @@ public class ThreadManager implements Loader.LoaderListener {
if (!shouldWatch()) { if (!shouldWatch()) {
loader.setAutoLoadMore(false); loader.setAutoLoadMore(false);
} }
if (result.size() > 0) { if (result.size() > 0) {
lastPost = result.get(result.size() - 1).no; lastPost = result.get(result.size() - 1).no;
} }
@ -277,7 +277,8 @@ public class ThreadManager implements Loader.LoaderListener {
String text = ""; String text = "";
if (post.hasImage) { if (post.hasImage) {
text += "File: " + post.filename + "." + post.ext + " \nSize: " + post.imageWidth + "x" + post.imageHeight + "\n\n"; text += "File: " + post.filename + "." + post.ext + " \nSize: " + post.imageWidth + "x" + post.imageHeight
+ "\n\n";
} }
text += "Time: " + post.date; text += "Time: " + post.date;
@ -536,7 +537,7 @@ public class ThreadManager implements Loader.LoaderListener {
} }
}); });
} }
private void updateLastSeen() { private void updateLastSeen() {
Pin pin = ChanApplication.getPinnedManager().findPinByLoadable(loader.getLoadable()); Pin pin = ChanApplication.getPinnedManager().findPinByLoadable(loader.getLoadable());
if (pin != null) { if (pin != null) {

@ -1,9 +1,8 @@
package org.floens.chan.core.model; package org.floens.chan.core.model;
/** /**
* Board key and value. * Board key and value. key is full name e.g. Literature. value is board key
* key is full name e.g. Literature. * e.g. lit.
* value is board key e.g. lit.
*/ */
public class Board { public class Board {
/** /**
@ -14,15 +13,16 @@ public class Board {
* Name of the url, e.g. lit * Name of the url, e.g. lit
*/ */
public String value; public String value;
public boolean workSafe = false; public boolean workSafe = false;
public boolean finish() { public boolean finish() {
if (key == null || value == null) return false; if (key == null || value == null)
return false;
return true; return true;
} }
@Override @Override
public String toString() { public String toString() {
return key; return key;

@ -13,40 +13,40 @@ import com.j256.ormlite.table.DatabaseTable;
public class Loadable { public class Loadable {
@DatabaseField(generatedId = true) @DatabaseField(generatedId = true)
private int id; private int id;
@DatabaseField @DatabaseField
public int mode = Mode.INVALID; public int mode = Mode.INVALID;
@DatabaseField @DatabaseField
public String board = ""; public String board = "";
@DatabaseField @DatabaseField
public int no = -1; public int no = -1;
@DatabaseField @DatabaseField
public String title = ""; public String title = "";
@DatabaseField @DatabaseField
public int listViewIndex; public int listViewIndex;
@DatabaseField @DatabaseField
public int listViewTop; public int listViewTop;
/** /**
* When simple mode is enabled, CPU intensive methods won't get called. * When simple mode is enabled, CPU intensive methods won't get called. This
* This is used for the thread watcher. * is used for the thread watcher.
*/ */
public boolean simpleMode = false; public boolean simpleMode = false;
/** /**
* Constructs an empty loadable. * Constructs an empty loadable. The mode is INVALID.
* The mode is INVALID.
*/ */
public Loadable() { public Loadable() {
} }
/** /**
* Quick constructor for a board loadable. * Quick constructor for a board loadable.
*
* @param board * @param board
*/ */
public Loadable(String board) { public Loadable(String board) {
@ -54,9 +54,10 @@ public class Loadable {
this.board = board; this.board = board;
no = 0; no = 0;
} }
/** /**
* Quick constructor for a thread loadable. * Quick constructor for a thread loadable.
*
* @param board * @param board
* @param no * @param no
*/ */
@ -65,9 +66,10 @@ public class Loadable {
this.board = board; this.board = board;
this.no = no; this.no = no;
} }
/** /**
* Quick constructor for a thread loadable with an title. * Quick constructor for a thread loadable with an title.
*
* @param board * @param board
* @param no * @param no
* @param title * @param title
@ -78,34 +80,32 @@ public class Loadable {
this.no = no; this.no = no;
this.title = title; this.title = title;
} }
/** /**
* Does not compare the title. * Does not compare the title.
*/ */
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
if (!(object instanceof Loadable)) return false; if (!(object instanceof Loadable))
return false;
Loadable other = (Loadable) object; Loadable other = (Loadable) object;
return return mode == other.mode && board.equals(other.board) && no == other.no;
mode == other.mode &&
board.equals(other.board) &&
no == other.no;
} }
public boolean isBoardMode() { public boolean isBoardMode() {
return mode == Mode.BOARD; return mode == Mode.BOARD;
} }
public boolean isThreadMode() { public boolean isThreadMode() {
return mode == Mode.THREAD; return mode == Mode.THREAD;
} }
public boolean isCatalogMode() { public boolean isCatalogMode() {
return mode == Mode.CATALOG; return mode == Mode.CATALOG;
} }
public void readFromBundle(Context context, Bundle bundle) { public void readFromBundle(Context context, Bundle bundle) {
String p = context.getPackageName(); String p = context.getPackageName();
mode = bundle.getInt(p + ".mode", Mode.INVALID); mode = bundle.getInt(p + ".mode", Mode.INVALID);
@ -115,7 +115,7 @@ public class Loadable {
listViewIndex = bundle.getInt(p + ".listViewIndex"); listViewIndex = bundle.getInt(p + ".listViewIndex");
listViewTop = bundle.getInt(p + ".listViewTop"); listViewTop = bundle.getInt(p + ".listViewTop");
} }
public void writeToBundle(Context context, Bundle bundle) { public void writeToBundle(Context context, Bundle bundle) {
String p = context.getPackageName(); String p = context.getPackageName();
bundle.putInt(p + ".mode", mode); bundle.putInt(p + ".mode", mode);
@ -125,7 +125,7 @@ public class Loadable {
bundle.putInt(p + ".listViewIndex", listViewIndex); bundle.putInt(p + ".listViewIndex", listViewIndex);
bundle.putInt(p + ".listViewTop", listViewTop); bundle.putInt(p + ".listViewTop", listViewTop);
} }
public void readFromBundle(Context context, String tag, Bundle bundle) { public void readFromBundle(Context context, String tag, Bundle bundle) {
String p = context.getPackageName(); String p = context.getPackageName();
mode = bundle.getInt(p + "." + tag + ".mode", Mode.INVALID); mode = bundle.getInt(p + "." + tag + ".mode", Mode.INVALID);
@ -135,7 +135,7 @@ public class Loadable {
listViewIndex = bundle.getInt(p + "." + tag + ".listViewIndex"); listViewIndex = bundle.getInt(p + "." + tag + ".listViewIndex");
listViewTop = bundle.getInt(p + "." + tag + ".listViewTop"); listViewTop = bundle.getInt(p + "." + tag + ".listViewTop");
} }
public void writeToBundle(Context context, String tag, Bundle bundle) { public void writeToBundle(Context context, String tag, Bundle bundle) {
String p = context.getPackageName(); String p = context.getPackageName();
bundle.putInt(p + "." + tag + ".mode", mode); bundle.putInt(p + "." + tag + ".mode", mode);
@ -145,7 +145,7 @@ public class Loadable {
bundle.putInt(p + "." + tag + ".listViewIndex", listViewIndex); bundle.putInt(p + "." + tag + ".listViewIndex", listViewIndex);
bundle.putInt(p + "." + tag + ".listViewTop", listViewTop); bundle.putInt(p + "." + tag + ".listViewTop", listViewTop);
} }
public Loadable copy() { public Loadable copy() {
Loadable copy = new Loadable(); Loadable copy = new Loadable();
copy.mode = mode; copy.mode = mode;
@ -155,10 +155,10 @@ public class Loadable {
copy.listViewIndex = listViewIndex; copy.listViewIndex = listViewIndex;
copy.listViewTop = listViewTop; copy.listViewTop = listViewTop;
copy.simpleMode = simpleMode; copy.simpleMode = simpleMode;
return copy; return copy;
} }
public static class Mode { public static class Mode {
public static final int INVALID = -1; public static final int INVALID = -1;
public static final int THREAD = 0; public static final int THREAD = 0;
@ -166,8 +166,3 @@ public class Loadable {
public static final int CATALOG = 2; public static final int CATALOG = 2;
} }
} }

@ -17,9 +17,9 @@ public class Pin {
// ListView Stuff // ListView Stuff
/** Header is used to display a static header in the drawer listview. */ /** Header is used to display a static header in the drawer listview. */
public Type type = Type.THREAD; public Type type = Type.THREAD;
public static enum Type { public static enum Type {
HEADER, HEADER, THREAD
THREAD
}; };
// PinnedService stuff // PinnedService stuff
@ -33,19 +33,19 @@ public class Pin {
@DatabaseField @DatabaseField
public int watchNewCount; public int watchNewCount;
@DatabaseField @DatabaseField
public int quoteLastCount; public int quoteLastCount;
@DatabaseField @DatabaseField
public int quoteNewCount; public int quoteNewCount;
public boolean isError = false; public boolean isError = false;
public PinWatcher getPinWatcher() { public PinWatcher getPinWatcher() {
return pinWatcher; return pinWatcher;
} }
public int getNewPostsCount() { public int getNewPostsCount() {
if (watchLastCount <= 0) { if (watchLastCount <= 0) {
return 0; return 0;
@ -53,7 +53,7 @@ public class Pin {
return Math.max(0, watchNewCount - watchLastCount); return Math.max(0, watchNewCount - watchLastCount);
} }
} }
public int getNewQuoteCount() { public int getNewQuoteCount() {
if (quoteLastCount <= 0) { if (quoteLastCount <= 0) {
return 0; return 0;
@ -61,7 +61,7 @@ public class Pin {
return Math.max(0, quoteNewCount - quoteLastCount); return Math.max(0, quoteNewCount - quoteLastCount);
} }
} }
public Post getLastSeenPost() { public Post getLastSeenPost() {
if (pinWatcher == null) { if (pinWatcher == null) {
return null; return null;
@ -85,6 +85,3 @@ public class Pin {
} }
} }
} }

@ -95,7 +95,7 @@ public class Post {
/** /**
* Finish up the data * Finish up the data
* *
* @return false if this data is invalid * @return false if this data is invalid
*/ */
public boolean finish(Loadable loadable) { public boolean finish(Loadable loadable) {

@ -9,7 +9,9 @@ import android.view.View;
* Anything that links to something in a post uses this entity. * Anything that links to something in a post uses this entity.
*/ */
public class PostLinkable extends ClickableSpan { public class PostLinkable extends ClickableSpan {
public static enum Type {QUOTE, LINK}; public static enum Type {
QUOTE, LINK
};
public final Post post; public final Post post;
public final String key; public final String key;

@ -7,13 +7,13 @@ import com.j256.ormlite.table.DatabaseTable;
public class SavedReply { public class SavedReply {
@DatabaseField(generatedId = true) @DatabaseField(generatedId = true)
private int id; private int id;
@DatabaseField @DatabaseField
public String board = ""; public String board = "";
@DatabaseField @DatabaseField
public int no; public int no;
@DatabaseField @DatabaseField
public String password = ""; public String password = "";
} }

@ -7,21 +7,21 @@ import com.android.volley.toolbox.ImageLoader.ImageCache;
public class BitmapLruImageCache extends LruCache<String, Bitmap> implements ImageCache { public class BitmapLruImageCache extends LruCache<String, Bitmap> implements ImageCache {
public BitmapLruImageCache(int maxSize) { public BitmapLruImageCache(int maxSize) {
super(maxSize); super(maxSize);
} }
@Override @Override
protected int sizeOf(String key, Bitmap value) { protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight(); return value.getRowBytes() * value.getHeight();
} }
@Override @Override
public Bitmap getBitmap(String url) { public Bitmap getBitmap(String url) {
return get(url); return get(url);
} }
@Override @Override
public void putBitmap(String url, Bitmap bitmap) { public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap); put(url, bitmap);
} }
} }

@ -14,7 +14,7 @@ public class BoardsRequest extends JsonReaderRequest<ArrayList<Board>> {
public BoardsRequest(String url, Listener<ArrayList<Board>> listener, ErrorListener errorListener) { public BoardsRequest(String url, Listener<ArrayList<Board>> listener, ErrorListener errorListener) {
super(url, listener, errorListener); super(url, listener, errorListener);
} }
@Override @Override
public ArrayList<Board> readJson(JsonReader reader) { public ArrayList<Board> readJson(JsonReader reader) {
return parseJson(reader); return parseJson(reader);
@ -22,49 +22,49 @@ public class BoardsRequest extends JsonReaderRequest<ArrayList<Board>> {
private ArrayList<Board> parseJson(JsonReader reader) { private ArrayList<Board> parseJson(JsonReader reader) {
ArrayList<Board> list = new ArrayList<Board>(); ArrayList<Board> list = new ArrayList<Board>();
try { try {
reader.beginObject(); reader.beginObject();
// Page object // Page object
while (reader.hasNext()) { while (reader.hasNext()) {
String key = reader.nextName(); String key = reader.nextName();
if (key.equals("boards")) { if (key.equals("boards")) {
reader.beginArray(); reader.beginArray();
while(reader.hasNext()) { while (reader.hasNext()) {
list.add(readBoardEntry(reader)); list.add(readBoardEntry(reader));
} }
reader.endArray(); reader.endArray();
} else { } else {
throw new IOException("Invalid data received"); throw new IOException("Invalid data received");
} }
} }
reader.endObject(); reader.endObject();
} catch(IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
e.printStackTrace(); e.printStackTrace();
} catch(IllegalStateException e) { } catch (IllegalStateException e) {
e.printStackTrace(); e.printStackTrace();
} }
return list; return list;
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return Priority.LOW; return Priority.LOW;
} }
private Board readBoardEntry(JsonReader reader) throws IOException { private Board readBoardEntry(JsonReader reader) throws IOException {
reader.beginObject(); reader.beginObject();
Board board = new Board(); Board board = new Board();
while(reader.hasNext()) { while (reader.hasNext()) {
String key = reader.nextName(); String key = reader.nextName();
if (key.equals("title")) { if (key.equals("title")) {
// Post number // Post number
board.key = reader.nextString(); board.key = reader.nextString();
@ -78,13 +78,13 @@ public class BoardsRequest extends JsonReaderRequest<ArrayList<Board>> {
reader.skipValue(); reader.skipValue();
} }
} }
reader.endObject(); reader.endObject();
if (!board.finish()) { if (!board.finish()) {
throw new IOException("Invalid data received about boards."); throw new IOException("Invalid data received about boards.");
} }
return board; return board;
} }
} }

@ -7,26 +7,24 @@ import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener; import com.android.volley.Response.Listener;
/** /**
* Request a plain byte[] * Request a plain byte[] Warning: no caching!
* Warning: no caching!
*/ */
public class ByteArrayRequest extends Request<byte[]> { public class ByteArrayRequest extends Request<byte[]> {
protected final Listener<byte[]> listener; protected final Listener<byte[]> listener;
public ByteArrayRequest(String url, Listener<byte[]> listener, ErrorListener errorListener) { public ByteArrayRequest(String url, Listener<byte[]> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener); super(Method.GET, url, errorListener);
this.listener = listener; this.listener = listener;
} }
@Override @Override
protected void deliverResponse(byte[] response) { protected void deliverResponse(byte[] response) {
listener.onResponse(response); listener.onResponse(response);
} }
@Override @Override
protected Response<byte[]> parseNetworkResponse(NetworkResponse response) { protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
return Response.success(response.data, null); return Response.success(response.data, null);
} }
} }

@ -25,14 +25,19 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
/** /**
* Creates a ChanReaderRequest with supplied params * Creates a ChanReaderRequest with supplied params
* @param mode ThreadManager mode *
* @param board board key * @param mode
* @param no page for board, no for threads * ThreadManager mode
* @param board
* board key
* @param no
* page for board, no for threads
* @param listener * @param listener
* @param errorListener * @param errorListener
* @return New instance of ChanReaderRequest * @return New instance of ChanReaderRequest
*/ */
public static ChanReaderRequest newInstance(Loadable loadable, List<Post> cached, Listener<List<Post>> listener, ErrorListener errorListener) { public static ChanReaderRequest newInstance(Loadable loadable, List<Post> cached, Listener<List<Post>> listener,
ErrorListener errorListener) {
String url; String url;
if (loadable.isBoardMode()) { if (loadable.isBoardMode()) {
@ -110,13 +115,13 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
} }
} }
reader.endObject(); reader.endObject();
} catch(IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(IllegalStateException e) { } catch (IllegalStateException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} }
@ -142,7 +147,8 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
list.add(readPostObject(reader)); list.add(readPostObject(reader));
// Only consume one post // Only consume one post
while (reader.hasNext()) reader.skipValue(); while (reader.hasNext())
reader.skipValue();
reader.endArray(); reader.endArray();
} else { } else {
@ -158,13 +164,13 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
} }
reader.endObject(); reader.endObject();
} catch(IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(IllegalStateException e) { } catch (IllegalStateException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} }
@ -199,13 +205,13 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
} }
reader.endArray(); reader.endArray();
} catch(IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} catch(IllegalStateException e) { } catch (IllegalStateException e) {
e.printStackTrace(); e.printStackTrace();
setError(new ParseError(e)); setError(new ParseError(e));
} }
@ -218,16 +224,16 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
post.board = loadable.board; post.board = loadable.board;
reader.beginObject(); reader.beginObject();
while(reader.hasNext()) { while (reader.hasNext()) {
String key = reader.nextName(); String key = reader.nextName();
if (key.equals("no")) { if (key.equals("no")) {
// Post number // Post number
post.no = reader.nextInt(); post.no = reader.nextInt();
/*} else if (key.equals("time")) { /*} else if (key.equals("time")) {
// Time // Time
long time = reader.nextLong(); long time = reader.nextLong();
post.date = new Date(time * 1000);*/ post.date = new Date(time * 1000);*/
} else if (key.equals("now")) { } else if (key.equals("now")) {
post.date = reader.nextString(); post.date = reader.nextString();
} else if (key.equals("name")) { } else if (key.equals("name")) {
@ -270,7 +276,7 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
post.capcode = reader.nextString(); post.capcode = reader.nextString();
} else { } else {
// Unknown/ignored key // Unknown/ignored key
// log("Unknown/ignored key: " + key + "."); // log("Unknown/ignored key: " + key + ".");
reader.skipValue(); reader.skipValue();
} }
} }
@ -295,8 +301,3 @@ public class ChanReaderRequest extends JsonReaderRequest<List<Post>> {
} }
} }
} }

@ -18,7 +18,7 @@ public class FileRequest extends Request<Void> {
public FileRequest(String url, Listener<File> listener, ErrorListener errorListener) { public FileRequest(String url, Listener<File> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener); super(Method.GET, url, errorListener);
this.listener = listener; this.listener = listener;
setShouldCache(true); setShouldCache(true);
} }

@ -15,24 +15,24 @@ import com.android.volley.toolbox.HttpHeaderParser;
public class GIFRequest extends Request<GIFView> { public class GIFRequest extends Request<GIFView> {
protected final Listener<GIFView> listener; protected final Listener<GIFView> listener;
private final Context context; private final Context context;
public GIFRequest(String url, Listener<GIFView> listener, ErrorListener errorListener, Context context) { public GIFRequest(String url, Listener<GIFView> listener, ErrorListener errorListener, Context context) {
super(Method.GET, url, errorListener); super(Method.GET, url, errorListener);
this.listener = listener; this.listener = listener;
this.context = context; this.context = context;
} }
@Override @Override
protected void deliverResponse(GIFView response) { protected void deliverResponse(GIFView response) {
listener.onResponse(response); listener.onResponse(response);
} }
@Override @Override
protected Response<GIFView> parseNetworkResponse(NetworkResponse response) { protected Response<GIFView> parseNetworkResponse(NetworkResponse response) {
GIFView gifView = new GIFView(context); GIFView gifView = new GIFView(context);
boolean success = gifView.setData(response.data); boolean success = gifView.setData(response.data);
if (success) { if (success) {
return Response.success(gifView, HttpHeaderParser.parseCacheHeaders(response)); return Response.success(gifView, HttpHeaderParser.parseCacheHeaders(response));
} else { } else {
@ -40,7 +40,3 @@ public class GIFRequest extends Request<GIFView> {
} }
} }
} }

@ -68,7 +68,3 @@ public abstract class JsonReaderRequest<T> extends Request<T> {
public abstract T readJson(JsonReader reader); public abstract T readJson(JsonReader reader);
} }

@ -55,7 +55,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {
switch(oldVersion) { switch (oldVersion) {
// Change tables if we make adjustments // Change tables if we make adjustments
} }
@ -83,8 +83,3 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
} }
} }
} }

@ -95,7 +95,7 @@ public class DatabaseManager {
for (Pin pin : pins) { for (Pin pin : pins) {
helper.loadableDao.update(pin.loadable); helper.loadableDao.update(pin.loadable);
} }
} catch(SQLException e) { } catch (SQLException e) {
Logger.e(TAG, "Error updating pins in db", e); Logger.e(TAG, "Error updating pins in db", e);
} }
} }
@ -103,10 +103,10 @@ public class DatabaseManager {
public List<Pin> getPinned() { public List<Pin> getPinned() {
List<Pin> list = null; List<Pin> list = null;
try { try {
list = helper.pinDao.queryForAll(); list = helper.pinDao.queryForAll();
for (Pin p : list) { for (Pin p : list) {
helper.loadableDao.refresh(p.loadable); helper.loadableDao.refresh(p.loadable);
} }
} catch (SQLException e) { } catch (SQLException e) {
Logger.e(TAG, "Error getting pins from db", e); Logger.e(TAG, "Error getting pins from db", e);
} }
@ -133,8 +133,3 @@ public class DatabaseManager {
loadSavedReplies(); loadSavedReplies();
} }
} }

@ -190,7 +190,7 @@ public class WatchService extends Service {
/** /**
* Returns the sleep time the user specified for background iteration * Returns the sleep time the user specified for background iteration
* *
* @return the sleep time in ms, or -1 if background reloading is disabled * @return the sleep time in ms, or -1 if background reloading is disabled
*/ */
private long getBackgroundTimeout() { private long getBackgroundTimeout() {

@ -26,12 +26,12 @@ public class ScrollerRunnable implements Runnable {
final int firstPos = mList.getFirstVisiblePosition(); final int firstPos = mList.getFirstVisiblePosition();
final int lastPos = firstPos + mList.getChildCount() - 1; final int lastPos = firstPos + mList.getChildCount() - 1;
// int viewTravelCount = 0; // int viewTravelCount = 0;
if (position <= firstPos) { if (position <= firstPos) {
// viewTravelCount = firstPos - position + 1; // viewTravelCount = firstPos - position + 1;
mMode = MOVE_UP_POS; mMode = MOVE_UP_POS;
} else if (position >= lastPos) { } else if (position >= lastPos) {
// viewTravelCount = position - lastPos + 1; // viewTravelCount = position - lastPos + 1;
mMode = MOVE_DOWN_POS; mMode = MOVE_DOWN_POS;
} else { } else {
// Already on screen, nothing to do // Already on screen, nothing to do
@ -64,8 +64,8 @@ public class ScrollerRunnable implements Runnable {
if (lastPos == mLastSeenPos) { if (lastPos == mLastSeenPos) {
// No new views, let things keep going. // No new views, let things keep going.
// mList.post(this); // mList.post(this);
// return; // return;
} }
final View lastView = mList.getChildAt(lastViewIndex); final View lastView = mList.getChildAt(lastViewIndex);
@ -86,8 +86,8 @@ public class ScrollerRunnable implements Runnable {
case MOVE_UP_POS: { case MOVE_UP_POS: {
if (firstPos == mLastSeenPos) { if (firstPos == mLastSeenPos) {
// No new views, let things keep going. // No new views, let things keep going.
// mList.post(this); // mList.post(this);
// return; // return;
} }
final View firstView = mList.getChildAt(0); final View firstView = mList.getChildAt(0);

@ -33,37 +33,44 @@ import android.widget.AbsListView;
import android.widget.ListView; import android.widget.ListView;
/** /**
* A {@link android.view.View.OnTouchListener} that makes the list items in a {@link ListView} * A {@link android.view.View.OnTouchListener} that makes the list items in a
* dismissable. {@link ListView} is given special treatment because by default it handles touches * {@link ListView} dismissable. {@link ListView} is given special treatment
* for its list items... i.e. it's in charge of drawing the pressed state (the list selector), * because by default it handles touches for its list items... i.e. it's in
* handling list item clicks, etc. * charge of drawing the pressed state (the list selector), handling list item
* * clicks, etc.
* <p>After creating the listener, the caller should also call {@link *
* ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}, passing in the scroll * <p>
* listener returned by {@link #makeScrollListener()}. If a scroll listener is already assigned, the * After creating the listener, the caller should also call
* caller should still pass scroll changes through to this listener. This will ensure that this * {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}
* {@link SwipeDismissListViewTouchListener} is paused during list view scrolling.</p> * , passing in the scroll listener returned by {@link #makeScrollListener()}.
* * If a scroll listener is already assigned, the caller should still pass scroll
* <p>Example usage:</p> * changes through to this listener. This will ensure that this
* * {@link SwipeDismissListViewTouchListener} is paused during list view
* scrolling.
* </p>
*
* <p>
* Example usage:
* </p>
*
* <pre> * <pre>
* SwipeDismissListViewTouchListener touchListener = * SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(listView,
* new SwipeDismissListViewTouchListener( * new SwipeDismissListViewTouchListener.OnDismissCallback() {
* listView, * public void onDismiss(ListView listView, int[] reverseSortedPositions) {
* new SwipeDismissListViewTouchListener.OnDismissCallback() { * for (int position : reverseSortedPositions) {
* public void onDismiss(ListView listView, int[] reverseSortedPositions) { * adapter.remove(adapter.getItem(position));
* for (int position : reverseSortedPositions) { * }
* adapter.remove(adapter.getItem(position)); * adapter.notifyDataSetChanged();
* } * }
* adapter.notifyDataSetChanged(); * });
* }
* });
* listView.setOnTouchListener(touchListener); * listView.setOnTouchListener(touchListener);
* listView.setOnScrollListener(touchListener.makeScrollListener()); * listView.setOnScrollListener(touchListener.makeScrollListener());
* </pre> * </pre>
* *
* <p>This class Requires API level 12 or later due to use of {@link * <p>
* android.view.ViewPropertyAnimator}.</p> * This class Requires API level 12 or later due to use of
* {@link android.view.ViewPropertyAnimator}.
* </p>
*/ */
public class SwipeDismissListViewTouchListener implements View.OnTouchListener { public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values // Cached ViewConfiguration and system-wide constant values
@ -88,8 +95,9 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
private boolean mPaused; private boolean mPaused;
/** /**
* The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client * The callback interface used by {@link SwipeDismissListViewTouchListener}
* about a successful dismissal of one or more list item positions. * to inform its client about a successful dismissal of one or more list
* item positions.
*/ */
public interface DismissCallbacks { public interface DismissCallbacks {
/** /**
@ -98,50 +106,57 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
boolean canDismiss(int position); boolean canDismiss(int position);
/** /**
* Called when the user has indicated they she would like to dismiss one or more list item * Called when the user has indicated they she would like to dismiss one
* positions. * or more list item positions.
* *
* @param listView The originating {@link ListView}. * @param listView
* @param reverseSortedPositions An array of positions to dismiss, sorted in descending * The originating {@link ListView}.
* order for convenience. * @param reverseSortedPositions
* An array of positions to dismiss, sorted in descending
* order for convenience.
*/ */
void onDismiss(ListView listView, int[] reverseSortedPositions); void onDismiss(ListView listView, int[] reverseSortedPositions);
} }
/** /**
* Constructs a new swipe-to-dismiss touch listener for the given list view. * Constructs a new swipe-to-dismiss touch listener for the given list view.
* *
* @param listView The list view whose items should be dismissable. * @param listView
* @param callbacks The callback to trigger when the user has indicated that she would like to * The list view whose items should be dismissable.
* dismiss one or more list items. * @param callbacks
* The callback to trigger when the user has indicated that she
* would like to dismiss one or more list items.
*/ */
public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) { public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) {
ViewConfiguration vc = ViewConfiguration.get(listView.getContext()); ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
mSlop = Math.max(1, (int)(vc.getScaledTouchSlop() * 0.5f)); mSlop = Math.max(1, (int) (vc.getScaledTouchSlop() * 0.5f));
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
mAnimationTime = listView.getContext().getResources().getInteger( mAnimationTime = listView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
android.R.integer.config_shortAnimTime);
mListView = listView; mListView = listView;
mCallbacks = callbacks; mCallbacks = callbacks;
} }
/** /**
* Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures. * Enables or disables (pauses or resumes) watching for swipe-to-dismiss
* * gestures.
* @param enabled Whether or not to watch for gestures. *
* @param enabled
* Whether or not to watch for gestures.
*/ */
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
mPaused = !enabled; mPaused = !enabled;
} }
/** /**
* Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the {@link * Returns an {@link android.widget.AbsListView.OnScrollListener} to be
* ListView} using {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}. * added to the {@link ListView} using
* If a scroll listener is already assigned, the caller should still pass scroll changes through * {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}
* to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is * . If a scroll listener is already assigned, the caller should still pass
* paused during list view scrolling.</p> * scroll changes through to this listener. This will ensure that this
* * {@link SwipeDismissListViewTouchListener} is paused during list view
* scrolling.</p>
*
* @see SwipeDismissListViewTouchListener * @see SwipeDismissListViewTouchListener
*/ */
public AbsListView.OnScrollListener makeScrollListener() { public AbsListView.OnScrollListener makeScrollListener() {
@ -158,8 +173,8 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
} }
/** /**
* Manually cause the item at the given position to be dismissed (trigger the dismiss * Manually cause the item at the given position to be dismissed (trigger
* animation). * the dismiss animation).
*/ */
public void dismiss(int position) { public void dismiss(int position) {
dismiss(getViewForPosition(position), position, true); dismiss(getViewForPosition(position), position, true);
@ -172,136 +187,126 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
} }
switch (motionEvent.getActionMasked()) { switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
if (mPaused) { if (mPaused) {
return false; return false;
}
// TODO: ensure this is a finger, and set a flag
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = mListView.getChildCount();
int[] listViewCoords = new int[2];
mListView.getLocationOnScreen(listViewCoords);
int x = (int) motionEvent.getRawX() - listViewCoords[0];
int y = (int) motionEvent.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = mListView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break;
}
}
if (mDownView != null) {
mDownX = motionEvent.getRawX();
mDownPosition = mListView.getPositionForView(mDownView);
if (mCallbacks.canDismiss(mDownPosition)) {
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
} else {
mDownView = null;
}
}
view.onTouchEvent(motionEvent);
return true;
} }
case MotionEvent.ACTION_UP: { // TODO: ensure this is a finger, and set a flag
if (mVelocityTracker == null) {
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = mListView.getChildCount();
int[] listViewCoords = new int[2];
mListView.getLocationOnScreen(listViewCoords);
int x = (int) motionEvent.getRawX() - listViewCoords[0];
int y = (int) motionEvent.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = mListView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break; break;
} }
}
float deltaX = motionEvent.getRawX() - mDownX; if (mDownView != null) {
mVelocityTracker.addMovement(motionEvent); mDownX = motionEvent.getRawX();
mVelocityTracker.computeCurrentVelocity(1000); mDownPosition = mListView.getPositionForView(mDownView);
float velocityX = mVelocityTracker.getXVelocity(); if (mCallbacks.canDismiss(mDownPosition)) {
float absVelocityX = Math.abs(velocityX); mVelocityTracker = VelocityTracker.obtain();
float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); mVelocityTracker.addMovement(motionEvent);
boolean dismiss = false;
boolean dismissRight = false;
if (Math.abs(deltaX) > mViewWidth / 2) {
dismiss = true;
dismissRight = deltaX > 0;
} else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
&& absVelocityY < absVelocityX) {
// dismiss only if flinging in the same direction as dragging
dismiss = (velocityX < 0) == (deltaX < 0);
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss) {
// dismiss
dismiss(mDownView, mDownPosition, dismissRight);
} else { } else {
// cancel mDownView = null;
mDownView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
} }
mVelocityTracker.recycle(); }
mVelocityTracker = null; view.onTouchEvent(motionEvent);
mDownX = 0; return true;
mDownView = null; }
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false; case MotionEvent.ACTION_UP: {
if (mVelocityTracker == null) {
break; break;
} }
case MotionEvent.ACTION_CANCEL: { float deltaX = motionEvent.getRawX() - mDownX;
if (mVelocityTracker == null) { mVelocityTracker.addMovement(motionEvent);
break; mVelocityTracker.computeCurrentVelocity(1000);
} float velocityX = mVelocityTracker.getXVelocity();
float absVelocityX = Math.abs(velocityX);
float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
boolean dismiss = false;
boolean dismissRight = false;
if (Math.abs(deltaX) > mViewWidth / 2) {
dismiss = true;
dismissRight = deltaX > 0;
} else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
&& absVelocityY < absVelocityX) {
// dismiss only if flinging in the same direction as dragging
dismiss = (velocityX < 0) == (deltaX < 0);
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss) {
// dismiss
dismiss(mDownView, mDownPosition, dismissRight);
} else {
// cancel
mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mDownX = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
break;
}
if (mDownView != null) { case MotionEvent.ACTION_CANCEL: {
// cancel if (mVelocityTracker == null) {
mDownView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mDownX = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
break; break;
} }
case MotionEvent.ACTION_MOVE: { if (mDownView != null) {
if (mVelocityTracker == null || mPaused) { // cancel
break; mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null);
} }
mVelocityTracker.recycle();
mVelocityTracker.addMovement(motionEvent); mVelocityTracker = null;
float deltaX = motionEvent.getRawX() - mDownX; mDownX = 0;
if (Math.abs(deltaX) > mSlop) { mDownView = null;
mSwiping = true; mDownPosition = ListView.INVALID_POSITION;
mListView.requestDisallowInterceptTouchEvent(true); mSwiping = false;
break;
// Cancel ListView's touch (un-highlighting the item) }
MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
(motionEvent.getActionIndex()
<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mListView.onTouchEvent(cancelEvent);
cancelEvent.recycle();
}
if (mSwiping) { case MotionEvent.ACTION_MOVE: {
mDownView.setTranslationX(deltaX); if (mVelocityTracker == null || mPaused) {
mDownView.setAlpha(Math.max(0.15f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break; break;
} }
mVelocityTracker.addMovement(motionEvent);
float deltaX = motionEvent.getRawX() - mDownX;
if (Math.abs(deltaX) > mSlop) {
mSwiping = true;
mListView.requestDisallowInterceptTouchEvent(true);
// Cancel ListView's touch (un-highlighting the item)
MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL
| (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mListView.onTouchEvent(cancelEvent);
cancelEvent.recycle();
}
if (mSwiping) {
mDownView.setTranslationX(deltaX);
mDownView.setAlpha(Math.max(0.15f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;
}
} }
return false; return false;
} }
@ -315,10 +320,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
return; return;
} }
view.animate() view.animate().translationX(dismissRight ? mViewWidth : -mViewWidth).alpha(0).setDuration(mAnimationTime)
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() { .setListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
@ -328,11 +330,8 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
} }
private View getViewForPosition(int position) { private View getViewForPosition(int position) {
int index = position int index = position - (mListView.getFirstVisiblePosition() - mListView.getHeaderViewsCount());
- (mListView.getFirstVisiblePosition() - mListView.getHeaderViewsCount()); return (index >= 0 && index < mListView.getChildCount()) ? mListView.getChildAt(index) : null;
return (index >= 0 && index < mListView.getChildCount())
? mListView.getChildAt(index)
: null;
} }
class PendingDismissData implements Comparable<PendingDismissData> { class PendingDismissData implements Comparable<PendingDismissData> {

@ -12,38 +12,26 @@ public class ViewFlipperAnimations {
public static TranslateAnimation BACK_OUT; public static TranslateAnimation BACK_OUT;
public static TranslateAnimation NEXT_IN; public static TranslateAnimation NEXT_IN;
public static TranslateAnimation NEXT_OUT; public static TranslateAnimation NEXT_OUT;
static { static {
// Setup the static TranslateAnimations for the ViewFlipper // Setup the static TranslateAnimations for the ViewFlipper
BACK_IN = new TranslateAnimation( BACK_IN = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, -1f, Animation.RELATIVE_TO_PARENT, 0f, 0, 0f, 0,
Animation.RELATIVE_TO_PARENT, -1f, 0f);
Animation.RELATIVE_TO_PARENT, 0f,
0, 0f, 0, 0f
);
BACK_IN.setInterpolator(new AccelerateDecelerateInterpolator()); BACK_IN.setInterpolator(new AccelerateDecelerateInterpolator());
BACK_IN.setDuration(300); BACK_IN.setDuration(300);
BACK_OUT = new TranslateAnimation( BACK_OUT = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 1f, 0, 0f, 0,
Animation.RELATIVE_TO_PARENT, 0f, 0f);
Animation.RELATIVE_TO_PARENT, 1f,
0, 0f, 0, 0f
);
BACK_OUT.setInterpolator(new AccelerateDecelerateInterpolator()); BACK_OUT.setInterpolator(new AccelerateDecelerateInterpolator());
BACK_OUT.setDuration(300); BACK_OUT.setDuration(300);
NEXT_IN = new TranslateAnimation( NEXT_IN = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 1f, Animation.RELATIVE_TO_PARENT, 0f, 0, 0f, 0,
Animation.RELATIVE_TO_PARENT, 1f, 0f);
Animation.RELATIVE_TO_PARENT, 0f,
0, 0f, 0, 0f
);
NEXT_IN.setInterpolator(new AccelerateDecelerateInterpolator()); NEXT_IN.setInterpolator(new AccelerateDecelerateInterpolator());
NEXT_IN.setDuration(300); NEXT_IN.setDuration(300);
NEXT_OUT = new TranslateAnimation( NEXT_OUT = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, -1f, 0, 0f,
Animation.RELATIVE_TO_PARENT, 0f, 0, 0f);
Animation.RELATIVE_TO_PARENT, -1f,
0, 0f, 0, 0f
);
NEXT_OUT.setInterpolator(new AccelerateDecelerateInterpolator()); NEXT_OUT.setInterpolator(new AccelerateDecelerateInterpolator());
NEXT_OUT.setDuration(300); NEXT_OUT.setDuration(300);
} }

@ -8,10 +8,10 @@ public class AboutActivity extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
WebView webView = new WebView(this); WebView webView = new WebView(this);
webView.loadUrl("file:///android_asset/html/licences.html"); webView.loadUrl("file:///android_asset/html/licences.html");
setContentView(webView); setContentView(webView);
} }
} }

@ -26,104 +26,100 @@ public class BoardEditor extends Activity {
private DynamicListView<Board> listView; private DynamicListView<Board> listView;
private BoardEditAdapter adapter; private BoardEditAdapter adapter;
private AlertDialog dialog; private AlertDialog dialog;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.board_edit); setContentView(R.layout.board_edit);
// Copy not a reference // Copy not a reference
list = (ArrayList<Board>) ChanApplication.getBoardManager().getMyBoards().clone(); list = (ArrayList<Board>) ChanApplication.getBoardManager().getMyBoards().clone();
adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); adapter = new BoardEditAdapter(this, R.layout.board_view, list, this);
listView = (DynamicListView<Board>) findViewById(R.id.board_edit_list); listView = (DynamicListView<Board>) findViewById(R.id.board_edit_list);
listView.setArrayList(list); listView.setArrayList(list);
listView.setAdapter(adapter); listView.setAdapter(adapter);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
// For runtime changes // For runtime changes
if (list.size() > 0) { if (list.size() > 0) {
ChanApplication.getBoardManager().setMyBoards((ArrayList<Board>) list.clone()); ChanApplication.getBoardManager().setMyBoards((ArrayList<Board>) list.clone());
} }
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (dialog != null) { if (dialog != null) {
dialog.dismiss(); dialog.dismiss();
} }
} }
private void addBoard(String value) { private void addBoard(String value) {
ChanApplication.getBoardManager().addBoard(list, value); ChanApplication.getBoardManager().addBoard(list, value);
adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); adapter = new BoardEditAdapter(this, R.layout.board_view, list, this);
listView.setArrayList(list); listView.setArrayList(list);
listView.setAdapter(adapter); listView.setAdapter(adapter);
} }
private void removeBoard(String value) { private void removeBoard(String value) {
if (list.size() <= 1) return; if (list.size() <= 1)
return;
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
Board e = list.get(i); Board e = list.get(i);
if (e.value == value) { if (e.value == value) {
list.remove(i); list.remove(i);
adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); adapter = new BoardEditAdapter(this, R.layout.board_view, list, this);
listView.setArrayList(list); listView.setArrayList(list);
listView.setAdapter(adapter); listView.setAdapter(adapter);
} }
} }
} }
public void onDeleteClicked(Board board) { public void onDeleteClicked(Board board) {
removeBoard(board.value); removeBoard(board.value);
} }
private void showAddBoardDialog() { private void showAddBoardDialog() {
final EditText text = new EditText(this); final EditText text = new EditText(this);
text.setSingleLine(); text.setSingleLine();
dialog = new AlertDialog.Builder(this) dialog = new AlertDialog.Builder(this).setPositiveButton(R.string.add, new DialogInterface.OnClickListener() {
.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { @Override
@Override public void onClick(DialogInterface d, int which) {
public void onClick(DialogInterface d, int which) { String value = text.getText().toString();
String value = text.getText().toString();
if (!TextUtils.isEmpty(value)) {
if (!TextUtils.isEmpty(value)) { addBoard(value);
addBoard(value);
}
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
} }
}) }
.setTitle(R.string.board_add) }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
.setView(text) @Override
.create(); public void onClick(DialogInterface d, int which) {
}
}).setTitle(R.string.board_add).setView(text).create();
text.requestFocus(); text.requestFocus();
dialog.setOnDismissListener(new OnDismissListener() { dialog.setOnDismissListener(new OnDismissListener() {
@Override @Override
public void onDismiss(DialogInterface d) { public void onDismiss(DialogInterface d) {
dialog = null; dialog = null;
} }
}); });
dialog.setOnShowListener(new OnShowListener() { dialog.setOnShowListener(new OnShowListener() {
@Override @Override
public void onShow(DialogInterface dialog) { public void onShow(DialogInterface dialog) {
@ -131,30 +127,25 @@ public class BoardEditor extends Activity {
imm.showSoftInput(text, 0); imm.showSoftInput(text, 0);
} }
}); });
dialog.show(); dialog.show();
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.board_edit, menu); getMenuInflater().inflate(R.menu.board_edit, menu);
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) { switch (item.getItemId()) {
case R.id.action_add_board: case R.id.action_add_board:
showAddBoardDialog(); showAddBoardDialog();
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }

@ -13,12 +13,12 @@ public class DeveloperActivity extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
LinearLayout wrapper = new LinearLayout(this); LinearLayout wrapper = new LinearLayout(this);
wrapper.setOrientation(LinearLayout.VERTICAL); wrapper.setOrientation(LinearLayout.VERTICAL);
Button crashButton = new Button(this); Button crashButton = new Button(this);
crashButton.setOnClickListener(new View.OnClickListener() { crashButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -27,19 +27,19 @@ public class DeveloperActivity extends Activity {
} }
}); });
crashButton.setText("Crash the app"); crashButton.setText("Crash the app");
wrapper.addView(crashButton); wrapper.addView(crashButton);
String dbSummary = ""; String dbSummary = "";
dbSummary += "Database summary:\n"; dbSummary += "Database summary:\n";
dbSummary += ChanApplication.getDatabaseManager().getSummary(); dbSummary += ChanApplication.getDatabaseManager().getSummary();
TextView db = new TextView(this); TextView db = new TextView(this);
db.setPadding(0, 25, 0, 0); db.setPadding(0, 25, 0, 0);
db.setText(dbSummary); db.setText(dbSummary);
wrapper.addView(db); wrapper.addView(db);
Button resetDbButton = new Button(this); Button resetDbButton = new Button(this);
resetDbButton.setOnClickListener(new View.OnClickListener() { resetDbButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -50,12 +50,7 @@ public class DeveloperActivity extends Activity {
}); });
resetDbButton.setText("Delete database"); resetDbButton.setText("Delete database");
wrapper.addView(resetDbButton); wrapper.addView(resetDbButton);
setContentView(wrapper); setContentView(wrapper);
} }
} }

@ -77,8 +77,3 @@ public class ImagePickActivity extends Activity {
} }
} }
} }

@ -125,7 +125,7 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang
public void invalidateActionBar() { public void invalidateActionBar() {
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
public void callOnSelect() { public void callOnSelect() {
ImageViewFragment fragment = getCurrentFragment(); ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) { if (fragment != null) {
@ -148,7 +148,7 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang
if (fragment != null) { if (fragment != null) {
fragment.customOnOptionsItemSelected(item); fragment.customOnOptionsItemSelected(item);
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
@ -156,17 +156,17 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.image_view, menu); getMenuInflater().inflate(R.menu.image_view, menu);
return true; return true;
} }
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
ImageViewFragment fragment = getCurrentFragment(); ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) { if (fragment != null) {
fragment.onPrepareOptionsMenu(currentPosition, adapter, menu); fragment.onPrepareOptionsMenu(currentPosition, adapter, menu);
} }
return super.onPrepareOptionsMenu(menu); return super.onPrepareOptionsMenu(menu);
} }

@ -11,40 +11,40 @@ import android.view.MenuItem;
public class ReplyActivity extends Activity { public class ReplyActivity extends Activity {
private static final String TAG = "ReplyActivity"; private static final String TAG = "ReplyActivity";
private static Loadable loadable; private static Loadable loadable;
public static void setLoadable(Loadable l) { public static void setLoadable(Loadable l) {
loadable = l; loadable = l;
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (loadable != null) { if (loadable != null) {
getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setDisplayHomeAsUpEnabled(true);
FragmentTransaction ft = getFragmentManager().beginTransaction(); FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, ReplyFragment.newInstance(loadable)); ft.replace(android.R.id.content, ReplyFragment.newInstance(loadable));
ft.commitAllowingStateLoss(); ft.commitAllowingStateLoss();
loadable = null; loadable = null;
} else { } else {
Logger.e(TAG, "ThreadFragment was null, exiting!"); Logger.e(TAG, "ThreadFragment was null, exiting!");
finish(); finish();
} }
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
finish(); finish();
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }

@ -9,7 +9,7 @@ public class SettingsActivity extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
} }
} }

@ -33,13 +33,13 @@ import android.widget.TextView;
public class BoardEditAdapter extends ArrayAdapter<Board> { public class BoardEditAdapter extends ArrayAdapter<Board> {
HashMap<Board, Integer> mIdMap = new HashMap<Board, Integer>(); HashMap<Board, Integer> mIdMap = new HashMap<Board, Integer>();
private final BoardEditor editor; private final BoardEditor editor;
public BoardEditAdapter(Context context, int textViewResourceId, List<Board> objects, BoardEditor editor) { public BoardEditAdapter(Context context, int textViewResourceId, List<Board> objects, BoardEditor editor) {
super(context, textViewResourceId, objects); super(context, textViewResourceId, objects);
this.editor = editor; this.editor = editor;
for (int i = 0; i < objects.size(); ++i) { for (int i = 0; i < objects.size(); ++i) {
mIdMap.put(objects.get(i), i); mIdMap.put(objects.get(i), i);
} }
@ -50,30 +50,30 @@ public class BoardEditAdapter extends ArrayAdapter<Board> {
if (position < 0 || position >= mIdMap.size()) { if (position < 0 || position >= mIdMap.size()) {
return -1; return -1;
} }
Board item = getItem(position); Board item = getItem(position);
return mIdMap.get(item); return mIdMap.get(item);
} }
@Override @Override
public View getView(final int position, View convertView, ViewGroup parent) { public View getView(final int position, View convertView, ViewGroup parent) {
String text = getItem(position).key; String text = getItem(position).key;
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.board_view, null); View view = inflater.inflate(R.layout.board_view, null);
TextView textView = (TextView)view.findViewById(R.id.board_view_text); TextView textView = (TextView) view.findViewById(R.id.board_view_text);
textView.setText(text); textView.setText(text);
Button button = (Button)view.findViewById(R.id.board_view_delete); Button button = (Button) view.findViewById(R.id.board_view_delete);
button.setOnClickListener(new View.OnClickListener() { button.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
editor.onDeleteClicked(getItem(position)); editor.onDeleteClicked(getItem(position));
} }
}); });
return view; return view;
} }
@ -82,4 +82,3 @@ public class BoardEditAdapter extends ArrayAdapter<Board> {
return true; return true;
} }
} }

@ -12,48 +12,43 @@ import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.View; import android.view.View;
public class ImageViewAdapter extends FragmentStatePagerAdapter { public class ImageViewAdapter extends FragmentStatePagerAdapter {
private final ImageViewActivity activity; private final ImageViewActivity activity;
private final ArrayList<Post> postList = new ArrayList<Post>(); private final ArrayList<Post> postList = new ArrayList<Post>();
public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) { public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) {
super(fragmentManager); super(fragmentManager);
this.activity = activity; this.activity = activity;
} }
@Override @Override
public int getCount() { public int getCount() {
return postList.size(); return postList.size();
} }
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
return ImageViewFragment.newInstance(postList.get(position), activity, position); return ImageViewFragment.newInstance(postList.get(position), activity, position);
} }
public Post getPost(int position) { public Post getPost(int position) {
if (position < 0 || position >= getCount()) return null; if (position < 0 || position >= getCount())
return null;
return postList.get(position); return postList.get(position);
} }
@Override @Override
public void destroyItem(View collection, int position, Object o) { public void destroyItem(View collection, int position, Object o) {
View view = (View)o; View view = (View) o;
((ViewPager) collection).removeView(view); ((ViewPager) collection).removeView(view);
view = null; view = null;
} }
public void setList(ArrayList<Post> list){ public void setList(ArrayList<Post> list) {
postList.clear(); postList.clear();
postList.addAll(list); postList.addAll(list);
notifyDataSetChanged(); notifyDataSetChanged();
} }
} }

@ -49,7 +49,7 @@ public class PinnedAdapter extends ArrayAdapter<Pin> {
frameLayout.setVisibility(View.VISIBLE); frameLayout.setVisibility(View.VISIBLE);
TextView itemCount = (TextView) view.findViewById(R.id.drawer_item_count); TextView itemCount = (TextView) view.findViewById(R.id.drawer_item_count);
if (item.isError) { if (item.isError) {
itemCount.setText("Err"); itemCount.setText("Err");
} else { } else {

@ -20,7 +20,6 @@ import android.widget.ListView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
public class PostAdapter extends BaseAdapter { public class PostAdapter extends BaseAdapter {
private final Context context; private final Context context;
private final ThreadManager threadManager; private final ThreadManager threadManager;
@ -37,7 +36,8 @@ public class PostAdapter extends BaseAdapter {
@Override @Override
public int getCount() { public int getCount() {
if ((threadManager.getLoadable() != null && threadManager.getLoadable().isBoardMode()) || threadManager.shouldWatch()) { if ((threadManager.getLoadable() != null && threadManager.getLoadable().isBoardMode())
|| threadManager.shouldWatch()) {
return postList.size() + 1; return postList.size() + 1;
} else { } else {
return postList.size(); return postList.size();
@ -157,8 +157,3 @@ public class PostAdapter extends BaseAdapter {
} }
} }
} }

@ -17,7 +17,8 @@ import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
/** /**
* A DialogFragment that shows a list of posts. Use the newInstance method for instantiating. * A DialogFragment that shows a list of posts. Use the newInstance method for
* instantiating.
*/ */
public class PostRepliesFragment extends DialogFragment { public class PostRepliesFragment extends DialogFragment {
private ListView listView; private ListView listView;
@ -119,8 +120,3 @@ public class PostRepliesFragment extends DialogFragment {
} }
} }
} }

@ -46,17 +46,17 @@ import com.micromobs.android.floatlabel.FloatLabelEditText;
public class ReplyFragment extends DialogFragment { public class ReplyFragment extends DialogFragment {
private static final String TAG = "ReplyFragment"; private static final String TAG = "ReplyFragment";
private int page = 0; private int page = 0;
private Loadable loadable; private Loadable loadable;
private final Reply draft = new Reply(); private final Reply draft = new Reply();
private boolean shouldSaveDraft = true; private boolean shouldSaveDraft = true;
private boolean gettingCaptcha = false; private boolean gettingCaptcha = false;
private String captchaChallenge = ""; private String captchaChallenge = "";
// Views // Views
private View container; private View container;
private ViewFlipper flipper; private ViewFlipper flipper;
@ -72,129 +72,131 @@ public class ReplyFragment extends DialogFragment {
private LoadView captchaContainer; private LoadView captchaContainer;
private TextView captchaText; private TextView captchaText;
private LoadView responseContainer; private LoadView responseContainer;
public static ReplyFragment newInstance(Loadable loadable) { public static ReplyFragment newInstance(Loadable loadable) {
ReplyFragment reply = new ReplyFragment(); ReplyFragment reply = new ReplyFragment();
reply.loadable = loadable; reply.loadable = loadable;
return reply; return reply;
} }
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
loadable.writeToBundle(getActivity(), outState); loadable.writeToBundle(getActivity(), outState);
} }
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
if (loadable == null && savedInstanceState != null) { if (loadable == null && savedInstanceState != null) {
loadable = new Loadable(); loadable = new Loadable();
loadable.readFromBundle(getActivity(), savedInstanceState); loadable.readFromBundle(getActivity(), savedInstanceState);
} }
if (loadable != null) { if (loadable != null) {
setClosable(true); setClosable(true);
Dialog dialog = getDialog(); Dialog dialog = getDialog();
Context context = getActivity(); Context context = getActivity();
String title = loadable.isThreadMode() ? String title = loadable.isThreadMode() ? context.getString(R.string.reply) + " /" + loadable.board + "/"
context.getString(R.string.reply) + " /" + loadable.board + "/" + loadable.no : + loadable.no : context.getString(R.string.reply_to_board) + " /" + loadable.board + "/";
context.getString(R.string.reply_to_board) + " /" + loadable.board + "/";
if (dialog == null) { if (dialog == null) {
getActivity().getActionBar().setTitle(title); getActivity().getActionBar().setTitle(title);
} else { } else {
dialog.setTitle(title); dialog.setTitle(title);
} }
if (getDialog() != null) { if (getDialog() != null) {
getDialog().setOnKeyListener(new Dialog.OnKeyListener() { getDialog().setOnKeyListener(new Dialog.OnKeyListener() {
@Override @Override
public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) { public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
if (page == 1) flipPage(0); if (page == 1)
else if (page == 2) closeReply(); flipPage(0);
else if (page == 2)
closeReply();
return true; return true;
} else return false; } else
return false;
} }
}); });
} }
Reply draft = ChanApplication.getReplyManager().getReplyDraft(); Reply draft = ChanApplication.getReplyManager().getReplyDraft();
if (TextUtils.isEmpty(draft.name)) { if (TextUtils.isEmpty(draft.name)) {
draft.name = ChanPreferences.getDefaultName(); draft.name = ChanPreferences.getDefaultName();
} }
if (TextUtils.isEmpty(draft.email)) { if (TextUtils.isEmpty(draft.email)) {
draft.email = ChanPreferences.getDefaultEmail(); draft.email = ChanPreferences.getDefaultEmail();
} }
nameView.getEditText().setText(draft.name); nameView.getEditText().setText(draft.name);
emailView.getEditText().setText(draft.email); emailView.getEditText().setText(draft.email);
subjectView.getEditText().setText(draft.subject); subjectView.getEditText().setText(draft.subject);
commentView.getEditText().setText(draft.comment); commentView.getEditText().setText(draft.comment);
setFile(draft.file); setFile(draft.file);
getCaptcha(); getCaptcha();
} else { } else {
Logger.e(TAG, "Loadable in ReplyFragment was null"); Logger.e(TAG, "Loadable in ReplyFragment was null");
closeReply(); closeReply();
} }
} }
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
ReplyManager replyManager = ChanApplication.getReplyManager(); ReplyManager replyManager = ChanApplication.getReplyManager();
if (shouldSaveDraft) { if (shouldSaveDraft) {
draft.name = nameView.getText().toString(); draft.name = nameView.getText().toString();
draft.email = emailView.getText().toString(); draft.email = emailView.getText().toString();
draft.subject = subjectView.getText().toString(); draft.subject = subjectView.getText().toString();
draft.comment = commentView.getText().toString(); draft.comment = commentView.getText().toString();
replyManager.setReplyDraft(draft); replyManager.setReplyDraft(draft);
} else { } else {
replyManager.removeReplyDraft(); replyManager.removeReplyDraft();
setFile(null); setFile(null);
} }
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
ReplyManager replyManager = ChanApplication.getReplyManager(); ReplyManager replyManager = ChanApplication.getReplyManager();
replyManager.removeFileListener(); replyManager.removeFileListener();
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
// Setup the views with listeners // Setup the views with listeners
container = inflater.inflate(R.layout.reply_view, null); container = inflater.inflate(R.layout.reply_view, null);
flipper = (ViewFlipper)container.findViewById(R.id.reply_flipper); flipper = (ViewFlipper) container.findViewById(R.id.reply_flipper);
nameView = (FloatLabelEditText)container.findViewById(R.id.reply_name); nameView = (FloatLabelEditText) container.findViewById(R.id.reply_name);
emailView = (FloatLabelEditText)container.findViewById(R.id.reply_email); emailView = (FloatLabelEditText) container.findViewById(R.id.reply_email);
subjectView = (FloatLabelEditText)container.findViewById(R.id.reply_subject); subjectView = (FloatLabelEditText) container.findViewById(R.id.reply_subject);
commentView = (FloatLabelEditText)container.findViewById(R.id.reply_comment); commentView = (FloatLabelEditText) container.findViewById(R.id.reply_comment);
imageViewContainer = (LoadView)container.findViewById(R.id.reply_image); imageViewContainer = (LoadView) container.findViewById(R.id.reply_image);
responseContainer = (LoadView)container.findViewById(R.id.reply_response); responseContainer = (LoadView) container.findViewById(R.id.reply_response);
captchaContainer = (LoadView)container.findViewById(R.id.reply_captcha_container); captchaContainer = (LoadView) container.findViewById(R.id.reply_captcha_container);
captchaContainer.setOnClickListener(new OnClickListener() { captchaContainer.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
getCaptcha(); getCaptcha();
} }
}); });
captchaText = (TextView)container.findViewById(R.id.reply_captcha); captchaText = (TextView) container.findViewById(R.id.reply_captcha);
cancelButton = (Button)container.findViewById(R.id.reply_cancel); cancelButton = (Button) container.findViewById(R.id.reply_cancel);
cancelButton.setOnClickListener(new OnClickListener() { cancelButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -205,8 +207,8 @@ public class ReplyFragment extends DialogFragment {
} }
} }
}); });
fileButton = (Button)container.findViewById(R.id.reply_file); fileButton = (Button) container.findViewById(R.id.reply_file);
fileButton.setOnClickListener(new OnClickListener() { fileButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -215,7 +217,7 @@ public class ReplyFragment extends DialogFragment {
public void onFile(File file) { public void onFile(File file) {
setFile(file); setFile(file);
} }
@Override @Override
public void onFileLoading() { public void onFileLoading() {
imageViewContainer.setView(null); imageViewContainer.setView(null);
@ -223,16 +225,16 @@ public class ReplyFragment extends DialogFragment {
}); });
} }
}); });
fileDeleteButton = (Button)container.findViewById(R.id.reply_file_delete); fileDeleteButton = (Button) container.findViewById(R.id.reply_file_delete);
fileDeleteButton.setOnClickListener(new OnClickListener() { fileDeleteButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
setFile(null); setFile(null);
} }
}); });
submitButton = (Button)container.findViewById(R.id.reply_submit); submitButton = (Button) container.findViewById(R.id.reply_submit);
submitButton.setOnClickListener(new OnClickListener() { submitButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -241,13 +243,14 @@ public class ReplyFragment extends DialogFragment {
} else if (page == 1) { } else if (page == 1) {
flipPage(2); flipPage(2);
submit(); submit();
}; }
;
} }
}); });
return container; return container;
} }
private void closeReply() { private void closeReply() {
if (getDialog() != null) { if (getDialog() != null) {
dismiss(); dismiss();
@ -255,9 +258,10 @@ public class ReplyFragment extends DialogFragment {
getActivity().finish(); getActivity().finish();
} }
} }
/** /**
* Set if the dialog is able to be closed, by pressing outside of the dialog, or something else. * Set if the dialog is able to be closed, by pressing outside of the
* dialog, or something else.
*/ */
private void setClosable(boolean e) { private void setClosable(boolean e) {
if (getDialog() != null) { if (getDialog() != null) {
@ -265,17 +269,19 @@ public class ReplyFragment extends DialogFragment {
setCancelable(e); setCancelable(e);
} }
} }
/** /**
* Flip to an page with an animation. * Flip to an page with an animation. Sets the correct text on the
* Sets the correct text on the cancelButton: * cancelButton:
* @param position 0-2 *
* @param position
* 0-2
*/ */
private void flipPage(int position) { private void flipPage(int position) {
boolean flipBack = position < page; boolean flipBack = position < page;
page = position; page = position;
if (flipBack) { if (flipBack) {
flipper.setInAnimation(ViewFlipperAnimations.BACK_IN); flipper.setInAnimation(ViewFlipperAnimations.BACK_IN);
flipper.setOutAnimation(ViewFlipperAnimations.BACK_OUT); flipper.setOutAnimation(ViewFlipperAnimations.BACK_OUT);
@ -285,7 +291,7 @@ public class ReplyFragment extends DialogFragment {
flipper.setOutAnimation(ViewFlipperAnimations.NEXT_OUT); flipper.setOutAnimation(ViewFlipperAnimations.NEXT_OUT);
flipper.showNext(); flipper.showNext();
} }
if (page == 0) { if (page == 0) {
cancelButton.setText(R.string.cancel); cancelButton.setText(R.string.cancel);
} else if (page == 1) { } else if (page == 1) {
@ -294,31 +300,32 @@ public class ReplyFragment extends DialogFragment {
cancelButton.setText(R.string.close); cancelButton.setText(R.string.close);
} }
} }
/** /**
* Set the picked image in the imageView. * Set the picked image in the imageView. Sets the file in the draft. Call
* Sets the file in the draft. * null on the file to empty the imageView.
* Call null on the file to empty the imageView. *
* @param imagePath file to image to send or null to clear * @param imagePath
* file to image to send or null to clear
*/ */
private void setFile(final File file) { private void setFile(final File file) {
draft.file = file; draft.file = file;
if (file == null) { if (file == null) {
fileDeleteButton.setEnabled(false); fileDeleteButton.setEnabled(false);
imageViewContainer.removeAllViews(); imageViewContainer.removeAllViews();
} else { } else {
fileDeleteButton.setEnabled(true); fileDeleteButton.setEnabled(true);
// UI Thread // UI Thread
final ImageView imageView = new ImageView(getActivity()); final ImageView imageView = new ImageView(getActivity());
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
// Other thread // Other thread
final Bitmap bitmap = ImageDecoder.decodeFile(file, imageViewContainer.getWidth(), 3000); final Bitmap bitmap = ImageDecoder.decodeFile(file, imageViewContainer.getWidth(), 3000);
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -328,7 +335,7 @@ public class ReplyFragment extends DialogFragment {
} else { } else {
imageView.setScaleType(ScaleType.CENTER_CROP); imageView.setScaleType(ScaleType.CENTER_CROP);
imageView.setImageBitmap(bitmap); imageView.setImageBitmap(bitmap);
imageViewContainer.setView(imageView); imageViewContainer.setView(imageView);
} }
} }
@ -337,29 +344,31 @@ public class ReplyFragment extends DialogFragment {
}).start(); }).start();
} }
} }
private void getCaptcha() { private void getCaptcha() {
if (gettingCaptcha) return; if (gettingCaptcha)
return;
gettingCaptcha = true; gettingCaptcha = true;
captchaContainer.setView(null); captchaContainer.setView(null);
String url = ChanUrls.getCaptchaChallengeUrl(); String url = ChanUrls.getCaptchaChallengeUrl();
ChanApplication.getVolleyRequestQueue().add(new StringRequest(Method.GET, url, new Response.Listener<String>() { ChanApplication.getVolleyRequestQueue().add(new StringRequest(Method.GET, url, new Response.Listener<String>() {
@Override @Override
public void onResponse(String result) { public void onResponse(String result) {
if (!isVisible()) return; if (!isVisible())
return;
String challenge = ReplyManager.getChallenge(result); String challenge = ReplyManager.getChallenge(result);
if (challenge != null) { if (challenge != null) {
captchaChallenge = challenge; captchaChallenge = challenge;
String imageUrl = ChanUrls.getCaptchaImageUrl(challenge); String imageUrl = ChanUrls.getCaptchaImageUrl(challenge);
NetworkImageView captchaImage = new NetworkImageView(getActivity()); NetworkImageView captchaImage = new NetworkImageView(getActivity());
captchaImage.setImageUrl(imageUrl, ChanApplication.getImageLoader()); captchaImage.setImageUrl(imageUrl, ChanApplication.getImageLoader());
captchaContainer.setView(captchaImage); captchaContainer.setView(captchaImage);
gettingCaptcha = false; gettingCaptcha = false;
} }
} }
@ -375,7 +384,7 @@ public class ReplyFragment extends DialogFragment {
} }
})); }));
} }
/** /**
* Submit button clicked at page 1 * Submit button clicked at page 1
*/ */
@ -383,9 +392,9 @@ public class ReplyFragment extends DialogFragment {
submitButton.setEnabled(false); submitButton.setEnabled(false);
cancelButton.setEnabled(false); cancelButton.setEnabled(false);
setClosable(false); setClosable(false);
responseContainer.setView(null); responseContainer.setView(null);
draft.name = nameView.getText().toString(); draft.name = nameView.getText().toString();
draft.email = emailView.getText().toString(); draft.email = emailView.getText().toString();
draft.subject = subjectView.getText().toString(); draft.subject = subjectView.getText().toString();
@ -393,10 +402,10 @@ public class ReplyFragment extends DialogFragment {
draft.captchaChallenge = captchaChallenge; draft.captchaChallenge = captchaChallenge;
draft.captchaResponse = captchaText.getText().toString(); draft.captchaResponse = captchaText.getText().toString();
draft.fileName = "image"; draft.fileName = "image";
draft.resto = loadable.isBoardMode() ? -1 : loadable.no; draft.resto = loadable.isBoardMode() ? -1 : loadable.no;
draft.board = loadable.board; draft.board = loadable.board;
ChanApplication.getReplyManager().sendReply(draft, new ReplyManager.ReplyListener() { ChanApplication.getReplyManager().sendReply(draft, new ReplyManager.ReplyListener() {
@Override @Override
public void onResponse(ReplyResponse response) { public void onResponse(ReplyResponse response) {
@ -404,16 +413,19 @@ public class ReplyFragment extends DialogFragment {
} }
}); });
} }
/** /**
* Got response about or reply from ReplyManager * Got response about or reply from ReplyManager
*
* @param response * @param response
*/ */
private void handleSubmitResponse(ReplyResponse response) { private void handleSubmitResponse(ReplyResponse response) {
if (getActivity() == null) return; if (getActivity() == null)
return;
if (response.isNetworkError || response.isUserError) { if (response.isNetworkError || response.isUserError) {
int resId = response.isCaptchaError ? R.string.reply_error_captcha : (response.isFileError ? R.string.reply_error_file : R.string.reply_error); int resId = response.isCaptchaError ? R.string.reply_error_captcha
: (response.isFileError ? R.string.reply_error_file : R.string.reply_error);
Toast.makeText(getActivity(), resId, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), resId, Toast.LENGTH_LONG).show();
submitButton.setEnabled(true); submitButton.setEnabled(true);
cancelButton.setEnabled(true); cancelButton.setEnabled(true);
@ -424,26 +436,21 @@ public class ReplyFragment extends DialogFragment {
} else if (response.isSuccessful) { } else if (response.isSuccessful) {
shouldSaveDraft = false; shouldSaveDraft = false;
Toast.makeText(getActivity(), R.string.reply_success, Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), R.string.reply_success, Toast.LENGTH_SHORT).show();
// threadFragment.reload(); // won't work: it takes 4chan a variable time to process the reply // threadFragment.reload(); // won't work: it takes 4chan a variable time to process the reply
closeReply(); closeReply();
} else { } else {
if (isVisible()) { if (isVisible()) {
cancelButton.setEnabled(true); cancelButton.setEnabled(true);
setClosable(true); setClosable(true);
WebView webView = new WebView(getActivity()); WebView webView = new WebView(getActivity());
WebSettings settings = webView.getSettings(); WebSettings settings = webView.getSettings();
settings.setSupportZoom(true); settings.setSupportZoom(true);
webView.loadData(response.responseData, "text/html", null); webView.loadData(response.responseData, "text/html", null);
responseContainer.setView(webView); responseContainer.setView(webView);
} }
} }
} }
} }

@ -48,8 +48,8 @@ public class SettingsFragment extends PreferenceFragment {
ChanPreferences.setDeveloper(enabled); ChanPreferences.setDeveloper(enabled);
updateDeveloperPreference(); updateDeveloperPreference();
Toast.makeText(getActivity(), Toast.makeText(getActivity(), (enabled ? "Enabled " : "Disabled ") + "developer options",
(enabled ? "Enabled " : "Disabled ") + "developer options", Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
} }
return true; return true;
@ -78,7 +78,8 @@ public class SettingsFragment extends PreferenceFragment {
final Preference watchPreference = findPreference("watch_settings"); final Preference watchPreference = findPreference("watch_settings");
if (watchPreference != null) { if (watchPreference != null) {
watchPreference.setSummary(ChanPreferences.getWatchEnabled() ? R.string.watch_summary_enabled : R.string.watch_summary_disabled); watchPreference.setSummary(ChanPreferences.getWatchEnabled() ? R.string.watch_summary_enabled
: R.string.watch_summary_disabled);
} }
} }

@ -34,114 +34,116 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
private BaseActivity baseActivity; private BaseActivity baseActivity;
private ThreadManager threadManager; private ThreadManager threadManager;
private Loadable loadable; private Loadable loadable;
private PostAdapter postAdapter; private PostAdapter postAdapter;
private LoadView container; private LoadView container;
private ListView listView; private ListView listView;
public static ThreadFragment newInstance(BaseActivity activity) { public static ThreadFragment newInstance(BaseActivity activity) {
ThreadFragment fragment = new ThreadFragment(); ThreadFragment fragment = new ThreadFragment();
fragment.baseActivity = activity; fragment.baseActivity = activity;
fragment.threadManager = new ThreadManager(activity, fragment); fragment.threadManager = new ThreadManager(activity, fragment);
return fragment; return fragment;
} }
public void bindLoadable(Loadable l) { public void bindLoadable(Loadable l) {
if (loadable != null) { if (loadable != null) {
threadManager.unbindLoader(); threadManager.unbindLoader();
} }
setEmpty(); setEmpty();
loadable = l; loadable = l;
threadManager.bindLoader(loadable); threadManager.bindLoader(loadable);
} }
public void requestData() { public void requestData() {
threadManager.requestData(); threadManager.requestData();
} }
private void setEmpty() { private void setEmpty() {
postAdapter = null; postAdapter = null;
if (container != null) { if (container != null) {
container.setView(null); container.setView(null);
} }
if (listView != null) { if (listView != null) {
listView.setOnScrollListener(null); listView.setOnScrollListener(null);
listView = null; listView = null;
} }
} }
public void reload() { public void reload() {
setEmpty(); setEmpty();
threadManager.requestData(); threadManager.requestData();
} }
public void openReply() { public void openReply() {
if (threadManager.hasLoader()) { if (threadManager.hasLoader()) {
threadManager.openReply(true); threadManager.openReply(true);
} }
} }
public boolean hasLoader() { public boolean hasLoader() {
return threadManager.hasLoader(); return threadManager.hasLoader();
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
if (threadManager != null) { if (threadManager != null) {
threadManager.onDestroy(); threadManager.onDestroy();
} }
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (threadManager != null) { if (threadManager != null) {
threadManager.onStart(); threadManager.onStart();
} }
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
if (threadManager != null) { if (threadManager != null) {
threadManager.onStop(); threadManager.onStop();
} }
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
container = new LoadView(inflater.getContext()); container = new LoadView(inflater.getContext());
return container; return container;
} }
@Override @Override
public void onThreadLoaded(List<Post> posts, boolean append) { public void onThreadLoaded(List<Post> posts, boolean append) {
if (postAdapter == null) { if (postAdapter == null) {
listView = new ListView(baseActivity); listView = new ListView(baseActivity);
postAdapter = new PostAdapter(baseActivity, threadManager, listView); postAdapter = new PostAdapter(baseActivity, threadManager, listView);
listView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); listView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
listView.setAdapter(postAdapter); listView.setAdapter(postAdapter);
listView.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop); listView.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop);
if (threadManager.getLoadable().isThreadMode()) { if (threadManager.getLoadable().isThreadMode()) {
listView.setOnScrollListener(new OnScrollListener() { listView.setOnScrollListener(new OnScrollListener() {
@Override @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {} public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (loadable != null) { if (loadable != null) {
loadable.listViewIndex = view.getFirstVisiblePosition(); loadable.listViewIndex = view.getFirstVisiblePosition();
View v = view.getChildAt(0); View v = view.getChildAt(0);
@ -150,19 +152,19 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
} }
}); });
} }
if (container != null) { if (container != null) {
container.setView(listView); container.setView(listView);
} }
} }
if (append) { if (append) {
postAdapter.appendList(posts); postAdapter.appendList(posts);
} else { } else {
postAdapter.setList(posts); postAdapter.setList(posts);
} }
} }
@Override @Override
public void onThreadLoadError(VolleyError error) { public void onThreadLoadError(VolleyError error) {
if (error instanceof EndOfLineException) { if (error instanceof EndOfLineException) {
@ -173,15 +175,16 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
} }
} }
} }
/** /**
* Returns an TextView containing the appropriate error message * Returns an TextView containing the appropriate error message
*
* @param error * @param error
* @return * @return
*/ */
public TextView getLoadErrorTextView(VolleyError error) { public TextView getLoadErrorTextView(VolleyError error) {
String errorMessage = ""; String errorMessage = "";
if ((error instanceof NoConnectionError) || (error instanceof NetworkError)) { if ((error instanceof NoConnectionError) || (error instanceof NetworkError)) {
errorMessage = getActivity().getString(R.string.thread_load_failed_network); errorMessage = getActivity().getString(R.string.thread_load_failed_network);
} else if (error instanceof ServerError) { } else if (error instanceof ServerError) {
@ -189,26 +192,26 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
} else { } else {
errorMessage = getActivity().getString(R.string.thread_load_failed_parsing); errorMessage = getActivity().getString(R.string.thread_load_failed_parsing);
} }
TextView view = new TextView(getActivity()); TextView view = new TextView(getActivity());
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
view.setText(errorMessage); view.setText(errorMessage);
view.setTextSize(24f); view.setTextSize(24f);
view.setGravity(Gravity.CENTER); view.setGravity(Gravity.CENTER);
return view; return view;
} }
@Override @Override
public void onOPClicked(Post post) { public void onOPClicked(Post post) {
baseActivity.onOPClicked(post); baseActivity.onOPClicked(post);
} }
@Override @Override
public void onThumbnailClicked(Post source) { public void onThumbnailClicked(Post source) {
if (postAdapter != null) { if (postAdapter != null) {
ImageViewActivity.setAdapter(postAdapter, source.no); ImageViewActivity.setAdapter(postAdapter, source.no);
Intent intent = new Intent(baseActivity, ImageViewActivity.class); Intent intent = new Intent(baseActivity, ImageViewActivity.class);
baseActivity.startActivity(intent); baseActivity.startActivity(intent);
baseActivity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out); baseActivity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
@ -222,8 +225,3 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
} }
} }
} }

@ -45,21 +45,22 @@ import android.widget.ListView;
/** /**
* The dynamic listview is an extension of listview that supports cell dragging * The dynamic listview is an extension of listview that supports cell dragging
* and swapping. * and swapping.
* *
* This layout is in charge of positioning the hover cell in the correct location * This layout is in charge of positioning the hover cell in the correct
* on the screen in response to user touch events. It uses the position of the * location on the screen in response to user touch events. It uses the position
* hover cell to determine when two cells should be swapped. If two cells should * of the hover cell to determine when two cells should be swapped. If two cells
* be swapped, all the corresponding data set and layout changes are handled here. * should be swapped, all the corresponding data set and layout changes are
* * handled here.
*
* If no cell is selected, all the touch events are passed down to the listview * If no cell is selected, all the touch events are passed down to the listview
* and behave normally. If one of the items in the listview experiences a * and behave normally. If one of the items in the listview experiences a long
* long press event, the contents of its current visible state are captured as * press event, the contents of its current visible state are captured as a
* a bitmap and its visibility is set to INVISIBLE. A hover cell is then created and * bitmap and its visibility is set to INVISIBLE. A hover cell is then created
* added to this layout as an overlaying BitmapDrawable above the listview. Once the * and added to this layout as an overlaying BitmapDrawable above the listview.
* hover cell is translated some distance to signify an item swap, a data set change * Once the hover cell is translated some distance to signify an item swap, a
* accompanied by animation takes place. When the user releases the hover cell, * data set change accompanied by animation takes place. When the user releases
* it animates into its corresponding position in the listview. * the hover cell, it animates into its corresponding position in the listview.
* *
* When the hover cell is either above or below the bounds of the listview, this * When the hover cell is either above or below the bounds of the listview, this
* listview also scrolls on its own so as to reveal additional content. * listview also scrolls on its own so as to reveal additional content.
*/ */
@ -116,34 +117,33 @@ public class DynamicListView<T> extends ListView {
setOnItemLongClickListener(mOnItemLongClickListener); setOnItemLongClickListener(mOnItemLongClickListener);
setOnScrollListener(mScrollListener); setOnScrollListener(mScrollListener);
DisplayMetrics metrics = context.getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mSmoothScrollAmountAtEdge = (int)(SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density); mSmoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
} }
/** /**
* Listens for long clicks on any items in the listview. When a cell has * Listens for long clicks on any items in the listview. When a cell has
* been selected, the hover cell is created and set up. * been selected, the hover cell is created and set up.
*/ */
private final AdapterView.OnItemLongClickListener mOnItemLongClickListener = private final AdapterView.OnItemLongClickListener mOnItemLongClickListener = new AdapterView.OnItemLongClickListener() {
new AdapterView.OnItemLongClickListener() { @Override
@Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos, long id) {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos, long id) { mTotalOffset = 0;
mTotalOffset = 0;
int position = pointToPosition(mDownX, mDownY); int position = pointToPosition(mDownX, mDownY);
int itemNum = position - getFirstVisiblePosition(); int itemNum = position - getFirstVisiblePosition();
View selectedView = getChildAt(itemNum); View selectedView = getChildAt(itemNum);
mMobileItemId = getAdapter().getItemId(position); mMobileItemId = getAdapter().getItemId(position);
mHoverCell = getAndAddHoverView(selectedView); mHoverCell = getAndAddHoverView(selectedView);
selectedView.setVisibility(INVISIBLE); selectedView.setVisibility(INVISIBLE);
mCellIsMobile = true; mCellIsMobile = true;
updateNeighborViewsForID(mMobileItemId); updateNeighborViewsForID(mMobileItemId);
return true; return true;
} }
}; };
/** /**
* Creates the hover cell with the appropriate bitmap and of appropriate * Creates the hover cell with the appropriate bitmap and of appropriate
@ -190,7 +190,7 @@ public class DynamicListView<T> extends ListView {
/** Returns a bitmap showing a screenshot of the view passed in. */ /** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) { private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas (bitmap); Canvas canvas = new Canvas(bitmap);
v.draw(canvas); v.draw(canvas);
return bitmap; return bitmap;
} }
@ -198,21 +198,21 @@ public class DynamicListView<T> extends ListView {
/** /**
* Stores a reference to the views above and below the item currently * Stores a reference to the views above and below the item currently
* corresponding to the hover cell. It is important to note that if this * corresponding to the hover cell. It is important to note that if this
* item is either at the top or bottom of the list, mAboveItemId or mBelowItemId * item is either at the top or bottom of the list, mAboveItemId or
* may be invalid. * mBelowItemId may be invalid.
*/ */
private void updateNeighborViewsForID(long itemID) { private void updateNeighborViewsForID(long itemID) {
int position = getPositionForID(itemID); int position = getPositionForID(itemID);
BoardEditAdapter adapter = ((BoardEditAdapter)getAdapter()); BoardEditAdapter adapter = ((BoardEditAdapter) getAdapter());
mAboveItemId = adapter.getItemId(position - 1); mAboveItemId = adapter.getItemId(position - 1);
mBelowItemId = adapter.getItemId(position + 1); mBelowItemId = adapter.getItemId(position + 1);
} }
/** Retrieves the view in the list corresponding to itemID */ /** Retrieves the view in the list corresponding to itemID */
public View getViewForID (long itemID) { public View getViewForID(long itemID) {
int firstVisiblePosition = getFirstVisiblePosition(); int firstVisiblePosition = getFirstVisiblePosition();
BoardEditAdapter adapter = ((BoardEditAdapter)getAdapter()); BoardEditAdapter adapter = ((BoardEditAdapter) getAdapter());
for(int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i); View v = getChildAt(i);
int position = firstVisiblePosition + i; int position = firstVisiblePosition + i;
long id = adapter.getItemId(position); long id = adapter.getItemId(position);
@ -224,7 +224,7 @@ public class DynamicListView<T> extends ListView {
} }
/** Retrieves the position in the list corresponding to itemID */ /** Retrieves the position in the list corresponding to itemID */
public int getPositionForID (long itemID) { public int getPositionForID(long itemID) {
View v = getViewForID(itemID); View v = getViewForID(itemID);
if (v == null) { if (v == null) {
return -1; return -1;
@ -234,9 +234,9 @@ public class DynamicListView<T> extends ListView {
} }
/** /**
* dispatchDraw gets invoked when all the child views are about to be drawn. * dispatchDraw gets invoked when all the child views are about to be drawn.
* By overriding this method, the hover cell (BitmapDrawable) can be drawn * By overriding this method, the hover cell (BitmapDrawable) can be drawn
* over the listview's items whenever the listview is redrawn. * over the listview's items whenever the listview is redrawn.
*/ */
@Override @Override
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
@ -247,58 +247,57 @@ public class DynamicListView<T> extends ListView {
} }
@Override @Override
public boolean onTouchEvent (MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) { switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
mDownX = (int)event.getX(); mDownX = (int) event.getX();
mDownY = (int)event.getY(); mDownY = (int) event.getY();
mActivePointerId = event.getPointerId(0); mActivePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER_ID) {
break; break;
case MotionEvent.ACTION_MOVE: }
if (mActivePointerId == INVALID_POINTER_ID) {
break;
}
int pointerIndex = event.findPointerIndex(mActivePointerId); int pointerIndex = event.findPointerIndex(mActivePointerId);
mLastEventY = (int) event.getY(pointerIndex); mLastEventY = (int) event.getY(pointerIndex);
int deltaY = mLastEventY - mDownY; int deltaY = mLastEventY - mDownY;
if (mCellIsMobile) { if (mCellIsMobile) {
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mHoverCellOriginalBounds.top + deltaY
mHoverCellOriginalBounds.top + deltaY + mTotalOffset); + mTotalOffset);
mHoverCell.setBounds(mHoverCellCurrentBounds); mHoverCell.setBounds(mHoverCellCurrentBounds);
invalidate(); invalidate();
handleCellSwitch(); handleCellSwitch();
mIsMobileScrolling = false; mIsMobileScrolling = false;
handleMobileCellScroll(); handleMobileCellScroll();
return false; return false;
} }
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
touchEventsEnded();
break;
case MotionEvent.ACTION_CANCEL:
touchEventsCancelled();
break;
case MotionEvent.ACTION_POINTER_UP:
/* If a multitouch event took place and the original touch dictating
* the movement of the hover cell has ended, then the dragging event
* ends and the hover cell is animated to its corresponding position
* in the listview. */
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
touchEventsEnded(); touchEventsEnded();
break; }
case MotionEvent.ACTION_CANCEL: break;
touchEventsCancelled(); default:
break; break;
case MotionEvent.ACTION_POINTER_UP:
/* If a multitouch event took place and the original touch dictating
* the movement of the hover cell has ended, then the dragging event
* ends and the hover cell is animated to its corresponding position
* in the listview. */
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
touchEventsEnded();
}
break;
default:
break;
} }
return super.onTouchEvent(event); return super.onTouchEvent(event);
@ -307,11 +306,11 @@ public class DynamicListView<T> extends ListView {
/** /**
* This method determines whether the hover cell has been shifted far enough * This method determines whether the hover cell has been shifted far enough
* to invoke a cell swap. If so, then the respective cell swap candidate is * to invoke a cell swap. If so, then the respective cell swap candidate is
* determined and the data set is changed. Upon posting a notification of the * determined and the data set is changed. Upon posting a notification of
* data set change, a layout is invoked to place the cells in the right place. * the data set change, a layout is invoked to place the cells in the right
* Using a ViewTreeObserver and a corresponding OnPreDrawListener, we can * place. Using a ViewTreeObserver and a corresponding OnPreDrawListener, we
* offset the cell being swapped to where it previously was and then animate it to * can offset the cell being swapped to where it previously was and then
* its new position. * animate it to its new position.
*/ */
private void handleCellSwitch() { private void handleCellSwitch() {
final int deltaY = mLastEventY - mDownY; final int deltaY = mLastEventY - mDownY;
@ -363,8 +362,7 @@ public class DynamicListView<T> extends ListView {
switchView.setTranslationY(delta); switchView.setTranslationY(delta);
ObjectAnimator animator = ObjectAnimator.ofFloat(switchView, ObjectAnimator animator = ObjectAnimator.ofFloat(switchView, View.TRANSLATION_Y, 0);
View.TRANSLATION_Y, 0);
animator.setDuration(MOVE_DURATION); animator.setDuration(MOVE_DURATION);
animator.start(); animator.start();
@ -380,14 +378,13 @@ public class DynamicListView<T> extends ListView {
arrayList.set(indexTwo, temp); arrayList.set(indexTwo, temp);
} }
/** /**
* Resets all the appropriate fields to a default state while also animating * Resets all the appropriate fields to a default state while also animating
* the hover cell back to its correct location. * the hover cell back to its correct location.
*/ */
private void touchEventsEnded () { private void touchEventsEnded() {
final View mobileView = getViewForID(mMobileItemId); final View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile|| mIsWaitingForScrollFinish) { if (mCellIsMobile || mIsWaitingForScrollFinish) {
mCellIsMobile = false; mCellIsMobile = false;
mIsWaitingForScrollFinish = false; mIsWaitingForScrollFinish = false;
mIsMobileScrolling = false; mIsMobileScrolling = false;
@ -403,8 +400,8 @@ public class DynamicListView<T> extends ListView {
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mobileView.getTop()); mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mobileView.getTop());
ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds", ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds", sBoundEvaluator,
sBoundEvaluator, mHoverCellCurrentBounds); mHoverCellCurrentBounds);
hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override @Override
public void onAnimationUpdate(ValueAnimator valueAnimator) { public void onAnimationUpdate(ValueAnimator valueAnimator) {
@ -437,7 +434,7 @@ public class DynamicListView<T> extends ListView {
/** /**
* Resets all the appropriate fields to a default state. * Resets all the appropriate fields to a default state.
*/ */
private void touchEventsCancelled () { private void touchEventsCancelled() {
View mobileView = getViewForID(mMobileItemId); View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile) { if (mCellIsMobile) {
mAboveItemId = INVALID_ID; mAboveItemId = INVALID_ID;
@ -460,28 +457,27 @@ public class DynamicListView<T> extends ListView {
private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() { private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() {
@Override @Override
public Rect evaluate(float fraction, Rect startValue, Rect endValue) { public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
return new Rect(interpolate(startValue.left, endValue.left, fraction), return new Rect(interpolate(startValue.left, endValue.left, fraction), interpolate(startValue.top,
interpolate(startValue.top, endValue.top, fraction), endValue.top, fraction), interpolate(startValue.right, endValue.right, fraction), interpolate(
interpolate(startValue.right, endValue.right, fraction), startValue.bottom, endValue.bottom, fraction));
interpolate(startValue.bottom, endValue.bottom, fraction));
} }
public int interpolate(int start, int end, float fraction) { public int interpolate(int start, int end, float fraction) {
return (int)(start + fraction * (end - start)); return (int) (start + fraction * (end - start));
} }
}; };
/** /**
* Determines whether this listview is in a scrolling state invoked * Determines whether this listview is in a scrolling state invoked by the
* by the fact that the hover cell is out of the bounds of the listview; * fact that the hover cell is out of the bounds of the listview;
*/ */
private void handleMobileCellScroll() { private void handleMobileCellScroll() {
mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds); mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
} }
/** /**
* This method is in charge of determining if the hover cell is above * This method is in charge of determining if the hover cell is above or
* or below the bounds of the listview. If so, the listview does an appropriate * below the bounds of the listview. If so, the listview does an appropriate
* upward or downward smooth scroll so as to reveal new items. * upward or downward smooth scroll so as to reveal new items.
*/ */
public boolean handleMobileCellScroll(Rect r) { public boolean handleMobileCellScroll(Rect r) {
@ -510,13 +506,14 @@ public class DynamicListView<T> extends ListView {
} }
/** /**
* This scroll listener is added to the listview in order to handle cell swapping * This scroll listener is added to the listview in order to handle cell
* when the cell is either at the top or bottom edge of the listview. If the hover * swapping when the cell is either at the top or bottom edge of the
* cell is at either edge of the listview, the listview will begin scrolling. As * listview. If the hover cell is at either edge of the listview, the
* scrolling takes place, the listview continuously checks if new cells became visible * listview will begin scrolling. As scrolling takes place, the listview
* and determines whether they are potential candidates for a cell swap. * continuously checks if new cells became visible and determines whether
* they are potential candidates for a cell swap.
*/ */
private final AbsListView.OnScrollListener mScrollListener = new AbsListView.OnScrollListener () { private final AbsListView.OnScrollListener mScrollListener = new AbsListView.OnScrollListener() {
private int mPreviousFirstVisibleItem = -1; private int mPreviousFirstVisibleItem = -1;
private int mPreviousVisibleItemCount = -1; private int mPreviousVisibleItemCount = -1;
@ -525,8 +522,7 @@ public class DynamicListView<T> extends ListView {
private int mCurrentScrollState; private int mCurrentScrollState;
@Override @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int totalItemCount) {
mCurrentFirstVisibleItem = firstVisibleItem; mCurrentFirstVisibleItem = firstVisibleItem;
mCurrentVisibleItemCount = visibleItemCount; mCurrentVisibleItemCount = visibleItemCount;
@ -550,12 +546,12 @@ public class DynamicListView<T> extends ListView {
} }
/** /**
* This method is in charge of invoking 1 of 2 actions. Firstly, if the listview * This method is in charge of invoking 1 of 2 actions. Firstly, if the
* is in a state of scrolling invoked by the hover cell being outside the bounds * listview is in a state of scrolling invoked by the hover cell being
* of the listview, then this scrolling event is continued. Secondly, if the hover * outside the bounds of the listview, then this scrolling event is
* cell has already been released, this invokes the animation for the hover cell * continued. Secondly, if the hover cell has already been released,
* to return to its correct position after the listview has entered an idle scroll * this invokes the animation for the hover cell to return to its
* state. * correct position after the listview has entered an idle scroll state.
*/ */
private void isScrollCompleted() { private void isScrollCompleted() {
if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) { if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {
@ -568,8 +564,9 @@ public class DynamicListView<T> extends ListView {
} }
/** /**
* Determines if the listview scrolled up enough to reveal a new cell at the * Determines if the listview scrolled up enough to reveal a new cell at
* top of the list. If so, then the appropriate parameters are updated. * the top of the list. If so, then the appropriate parameters are
* updated.
*/ */
public void checkAndHandleFirstVisibleCellChange() { public void checkAndHandleFirstVisibleCellChange() {
if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) { if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
@ -581,8 +578,9 @@ public class DynamicListView<T> extends ListView {
} }
/** /**
* Determines if the listview scrolled down enough to reveal a new cell at the * Determines if the listview scrolled down enough to reveal a new cell
* bottom of the list. If so, then the appropriate parameters are updated. * at the bottom of the list. If so, then the appropriate parameters are
* updated.
*/ */
public void checkAndHandleLastVisibleCellChange() { public void checkAndHandleLastVisibleCellChange() {
int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount; int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;

@ -12,34 +12,34 @@ import android.view.View;
public class GIFView extends View { public class GIFView extends View {
private Movie movie; private Movie movie;
private long movieStart; private long movieStart;
public GIFView(Context activity) { public GIFView(Context activity) {
super(activity); super(activity);
init(); init();
} }
public GIFView(Context activity, AttributeSet attbs) { public GIFView(Context activity, AttributeSet attbs) {
super(activity, attbs); super(activity, attbs);
init(); init();
} }
public GIFView(Context activity, AttributeSet attbs, int style) { public GIFView(Context activity, AttributeSet attbs, int style) {
super(activity, attbs, style); super(activity, attbs, style);
init(); init();
} }
private void init() { private void init() {
Paint paint = new Paint(); Paint paint = new Paint();
paint.setAntiAlias(true); paint.setAntiAlias(true);
setLayerType(LAYER_TYPE_SOFTWARE, paint); setLayerType(LAYER_TYPE_SOFTWARE, paint);
} }
public boolean setData(byte[] array) { public boolean setData(byte[] array) {
Movie movie = Movie.decodeByteArray(array, 0, array.length); Movie movie = Movie.decodeByteArray(array, 0, array.length);
return onMovieLoaded(movie); return onMovieLoaded(movie);
} }
private boolean onMovieLoaded(Movie movie) { private boolean onMovieLoaded(Movie movie) {
if (movie != null) { if (movie != null) {
this.movie = movie; this.movie = movie;
@ -49,53 +49,48 @@ public class GIFView extends View {
return false; return false;
} }
} }
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT); canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas); super.onDraw(canvas);
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
if (movieStart == 0) { // first time if (movieStart == 0) { // first time
movieStart = now; movieStart = now;
} }
if (movie != null) { if (movie != null) {
int dur = movie.duration(); int dur = movie.duration();
if (dur == 0) { if (dur == 0) {
dur = 1000; dur = 1000;
} }
int relTime = (int)((now - movieStart) % dur); int relTime = (int) ((now - movieStart) % dur);
movie.setTime(relTime); movie.setTime(relTime);
canvas.save(); canvas.save();
float width = (float)getWidth() / (float)movie.width(); float width = (float) getWidth() / (float) movie.width();
float height = (float)getHeight() / (float)movie.height(); float height = (float) getHeight() / (float) movie.height();
float scale = width > height ? height : width; float scale = width > height ? height : width;
int widthPixels = (int) (movie.width() * scale); int widthPixels = (int) (movie.width() * scale);
int heightPixels = (int) (movie.height() * scale); int heightPixels = (int) (movie.height() * scale);
canvas.translate((getWidth() - widthPixels) / 2, (getHeight() - heightPixels) / 2); canvas.translate((getWidth() - widthPixels) / 2, (getHeight() - heightPixels) / 2);
canvas.scale(scale, scale); canvas.scale(scale, scale);
movie.draw(canvas, 0, 0); movie.draw(canvas, 0, 0);
canvas.restore(); canvas.restore();
invalidate(); invalidate();
} }
} }
} }

@ -15,7 +15,7 @@ import android.view.MotionEvent;
* *
* There's not much I can do in my code for now, but we can mask the result by * There's not much I can do in my code for now, but we can mask the result by
* just catching the problem and ignoring it. * just catching the problem and ignoring it.
* *
* @author Chris Banes * @author Chris Banes
*/ */
public class HackyViewPager extends ViewPager { public class HackyViewPager extends ViewPager {

@ -12,9 +12,8 @@ import android.widget.Toast;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
/** /**
* Extends NetworkImageView. * Extends NetworkImageView. Attaches a PhotoViewAttacher when setBitmap is
* Attaches a PhotoViewAttacher when setBitmap is called. * called. Sets the progressBar to false when a bitmap gets set.
* Sets the progressBar to false when a bitmap gets set.
*/ */
public class NetworkPhotoView extends CustomNetworkImageView { public class NetworkPhotoView extends CustomNetworkImageView {
private PhotoViewAttacher attacher; private PhotoViewAttacher attacher;
@ -70,8 +69,3 @@ public class NetworkPhotoView extends CustomNetworkImageView {
} }
} }
} }

@ -216,7 +216,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
} else { } else {
full.setBackgroundColor(0x00000000); full.setBackgroundColor(0x00000000);
} }
if (manager.isPostLastSeen(post)) { if (manager.isPostLastSeen(post)) {
lastSeen.setVisibility(View.VISIBLE); lastSeen.setVisibility(View.VISIBLE);
} else { } else {

@ -15,25 +15,25 @@ public class ThreadWatchCounterView extends TextView implements View.OnClickList
private boolean detached = false; private boolean detached = false;
private ThreadManager tm; private ThreadManager tm;
private BaseAdapter ad; private BaseAdapter ad;
public ThreadWatchCounterView(Context activity) { public ThreadWatchCounterView(Context activity) {
super(activity); super(activity);
} }
public ThreadWatchCounterView(Context activity, AttributeSet attbs) { public ThreadWatchCounterView(Context activity, AttributeSet attbs) {
super(activity, attbs); super(activity, attbs);
} }
public ThreadWatchCounterView(Context activity, AttributeSet attbs, int style) { public ThreadWatchCounterView(Context activity, AttributeSet attbs, int style) {
super(activity, attbs, style); super(activity, attbs, style);
} }
public void init(final ThreadManager threadManager, final ListView listView, final BaseAdapter adapter) { public void init(final ThreadManager threadManager, final ListView listView, final BaseAdapter adapter) {
tm = threadManager; tm = threadManager;
ad = adapter; ad = adapter;
updateCounterText(threadManager); updateCounterText(threadManager);
new Handler().postDelayed(new Runnable() { new Handler().postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -42,35 +42,36 @@ public class ThreadWatchCounterView extends TextView implements View.OnClickList
} }
} }
}, 1000); }, 1000);
setOnClickListener(this); setOnClickListener(this);
} }
@Override @Override
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
setOnClickListener(null); setOnClickListener(null);
detached = true; detached = true;
} }
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Loader loader = tm.getLoader(); Loader loader = tm.getLoader();
if (loader != null) { if (loader != null) {
loader.requestMoreDataAndResetTimer(); loader.requestMoreDataAndResetTimer();
} }
ad.notifyDataSetChanged(); ad.notifyDataSetChanged();
} }
private void updateCounterText(ThreadManager threadManager) { private void updateCounterText(ThreadManager threadManager) {
Loader loader = tm.getLoader(); Loader loader = tm.getLoader();
if (loader == null) return; if (loader == null)
return;
int time = Math.round(loader.getTimeUntilLoadMore() / 1000f); int time = Math.round(loader.getTimeUntilLoadMore() / 1000f);
if (time <= 0) { if (time <= 0) {
setText("Loading"); setText("Loading");
} else { } else {
@ -78,7 +79,3 @@ public class ThreadWatchCounterView extends TextView implements View.OnClickList
} }
} }
} }

@ -11,7 +11,7 @@ import java.io.Writer;
public class IOUtils { public class IOUtils {
public static String readString(InputStream is) { public static String readString(InputStream is) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
try { try {
copy(new InputStreamReader(is), sw); copy(new InputStreamReader(is), sw);
is.close(); is.close();
@ -19,12 +19,13 @@ public class IOUtils {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return sw.toString(); return sw.toString();
} }
/** /**
* Copies the inputstream to the outputstream and closes both streams. * Copies the inputstream to the outputstream and closes both streams.
*
* @param is * @param is
* @param os * @param os
* @throws IOException * @throws IOException
@ -35,11 +36,11 @@ public class IOUtils {
while ((read = is.read(buffer)) != -1) { while ((read = is.read(buffer)) != -1) {
os.write(buffer, 0, read); os.write(buffer, 0, read);
} }
is.close(); is.close();
os.close(); os.close();
} }
public static void copy(Reader input, Writer output) throws IOException { public static void copy(Reader input, Writer output) throws IOException {
char[] buffer = new char[4096]; char[] buffer = new char[4096];
int read = 0; int read = 0;

@ -12,6 +12,7 @@ public class IconCache {
/** /**
* Load the icons in the cache. Lightweight icons only! Icons can be null! * Load the icons in the cache. Lightweight icons only! Icons can be null!
*
* @param context * @param context
*/ */
public static void createIcons(final Context context) { public static void createIcons(final Context context) {

@ -14,24 +14,25 @@ import android.graphics.BitmapFactory;
*/ */
public class ImageDecoder { public class ImageDecoder {
public static Bitmap decodeFile(File file, int maxWidth, int maxHeight) { public static Bitmap decodeFile(File file, int maxWidth, int maxHeight) {
if (!file.exists()) return null; if (!file.exists())
return null;
FileInputStream fis; FileInputStream fis;
try { try {
fis = new FileInputStream(file); fis = new FileInputStream(file);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap bitmap = null; Bitmap bitmap = null;
try { try {
IOUtils.copy(fis, baos); IOUtils.copy(fis, baos);
bitmap = decode(baos.toByteArray(), maxWidth, maxHeight); bitmap = decode(baos.toByteArray(), maxWidth, maxHeight);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -45,14 +46,14 @@ public class ImageDecoder {
e.printStackTrace(); e.printStackTrace();
} }
} }
return bitmap; return bitmap;
} }
public static Bitmap decode(byte[] data, int maxWidth, int maxHeight) { public static Bitmap decode(byte[] data, int maxWidth, int maxHeight) {
BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
Bitmap bitmap = null; Bitmap bitmap = null;
// If we have to resize this image, first get the natural bounds. // If we have to resize this image, first get the natural bounds.
decodeOptions.inJustDecodeBounds = true; decodeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
@ -60,25 +61,19 @@ public class ImageDecoder {
int actualHeight = decodeOptions.outHeight; int actualHeight = decodeOptions.outHeight;
// Then compute the dimensions we would ideally like to decode to. // Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(maxWidth, maxHeight, int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight);
actualWidth, actualHeight); int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth);
int desiredHeight = getResizedDimension(maxHeight, maxWidth,
actualHeight, actualWidth);
// Decode to the nearest power of two scaling factor. // Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false; decodeOptions.inJustDecodeBounds = false;
// decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
decodeOptions.inSampleSize = decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
Bitmap tempBitmap =
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
// If necessary, scale down to the maximal acceptable size. // If necessary, scale down to the maximal acceptable size.
if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) {
tempBitmap.getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);
bitmap = Bitmap.createScaledBitmap(tempBitmap,
desiredWidth, desiredHeight, true);
tempBitmap.recycle(); tempBitmap.recycle();
} else { } else {
bitmap = tempBitmap; bitmap = tempBitmap;
@ -86,7 +81,7 @@ public class ImageDecoder {
return bitmap; return bitmap;
} }
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) { private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) {
// If no dominant value at all, just return the actual. // If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) { if (maxPrimary == 0 && maxSecondary == 0) {
@ -110,7 +105,7 @@ public class ImageDecoder {
} }
return resized; return resized;
} }
private static int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { private static int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth; double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight; double hr = (double) actualHeight / desiredHeight;

@ -20,7 +20,7 @@ import com.android.volley.VolleyError;
public class ImageSaver { public class ImageSaver {
private static final String TAG = "ImageSaver"; private static final String TAG = "ImageSaver";
public static void save(final Context context, String imageUrl, final String name, final String extension) { public static void save(final Context context, String imageUrl, final String name, final String extension) {
ByteArrayRequest request = new ByteArrayRequest(imageUrl, new Response.Listener<byte[]>() { ByteArrayRequest request = new ByteArrayRequest(imageUrl, new Response.Listener<byte[]>() {
@Override @Override
@ -33,63 +33,61 @@ public class ImageSaver {
Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show();
} }
}); });
ChanApplication.getVolleyRequestQueue().add(request); ChanApplication.getVolleyRequestQueue().add(request);
} }
private static void storeImage(final Context context, byte[] data, String name, String extension) { private static void storeImage(final Context context, byte[] data, String name, String extension) {
String errorReason = null; String errorReason = null;
try { try {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
errorReason = context.getString(R.string.image_save_not_mounted); errorReason = context.getString(R.string.image_save_not_mounted);
throw new IOException(errorReason); throw new IOException(errorReason);
} }
File path = new File(Environment.getExternalStorageDirectory() + File.separator + ChanPreferences.getImageSaveDirectory()); File path = new File(Environment.getExternalStorageDirectory() + File.separator
+ ChanPreferences.getImageSaveDirectory());
if (!path.exists()) { if (!path.exists()) {
if (!path.mkdirs()) { if (!path.mkdirs()) {
errorReason = context.getString(R.string.image_save_directory_error); errorReason = context.getString(R.string.image_save_directory_error);
throw new IOException(errorReason); throw new IOException(errorReason);
} }
} }
File file = new File(path, name + "." + extension); File file = new File(path, name + "." + extension);
int nextFileNameNumber = 0; int nextFileNameNumber = 0;
String newName; String newName;
while(file.exists()) { while (file.exists()) {
newName = name + "_" + Integer.toString(nextFileNameNumber) + "." + extension; newName = name + "_" + Integer.toString(nextFileNameNumber) + "." + extension;
file = new File(path, newName); file = new File(path, newName);
nextFileNameNumber++; nextFileNameNumber++;
} }
Logger.i(TAG, "Saving image to: " + file.getPath()); Logger.i(TAG, "Saving image to: " + file.getPath());
FileOutputStream outputStream = new FileOutputStream(file); FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(data); outputStream.write(data);
outputStream.close(); outputStream.close();
MediaScannerConnection.scanFile(context, new String[] { file.toString() }, null, new MediaScannerConnection.OnScanCompletedListener() { MediaScannerConnection.scanFile(context, new String[] { file.toString() }, null,
@Override new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) { @Override
Logger.i(TAG, "Media scan succeeded: " + uri); public void onScanCompleted(String path, Uri uri) {
} Logger.i(TAG, "Media scan succeeded: " + uri);
}); }
});
String message = context.getResources().getString(R.string.image_save_succeeded) + " " + file.getName(); String message = context.getResources().getString(R.string.image_save_succeeded) + " " + file.getName();
Toast.makeText(context, message, Toast.LENGTH_LONG).show(); Toast.makeText(context, message, Toast.LENGTH_LONG).show();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
if (errorReason == null) errorReason = context.getString(R.string.image_save_failed); if (errorReason == null)
errorReason = context.getString(R.string.image_save_failed);
Toast.makeText(context, errorReason, Toast.LENGTH_LONG).show(); Toast.makeText(context, errorReason, Toast.LENGTH_LONG).show();
} }
} }
} }

@ -7,47 +7,47 @@ import android.util.Log;
public class Logger { public class Logger {
private static final String TAG = "Chan"; private static final String TAG = "Chan";
private static final String TAG_SPACER = " | "; private static final String TAG_SPACER = " | ";
public static void v(String tag, String message) { public static void v(String tag, String message) {
Log.v(TAG + TAG_SPACER + tag, message); Log.v(TAG + TAG_SPACER + tag, message);
} }
public static void v(String tag, String message, Throwable throwable) { public static void v(String tag, String message, Throwable throwable) {
Log.v(TAG + TAG_SPACER + tag, message, throwable); Log.v(TAG + TAG_SPACER + tag, message, throwable);
} }
public static void d(String tag, String message) { public static void d(String tag, String message) {
if (ChanApplication.DEVELOPER_MODE) { if (ChanApplication.DEVELOPER_MODE) {
Log.d(TAG + TAG_SPACER + tag, message); Log.d(TAG + TAG_SPACER + tag, message);
} }
} }
public static void d(String tag, String message, Throwable throwable) { public static void d(String tag, String message, Throwable throwable) {
if (ChanApplication.DEVELOPER_MODE) { if (ChanApplication.DEVELOPER_MODE) {
Log.d(TAG + TAG_SPACER + tag, message, throwable); Log.d(TAG + TAG_SPACER + tag, message, throwable);
} }
} }
public static void i(String tag, String message) { public static void i(String tag, String message) {
Log.i(TAG + TAG_SPACER + tag, message); Log.i(TAG + TAG_SPACER + tag, message);
} }
public static void i(String tag, String message, Throwable throwable) { public static void i(String tag, String message, Throwable throwable) {
Log.i(TAG + TAG_SPACER + tag, message, throwable); Log.i(TAG + TAG_SPACER + tag, message, throwable);
} }
public static void w(String tag, String message) { public static void w(String tag, String message) {
Log.w(TAG + TAG_SPACER + tag, message); Log.w(TAG + TAG_SPACER + tag, message);
} }
public static void w(String tag, String message, Throwable throwable) { public static void w(String tag, String message, Throwable throwable) {
Log.w(TAG + TAG_SPACER + tag, message, throwable); Log.w(TAG + TAG_SPACER + tag, message, throwable);
} }
public static void e(String tag, String message) { public static void e(String tag, String message) {
Log.e(TAG + TAG_SPACER + tag, message); Log.e(TAG + TAG_SPACER + tag, message);
} }
public static void e(String tag, String message, Throwable throwable) { public static void e(String tag, String message, Throwable throwable) {
Log.e(TAG + TAG_SPACER + tag, message, throwable); Log.e(TAG + TAG_SPACER + tag, message, throwable);
} }
@ -55,21 +55,20 @@ public class Logger {
public static void wtf(String tag, String message) { public static void wtf(String tag, String message) {
Log.wtf(TAG + TAG_SPACER + tag, message); Log.wtf(TAG + TAG_SPACER + tag, message);
} }
public static void wtf(String tag, String message, Throwable throwable) { public static void wtf(String tag, String message, Throwable throwable) {
Log.wtf(TAG + TAG_SPACER + tag, message, throwable); Log.wtf(TAG + TAG_SPACER + tag, message, throwable);
} }
public static void test(String message) { public static void test(String message) {
if (ChanApplication.DEVELOPER_MODE) { if (ChanApplication.DEVELOPER_MODE) {
Log.i(TAG + TAG_SPACER + "test", message); Log.i(TAG + TAG_SPACER + "test", message);
} }
} }
public static void test(String message, Throwable throwable) { public static void test(String message, Throwable throwable) {
if (ChanApplication.DEVELOPER_MODE) { if (ChanApplication.DEVELOPER_MODE) {
Log.i(TAG + TAG_SPACER + "test", message, throwable); Log.i(TAG + TAG_SPACER + "test", message, throwable);
} }
} }
} }

@ -13,23 +13,29 @@ import android.view.ViewGroup;
public class Utils { public class Utils {
private static DisplayMetrics displayMetrics; private static DisplayMetrics displayMetrics;
public final static ViewGroup.LayoutParams MATCH_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); public final static ViewGroup.LayoutParams MATCH_PARAMS = new ViewGroup.LayoutParams(
public final static ViewGroup.LayoutParams WRAP_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
public final static ViewGroup.LayoutParams MATCH_WRAP_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); public final static ViewGroup.LayoutParams WRAP_PARAMS = new ViewGroup.LayoutParams(
public final static ViewGroup.LayoutParams WRAP_MATCH_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
public final static ViewGroup.LayoutParams MATCH_WRAP_PARAMS = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
public final static ViewGroup.LayoutParams WRAP_MATCH_PARAMS = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
public static int dp(float dp) { public static int dp(float dp) {
// return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); // return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
if (displayMetrics == null) { if (displayMetrics == null) {
displayMetrics = ChanApplication.getInstance().getResources().getDisplayMetrics(); displayMetrics = ChanApplication.getInstance().getResources().getDisplayMetrics();
} }
return (int) (dp * displayMetrics.density); return (int) (dp * displayMetrics.density);
} }
/** /**
* Sets the android.R.attr.selectableItemBackground as background drawable on the view. * Sets the android.R.attr.selectableItemBackground as background drawable
* on the view.
*
* @param view * @param view
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@ -39,8 +45,7 @@ public class Utils {
} }
public static Drawable getSelectableBackgroundDrawable(Context context) { public static Drawable getSelectableBackgroundDrawable(Context context) {
TypedArray arr = context.obtainStyledAttributes( TypedArray arr = context.obtainStyledAttributes(new int[] { android.R.attr.selectableItemBackground });
new int[] {android.R.attr.selectableItemBackground});
Drawable drawable = arr.getDrawable(0); Drawable drawable = arr.getDrawable(0);
@ -50,8 +55,9 @@ public class Utils {
} }
/** /**
* Causes the runnable to be added to the message queue. * Causes the runnable to be added to the message queue. The runnable will
* The runnable will be run on the ui thread. * be run on the ui thread.
*
* @param runnable * @param runnable
*/ */
public static void runOnUiThread(Runnable runnable) { public static void runOnUiThread(Runnable runnable) {

Loading…
Cancel
Save