Refactored BoardManager and BoardEditor to use the database.

Added autofill to the add board dialog.
Allow unknown boards.
This to easy for users
captchafix
Florens Douwes 11 years ago
parent 5d4da74365
commit bb8bfe7984
  1. 1
      Chan/AndroidManifest.xml
  2. 7
      Chan/res/values/strings.xml
  3. 2
      Chan/src/org/floens/chan/ChanApplication.java
  4. 5
      Chan/src/org/floens/chan/core/loader/Loader.java
  5. 257
      Chan/src/org/floens/chan/core/manager/BoardManager.java
  6. 34
      Chan/src/org/floens/chan/core/model/Board.java
  7. 11
      Chan/src/org/floens/chan/core/net/BoardsRequest.java
  8. 9
      Chan/src/org/floens/chan/database/DatabaseHelper.java
  9. 51
      Chan/src/org/floens/chan/database/DatabaseManager.java
  10. 14
      Chan/src/org/floens/chan/ui/activity/BaseActivity.java
  11. 17
      Chan/src/org/floens/chan/ui/activity/BoardActivity.java
  12. 286
      Chan/src/org/floens/chan/ui/activity/BoardEditor.java
  13. 5
      Chan/src/org/floens/chan/ui/adapter/PostAdapter.java
  14. 8
      Chan/src/org/floens/chan/ui/view/DynamicListView.java
  15. 3
      Chan/src/org/floens/chan/ui/view/PostView.java
  16. 11
      Chan/src/org/floens/chan/utils/Time.java
  17. 15
      Chan/src/org/floens/chan/utils/Utils.java

@ -94,6 +94,7 @@
<activity <activity
android:name="org.floens.chan.ui.activity.BoardEditor" android:name="org.floens.chan.ui.activity.BoardEditor"
android:label="@string/board_edit" android:label="@string/board_edit"
android:configChanges="keyboardHidden|orientation|screenSize"
android:parentActivityName="org.floens.chan.ui.activity.BoardActivity" > android:parentActivityName="org.floens.chan.ui.activity.BoardActivity" >
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"

@ -42,9 +42,10 @@
<string name="board_edit">Edit my boards</string> <string name="board_edit">Edit my boards</string>
<string name="board_add">Add board</string> <string name="board_add">Add board</string>
<string name="board_add_fail">Unknown board code</string> <string name="board_add_success">Added board</string>
<string name="board_add_success">Added</string> <string name="board_add_duplicate">Board already added</string>
<string name="board_add_duplicate">You already have that board</string> <string name="board_add_unknown_title">Unknown board code</string>
<string name="board_add_unknown">The board with code CODE is not know. Press OK to add it anyway.</string>
<string name="drawer_open">Open drawer</string> <string name="drawer_open">Open drawer</string>
<string name="drawer_close">Close drawer</string> <string name="drawer_close">Close drawer</string>

@ -95,7 +95,7 @@ public class ChanApplication extends Application implements PinListener {
imageLoader = new ImageLoader(volleyRequestQueue, new BitmapLruImageCache(1024 * 1024 * 8)); imageLoader = new ImageLoader(volleyRequestQueue, new BitmapLruImageCache(1024 * 1024 * 8));
databaseManager = new DatabaseManager(this); databaseManager = new DatabaseManager(this);
boardManager = new BoardManager(this); boardManager = new BoardManager();
pinnedManager = new PinnedManager(this); pinnedManager = new PinnedManager(this);
pinnedManager.addPinListener(this); pinnedManager.addPinListener(this);
replyManager = new ReplyManager(this); replyManager = new ReplyManager(this);

@ -8,6 +8,7 @@ import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.core.net.ChanReaderRequest; import org.floens.chan.core.net.ChanReaderRequest;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import org.floens.chan.utils.Time;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -173,7 +174,7 @@ public class Loader {
return 0L; return 0L;
} else { } else {
long waitTime = watchTimeouts[currentTimeout] * 1000L; long waitTime = watchTimeouts[currentTimeout] * 1000L;
return lastLoadTime + waitTime - System.currentTimeMillis(); return lastLoadTime + waitTime - Time.get();
} }
} }
@ -263,7 +264,7 @@ public class Loader {
l.onData(result, loadable.isBoardMode()); l.onData(result, loadable.isBoardMode());
} }
lastLoadTime = System.currentTimeMillis(); lastLoadTime = Time.get();
if (loadable.isThreadMode()) { if (loadable.isThreadMode()) {
setTimer(result.size()); setTimer(result.size());

@ -1,111 +1,66 @@
package org.floens.chan.core.manager; package org.floens.chan.core.manager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Scanner; import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.chan.ChanUrls; import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Board;
import org.floens.chan.core.net.BoardsRequest; import org.floens.chan.core.net.BoardsRequest;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import org.floens.chan.utils.Time;
import android.content.Context;
import android.widget.Toast;
import com.android.volley.Response; import com.android.volley.Response;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
public class BoardManager { public class BoardManager {
private static final String TAG = "BoardManager"; private static final String TAG = "BoardManager";
private static final Comparator<Board> savedOrder = new Comparator<Board>() {
@Override
public int compare(Board lhs, Board rhs) {
return lhs.order < rhs.order ? -1 : 1;
}
};
private final Context context; private List<Board> allBoards;
// Including nsfw ones
private ArrayList<Board> allBoards = new ArrayList<Board>();
private ArrayList<Board> myBoards = new ArrayList<Board>();
private final ArrayList<String> myBoardsKeys = new ArrayList<String>();
private final ArrayList<String> myBoardsValues = new ArrayList<String>();
public BoardManager(Context context) { private final List<String> savedKeys = new ArrayList<String>();
this.context = context; private final List<String> savedValues = new ArrayList<String>();
public BoardManager() {
loadBoards();
loadFromServer(); loadFromServer();
myBoards = loadMyBoards();
updateMyBoardsKeysAndValues(myBoards);
}
/**
* Avoid having 0 boards, which causes graphical problems
*
* @param list
*/
private ArrayList<Board> getDefaultBoards() {
ArrayList<Board> list = new ArrayList<Board>();
{
Board board = new Board();
board.key = "Video Games";
board.value = "v";
list.add(board);
}
{
Board board = new Board();
board.key = "Anime & Manga";
board.value = "a";
list.add(board);
}
{
Board board = new Board();
board.key = "Comics & Cartoons";
board.value = "co";
list.add(board);
}
{
Board board = new Board();
board.key = "Health & Fitness";
board.value = "fit";
list.add(board);
}
{
Board board = new Board();
board.key = "Technology";
board.value = "g";
list.add(board);
}
return list;
}
public ArrayList<Board> getMyBoards() {
return myBoards;
} }
public void setMyBoards(ArrayList<Board> list) { public List<Board> getAllBoards() {
myBoards.clear(); return allBoards;
myBoards = list;
updateMyBoardsKeysAndValues(list);
storeBoardListInDatabase("myBoards", myBoards);
} }
private void updateMyBoardsKeysAndValues(ArrayList<Board> list) { public List<Board> getSavedBoards() {
myBoardsKeys.clear(); List<Board> saved = new ArrayList<Board>(allBoards.size());
myBoardsValues.clear();
for (Board board : list) { for (Board b : allBoards) {
myBoardsKeys.add(board.key); if (b.saved)
myBoardsValues.add(board.value); saved.add(b);
} }
Collections.sort(saved, savedOrder);
return saved;
} }
public ArrayList<String> getMyBoardsKeys() { public List<String> getSavedKeys() {
return myBoardsKeys; return savedKeys;
} }
public ArrayList<String> getMyBoardsValues() { public List<String> getSavedValues() {
return myBoardsValues; return savedValues;
} }
public boolean getBoardExists(String board) { public boolean getBoardExists(String board) {
for (Board e : allBoards) { for (Board e : getAllBoards()) {
if (e.value.equals(board)) { if (e.value.equals(board)) {
return true; return true;
} }
@ -124,101 +79,87 @@ public class BoardManager {
return null; return null;
} }
/** public void updateSavedBoards() {
* Try to add value to the supplied list. long start = Time.get();
* ChanApplication.getDatabaseManager().updateBoards(allBoards);
* @param list reloadSavedKeysValues();
* @param value Logger.d(TAG, "updateSavedBoards took " + Time.get(start));
*/ }
public void addBoard(ArrayList<Board> list, String value) {
for (Board board : list) { private void reloadSavedKeysValues() {
if (board.value.equals(value)) { List<Board> saved = getSavedBoards();
Toast.makeText(context, R.string.board_add_duplicate, Toast.LENGTH_LONG).show();
savedKeys.clear();
return; for (Board board : saved) {
} savedKeys.add(board.key);
} }
for (Board board : allBoards) { savedValues.clear();
if (board.value.equals(value)) { for (Board board : saved) {
list.add(board); savedValues.add(board.value);
String text = context.getString(R.string.board_add_success) + " " + board.key;
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
return;
}
} }
Toast.makeText(context, R.string.board_add_fail, Toast.LENGTH_LONG).show();
} }
private ArrayList<Board> loadMyBoards() { private void storeBoards() {
ArrayList<Board> list = getBoardListFromDatabase("myBoards"); long start = Time.get();
if (list == null || list.size() == 0) { Logger.d(TAG, "Storing boards in database");
list = getDefaultBoards();
for (Board test : allBoards) {
storeBoardListInDatabase("myBoards", list); if (test.saved) {
Logger.w(TAG, "Board with value " + test.value + " saved");
}
} }
return list; ChanApplication.getDatabaseManager().setBoards(allBoards);
Logger.d(TAG, "storeBoards took " + Time.get(start));
} }
private void storeBoardListInDatabase(String key, ArrayList<Board> list) { private void loadBoards() {
String total = ""; long start = Time.get();
for (Board board : list) { allBoards = ChanApplication.getDatabaseManager().getBoards();
total += board.key + "|" + board.value + "\n"; if (allBoards.size() == 0) {
Logger.d(TAG, "Loading default boards");
allBoards = getDefaultBoards();
storeBoards();
} }
ChanApplication.getPreferences().edit().putString(key, total).commit(); reloadSavedKeysValues();
Logger.d(TAG, "loadBoards took " + Time.get(start));
} }
private ArrayList<Board> getBoardListFromDatabase(String key) { private void setBoardsFromServer(List<Board> list) {
String total = ChanApplication.getPreferences().getString(key, null); boolean changed = false;
if (total == null) for (Board serverBoard : list) {
return null; boolean has = false;
for (Board b : allBoards) {
ArrayList<Board> list = new ArrayList<Board>(); if (b.valueEquals(serverBoard)) {
has = true;
Scanner scanner = new Scanner(total); break;
}
while (scanner.hasNextLine()) { }
String line = scanner.nextLine();
String[] splitted = line.split("\\|"); if (!has) {
Logger.d(TAG, "Adding unknown board: " + serverBoard.value);
if (splitted.length < 2) allBoards.add(serverBoard);
continue; changed = true;
Board board = new Board();
board.key = splitted[0];
board.value = splitted[1];
if (!board.finish()) {
Logger.wtf(TAG, "board.finish in loadfrompreferences threw up");
} }
list.add(board);
} }
scanner.close(); if (changed) {
storeBoards();
return list; }
} }
private void loadFromServer() { private void loadFromServer() {
ArrayList<Board> temp = getBoardListFromDatabase("allBoards");
if (temp != null) {
allBoards = temp;
}
ChanApplication.getVolleyRequestQueue().add( ChanApplication.getVolleyRequestQueue().add(
new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener<ArrayList<Board>>() { new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener<List<Board>>() {
@Override @Override
public void onResponse(ArrayList<Board> data) { public void onResponse(List<Board> data) {
storeBoardListInDatabase("allBoards", data);
allBoards = data;
Logger.i(TAG, "Got boards from server"); Logger.i(TAG, "Got boards from server");
setBoardsFromServer(data);
} }
}, new Response.ErrorListener() { }, new Response.ErrorListener() {
@Override @Override
@ -227,4 +168,14 @@ public class BoardManager {
} }
})); }));
} }
private List<Board> getDefaultBoards() {
List<Board> list = new ArrayList<Board>();
list.add(new Board("Technology", "g", true, true));
list.add(new Board("Video Games", "v", true, true));
list.add(new Board("Anime & Manga", "a", true, true));
list.add(new Board("Comics & Cartoons", "co", true, true));
list.add(new Board("International", "int", true, true));
return list;
}
} }

@ -1,21 +1,43 @@
package org.floens.chan.core.model; package org.floens.chan.core.model;
/** import com.j256.ormlite.field.DatabaseField;
* Board key and value. key is full name e.g. Literature. value is board key import com.j256.ormlite.table.DatabaseTable;
* e.g. lit.
*/ @DatabaseTable
public class Board { public class Board {
public Board() {
}
public Board(String key, String value, boolean saved, boolean workSafe) {
this.key = key;
this.value = value;
this.saved = saved;
this.workSafe = workSafe;
}
@DatabaseField(generatedId = true)
public int id;
/** /**
* Name of the board, e.g. Literature * Name of the board, e.g. Literature
*/ */
@DatabaseField
public String key; public String key;
/** /**
* Name of the url, e.g. lit * Name of the url, e.g. lit
*/ */
@DatabaseField
public String value; public String value;
@DatabaseField
public boolean workSafe = false; public boolean workSafe = false;
@DatabaseField
public boolean saved = false;
@DatabaseField
public int order;
public boolean finish() { public boolean finish() {
if (key == null || value == null) if (key == null || value == null)
return false; return false;
@ -27,4 +49,8 @@ public class Board {
public String toString() { public String toString() {
return key; return key;
} }
public boolean valueEquals(Board other) {
return value.equals(other.value);
}
} }

@ -2,6 +2,7 @@ package org.floens.chan.core.net;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Board;
@ -10,18 +11,18 @@ import android.util.JsonReader;
import com.android.volley.Response.ErrorListener; import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener; import com.android.volley.Response.Listener;
public class BoardsRequest extends JsonReaderRequest<ArrayList<Board>> { public class BoardsRequest extends JsonReaderRequest<List<Board>> {
public BoardsRequest(String url, Listener<ArrayList<Board>> listener, ErrorListener errorListener) { public BoardsRequest(String url, Listener<List<Board>> listener, ErrorListener errorListener) {
super(url, listener, errorListener); super(url, listener, errorListener);
} }
@Override @Override
public ArrayList<Board> readJson(JsonReader reader) { public List<Board> readJson(JsonReader reader) {
return parseJson(reader); return parseJson(reader);
} }
private ArrayList<Board> parseJson(JsonReader reader) { private List<Board> parseJson(JsonReader reader) {
ArrayList<Board> list = new ArrayList<Board>(); List<Board> list = new ArrayList<Board>();
try { try {
reader.beginObject(); reader.beginObject();

@ -2,6 +2,7 @@ package org.floens.chan.database;
import java.sql.SQLException; import java.sql.SQLException;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Pin;
import org.floens.chan.core.model.SavedReply; import org.floens.chan.core.model.SavedReply;
@ -19,12 +20,13 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final String TAG = "DatabaseHelper"; private static final String TAG = "DatabaseHelper";
private static final String DATABASE_NAME = "ChanDB"; private static final String DATABASE_NAME = "ChanDB";
private static final int DATABASE_VERSION = 9; private static final int DATABASE_VERSION = 11;
public Dao<Pin, Integer> pinDao; public Dao<Pin, Integer> pinDao;
public Dao<Loadable, Integer> loadableDao; public Dao<Loadable, Integer> loadableDao;
public Dao<SavedReply, Integer> savedDao; public Dao<SavedReply, Integer> savedDao;
public Dao<Board, Integer> boardsDao;
private final Context context; private final Context context;
public DatabaseHelper(Context context) { public DatabaseHelper(Context context) {
@ -36,6 +38,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
pinDao = getDao(Pin.class); pinDao = getDao(Pin.class);
loadableDao = getDao(Loadable.class); loadableDao = getDao(Loadable.class);
savedDao = getDao(SavedReply.class); savedDao = getDao(SavedReply.class);
boardsDao = getDao(Board.class);
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -47,6 +50,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TableUtils.createTable(connectionSource, Pin.class); TableUtils.createTable(connectionSource, Pin.class);
TableUtils.createTable(connectionSource, Loadable.class); TableUtils.createTable(connectionSource, Loadable.class);
TableUtils.createTable(connectionSource, SavedReply.class); TableUtils.createTable(connectionSource, SavedReply.class);
TableUtils.createTable(connectionSource, Board.class);
} catch (SQLException e) { } catch (SQLException e) {
Logger.e(TAG, "Error creating db", e); Logger.e(TAG, "Error creating db", e);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -76,6 +80,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TableUtils.dropTable(connectionSource, Pin.class, true); TableUtils.dropTable(connectionSource, Pin.class, true);
TableUtils.dropTable(connectionSource, Loadable.class, true); TableUtils.dropTable(connectionSource, Loadable.class, true);
TableUtils.dropTable(connectionSource, SavedReply.class, true); TableUtils.dropTable(connectionSource, SavedReply.class, true);
TableUtils.dropTable(connectionSource, Board.class, true);
onCreate(database, connectionSource); onCreate(database, connectionSource);
} catch (SQLException e) { } catch (SQLException e) {

@ -2,10 +2,13 @@ package org.floens.chan.database;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Pin;
import org.floens.chan.core.model.SavedReply; import org.floens.chan.core.model.SavedReply;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import org.floens.chan.utils.Time;
import android.content.Context; import android.content.Context;
@ -114,6 +117,54 @@ public class DatabaseManager {
return list; return list;
} }
public void setBoards(final List<Board> boards) {
try {
helper.boardsDao.callBatchTasks(new Callable<Void>() {
@Override
public Void call() throws SQLException {
for (Board b : boards) {
helper.boardsDao.createOrUpdate(b);
}
return null;
}
});
} catch (Exception e) {
Logger.e(TAG, "Error setting boards in db", e);
}
}
public void updateBoards(final List<Board> boards) {
try {
helper.boardsDao.callBatchTasks(new Callable<Void>() {
@Override
public Void call() throws SQLException {
long start = Time.get();
for (Board b : boards) {
helper.boardsDao.update(b);
}
Logger.d(TAG, "Update board took " + Time.get(start));
return null;
}
});
} catch (Exception e) {
Logger.e(TAG, "Error updating boards in db", e);
}
}
public List<Board> getBoards() {
List<Board> boards = null;
try {
boards = helper.boardsDao.queryForAll();
} catch (SQLException e) {
Logger.e(TAG, "Error getting boards from db", e);
}
return boards;
}
public String getSummary() { public String getSummary() {
String o = ""; String o = "";

@ -11,12 +11,11 @@ import org.floens.chan.ui.BadgeDrawable;
import org.floens.chan.ui.SwipeDismissListViewTouchListener; import org.floens.chan.ui.SwipeDismissListViewTouchListener;
import org.floens.chan.ui.SwipeDismissListViewTouchListener.DismissCallbacks; import org.floens.chan.ui.SwipeDismissListViewTouchListener.DismissCallbacks;
import org.floens.chan.ui.adapter.PinnedAdapter; import org.floens.chan.ui.adapter.PinnedAdapter;
import org.floens.chan.utils.Utils;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnShowListener;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
@ -33,7 +32,6 @@ import android.text.TextUtils;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener; import android.widget.AdapterView.OnItemLongClickListener;
@ -221,15 +219,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
} }
}).setTitle(R.string.drawer_pinned_change_title).setView(text).create(); }).setTitle(R.string.drawer_pinned_change_title).setView(text).create();
text.requestFocus(); Utils.requestKeyboardFocus(dialog, text);
dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(text, 0);
}
});
dialog.show(); dialog.show();
} }

@ -53,7 +53,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
final ActionBar actionBar = getActionBar(); final ActionBar actionBar = getActionBar();
actionBar.setListNavigationCallbacks( actionBar.setListNavigationCallbacks(
new ArrayAdapter<String>(actionBar.getThemedContext(), R.layout.board_select_spinner, new ArrayAdapter<String>(actionBar.getThemedContext(), R.layout.board_select_spinner,
android.R.id.text1, ChanApplication.getBoardManager().getMyBoardsKeys()), this); android.R.id.text1, ChanApplication.getBoardManager().getSavedKeys()), this);
updatePaneState(); updatePaneState();
updateActionBarState(); updateActionBarState();
@ -75,8 +75,10 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
} }
if (boardLoadable.mode == Loadable.Mode.INVALID) { if (boardLoadable.mode == Loadable.Mode.INVALID) {
String board = ChanApplication.getBoardManager().getMyBoardsValues().get(0); List<String> savedValues = ChanApplication.getBoardManager().getSavedValues();
loadBoard(board); if (savedValues.size() > 0) {
loadBoard(savedValues.get(0));
}
} }
} }
} }
@ -396,8 +398,11 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
if (!actionBarSetToListNavigation) { if (!actionBarSetToListNavigation) {
actionBarSetToListNavigation = true; actionBarSetToListNavigation = true;
} else { } else {
boardLoadable = new Loadable(ChanApplication.getBoardManager().getMyBoardsValues().get(position)); List<String> savedValues = ChanApplication.getBoardManager().getSavedValues();
startLoadingBoard(boardLoadable); if (position >= 0 && position < savedValues.size()) {
boardLoadable = new Loadable(savedValues.get(position));
startLoadingBoard(boardLoadable);
}
} }
return true; return true;
@ -496,7 +501,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
} }
private int getBoardIndexNavigator(String boardValue) { private int getBoardIndexNavigator(String boardValue) {
List<String> list = ChanApplication.getBoardManager().getMyBoardsValues(); List<String> list = ChanApplication.getBoardManager().getSavedValues();
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals(boardValue)) { if (list.get(i).equals(boardValue)) {
return i; return i;

@ -1,31 +1,44 @@
package org.floens.chan.ui.activity; package org.floens.chan.ui.activity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Board;
import org.floens.chan.ui.adapter.BoardEditAdapter; import org.floens.chan.ui.adapter.BoardEditAdapter;
import org.floens.chan.ui.view.DynamicListView; import org.floens.chan.ui.view.DynamicListView;
import org.floens.chan.utils.Logger;
import org.floens.chan.utils.Utils;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnShowListener;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import android.widget.Toast;
public class BoardEditor extends Activity { public class BoardEditor extends Activity {
private ArrayList<Board> list = new ArrayList<Board>(); private final BoardManager boardManager = ChanApplication.getBoardManager();
private List<Board> list;
private DynamicListView<Board> listView; private DynamicListView<Board> listView;
private BoardEditAdapter adapter; private BoardEditAdapter adapter;
private AlertDialog dialog;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -33,43 +46,84 @@ public class BoardEditor extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.board_edit); setContentView(R.layout.board_edit);
list = boardManager.getSavedBoards();
// Copy not a reference
list = (ArrayList<Board>) ChanApplication.getBoardManager().getMyBoards().clone();
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); reload();
listView.setAdapter(adapter);
} }
@SuppressWarnings("unchecked")
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
// For runtime changes
if (list.size() > 0) { if (list.size() > 0) {
ChanApplication.getBoardManager().setMyBoards((ArrayList<Board>) list.clone()); // Order
for (int i = 0; i < list.size(); i++) {
list.get(i).order = i;
}
boardManager.updateSavedBoards();
} }
} }
public void onDeleteClicked(Board board) {
removeBoard(board.value);
}
@Override @Override
protected void onDestroy() { public boolean onCreateOptionsMenu(Menu menu) {
super.onDestroy(); getMenuInflater().inflate(R.menu.board_edit, menu);
return true;
}
if (dialog != null) { @Override
dialog.dismiss(); public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_board:
showAddBoardDialog();
return true;
} }
return super.onOptionsItemSelected(item);
} }
private void addBoard(String value) { private void addBoard(final String value) {
ChanApplication.getBoardManager().addBoard(list, value); for (Board board : list) {
if (board.value.equals(value)) {
Toast.makeText(this, R.string.board_add_duplicate, Toast.LENGTH_LONG).show();
adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); return;
listView.setArrayList(list); }
listView.setAdapter(adapter); }
List<Board> all = ChanApplication.getBoardManager().getAllBoards();
for (Board board : all) {
if (board.value.equals(value)) {
board.saved = true;
list.add(board);
Toast.makeText(this, getString(R.string.board_add_success) + " " + board.key, Toast.LENGTH_LONG).show();
reload();
return;
}
}
String message = getString(R.string.board_add_unknown).replace("CODE", value);
new AlertDialog.Builder(this).setTitle(R.string.board_add_unknown_title).setMessage(message)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (list != null)
list.add(new Board(value, value, true, true));
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).create().show();
} }
private void removeBoard(String value) { private void removeBoard(String value) {
@ -77,32 +131,41 @@ public class BoardEditor extends Activity {
return; return;
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
Board e = list.get(i); Board b = list.get(i);
if (e.value == value) { if (b.value == value) {
list.remove(i); list.remove(i);
b.saved = false;
adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); break;
listView.setArrayList(list);
listView.setAdapter(adapter);
} }
} }
reload();
} }
public void onDeleteClicked(Board board) { private void reload() {
removeBoard(board.value); adapter = new BoardEditAdapter(this, R.layout.board_view, list, this);
listView.setArrayList(list);
listView.setAdapter(adapter);
} }
private void showAddBoardDialog() { private void showAddBoardDialog() {
final EditText text = new EditText(this); final AutoCompleteTextView text = new AutoCompleteTextView(this);
text.setSingleLine(); text.setSingleLine();
dialog = new AlertDialog.Builder(this).setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { FillAdapter fillAdapter = new FillAdapter(this, 0);
fillAdapter.setEditingList(list);
fillAdapter.setAutoCompleteView(text);
text.setAdapter(fillAdapter);
text.setThreshold(1);
text.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
AlertDialog dialog = new AlertDialog.Builder(this).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.toLowerCase(Locale.ENGLISH));
} }
} }
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@ -111,41 +174,140 @@ public class BoardEditor extends Activity {
} }
}).setTitle(R.string.board_add).setView(text).create(); }).setTitle(R.string.board_add).setView(text).create();
text.requestFocus(); Utils.requestKeyboardFocus(dialog, text);
dialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface d) {
dialog = null;
}
});
dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(text, 0);
}
});
dialog.show(); dialog.show();
} }
@Override private static class FillAdapter extends ArrayAdapter<String> implements Filterable {
public boolean onCreateOptionsMenu(Menu menu) { private List<Board> currentlyEditing;
getMenuInflater().inflate(R.menu.board_edit, menu); private View autoCompleteView;
return true; private final Filter filter;
} private final List<Board> filtered = new ArrayList<Board>();
@Override public FillAdapter(Context context, int resource) {
public boolean onOptionsItemSelected(MenuItem item) { super(context, resource);
switch (item.getItemId()) {
case R.id.action_add_board:
showAddBoardDialog();
return true; filter = new Filter() {
@Override
protected synchronized FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint) || (constraint.toString().contains(" "))) {
results.values = null;
results.count = 0;
} else {
List<Board> keys = getFiltered(constraint.toString());
results.values = keys;
results.count = keys.size();
}
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filtered.clear();
if (results.values != null) {
filtered.addAll((List<Board>) results.values);
} else {
filtered.addAll(getBoards());
}
notifyDataSetChanged();
}
};
} }
return super.onOptionsItemSelected(item); public void setEditingList(List<Board> list) {
currentlyEditing = list;
}
public void setAutoCompleteView(View autoCompleteView) {
this.autoCompleteView = autoCompleteView;
}
@Override
public int getCount() {
return filtered.size();
}
@Override
public String getItem(int position) {
return filtered.get(position).value;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TextView view = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, null);
Board b = filtered.get(position);
view.setText(b.value + " - " + b.key);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(autoCompleteView.getWindowToken(), 0);
}
return false;
}
});
return view;
}
@Override
public Filter getFilter() {
return filter;
}
private List<Board> getFiltered(String filter) {
String lowered = filter.toLowerCase(Locale.ENGLISH);
List<Board> list = new ArrayList<Board>();
for (Board b : getBoards()) {
if (!haveBoard(b.value)
&& (b.key.toLowerCase(Locale.ENGLISH).contains(lowered) || b.value.toLowerCase(Locale.ENGLISH)
.contains(lowered))) {
list.add(b);
}
}
return list;
}
private boolean haveBoard(String value) {
for (Board b : currentlyEditing) {
if (b.value.equals(value))
return true;
}
return false;
}
private List<Board> getBoards() {
// Lets be cheaty here: if the user has nsfw boards in the list,
// show them in the autofiller.
boolean showUnsafe = false;
for (Board has : currentlyEditing) {
if (!has.workSafe) {
Logger.test("Unsafe: " + has.key + ", " + has.workSafe);
showUnsafe = true;
break;
}
}
Logger.test("Showing unsafe: " + showUnsafe + ", " + currentlyEditing.size());
List<Board> s = new ArrayList<Board>();
for (Board b : ChanApplication.getBoardManager().getAllBoards()) {
if (showUnsafe || b.workSafe)
s.add(b);
}
return s;
}
} }
} }

@ -9,6 +9,7 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.ui.ScrollerRunnable; import org.floens.chan.ui.ScrollerRunnable;
import org.floens.chan.ui.view.PostView; import org.floens.chan.ui.view.PostView;
import org.floens.chan.ui.view.ThreadWatchCounterView; import org.floens.chan.ui.view.ThreadWatchCounterView;
import org.floens.chan.utils.Time;
import org.floens.chan.utils.Utils; import org.floens.chan.utils.Utils;
import android.content.Context; import android.content.Context;
@ -62,8 +63,8 @@ public class PostAdapter extends BaseAdapter {
} }
if (position >= postList.size()) { if (position >= postList.size()) {
if (System.currentTimeMillis() - lastViewedTime > 10000L) { if (Time.get(lastViewedTime) > 10000L) {
lastViewedTime = System.currentTimeMillis(); lastViewedTime = Time.get();
threadManager.bottomPostViewed(); threadManager.bottomPostViewed();
} }

@ -16,7 +16,7 @@
package org.floens.chan.ui.view; package org.floens.chan.ui.view;
import java.util.ArrayList; import java.util.List;
import org.floens.chan.ui.adapter.BoardEditAdapter; import org.floens.chan.ui.adapter.BoardEditAdapter;
@ -70,7 +70,7 @@ public class DynamicListView<T> extends ListView {
private final int MOVE_DURATION = 150; private final int MOVE_DURATION = 150;
private final int LINE_THICKNESS = 10; private final int LINE_THICKNESS = 10;
public ArrayList<T> mCheeseList; public List<T> mCheeseList;
private int mLastEventY = -1; private int mLastEventY = -1;
@ -372,7 +372,7 @@ public class DynamicListView<T> extends ListView {
} }
} }
private void swapElements(ArrayList<T> arrayList, int indexOne, int indexTwo) { private void swapElements(List<T> arrayList, int indexOne, int indexTwo) {
T temp = arrayList.get(indexOne); T temp = arrayList.get(indexOne);
arrayList.set(indexOne, arrayList.get(indexTwo)); arrayList.set(indexOne, arrayList.get(indexTwo));
arrayList.set(indexTwo, temp); arrayList.set(indexTwo, temp);
@ -501,7 +501,7 @@ public class DynamicListView<T> extends ListView {
return false; return false;
} }
public void setArrayList(ArrayList<T> cheeseList) { public void setArrayList(List<T> cheeseList) {
mCheeseList = cheeseList; mCheeseList = cheeseList;
} }

@ -7,6 +7,7 @@ import org.floens.chan.core.manager.ThreadManager;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable; import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.utils.IconCache; import org.floens.chan.utils.IconCache;
import org.floens.chan.utils.Time;
import org.floens.chan.utils.Utils; import org.floens.chan.utils.Utils;
import android.app.Activity; import android.app.Activity;
@ -126,7 +127,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
total = TextUtils.concat(total, post.capcodeSpan, " "); total = TextUtils.concat(total, post.capcodeSpan, " ");
} }
CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(post.time * 1000L, System.currentTimeMillis(), CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(post.time * 1000L, Time.get(),
DateUtils.SECOND_IN_MILLIS, 0); DateUtils.SECOND_IN_MILLIS, 0);
SpannableString date = new SpannableString("No." + post.no + " " + relativeTime); SpannableString date = new SpannableString("No." + post.no + " " + relativeTime);

@ -0,0 +1,11 @@
package org.floens.chan.utils;
public class Time {
public static long get() {
return System.currentTimeMillis();
}
public static long get(long other) {
return System.currentTimeMillis() - other;
}
}

@ -2,7 +2,10 @@ package org.floens.chan.utils;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnShowListener;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
@ -10,6 +13,7 @@ import android.os.Looper;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
public class Utils { public class Utils {
private static DisplayMetrics displayMetrics; private static DisplayMetrics displayMetrics;
@ -63,4 +67,15 @@ public class Utils {
public static void runOnUiThread(Runnable runnable) { public static void runOnUiThread(Runnable runnable) {
new Handler(Looper.getMainLooper()).post(runnable); new Handler(Looper.getMainLooper()).post(runnable);
} }
public static void requestKeyboardFocus(Dialog dialog, final View view) {
view.requestFocus();
dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, 0);
}
});
}
} }

Loading…
Cancel
Save