Add thread hiding

filtering
Floens 10 years ago
parent 6601d265ab
commit 7ecc9f183b
  1. 58
      Clover/app/src/main/java/org/floens/chan/core/model/ThreadHide.java
  2. 13
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  3. 14
      Clover/app/src/main/java/org/floens/chan/database/DatabaseHelper.java
  4. 107
      Clover/app/src/main/java/org/floens/chan/database/DatabaseManager.java
  5. 1
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  6. 14
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostFilter.java
  7. 4
      Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java
  8. 8
      Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java
  9. 6
      Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java
  10. 31
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  11. 7
      Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java
  12. 4
      Clover/app/src/main/res/values/strings.xml
  13. 3
      docs/database.txt

@ -0,0 +1,58 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.model;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable
public class ThreadHide {
@DatabaseField(generatedId = true)
public int id;
@DatabaseField
public String board;
@DatabaseField
public int no;
public ThreadHide() {
}
public ThreadHide(String board, int no) {
this.board = board;
this.no = no;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreadHide that = (ThreadHide) o;
return no == that.no && board.equals(that.board);
}
@Override
public int hashCode() {
int result = board.hashCode();
result = 31 * result + no;
return result;
}
}

@ -66,6 +66,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
private static final int POST_OPTION_PIN = 9;
private static final int POST_OPTION_SHARE = 10;
private static final int POST_OPTION_HIGHLIGHT_TRIPCODE = 11;
private static final int POST_OPTION_HIDE = 12;
private WatchManager watchManager;
private DatabaseManager databaseManager;
@ -186,6 +187,10 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
}
public void refreshUI() {
showPosts();
}
@Override
public Loadable getLoadable() {
return loadable;
@ -331,6 +336,10 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
menu.add(new FloatingMenuItem(POST_OPTION_COPY_TEXT, R.string.post_copy_text));
menu.add(new FloatingMenuItem(POST_OPTION_REPORT, R.string.post_report));
if (!loadable.isThreadMode()) {
menu.add(new FloatingMenuItem(POST_OPTION_HIDE, R.string.post_hide));
}
if (!TextUtils.isEmpty(post.id)) {
menu.add(new FloatingMenuItem(POST_OPTION_HIGHLIGHT_ID, R.string.post_highlight_id));
}
@ -392,6 +401,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
ChanUrls.getThreadUrlDesktop(post.board, loadable.no, post.no)
);
break;
case POST_OPTION_HIDE:
threadPresenterCallback.hideThread(post);
}
}
@ -589,5 +600,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
void showDeleting();
void hideDeleting(String message);
void hideThread(Post post);
}
}

@ -29,6 +29,7 @@ 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;
import org.floens.chan.core.model.ThreadHide;
import org.floens.chan.utils.Logger;
import java.sql.SQLException;
@ -40,12 +41,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 = 15;
private static final int DATABASE_VERSION = 16;
public Dao<Pin, Integer> pinDao;
public Dao<Loadable, Integer> loadableDao;
public Dao<SavedReply, Integer> savedDao;
public Dao<Board, Integer> boardsDao;
public Dao<ThreadHide, Integer> threadHideDao;
private final Context context;
@ -59,6 +61,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
loadableDao = getDao(Loadable.class);
savedDao = getDao(SavedReply.class);
boardsDao = getDao(Board.class);
threadHideDao = getDao(ThreadHide.class);
} catch (SQLException e) {
Logger.e(TAG, "Error creating Daos", e);
}
@ -71,6 +74,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TableUtils.createTable(connectionSource, Loadable.class);
TableUtils.createTable(connectionSource, SavedReply.class);
TableUtils.createTable(connectionSource, Board.class);
TableUtils.createTable(connectionSource, ThreadHide.class);
} catch (SQLException e) {
Logger.e(TAG, "Error creating db", e);
throw new RuntimeException(e);
@ -143,6 +147,14 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
Logger.e(TAG, "Error upgrading to version 15", e);
}
}
if (oldVersion < 16) {
try {
TableUtils.createTable(connectionSource, ThreadHide.class);
} catch (SQLException e) {
Logger.e(TAG, "Error upgrading to version 16", e);
}
}
}
public void reset() {

@ -20,10 +20,13 @@ package org.floens.chan.database;
import android.content.Context;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.table.TableUtils;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Pin;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.SavedReply;
import org.floens.chan.core.model.ThreadHide;
import org.floens.chan.utils.Logger;
import java.sql.SQLException;
@ -37,12 +40,17 @@ public class DatabaseManager {
private static final long SAVED_REPLY_TRIM_TRIGGER = 250;
private static final long SAVED_REPLY_TRIM_COUNT = 50;
private static final long THREAD_HIDE_TRIM_TRIGGER = 250;
private static final long THREAD_HIDE_TRIM_COUNT = 50;
private final DatabaseHelper helper;
private List<SavedReply> savedReplies = new ArrayList<>();
private HashSet<Integer> savedRepliesIds = new HashSet<>();
private List<ThreadHide> threadHides = new ArrayList<>();
private HashSet<Integer> threadHidesIds = new HashSet<>();
public DatabaseManager(Context context) {
helper = new DatabaseHelper(context);
initialize();
@ -50,6 +58,7 @@ public class DatabaseManager {
/**
* Save a reply to the savedreply table.
*
* @param saved the {@link SavedReply} to save
*/
public void saveReply(SavedReply saved) {
@ -65,8 +74,9 @@ public class DatabaseManager {
/**
* Searches a saved reply. This is done through caching members, no database lookups.
*
* @param board board for the reply to search
* @param no no for the reply to search
* @param no no for the reply to search
* @return A {@link SavedReply} that matches {@code board} and {@code no}, or {@code null}
*/
public SavedReply getSavedReply(String board, int no) {
@ -83,8 +93,9 @@ public class DatabaseManager {
/**
* Searches if a saved reply exists. This is done through caching members, no database lookups.
*
* @param board board for the reply to search
* @param no no for the reply to search
* @param no no for the reply to search
* @return true if a {@link SavedReply} matched {@code board} and {@code no}, {@code false} otherwise
*/
public boolean isSavedReply(String board, int no) {
@ -93,6 +104,7 @@ public class DatabaseManager {
/**
* Adds a {@link Pin} to the pin table.
*
* @param pin Pin to save
*/
public void addPin(Pin pin) {
@ -106,6 +118,7 @@ public class DatabaseManager {
/**
* Deletes a {@link Pin} from the pin table.
*
* @param pin Pin to delete
*/
public void removePin(Pin pin) {
@ -119,6 +132,7 @@ public class DatabaseManager {
/**
* Updates a {@link Pin} in the pin table.
*
* @param pin Pin to update
*/
public void updatePin(Pin pin) {
@ -132,6 +146,7 @@ public class DatabaseManager {
/**
* Updates all {@link Pin}s in the list to the pin table.
*
* @param pins Pins to update
*/
public void updatePins(final List<Pin> pins) {
@ -157,6 +172,7 @@ public class DatabaseManager {
/**
* Get a list of {@link Pin}s from the pin table.
*
* @return List of Pins
*/
public List<Pin> getPinned() {
@ -175,6 +191,7 @@ public class DatabaseManager {
/**
* Create or updates these boards in the boards table.
*
* @param boards List of boards to create or update
*/
public void setBoards(final List<Board> boards) {
@ -196,6 +213,7 @@ public class DatabaseManager {
/**
* Get all boards from the boards table.
*
* @return all boards from the boards table
*/
public List<Board> getBoards() {
@ -209,8 +227,70 @@ public class DatabaseManager {
return boards;
}
/**
* Check if the post is added in the threadhide table.
*
* @param post Post to check the board and no on
* @return true if it was hidden, false otherwise
*/
public boolean isThreadHidden(Post post) {
if (threadHidesIds.contains(post.no)) {
for (ThreadHide hide : threadHides) {
if (hide.no == post.no && hide.board.equals(post.board)) {
return true;
}
}
}
return false;
}
/**
* Adds an entry to the threadhide table and updates any caching members.
*
* @param threadHide The {@link ThreadHide} to add.
*/
public void addThreadHide(ThreadHide threadHide) {
try {
helper.threadHideDao.create(threadHide);
threadHides.add(threadHide);
threadHidesIds.add(threadHide.no);
} catch (SQLException e) {
Logger.e(TAG, "Error adding threadhide", e);
}
}
/**
* Removes the entry from the threadhide table and updates any caching members.
*
* @param threadHide The {@link ThreadHide} to remove.
*/
public void removeThreadHide(ThreadHide threadHide) {
try {
helper.threadHideDao.delete(threadHide);
threadHides.remove(threadHide);
// ThreadHidesIds not removed because there may be another post with the same id on another board
// It's just an caching thing, it'll reset itself after a restart
} catch (SQLException e) {
Logger.e(TAG, "Error deleting threadhide", e);
}
}
/**
* Clears all {@link ThreadHide}s from the table and resets any caching members.
*/
public void clearAllThreadHides() {
try {
TableUtils.clearTable(helper.getConnectionSource(), ThreadHide.class);
threadHides.clear();
threadHidesIds.clear();
} catch (SQLException e) {
Logger.e(TAG, "Error clearing threadhide table", e);
}
}
/**
* Summary of the database tables row count, for the developer screen.
*
* @return list of all tables and their row count.
*/
public String getSummary() {
@ -238,6 +318,7 @@ public class DatabaseManager {
private void initialize() {
loadSavedReplies();
loadThreadHides();
}
private void loadSavedReplies() {
@ -255,12 +336,28 @@ public class DatabaseManager {
}
}
private void loadThreadHides() {
try {
trimTable(helper.threadHideDao, "threadhide", THREAD_HIDE_TRIM_TRIGGER, THREAD_HIDE_TRIM_COUNT);
threadHides.clear();
threadHides.addAll(helper.threadHideDao.queryForAll());
threadHidesIds.clear();
for (ThreadHide hide : threadHides) {
threadHidesIds.add(hide.no);
}
} catch (SQLException e) {
Logger.e(TAG, "Error loading thread hides", e);
}
}
/**
* Trim a table with the specified trigger and trim count.
* @param dao {@link Dao} to use.
* @param table name of the table, used in the query (not escaped).
*
* @param dao {@link Dao} to use.
* @param table name of the table, used in the query (not escaped).
* @param trigger Trim if there are more rows than {@code trigger}.
* @param trim Count of rows to trim.
* @param trim Count of rows to trim.
*/
private void trimTable(Dao dao, String table, long trigger, long trim) {
try {

@ -33,7 +33,6 @@ import java.util.ArrayList;
import java.util.List;
public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_POST = 0;
private static final int TYPE_STATUS = 1;

@ -19,7 +19,9 @@ package org.floens.chan.ui.adapter;
import android.text.TextUtils;
import org.floens.chan.Chan;
import org.floens.chan.core.model.Post;
import org.floens.chan.database.DatabaseManager;
import java.util.ArrayList;
import java.util.Collections;
@ -57,12 +59,15 @@ public class PostFilter {
}
};
private final DatabaseManager databaseManager;
private Order order;
private String query;
public PostFilter(Order order, String query) {
this.order = order;
this.query = query;
databaseManager = Chan.getDatabaseManager();
}
/**
@ -116,6 +121,15 @@ public class PostFilter {
}
}
// Process hidden
Iterator<Post> i = posts.iterator();
while (i.hasNext()) {
Post post = i.next();
if (databaseManager.isThreadHidden(post)) {
i.remove();
}
}
return posts;
}

@ -52,6 +52,7 @@ import java.util.List;
import java.util.Locale;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.fixSnackbarText;
public class BoardEditController extends Controller implements SwipeListener.Callback, View.OnClickListener {
@ -194,8 +195,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal
recyclerView.smoothScrollToPosition(boards.size());
Snackbar snackbar = Snackbar.make(view, string(R.string.board_add_success) + " " + board.key, Snackbar.LENGTH_LONG);
TextView snackbarText = (TextView) snackbar.getView().findViewById(R.id.snackbar_text);
snackbarText.setTextColor(0xffffffff);
fixSnackbarText(context, snackbar);
snackbar.show();
return;

@ -23,6 +23,7 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.settings.BooleanSettingView;
@ -163,6 +164,13 @@ public class MainSettingsController extends SettingsController implements Toolba
imageAutoLoadView = browsing.add(new BooleanSettingView(this, ChanSettings.imageAutoLoad, s(R.string.setting_image_auto_load), null));
videoAutoLoadView = browsing.add(new BooleanSettingView(this, ChanSettings.videoAutoLoad, s(R.string.setting_video_auto_load), null));
browsing.add(new BooleanSettingView(this, ChanSettings.videoOpenExternal, s(R.string.setting_video_open_external), s(R.string.setting_video_open_external_description)));
browsing.add(new LinkSettingView(this, string(R.string.setting_clear_thread_hides), null, new View.OnClickListener() {
@Override
public void onClick(View v) {
Chan.getDatabaseManager().clearAllThreadHides();
Toast.makeText(context, R.string.setting_cleared_thread_hides, Toast.LENGTH_LONG).show();
}
}));
groups.add(browsing);

@ -53,7 +53,7 @@ import de.greenrobot.event.EventBus;
import static org.floens.chan.ui.theme.ThemeHelper.theme;
import static org.floens.chan.utils.AndroidUtils.ROBOTO_MEDIUM;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.getAttrColor;
import static org.floens.chan.utils.AndroidUtils.fixSnackbarText;
public class RootNavigationController extends NavigationController implements PinAdapter.Callback, View.OnClickListener {
private WatchManager watchManager;
@ -208,15 +208,13 @@ public class RootNavigationController extends NavigationController implements Pi
watchManager.removePin(pin);
Snackbar snackbar = Snackbar.make(drawerLayout, context.getString(R.string.drawer_pin_removed, pin.loadable.title), Snackbar.LENGTH_LONG);
TextView snackbarText = (TextView) snackbar.getView().findViewById(R.id.snackbar_text);
snackbarText.setTextColor(0xffffffff);
fixSnackbarText(context, snackbar);
snackbar.setAction(R.string.undo, new View.OnClickListener() {
@Override
public void onClick(View v) {
watchManager.addPin(pin);
}
});
snackbar.setActionTextColor(getAttrColor(context, R.attr.colorAccent));
snackbar.show();
}

@ -27,6 +27,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@ -42,6 +43,7 @@ import com.android.volley.NoConnectionError;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.ChanThread;
@ -49,8 +51,10 @@ import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.core.model.ThreadHide;
import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.database.DatabaseManager;
import org.floens.chan.ui.adapter.PostFilter;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.helper.PostPopupHelper;
@ -62,6 +66,7 @@ import java.util.List;
import javax.net.ssl.SSLException;
import static org.floens.chan.utils.AndroidUtils.fixSnackbarText;
import static org.floens.chan.utils.AndroidUtils.getString;
/**
@ -74,6 +79,8 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
ERROR;
}
private DatabaseManager databaseManager;
private ThreadLayoutCallback callback;
private ThreadPresenter presenter;
@ -93,14 +100,17 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
public ThreadLayout(Context context) {
super(context);
init();
}
public ThreadLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ThreadLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
@ -352,6 +362,23 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
}
}
@Override
public void hideThread(Post post) {
final ThreadHide threadHide = new ThreadHide(post.board, post.no);
databaseManager.addThreadHide(threadHide);
presenter.refreshUI();
Snackbar snackbar = Snackbar.make(this, R.string.post_hidden, Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.undo, new OnClickListener() {
@Override
public void onClick(View v) {
databaseManager.removeThreadHide(threadHide);
presenter.refreshUI();
}
}).show();
fixSnackbarText(getContext(), snackbar);
}
public ThumbnailView getThumbnail(PostImage postImage) {
if (postPopupHelper.isOpen()) {
return postPopupHelper.getThumbnail(postImage);
@ -412,6 +439,10 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
}
}
private void init() {
databaseManager = Chan.getDatabaseManager();
}
@Override
public void presentRepliesController(Controller controller) {
callback.presentRepliesController(controller);

@ -34,12 +34,14 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toast;
import org.floens.chan.Chan;
@ -301,4 +303,9 @@ public class AndroidUtils {
private static void setElevationLollipop(View view, float elevation) {
view.setElevation(elevation);
}
public static void fixSnackbarText(Context context, Snackbar snackbar) {
((TextView) snackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(0xffffffff);
snackbar.setActionTextColor(getAttrColor(context, R.attr.colorAccent));
}
}

@ -130,6 +130,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="post_share">Share</string>
<string name="post_copy_text">Copy text</string>
<string name="post_report">Report</string>
<string name="post_hide">Hide</string>
<string name="post_hidden">Thread hidden</string>
<string name="post_delete">Delete</string>
<string name="reply_name">Name</string>
@ -201,6 +203,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="setting_video_auto_load">Auto load videos</string>
<string name="setting_video_open_external">Open videos external</string>
<string name="setting_video_open_external_description">Open videos in an external media player</string>
<string name="setting_clear_thread_hides">Clear all thread hides</string>
<string name="setting_cleared_thread_hides">Cleared all thread hides</string>
<string name="settings_group_posting">Posting</string>
<string name="setting_post_default_name">Default post name</string>

@ -40,3 +40,6 @@ ALTER TABLE pin ADD COLUMN order INTEGER;
Changes is version 15:
ALTER TABLE pin ADD COLUMN archived INTEGER;
Changes in version 16:
Table ThreadHide added

Loading…
Cancel
Save