From bb8bfe798402625f8f70e993bc4f733a65852e7a Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Thu, 1 May 2014 15:25:09 +0200 Subject: [PATCH] Refactored BoardManager and BoardEditor to use the database. Added autofill to the add board dialog. Allow unknown boards. This to easy for users --- Chan/AndroidManifest.xml | 1 + Chan/res/values/strings.xml | 7 +- Chan/src/org/floens/chan/ChanApplication.java | 2 +- .../org/floens/chan/core/loader/Loader.java | 5 +- .../chan/core/manager/BoardManager.java | 257 +++++++--------- .../src/org/floens/chan/core/model/Board.java | 34 ++- .../floens/chan/core/net/BoardsRequest.java | 11 +- .../floens/chan/database/DatabaseHelper.java | 9 +- .../floens/chan/database/DatabaseManager.java | 51 ++++ .../floens/chan/ui/activity/BaseActivity.java | 14 +- .../chan/ui/activity/BoardActivity.java | 17 +- .../floens/chan/ui/activity/BoardEditor.java | 286 ++++++++++++++---- .../floens/chan/ui/adapter/PostAdapter.java | 5 +- .../floens/chan/ui/view/DynamicListView.java | 8 +- .../src/org/floens/chan/ui/view/PostView.java | 3 +- Chan/src/org/floens/chan/utils/Time.java | 11 + Chan/src/org/floens/chan/utils/Utils.java | 15 + 17 files changed, 479 insertions(+), 257 deletions(-) create mode 100644 Chan/src/org/floens/chan/utils/Time.java diff --git a/Chan/AndroidManifest.xml b/Chan/AndroidManifest.xml index 730f1580..b170dc35 100644 --- a/Chan/AndroidManifest.xml +++ b/Chan/AndroidManifest.xml @@ -94,6 +94,7 @@ Edit my boards Add board - Unknown board code - Added - You already have that board + Added board + Board already added + Unknown board code + The board with code CODE is not know. Press OK to add it anyway. Open drawer Close drawer diff --git a/Chan/src/org/floens/chan/ChanApplication.java b/Chan/src/org/floens/chan/ChanApplication.java index f92bce9a..c4eeadf7 100644 --- a/Chan/src/org/floens/chan/ChanApplication.java +++ b/Chan/src/org/floens/chan/ChanApplication.java @@ -95,7 +95,7 @@ public class ChanApplication extends Application implements PinListener { imageLoader = new ImageLoader(volleyRequestQueue, new BitmapLruImageCache(1024 * 1024 * 8)); databaseManager = new DatabaseManager(this); - boardManager = new BoardManager(this); + boardManager = new BoardManager(); pinnedManager = new PinnedManager(this); pinnedManager.addPinListener(this); replyManager = new ReplyManager(this); diff --git a/Chan/src/org/floens/chan/core/loader/Loader.java b/Chan/src/org/floens/chan/core/loader/Loader.java index 2970f9ac..23a4283d 100644 --- a/Chan/src/org/floens/chan/core/loader/Loader.java +++ b/Chan/src/org/floens/chan/core/loader/Loader.java @@ -8,6 +8,7 @@ import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; import org.floens.chan.core.net.ChanReaderRequest; import org.floens.chan.utils.Logger; +import org.floens.chan.utils.Time; import android.os.Handler; import android.os.Looper; @@ -173,7 +174,7 @@ public class Loader { return 0L; } else { 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()); } - lastLoadTime = System.currentTimeMillis(); + lastLoadTime = Time.get(); if (loadable.isThreadMode()) { setTimer(result.size()); diff --git a/Chan/src/org/floens/chan/core/manager/BoardManager.java b/Chan/src/org/floens/chan/core/manager/BoardManager.java index e682d201..78a30ab1 100644 --- a/Chan/src/org/floens/chan/core/manager/BoardManager.java +++ b/Chan/src/org/floens/chan/core/manager/BoardManager.java @@ -1,111 +1,66 @@ package org.floens.chan.core.manager; 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.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.model.Board; import org.floens.chan.core.net.BoardsRequest; import org.floens.chan.utils.Logger; - -import android.content.Context; -import android.widget.Toast; +import org.floens.chan.utils.Time; import com.android.volley.Response; import com.android.volley.VolleyError; public class BoardManager { private static final String TAG = "BoardManager"; + private static final Comparator savedOrder = new Comparator() { + @Override + public int compare(Board lhs, Board rhs) { + return lhs.order < rhs.order ? -1 : 1; + } + }; - private final Context context; - // Including nsfw ones - private ArrayList allBoards = new ArrayList(); - private ArrayList myBoards = new ArrayList(); - - private final ArrayList myBoardsKeys = new ArrayList(); - private final ArrayList myBoardsValues = new ArrayList(); + private List allBoards; - public BoardManager(Context context) { - this.context = context; + private final List savedKeys = new ArrayList(); + private final List savedValues = new ArrayList(); + public BoardManager() { + loadBoards(); loadFromServer(); - myBoards = loadMyBoards(); - updateMyBoardsKeysAndValues(myBoards); - } - - /** - * Avoid having 0 boards, which causes graphical problems - * - * @param list - */ - private ArrayList getDefaultBoards() { - ArrayList list = new ArrayList(); - { - 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 getMyBoards() { - return myBoards; } - public void setMyBoards(ArrayList list) { - myBoards.clear(); - myBoards = list; - updateMyBoardsKeysAndValues(list); - storeBoardListInDatabase("myBoards", myBoards); + public List getAllBoards() { + return allBoards; } - private void updateMyBoardsKeysAndValues(ArrayList list) { - myBoardsKeys.clear(); - myBoardsValues.clear(); + public List getSavedBoards() { + List saved = new ArrayList(allBoards.size()); - for (Board board : list) { - myBoardsKeys.add(board.key); - myBoardsValues.add(board.value); + for (Board b : allBoards) { + if (b.saved) + saved.add(b); } + + Collections.sort(saved, savedOrder); + + return saved; } - public ArrayList getMyBoardsKeys() { - return myBoardsKeys; + public List getSavedKeys() { + return savedKeys; } - public ArrayList getMyBoardsValues() { - return myBoardsValues; + public List getSavedValues() { + return savedValues; } public boolean getBoardExists(String board) { - for (Board e : allBoards) { + for (Board e : getAllBoards()) { if (e.value.equals(board)) { return true; } @@ -124,101 +79,87 @@ public class BoardManager { return null; } - /** - * Try to add value to the supplied list. - * - * @param list - * @param value - */ - public void addBoard(ArrayList list, String value) { - for (Board board : list) { - if (board.value.equals(value)) { - Toast.makeText(context, R.string.board_add_duplicate, Toast.LENGTH_LONG).show(); - - return; - } + public void updateSavedBoards() { + long start = Time.get(); + ChanApplication.getDatabaseManager().updateBoards(allBoards); + reloadSavedKeysValues(); + Logger.d(TAG, "updateSavedBoards took " + Time.get(start)); + } + + private void reloadSavedKeysValues() { + List saved = getSavedBoards(); + + savedKeys.clear(); + for (Board board : saved) { + savedKeys.add(board.key); } - for (Board board : allBoards) { - if (board.value.equals(value)) { - list.add(board); - - String text = context.getString(R.string.board_add_success) + " " + board.key; - Toast.makeText(context, text, Toast.LENGTH_LONG).show(); - - return; - } + savedValues.clear(); + for (Board board : saved) { + savedValues.add(board.value); } - - Toast.makeText(context, R.string.board_add_fail, Toast.LENGTH_LONG).show(); } - private ArrayList loadMyBoards() { - ArrayList list = getBoardListFromDatabase("myBoards"); - if (list == null || list.size() == 0) { - list = getDefaultBoards(); - - storeBoardListInDatabase("myBoards", list); + private void storeBoards() { + long start = Time.get(); + Logger.d(TAG, "Storing boards in database"); + + for (Board test : allBoards) { + 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 list) { - String total = ""; - - for (Board board : list) { - total += board.key + "|" + board.value + "\n"; + private void loadBoards() { + long start = Time.get(); + + allBoards = ChanApplication.getDatabaseManager().getBoards(); + 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 getBoardListFromDatabase(String key) { - String total = ChanApplication.getPreferences().getString(key, null); - if (total == null) - return null; - - ArrayList list = new ArrayList(); - - Scanner scanner = new Scanner(total); - - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - String[] splitted = line.split("\\|"); - - if (splitted.length < 2) - continue; - - Board board = new Board(); - board.key = splitted[0]; - board.value = splitted[1]; - if (!board.finish()) { - Logger.wtf(TAG, "board.finish in loadfrompreferences threw up"); + + private void setBoardsFromServer(List list) { + boolean changed = false; + for (Board serverBoard : list) { + boolean has = false; + for (Board b : allBoards) { + if (b.valueEquals(serverBoard)) { + has = true; + break; + } + } + + if (!has) { + Logger.d(TAG, "Adding unknown board: " + serverBoard.value); + allBoards.add(serverBoard); + changed = true; } - - list.add(board); } - - scanner.close(); - - return list; + + if (changed) { + storeBoards(); + } } private void loadFromServer() { - ArrayList temp = getBoardListFromDatabase("allBoards"); - if (temp != null) { - allBoards = temp; - } - ChanApplication.getVolleyRequestQueue().add( - new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener>() { + new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener>() { @Override - public void onResponse(ArrayList data) { - storeBoardListInDatabase("allBoards", data); - allBoards = data; - + public void onResponse(List data) { Logger.i(TAG, "Got boards from server"); + setBoardsFromServer(data); } }, new Response.ErrorListener() { @Override @@ -227,4 +168,14 @@ public class BoardManager { } })); } + + private List getDefaultBoards() { + List list = new ArrayList(); + 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; + } } diff --git a/Chan/src/org/floens/chan/core/model/Board.java b/Chan/src/org/floens/chan/core/model/Board.java index 73f45ca4..1303a58a 100644 --- a/Chan/src/org/floens/chan/core/model/Board.java +++ b/Chan/src/org/floens/chan/core/model/Board.java @@ -1,21 +1,43 @@ package org.floens.chan.core.model; -/** - * Board key and value. key is full name e.g. Literature. value is board key - * e.g. lit. - */ +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable 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 */ + @DatabaseField public String key; /** * Name of the url, e.g. lit */ + @DatabaseField public String value; + @DatabaseField public boolean workSafe = false; + @DatabaseField + public boolean saved = false; + + @DatabaseField + public int order; + public boolean finish() { if (key == null || value == null) return false; @@ -27,4 +49,8 @@ public class Board { public String toString() { return key; } + + public boolean valueEquals(Board other) { + return value.equals(other.value); + } } diff --git a/Chan/src/org/floens/chan/core/net/BoardsRequest.java b/Chan/src/org/floens/chan/core/net/BoardsRequest.java index 1d09f8db..ef22319e 100644 --- a/Chan/src/org/floens/chan/core/net/BoardsRequest.java +++ b/Chan/src/org/floens/chan/core/net/BoardsRequest.java @@ -2,6 +2,7 @@ package org.floens.chan.core.net; import java.io.IOException; import java.util.ArrayList; +import java.util.List; 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.Listener; -public class BoardsRequest extends JsonReaderRequest> { - public BoardsRequest(String url, Listener> listener, ErrorListener errorListener) { +public class BoardsRequest extends JsonReaderRequest> { + public BoardsRequest(String url, Listener> listener, ErrorListener errorListener) { super(url, listener, errorListener); } @Override - public ArrayList readJson(JsonReader reader) { + public List readJson(JsonReader reader) { return parseJson(reader); } - private ArrayList parseJson(JsonReader reader) { - ArrayList list = new ArrayList(); + private List parseJson(JsonReader reader) { + List list = new ArrayList(); try { reader.beginObject(); diff --git a/Chan/src/org/floens/chan/database/DatabaseHelper.java b/Chan/src/org/floens/chan/database/DatabaseHelper.java index c968692e..750723b8 100644 --- a/Chan/src/org/floens/chan/database/DatabaseHelper.java +++ b/Chan/src/org/floens/chan/database/DatabaseHelper.java @@ -2,6 +2,7 @@ package org.floens.chan.database; import java.sql.SQLException; +import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Pin; 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 DATABASE_NAME = "ChanDB"; - private static final int DATABASE_VERSION = 9; + private static final int DATABASE_VERSION = 11; public Dao pinDao; public Dao loadableDao; public Dao savedDao; - + public Dao boardsDao; + private final Context context; public DatabaseHelper(Context context) { @@ -36,6 +38,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { pinDao = getDao(Pin.class); loadableDao = getDao(Loadable.class); savedDao = getDao(SavedReply.class); + boardsDao = getDao(Board.class); } catch (SQLException e) { e.printStackTrace(); } @@ -47,6 +50,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.createTable(connectionSource, Pin.class); TableUtils.createTable(connectionSource, Loadable.class); TableUtils.createTable(connectionSource, SavedReply.class); + TableUtils.createTable(connectionSource, Board.class); } catch (SQLException e) { Logger.e(TAG, "Error creating db", e); throw new RuntimeException(e); @@ -76,6 +80,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.dropTable(connectionSource, Pin.class, true); TableUtils.dropTable(connectionSource, Loadable.class, true); TableUtils.dropTable(connectionSource, SavedReply.class, true); + TableUtils.dropTable(connectionSource, Board.class, true); onCreate(database, connectionSource); } catch (SQLException e) { diff --git a/Chan/src/org/floens/chan/database/DatabaseManager.java b/Chan/src/org/floens/chan/database/DatabaseManager.java index bea54329..6f50747c 100644 --- a/Chan/src/org/floens/chan/database/DatabaseManager.java +++ b/Chan/src/org/floens/chan/database/DatabaseManager.java @@ -2,10 +2,13 @@ package org.floens.chan.database; import java.sql.SQLException; 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.SavedReply; import org.floens.chan.utils.Logger; +import org.floens.chan.utils.Time; import android.content.Context; @@ -114,6 +117,54 @@ public class DatabaseManager { return list; } + public void setBoards(final List boards) { + try { + helper.boardsDao.callBatchTasks(new Callable() { + @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 boards) { + try { + helper.boardsDao.callBatchTasks(new Callable() { + @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 getBoards() { + List boards = null; + try { + boards = helper.boardsDao.queryForAll(); + } catch (SQLException e) { + Logger.e(TAG, "Error getting boards from db", e); + } + + return boards; + } + public String getSummary() { String o = ""; diff --git a/Chan/src/org/floens/chan/ui/activity/BaseActivity.java b/Chan/src/org/floens/chan/ui/activity/BaseActivity.java index 42c7aa62..ff77a9b2 100644 --- a/Chan/src/org/floens/chan/ui/activity/BaseActivity.java +++ b/Chan/src/org/floens/chan/ui/activity/BaseActivity.java @@ -11,12 +11,11 @@ import org.floens.chan.ui.BadgeDrawable; import org.floens.chan.ui.SwipeDismissListViewTouchListener; import org.floens.chan.ui.SwipeDismissListViewTouchListener.DismissCallbacks; import org.floens.chan.ui.adapter.PinnedAdapter; +import org.floens.chan.utils.Utils; import android.app.Activity; import android.app.AlertDialog; -import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnShowListener; import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -33,7 +32,6 @@ import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; 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(); - text.requestFocus(); - - dialog.setOnShowListener(new OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(text, 0); - } - }); + Utils.requestKeyboardFocus(dialog, text); dialog.show(); } diff --git a/Chan/src/org/floens/chan/ui/activity/BoardActivity.java b/Chan/src/org/floens/chan/ui/activity/BoardActivity.java index 233647c9..44ac52c3 100644 --- a/Chan/src/org/floens/chan/ui/activity/BoardActivity.java +++ b/Chan/src/org/floens/chan/ui/activity/BoardActivity.java @@ -53,7 +53,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio final ActionBar actionBar = getActionBar(); actionBar.setListNavigationCallbacks( new ArrayAdapter(actionBar.getThemedContext(), R.layout.board_select_spinner, - android.R.id.text1, ChanApplication.getBoardManager().getMyBoardsKeys()), this); + android.R.id.text1, ChanApplication.getBoardManager().getSavedKeys()), this); updatePaneState(); updateActionBarState(); @@ -75,8 +75,10 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } if (boardLoadable.mode == Loadable.Mode.INVALID) { - String board = ChanApplication.getBoardManager().getMyBoardsValues().get(0); - loadBoard(board); + List savedValues = ChanApplication.getBoardManager().getSavedValues(); + if (savedValues.size() > 0) { + loadBoard(savedValues.get(0)); + } } } } @@ -396,8 +398,11 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio if (!actionBarSetToListNavigation) { actionBarSetToListNavigation = true; } else { - boardLoadable = new Loadable(ChanApplication.getBoardManager().getMyBoardsValues().get(position)); - startLoadingBoard(boardLoadable); + List savedValues = ChanApplication.getBoardManager().getSavedValues(); + if (position >= 0 && position < savedValues.size()) { + boardLoadable = new Loadable(savedValues.get(position)); + startLoadingBoard(boardLoadable); + } } return true; @@ -496,7 +501,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } private int getBoardIndexNavigator(String boardValue) { - List list = ChanApplication.getBoardManager().getMyBoardsValues(); + List list = ChanApplication.getBoardManager().getSavedValues(); for (int i = 0; i < list.size(); i++) { if (list.get(i).equals(boardValue)) { return i; diff --git a/Chan/src/org/floens/chan/ui/activity/BoardEditor.java b/Chan/src/org/floens/chan/ui/activity/BoardEditor.java index 29a7b796..c911d79e 100644 --- a/Chan/src/org/floens/chan/ui/activity/BoardEditor.java +++ b/Chan/src/org/floens/chan/ui/activity/BoardEditor.java @@ -1,31 +1,44 @@ package org.floens.chan.ui.activity; import java.util.ArrayList; +import java.util.List; +import java.util.Locale; import org.floens.chan.ChanApplication; import org.floens.chan.R; +import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.Board; import org.floens.chan.ui.adapter.BoardEditAdapter; 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.AlertDialog; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.content.DialogInterface.OnShowListener; import android.os.Bundle; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; 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 { - private ArrayList list = new ArrayList(); + private final BoardManager boardManager = ChanApplication.getBoardManager(); + + private List list; private DynamicListView listView; private BoardEditAdapter adapter; - private AlertDialog dialog; @SuppressWarnings("unchecked") @Override @@ -33,43 +46,84 @@ public class BoardEditor extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.board_edit); + + list = boardManager.getSavedBoards(); - // Copy not a reference - list = (ArrayList) ChanApplication.getBoardManager().getMyBoards().clone(); - - adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); listView = (DynamicListView) findViewById(R.id.board_edit_list); - listView.setArrayList(list); - listView.setAdapter(adapter); + reload(); } - @SuppressWarnings("unchecked") @Override protected void onPause() { super.onPause(); - // For runtime changes if (list.size() > 0) { - ChanApplication.getBoardManager().setMyBoards((ArrayList) 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 - protected void onDestroy() { - super.onDestroy(); + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.board_edit, menu); + return true; + } - if (dialog != null) { - dialog.dismiss(); + @Override + 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) { - ChanApplication.getBoardManager().addBoard(list, value); + private void addBoard(final String 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); - listView.setArrayList(list); - listView.setAdapter(adapter); + return; + } + } + + List 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) { @@ -77,32 +131,41 @@ public class BoardEditor extends Activity { return; for (int i = 0; i < list.size(); i++) { - Board e = list.get(i); - if (e.value == value) { + Board b = list.get(i); + if (b.value == value) { list.remove(i); - - adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); - listView.setArrayList(list); - listView.setAdapter(adapter); + b.saved = false; + break; } } + + reload(); } - public void onDeleteClicked(Board board) { - removeBoard(board.value); + private void reload() { + adapter = new BoardEditAdapter(this, R.layout.board_view, list, this); + listView.setArrayList(list); + listView.setAdapter(adapter); } private void showAddBoardDialog() { - final EditText text = new EditText(this); + final AutoCompleteTextView text = new AutoCompleteTextView(this); 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 public void onClick(DialogInterface d, int which) { String value = text.getText().toString(); if (!TextUtils.isEmpty(value)) { - addBoard(value); + addBoard(value.toLowerCase(Locale.ENGLISH)); } } }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @@ -111,41 +174,140 @@ public class BoardEditor extends Activity { } }).setTitle(R.string.board_add).setView(text).create(); - text.requestFocus(); - - 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); - } - }); + Utils.requestKeyboardFocus(dialog, text); dialog.show(); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.board_edit, menu); - return true; - } + private static class FillAdapter extends ArrayAdapter implements Filterable { + private List currentlyEditing; + private View autoCompleteView; + private final Filter filter; + private final List filtered = new ArrayList(); - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add_board: - showAddBoardDialog(); + public FillAdapter(Context context, int resource) { + super(context, resource); - 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 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) results.values); + } else { + filtered.addAll(getBoards()); + } + + notifyDataSetChanged(); + } + }; } - return super.onOptionsItemSelected(item); + public void setEditingList(List 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 getFiltered(String filter) { + String lowered = filter.toLowerCase(Locale.ENGLISH); + List list = new ArrayList(); + 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 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 s = new ArrayList(); + for (Board b : ChanApplication.getBoardManager().getAllBoards()) { + if (showUnsafe || b.workSafe) + s.add(b); + } + return s; + } } } diff --git a/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java b/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java index c18c648d..aa8d8329 100644 --- a/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java +++ b/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java @@ -9,6 +9,7 @@ import org.floens.chan.core.model.Post; import org.floens.chan.ui.ScrollerRunnable; import org.floens.chan.ui.view.PostView; import org.floens.chan.ui.view.ThreadWatchCounterView; +import org.floens.chan.utils.Time; import org.floens.chan.utils.Utils; import android.content.Context; @@ -62,8 +63,8 @@ public class PostAdapter extends BaseAdapter { } if (position >= postList.size()) { - if (System.currentTimeMillis() - lastViewedTime > 10000L) { - lastViewedTime = System.currentTimeMillis(); + if (Time.get(lastViewedTime) > 10000L) { + lastViewedTime = Time.get(); threadManager.bottomPostViewed(); } diff --git a/Chan/src/org/floens/chan/ui/view/DynamicListView.java b/Chan/src/org/floens/chan/ui/view/DynamicListView.java index 6fec0a17..424f3342 100644 --- a/Chan/src/org/floens/chan/ui/view/DynamicListView.java +++ b/Chan/src/org/floens/chan/ui/view/DynamicListView.java @@ -16,7 +16,7 @@ package org.floens.chan.ui.view; -import java.util.ArrayList; +import java.util.List; import org.floens.chan.ui.adapter.BoardEditAdapter; @@ -70,7 +70,7 @@ public class DynamicListView extends ListView { private final int MOVE_DURATION = 150; private final int LINE_THICKNESS = 10; - public ArrayList mCheeseList; + public List mCheeseList; private int mLastEventY = -1; @@ -372,7 +372,7 @@ public class DynamicListView extends ListView { } } - private void swapElements(ArrayList arrayList, int indexOne, int indexTwo) { + private void swapElements(List arrayList, int indexOne, int indexTwo) { T temp = arrayList.get(indexOne); arrayList.set(indexOne, arrayList.get(indexTwo)); arrayList.set(indexTwo, temp); @@ -501,7 +501,7 @@ public class DynamicListView extends ListView { return false; } - public void setArrayList(ArrayList cheeseList) { + public void setArrayList(List cheeseList) { mCheeseList = cheeseList; } diff --git a/Chan/src/org/floens/chan/ui/view/PostView.java b/Chan/src/org/floens/chan/ui/view/PostView.java index 003290aa..1a7605c5 100644 --- a/Chan/src/org/floens/chan/ui/view/PostView.java +++ b/Chan/src/org/floens/chan/ui/view/PostView.java @@ -7,6 +7,7 @@ import org.floens.chan.core.manager.ThreadManager; import org.floens.chan.core.model.Post; import org.floens.chan.core.model.PostLinkable; import org.floens.chan.utils.IconCache; +import org.floens.chan.utils.Time; import org.floens.chan.utils.Utils; import android.app.Activity; @@ -126,7 +127,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View 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); SpannableString date = new SpannableString("No." + post.no + " " + relativeTime); diff --git a/Chan/src/org/floens/chan/utils/Time.java b/Chan/src/org/floens/chan/utils/Time.java new file mode 100644 index 00000000..c43a3f30 --- /dev/null +++ b/Chan/src/org/floens/chan/utils/Time.java @@ -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; + } +} diff --git a/Chan/src/org/floens/chan/utils/Utils.java b/Chan/src/org/floens/chan/utils/Utils.java index a5f59bbc..f372ead5 100644 --- a/Chan/src/org/floens/chan/utils/Utils.java +++ b/Chan/src/org/floens/chan/utils/Utils.java @@ -2,7 +2,10 @@ package org.floens.chan.utils; import org.floens.chan.ChanApplication; +import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnShowListener; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -10,6 +13,7 @@ import android.os.Looper; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; public class Utils { private static DisplayMetrics displayMetrics; @@ -63,4 +67,15 @@ public class Utils { public static void runOnUiThread(Runnable 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); + } + }); + } }