From 26077be39a35989c2a7a6d4c7a461a79ee92b543 Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Sun, 29 Aug 2021 00:20:45 +0200 Subject: [PATCH] added user exclude database, added search filtering, bug fix Signed-off-by: nuclearfog --- .../twidda/activity/ListDetail.java | 25 +-- .../twidda/activity/SearchPage.java | 12 +- .../twidda/activity/UserExclude.java | 118 ++++++++++++- .../nuclearfog/twidda/backend/UserAction.java | 7 + .../twidda/backend/UserExcludeLoader.java | 81 +++++++++ .../twidda/backend/engine/TwitterEngine.java | 93 ++++++++++- .../twidda/database/DatabaseAdapter.java | 33 +++- .../twidda/database/ExcludeDatabase.java | 155 ++++++++++++++++++ .../twidda/database/GlobalSettings.java | 43 ++++- app/src/main/res/menu/excludelist.xml | 12 ++ app/src/main/res/menu/search.xml | 5 + app/src/main/res/values-de-rDE/strings.xml | 5 + app/src/main/res/values/strings.xml | 6 +- 13 files changed, 556 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/org/nuclearfog/twidda/backend/UserExcludeLoader.java create mode 100644 app/src/main/java/org/nuclearfog/twidda/database/ExcludeDatabase.java create mode 100644 app/src/main/res/menu/excludelist.xml diff --git a/app/src/main/java/org/nuclearfog/twidda/activity/ListDetail.java b/app/src/main/java/org/nuclearfog/twidda/activity/ListDetail.java index 319cf7fb..163f51c6 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activity/ListDetail.java +++ b/app/src/main/java/org/nuclearfog/twidda/activity/ListDetail.java @@ -1,5 +1,17 @@ package org.nuclearfog.twidda.activity; +import static android.os.AsyncTask.Status.RUNNING; +import static org.nuclearfog.twidda.activity.ListEditor.KEY_LIST_EDITOR_DATA; +import static org.nuclearfog.twidda.backend.ListAction.Action.DELETE; +import static org.nuclearfog.twidda.backend.ListAction.Action.FOLLOW; +import static org.nuclearfog.twidda.backend.ListAction.Action.LOAD; +import static org.nuclearfog.twidda.backend.ListAction.Action.UNFOLLOW; +import static org.nuclearfog.twidda.backend.ListManager.Action.ADD_USER; +import static org.nuclearfog.twidda.fragment.UserListFragment.RESULT_REMOVED_LIST_ID; +import static org.nuclearfog.twidda.fragment.UserListFragment.RESULT_UPDATE_LIST; +import static org.nuclearfog.twidda.fragment.UserListFragment.RETURN_LIST_REMOVED; +import static org.nuclearfog.twidda.fragment.UserListFragment.RETURN_LIST_UPDATED; + import android.app.Dialog; import android.content.Intent; import android.graphics.Color; @@ -37,18 +49,6 @@ import org.nuclearfog.twidda.dialog.ConfirmDialog.OnConfirmListener; import java.util.regex.Pattern; -import static android.os.AsyncTask.Status.RUNNING; -import static org.nuclearfog.twidda.activity.ListEditor.KEY_LIST_EDITOR_DATA; -import static org.nuclearfog.twidda.backend.ListAction.Action.DELETE; -import static org.nuclearfog.twidda.backend.ListAction.Action.FOLLOW; -import static org.nuclearfog.twidda.backend.ListAction.Action.LOAD; -import static org.nuclearfog.twidda.backend.ListAction.Action.UNFOLLOW; -import static org.nuclearfog.twidda.backend.ListManager.Action.ADD_USER; -import static org.nuclearfog.twidda.fragment.UserListFragment.RESULT_REMOVED_LIST_ID; -import static org.nuclearfog.twidda.fragment.UserListFragment.RESULT_UPDATE_LIST; -import static org.nuclearfog.twidda.fragment.UserListFragment.RETURN_LIST_REMOVED; -import static org.nuclearfog.twidda.fragment.UserListFragment.RETURN_LIST_UPDATED; - /** * Activity to show an user list, members and tweets * @@ -312,6 +312,7 @@ public class ListDetail extends AppCompatActivity implements OnTabSelectedListen info = '@' + info; info += getString(R.string.info_user_added_to_list); Toast.makeText(this, info, Toast.LENGTH_SHORT).show(); + invalidateOptionsMenu(); } diff --git a/app/src/main/java/org/nuclearfog/twidda/activity/SearchPage.java b/app/src/main/java/org/nuclearfog/twidda/activity/SearchPage.java index 3d7dd505..f8b5e23f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activity/SearchPage.java +++ b/app/src/main/java/org/nuclearfog/twidda/activity/SearchPage.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.activity; +import static org.nuclearfog.twidda.activity.TweetEditor.KEY_TWEETPOPUP_TEXT; + import android.content.Intent; import android.graphics.Color; import android.os.Bundle; @@ -25,8 +27,6 @@ import org.nuclearfog.twidda.adapter.FragmentAdapter; import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.database.GlobalSettings; -import static org.nuclearfog.twidda.activity.TweetEditor.KEY_TWEETPOPUP_TEXT; - /** * Twitter search Activity * @@ -88,7 +88,9 @@ public class SearchPage extends AppCompatActivity implements OnTabSelectedListen getMenuInflater().inflate(R.menu.search, m); AppStyles.setMenuIconColor(m, settings.getIconColor()); MenuItem searchItem = m.findItem(R.id.new_search); + MenuItem searchFilter = m.findItem(R.id.search_filter); SearchView searchView = (SearchView) searchItem.getActionView(); + searchFilter.setChecked(settings.filterResults()); AppStyles.setTheme(settings, searchView); searchView.setQueryHint(search); searchView.setOnQueryTextListener(this); @@ -110,6 +112,12 @@ public class SearchPage extends AppCompatActivity implements OnTabSelectedListen SearchView searchView = (SearchView) item.getActionView(); AppStyles.setTheme(settings, searchView, Color.TRANSPARENT); } + // enable/disable search filter + else if (item.getItemId() == R.id.search_filter) { + boolean enable = !item.isChecked(); + settings.setFilterResults(enable); + item.setChecked(enable); + } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/org/nuclearfog/twidda/activity/UserExclude.java b/app/src/main/java/org/nuclearfog/twidda/activity/UserExclude.java index 1f10e17e..07be6573 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activity/UserExclude.java +++ b/app/src/main/java/org/nuclearfog/twidda/activity/UserExclude.java @@ -1,9 +1,19 @@ package org.nuclearfog.twidda.activity; +import static org.nuclearfog.twidda.backend.UserExcludeLoader.Mode.BLOCK_USER; +import static org.nuclearfog.twidda.backend.UserExcludeLoader.Mode.MUTE_USER; +import static org.nuclearfog.twidda.backend.UserExcludeLoader.Mode.REFRESH; + +import android.os.AsyncTask; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.SearchView.OnQueryTextListener; import androidx.appcompat.widget.Toolbar; import androidx.viewpager.widget.ViewPager; @@ -13,13 +23,24 @@ import com.google.android.material.tabs.TabLayout.Tab; import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.adapter.FragmentAdapter; +import org.nuclearfog.twidda.backend.UserExcludeLoader; +import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.utils.AppStyles; +import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.database.GlobalSettings; /** + * Activity to show block and mute list of the current user * + * @author nuclearfog */ -public class UserExclude extends AppCompatActivity implements OnTabSelectedListener { +public class UserExclude extends AppCompatActivity implements OnTabSelectedListener, OnQueryTextListener { + + private GlobalSettings settings; + private UserExcludeLoader userExclTask; + + private Toolbar toolbar; + private TabLayout tablayout; @Override @@ -27,12 +48,10 @@ public class UserExclude extends AppCompatActivity implements OnTabSelectedListe super.onCreate(savedInst); setContentView(R.layout.page_user_exclude); View root = findViewById(R.id.userexclude_root); - Toolbar toolbar = findViewById(R.id.userexclude_toolbar); - TabLayout tablayout = findViewById(R.id.userexclude_tab); + toolbar = findViewById(R.id.userexclude_toolbar); + tablayout = findViewById(R.id.userexclude_tab); ViewPager pager = findViewById(R.id.userexclude_pager); - GlobalSettings settings = GlobalSettings.getInstance(this); - FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager()); pager.setAdapter(adapter); pager.setOffscreenPageLimit(2); @@ -40,30 +59,115 @@ public class UserExclude extends AppCompatActivity implements OnTabSelectedListe tablayout.setupWithViewPager(pager); tablayout.addOnTabSelectedListener(this); + toolbar.setTitle(""); setSupportActionBar(toolbar); + settings = GlobalSettings.getInstance(this); AppStyles.setTheme(settings, root); AppStyles.setTabIcons(tablayout, settings, R.array.user_exclude_icons); } + @Override + public boolean onCreateOptionsMenu(Menu m) { + getMenuInflater().inflate(R.menu.excludelist, m); + MenuItem search = m.findItem(R.id.menu_exclude_user); + SearchView searchView = (SearchView) search.getActionView(); + searchView.setOnQueryTextListener(this); + AppStyles.setMenuIconColor(m, settings.getIconColor()); + AppStyles.setOverflowIcon(toolbar, settings.getIconColor()); + return super.onCreateOptionsMenu(m); + } + + + @Override + public boolean onPrepareOptionsMenu(Menu m) { + SearchView searchView = (SearchView) m.findItem(R.id.menu_exclude_user).getActionView(); + if (tablayout.getSelectedTabPosition() == 0) { + String hint = getString(R.string.menu_hint_mute_user); + searchView.setQueryHint(hint); + } else if (tablayout.getSelectedTabPosition() == 1) { + String hint = getString(R.string.menu_hint_block_user); + searchView.setQueryHint(hint); + } + return super.onPrepareOptionsMenu(m); + } + + @Override protected void onStart() { super.onStart(); + if (userExclTask == null) { + userExclTask = new UserExcludeLoader(this, ""); + userExclTask.execute(REFRESH); + } + } + + + @Override + protected void onDestroy() { + super.onDestroy(); } @Override public void onTabSelected(Tab tab) { - + invalidateOptionsMenu(); } + @Override public void onTabUnselected(Tab tab) { - } + @Override public void onTabReselected(Tab tab) { + } + + @Override + public boolean onQueryTextSubmit(String query) { + if (userExclTask == null || userExclTask.getStatus() != AsyncTask.Status.RUNNING) { + userExclTask = new UserExcludeLoader(this, query); + if (tablayout.getSelectedTabPosition() == 0) { + userExclTask.execute(MUTE_USER); + return true; + } + if (tablayout.getSelectedTabPosition() == 1) { + userExclTask.execute(BLOCK_USER); + return true; + } + } + return false; + } + + + @Override + public boolean onQueryTextChange(String newText) { + return false; + } + + /** + * called from {@link UserExcludeLoader} if task finished successfully + */ + public void onSuccess(UserExcludeLoader.Mode mode) { + switch (mode) { + case MUTE_USER: + Toast.makeText(this, R.string.info_user_muted, Toast.LENGTH_SHORT).show(); + invalidateOptionsMenu(); + break; + + case BLOCK_USER: + Toast.makeText(this, R.string.info_user_blocked, Toast.LENGTH_SHORT).show(); + invalidateOptionsMenu(); + break; + } + } + + /** + * called from {@link UserExcludeLoader} if an error occurs + */ + public void onError(EngineException err) { + ErrorHandler.handleFailure(this, err); } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/UserAction.java b/app/src/main/java/org/nuclearfog/twidda/backend/UserAction.java index 2903ddb9..3bce7d38 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/UserAction.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/UserAction.java @@ -8,6 +8,7 @@ import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.model.Relation; import org.nuclearfog.twidda.backend.model.User; import org.nuclearfog.twidda.database.AppDatabase; +import org.nuclearfog.twidda.database.ExcludeDatabase; import java.lang.ref.WeakReference; @@ -60,6 +61,7 @@ public class UserAction extends AsyncTask { private EngineException twException; private WeakReference callback; private TwitterEngine mTwitter; + private ExcludeDatabase excludeDb; private AppDatabase db; private long userId; @@ -71,6 +73,7 @@ public class UserAction extends AsyncTask { super(); this.callback = new WeakReference<>(callback); mTwitter = TwitterEngine.getInstance(callback); + excludeDb = ExcludeDatabase.getInstance(callback); db = new AppDatabase(callback); this.userId = userId; } @@ -117,24 +120,28 @@ public class UserAction extends AsyncTask { user = mTwitter.blockUser(userId); publishProgress(user); db.muteUser(userId, true); + excludeDb.addUser(userId); break; case ACTION_UNBLOCK: user = mTwitter.unblockUser(userId); publishProgress(user); db.muteUser(userId, false); + excludeDb.removeUser(userId); break; case ACTION_MUTE: user = mTwitter.muteUser(userId); publishProgress(user); db.muteUser(userId, true); + excludeDb.addUser(userId); break; case ACTION_UNMUTE: user = mTwitter.unmuteUser(userId); publishProgress(user); db.muteUser(userId, false); + excludeDb.removeUser(userId); break; } return mTwitter.getConnection(userId); diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/UserExcludeLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/UserExcludeLoader.java new file mode 100644 index 00000000..172d0b53 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/backend/UserExcludeLoader.java @@ -0,0 +1,81 @@ +package org.nuclearfog.twidda.backend; + +import android.os.AsyncTask; + +import androidx.annotation.Nullable; + +import org.nuclearfog.twidda.activity.UserExclude; +import org.nuclearfog.twidda.backend.engine.EngineException; +import org.nuclearfog.twidda.backend.engine.TwitterEngine; +import org.nuclearfog.twidda.database.ExcludeDatabase; + +import java.lang.ref.WeakReference; +import java.util.List; + +/** + * Backend of {@link UserExclude} + * performs user mute or block actions and exports block list to database + * + * @author nuclearfog + */ +public class UserExcludeLoader extends AsyncTask { + + public enum Mode { + REFRESH, + MUTE_USER, + BLOCK_USER + } + + @Nullable + private EngineException err; + private WeakReference callback; + private ExcludeDatabase excludeDatabase; + private TwitterEngine mTwitter; + private String name; + + + public UserExcludeLoader(UserExclude callback, String name) { + super(); + mTwitter = TwitterEngine.getInstance(callback); + excludeDatabase = ExcludeDatabase.getInstance(callback); + this.callback = new WeakReference<>(callback); + this.name = name; + } + + + @Override + protected Mode doInBackground(Mode... mode) { + try { + switch (mode[0]) { + case REFRESH: + List ids = mTwitter.getExcludedUserIDs(); + excludeDatabase.setExcludeList(ids); + break; + + case MUTE_USER: + mTwitter.muteUser(name); + break; + + case BLOCK_USER: + mTwitter.blockUser(name); + break; + } + } catch (EngineException err) { + this.err = err; + } + return mode[0]; + } + + + @Override + protected void onPostExecute(Mode mode) { + UserExclude activity = callback.get(); + if (activity != null) { + if (err == null) { + activity.onSuccess(mode); + } else { + activity.onError(err); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java b/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java index 9de1d61c..875c2fe0 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java @@ -20,6 +20,7 @@ import org.nuclearfog.twidda.backend.model.Tweet; import org.nuclearfog.twidda.backend.model.TwitterList; import org.nuclearfog.twidda.backend.model.User; import org.nuclearfog.twidda.database.AccountDatabase; +import org.nuclearfog.twidda.database.ExcludeDatabase; import org.nuclearfog.twidda.database.GlobalSettings; import java.io.File; @@ -32,6 +33,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import io.michaelrocks.paranoid.Obfuscate; import twitter4j.DirectMessage; @@ -67,6 +70,7 @@ public class TwitterEngine { private RequestToken reqToken; private GlobalSettings settings; private AccountDatabase accountDB; + private ExcludeDatabase excludeDB; private Twitter twitter; private boolean isInitialized = false; @@ -121,6 +125,7 @@ public class TwitterEngine { // initialize database and settings mTwitter.settings = GlobalSettings.getInstance(context); mTwitter.accountDB = AccountDatabase.getInstance(context); + mTwitter.excludeDB = ExcludeDatabase.getInstance(context); // check if already logged in if (mTwitter.settings.isLoggedIn()) { // init login access @@ -259,6 +264,8 @@ public class TwitterEngine { q.setMaxId(maxId - 1); QueryResult result = twitter.search(q); List results = result.getTweets(); + if (settings.filterResults()) + return convertStatusListFiltered(results); return convertStatusList(results); } catch (Exception err) { throw new EngineException(err); @@ -312,14 +319,20 @@ public class TwitterEngine { */ public UserList searchUsers(String search, long cursor) throws EngineException { try { + List users; int currentPage = 1; if (cursor > 0) currentPage = (int) cursor; long prevPage = currentPage - 1; long nextPage = currentPage + 1; - List users = convertUserList(twitter.searchUsers(search, currentPage)); - if (users.size() < 20) + if (settings.filterResults()) { + users = convertUserListFiltered(twitter.searchUsers(search, currentPage)); + } else { + users = convertUserList(twitter.searchUsers(search, currentPage)); + } + if (users.size() < 20) { nextPage = 0; + } UserList result = new UserList(prevPage, nextPage); result.addAll(users); return result; @@ -483,6 +496,8 @@ public class TwitterEngine { */ public void blockUser(String name) throws EngineException { try { + if (!name.startsWith("@")) + name = '@' + name; twitter.createBlock(name); } catch (Exception err) { throw new EngineException(err); @@ -527,6 +542,8 @@ public class TwitterEngine { */ public void muteUser(String name) throws EngineException { try { + if (!name.startsWith("@")) + name = '@' + name; twitter.createMute(name); } catch (Exception err) { throw new EngineException(err); @@ -627,6 +644,29 @@ public class TwitterEngine { } } + /** + * get a list of blocked/muted user IDs + * + * @return list of user IDs + * @throws EngineException if twitter service is unavailable + */ + public List getExcludedUserIDs() throws EngineException { + try { + IDs[] ids = new IDs[2]; + ids[0] = twitter.getBlocksIDs(); + ids[1] = twitter.getBlocksIDs(); + Set idSet = new TreeSet<>(); + for (IDs id : ids) { + for (long userId : id.getIDs()) { + idSet.add(userId); + } + } + return new ArrayList<>(idSet); + } catch (TwitterException err) { + throw new EngineException(err); + } + } + /** * send tweet * @@ -695,6 +735,8 @@ public class TwitterEngine { answers.add(reply); } } + if (settings.filterResults()) + return convertStatusListFiltered(answers); return convertStatusList(answers); } catch (Exception err) { throw new EngineException(err); @@ -1213,17 +1255,35 @@ public class TwitterEngine { } /** - * convert #twitter4j.User to User List + * convert {@link twitter4j.User} to User List * * @param users Twitter4J user List * @return User */ private List convertUserList(List users) throws TwitterException { + long id = twitter.getId(); ArrayList result = new ArrayList<>(); result.ensureCapacity(users.size()); for (twitter4j.User user : users) { - User item = new User(user, twitter.getId()); - result.add(item); + result.add(new User(user, id)); + } + return result; + } + + /** + * convert {@link twitter4j.User} to User List and filter excluded users + * + * @param users Twitter4J user List + * @return User + */ + private List convertUserListFiltered(List users) throws TwitterException { + long id = twitter.getId(); + Set exclude = excludeDB.getExcludeSet(); + List result = new LinkedList<>(); + for (twitter4j.User user : users) { + if (!exclude.contains(user.getId())) { + result.add(new User(user, id)); + } } return result; } @@ -1246,7 +1306,7 @@ public class TwitterEngine { } /** - * convert #twitter4j.Status to Tweet List + * convert {@link twitter4j.Status} to Tweet List * * @param statuses Twitter4J status List * @return TwitterStatus @@ -1254,8 +1314,27 @@ public class TwitterEngine { private List convertStatusList(List statuses) throws TwitterException { ArrayList result = new ArrayList<>(); result.ensureCapacity(statuses.size()); + long id = twitter.getId(); for (Status status : statuses) - result.add(new Tweet(status, twitter.getId())); + result.add(new Tweet(status, id)); + return result; + } + + /** + * convert {@link twitter4j.Status} to Tweet List and filter tweet from excluded users + * + * @param statuses Twitter4J status List + * @return TwitterStatus + */ + private List convertStatusListFiltered(List statuses) throws TwitterException { + List result = new LinkedList<>(); + long id = twitter.getId(); + Set exclude = excludeDB.getExcludeSet(); + for (Status status : statuses) { + if (!exclude.contains(status.getUser().getId())) { + result.add(new Tweet(status, id)); + } + } return result; } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java index 6bf5b6ac..f6a97e3f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.database; +import static android.content.Context.MODE_PRIVATE; + import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -7,8 +9,6 @@ import androidx.annotation.NonNull; import java.io.File; -import static android.content.Context.MODE_PRIVATE; - /** * This class creates and manages SQLite table versions * @@ -133,6 +133,14 @@ public class DatabaseAdapter { + AccountTable.KEY1 + " TEXT," + AccountTable.KEY2 + " TEXT);"; + /** + * SQL query to create user exclude table + */ + private static final String TABLE_USER_EXCLUDE = "CREATE TABLE IF NOT EXISTS " + + UserExcludeTable.NAME + "(" + + UserExcludeTable.OWNER + " INTEGER," + + UserExcludeTable.ID + " INTEGER);"; + /** * table index for tweet table */ @@ -266,6 +274,7 @@ public class DatabaseAdapter { db.execSQL(TABLE_TRENDS); db.execSQL(TABLE_MESSAGES); db.execSQL(TABLE_LOGINS); + db.execSQL(TABLE_USER_EXCLUDE); db.execSQL(TABLE_TWEET_REGISTER); db.execSQL(TABLE_USER_REGISTER); // create index @@ -610,4 +619,24 @@ public class DatabaseAdapter { */ String REGISTER = "userRegister"; } + + /** + * + */ + public interface UserExcludeTable { + /** + * + */ + String NAME = "userExclude"; + + /** + * + */ + String OWNER = "listOwner"; + + /** + * + */ + String ID = "userID"; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/database/ExcludeDatabase.java b/app/src/main/java/org/nuclearfog/twidda/database/ExcludeDatabase.java new file mode 100644 index 00000000..680f1def --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/database/ExcludeDatabase.java @@ -0,0 +1,155 @@ +package org.nuclearfog.twidda.database; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import org.nuclearfog.twidda.database.DatabaseAdapter.UserExcludeTable; + +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** + * Database class of the user exclude list + * + * @author nuclearfog + */ +public class ExcludeDatabase { + + /** + * selection to get the exclude list of the current user + */ + private static final String LIST_SELECT = UserExcludeTable.OWNER + "=?"; + + /** + * selection to get a column + */ + private static final String COLUMN_SELECT = LIST_SELECT + " AND " + UserExcludeTable.ID + "=?"; + + /** + * column to fetch from the database + */ + private static final String[] LIST_ID_COL = {UserExcludeTable.ID}; + + + private static final ExcludeDatabase INSTANCE = new ExcludeDatabase(); + private DatabaseAdapter dataHelper; + private GlobalSettings settings; + + + private ExcludeDatabase() { + } + + /** + * get singleton instance + * + * @param context activity context + * @return instance of this class + */ + public static ExcludeDatabase getInstance(Context context) { + if (INSTANCE.dataHelper == null) { + INSTANCE.dataHelper = DatabaseAdapter.getInstance(context.getApplicationContext()); + INSTANCE.settings = GlobalSettings.getInstance(context); + } + return INSTANCE; + } + + /** + * replace exclude list with a new version + * + * @param ids list of user IDs + */ + public void setExcludeList(List ids) { + long homeId = settings.getCurrentUserId(); + String[] args = {Long.toString(homeId)}; + SQLiteDatabase db = getDbWrite(); + + db.delete(UserExcludeTable.NAME, LIST_SELECT, args); + for (long id : ids) { + ContentValues column = new ContentValues(2); + column.put(UserExcludeTable.ID, id); + column.put(UserExcludeTable.OWNER, homeId); + db.insert(UserExcludeTable.NAME, null, column); + } + commit(db); + } + + /** + * return current users exclude set containing user IDs + * + * @return a set of user IDs + */ + public Set getExcludeSet() { + String[] args = {Long.toString(settings.getCurrentUserId())}; + SQLiteDatabase db = getDbRead(); + Cursor cursor = db.query(UserExcludeTable.NAME, LIST_ID_COL, LIST_SELECT, args, null, null, null, null); + + Set result = new TreeSet<>(); + if (cursor.moveToFirst()) { + do { + long id = cursor.getLong(0); + result.add(id); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; + } + + /** + * add user to the exclude database + * + * @param userId ID of the user + */ + public void addUser(long userId) { + SQLiteDatabase db = getDbWrite(); + ContentValues column = new ContentValues(2); + column.put(UserExcludeTable.ID, userId); + column.put(UserExcludeTable.OWNER, settings.getCurrentUserId()); + db.insert(UserExcludeTable.NAME, null, column); + commit(db); + } + + /** + * remove user from the exclude database + * + * @param userId ID of the user + */ + public void removeUser(long userId) { + String[] args = {Long.toString(settings.getCurrentUserId()), Long.toString(userId)}; + SQLiteDatabase db = getDbWrite(); + db.delete(UserExcludeTable.NAME, COLUMN_SELECT, args); + commit(db); + } + + /** + * Get SQLite instance for reading database + * + * @return SQLite instance + */ + private synchronized SQLiteDatabase getDbRead() { + return dataHelper.getDatabase(); + } + + /** + * GET SQLite instance for writing database + * + * @return SQLite instance + */ + private synchronized SQLiteDatabase getDbWrite() { + SQLiteDatabase db = dataHelper.getDatabase(); + db.beginTransaction(); + return db; + } + + /** + * Commit changes and close Database + * + * @param db database instance + */ + private synchronized void commit(SQLiteDatabase db) { + db.setTransactionSuccessful(); + db.endTransaction(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java b/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java index dbe75e4f..5f54689c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java @@ -1,5 +1,12 @@ package org.nuclearfog.twidda.database; +import static android.content.Context.MODE_PRIVATE; +import static android.graphics.Typeface.DEFAULT; +import static android.graphics.Typeface.MONOSPACE; +import static android.graphics.Typeface.NORMAL; +import static android.graphics.Typeface.SANS_SERIF; +import static android.graphics.Typeface.SERIF; + import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -11,13 +18,6 @@ import androidx.annotation.NonNull; import org.nuclearfog.twidda.backend.model.TrendLocation; -import static android.content.Context.MODE_PRIVATE; -import static android.graphics.Typeface.DEFAULT; -import static android.graphics.Typeface.MONOSPACE; -import static android.graphics.Typeface.NORMAL; -import static android.graphics.Typeface.SANS_SERIF; -import static android.graphics.Typeface.SERIF; - /** * This class manages app settings * @@ -91,6 +91,7 @@ public class GlobalSettings { private static final String TREND_LOC = "location"; private static final String TREND_ID = "world_id"; private static final String LINK_PREVIEW = "link_preview"; + private static final String FILTER_RESULTS = "filter_results"; private static final String CUSTOM_CONSUMER_KEY_SET = "custom_api_keys"; private static final String CUSTOM_CONSUMER_KEY_1 = "api_key1"; private static final String CUSTOM_CONSUMER_KEY_2 = "api_key2"; @@ -135,6 +136,7 @@ public class GlobalSettings { private boolean customAPIKey; private boolean toolbarOverlap; private boolean linkPreview; + private boolean filterResults; private int background_color; private int font_color; private int highlight_color; @@ -449,14 +451,16 @@ public class GlobalSettings { } /** - * + * check if linke preview is enabled */ public boolean linkPreviewEnabled() { return linkPreview; } /** + * enable/disable link preview * + * @param enable true enables link preview */ public void setLinkPreview(boolean enable) { linkPreview = enable; @@ -466,6 +470,28 @@ public class GlobalSettings { edit.apply(); } + /** + * check if search filter is enabled + * + * @return true if search filter is enabled + */ + public boolean filterResults() { + return filterResults; + } + + /** + * enable/disable search filter + * + * @param enable true to enable search filter + */ + public void setFilterResults(boolean enable) { + filterResults = enable; + + Editor edit = settings.edit(); + edit.putBoolean(FILTER_RESULTS, enable); + edit.apply(); + } + /** * sets image quality * @@ -893,6 +919,7 @@ public class GlobalSettings { hqImages = settings.getBoolean(IMAGE_QUALITY, false); toolbarOverlap = settings.getBoolean(PROFILE_OVERLAP, true); linkPreview = settings.getBoolean(LINK_PREVIEW, false); + filterResults = settings.getBoolean(FILTER_RESULTS, true); customAPIKey = settings.getBoolean(CUSTOM_CONSUMER_KEY_SET, false); proxyHost = settings.getString(PROXY_ADDR, ""); proxyPort = settings.getString(PROXY_PORT, ""); diff --git a/app/src/main/res/menu/excludelist.xml b/app/src/main/res/menu/excludelist.xml new file mode 100644 index 00000000..0332b28f --- /dev/null +++ b/app/src/main/res/menu/excludelist.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/search.xml b/app/src/main/res/menu/search.xml index c93a5f37..c6cfb332 100644 --- a/app/src/main/res/menu/search.xml +++ b/app/src/main/res/menu/search.xml @@ -15,4 +15,9 @@ android:title="@string/menu_tweet" app:showAsAction="always" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index d4d3fd00..dc5c960d 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -207,4 +207,9 @@ Follow Icon Kompakte Profilansicht \'unbenannt\' + Blocklisten + \@Name stummschalten + \@Name blockieren + Nutzer ausschließen + Ergebnisse filtern \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index beb16b51..380cf055 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -218,7 +218,11 @@ unfollow list unfollow unblock - Block/Mute list + Block lists + exclude user + filter results + enter @name to mute + enter @name to block Yes