added domain block support
This commit is contained in:
parent
1b574992ed
commit
899a82989e
|
@ -2,6 +2,7 @@ package org.nuclearfog.twidda.backend.api;
|
|||
|
||||
import org.nuclearfog.twidda.backend.helper.ConnectionConfig;
|
||||
import org.nuclearfog.twidda.backend.helper.MediaStatus;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
import org.nuclearfog.twidda.lists.Messages;
|
||||
import org.nuclearfog.twidda.backend.helper.update.ProfileUpdate;
|
||||
import org.nuclearfog.twidda.backend.helper.update.StatusUpdate;
|
||||
|
@ -448,6 +449,28 @@ public interface Connection {
|
|||
*/
|
||||
Status uploadStatus(StatusUpdate update, List<Long> mediaIds) throws ConnectionException;
|
||||
|
||||
/**
|
||||
* return a list of domain names the current user has blocked
|
||||
*
|
||||
* @param cursor cursor to parse the pages or 0L if not defined
|
||||
* @return domain list
|
||||
*/
|
||||
Domains getDomainBlocks(long cursor) throws ConnectionException;
|
||||
|
||||
/**
|
||||
* block specific domain name
|
||||
*
|
||||
* @param domain domain name (without "https://")
|
||||
*/
|
||||
void blockDomain(String domain) throws ConnectionException;
|
||||
|
||||
/**
|
||||
* remove block of a specific domain name
|
||||
*
|
||||
* @param domain domain name (without "https://")
|
||||
*/
|
||||
void unblockDomain(String domain) throws ConnectionException;
|
||||
|
||||
/**
|
||||
* create userlist
|
||||
*
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.nuclearfog.twidda.backend.api.mastodon.impl.MastodonTrend;
|
|||
import org.nuclearfog.twidda.backend.api.mastodon.impl.MastodonUser;
|
||||
import org.nuclearfog.twidda.backend.helper.ConnectionConfig;
|
||||
import org.nuclearfog.twidda.backend.helper.MediaStatus;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
import org.nuclearfog.twidda.lists.Messages;
|
||||
import org.nuclearfog.twidda.backend.helper.update.PollUpdate;
|
||||
import org.nuclearfog.twidda.backend.helper.update.ProfileUpdate;
|
||||
|
@ -124,6 +125,7 @@ public class Mastodon implements Connection {
|
|||
private static final String ENDPOINT_PUBLIC_TIMELINE = "/api/v1/timelines/public";
|
||||
private static final String ENDPOINT_CUSTOM_EMOJIS = "/api/v1/custom_emojis";
|
||||
private static final String ENDPOINT_POLL = "/api/v1/polls/";
|
||||
private static final String ENDPOINT_DOMAIN_BLOCK = "/api/v1/domain_blocks";
|
||||
|
||||
private static final MediaType TYPE_TEXT = MediaType.parse("text/plain");
|
||||
private static final MediaType TYPE_STREAM = MediaType.parse("application/octet-stream");
|
||||
|
@ -663,6 +665,61 @@ public class Mastodon implements Connection {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Domains getDomainBlocks(long cursor) throws ConnectionException {
|
||||
try {
|
||||
List<String> params = new ArrayList<>();
|
||||
params.add("limit=" + settings.getListSize());
|
||||
params.add("maxId=" + cursor);
|
||||
Response response = get(ENDPOINT_DOMAIN_BLOCK, params);
|
||||
ResponseBody body = response.body();
|
||||
if (response.code() == 200 && body != null) {
|
||||
JSONArray array = new JSONArray(body.string());
|
||||
long[] cursors = getCursors(response);
|
||||
Domains result = new Domains(cursors[0], cursors[1]);
|
||||
for (int i = 0 ; i < array.length() ; i++) {
|
||||
result.add(array.getString(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
throw new MastodonException(response);
|
||||
|
||||
} catch (JSONException | IOException e) {
|
||||
throw new MastodonException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void blockDomain(String domain) throws ConnectionException {
|
||||
try {
|
||||
List<String> params = new ArrayList<>();
|
||||
params.add("domain=" + domain);
|
||||
Response response = post(ENDPOINT_DOMAIN_BLOCK, params);
|
||||
if (response.code() != 200) {
|
||||
throw new MastodonException(response);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MastodonException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void unblockDomain(String domain) throws ConnectionException {
|
||||
try {
|
||||
List<String> params = new ArrayList<>();
|
||||
params.add("domain=" + domain);
|
||||
Response response = delete(ENDPOINT_DOMAIN_BLOCK, params);
|
||||
if (response.code() != 200) {
|
||||
throw new MastodonException(response);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MastodonException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserList createUserlist(UserListUpdate update) throws MastodonException {
|
||||
List<String> params = new ArrayList<>();
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.nuclearfog.twidda.backend.api.twitter.v1.impl.UserListV1;
|
|||
import org.nuclearfog.twidda.backend.api.twitter.v1.impl.UserV1;
|
||||
import org.nuclearfog.twidda.backend.helper.ConnectionConfig;
|
||||
import org.nuclearfog.twidda.backend.helper.MediaStatus;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
import org.nuclearfog.twidda.lists.Messages;
|
||||
import org.nuclearfog.twidda.backend.helper.update.ProfileUpdate;
|
||||
import org.nuclearfog.twidda.backend.helper.update.StatusUpdate;
|
||||
|
@ -764,6 +765,24 @@ public class TwitterV1 implements Connection {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Domains getDomainBlocks(long cursor) throws ConnectionException {
|
||||
throw new TwitterException("not supported");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void blockDomain(String domain) throws ConnectionException {
|
||||
throw new TwitterException("not supported");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void unblockDomain(String domain) throws ConnectionException {
|
||||
throw new TwitterException("not supported");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserList createUserlist(UserListUpdate update) throws TwitterException {
|
||||
List<String> params = new ArrayList<>();
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package org.nuclearfog.twidda.backend.async;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.backend.api.Connection;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionException;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionManager;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
|
||||
/**
|
||||
* background executor to load/block domains
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class DomainAction extends AsyncExecutor<DomainAction.DomainParam, DomainAction.DomainResult> {
|
||||
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public DomainAction(Context context) {
|
||||
connection = ConnectionManager.getDefaultConnection(context);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected DomainResult doInBackground(@NonNull DomainParam param) {
|
||||
try {
|
||||
switch (param.mode) {
|
||||
case DomainParam.MODE_LOAD:
|
||||
Domains result = connection.getDomainBlocks(param.cursor);
|
||||
return new DomainResult(DomainResult.MODE_LOAD, param.index, result, param.domain, null);
|
||||
|
||||
case DomainParam.MODE_BLOCK:
|
||||
connection.blockDomain(param.domain);
|
||||
return new DomainResult(DomainResult.MODE_BLOCK, param.index, null, param.domain, null);
|
||||
|
||||
case DomainParam.MODE_UNBLOCK:
|
||||
connection.unblockDomain(param.domain);
|
||||
return new DomainResult(DomainResult.MODE_UNBLOCK, param.index, null, param.domain, null);
|
||||
}
|
||||
} catch (ConnectionException exception) {
|
||||
return new DomainResult(DomainResult.ERROR, param.index, null, param.domain, exception);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return new DomainResult(DomainResult.ERROR, param.index, null, param.domain, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class DomainParam {
|
||||
|
||||
public static final int MODE_LOAD = 1;
|
||||
public static final int MODE_BLOCK = 2;
|
||||
public static final int MODE_UNBLOCK = 3;
|
||||
|
||||
public static final long NO_CURSOR = 0L;
|
||||
|
||||
final String domain;
|
||||
final long cursor;
|
||||
final int mode;
|
||||
final int index;
|
||||
|
||||
public DomainParam(int mode, int index, long cursor, String domain) {
|
||||
this.mode = mode;
|
||||
this.cursor = cursor;
|
||||
this.domain = domain;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class DomainResult {
|
||||
|
||||
public static final int ERROR = -1;
|
||||
public static final int MODE_LOAD = 4;
|
||||
public static final int MODE_BLOCK = 5;
|
||||
public static final int MODE_UNBLOCK = 6;
|
||||
|
||||
public final int mode;
|
||||
public final int index;
|
||||
@Nullable
|
||||
public final Domains domains;
|
||||
@Nullable
|
||||
public final ConnectionException exception;
|
||||
@Nullable
|
||||
public final String domain;
|
||||
|
||||
DomainResult(int mode, int index, @Nullable Domains domains, @Nullable String domain, @Nullable ConnectionException exception) {
|
||||
this.domains = domains;
|
||||
this.domain = domain;
|
||||
this.exception = exception;
|
||||
this.mode = mode;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,15 +44,19 @@ public class FilterLoader extends AsyncExecutor<FilterLoader.FilterParam, Filter
|
|||
db.saveFilterlist(ids);
|
||||
return new FilterResult(FilterResult.RELOAD, null);
|
||||
|
||||
case FilterParam.MUTE:
|
||||
case FilterParam.MUTE_USER:
|
||||
Relation relation = connection.muteUser(param.name);
|
||||
db.muteUser(relation.getId(), true);
|
||||
return new FilterResult(FilterResult.MUTE, null);
|
||||
return new FilterResult(FilterResult.MUTE_USER, null);
|
||||
|
||||
case FilterParam.BLOCK:
|
||||
case FilterParam.BLOCK_USER:
|
||||
relation = connection.blockUser(param.name);
|
||||
db.muteUser(relation.getId(), true);
|
||||
return new FilterResult(FilterResult.BLOCK, null);
|
||||
return new FilterResult(FilterResult.BLOCK_USER, null);
|
||||
|
||||
case FilterParam.BLOCK_DOMAIN:
|
||||
connection.blockDomain(param.name);
|
||||
return new FilterResult(FilterResult.BLOCK_DOMAIN, null);
|
||||
}
|
||||
} catch (ConnectionException exception) {
|
||||
return new FilterResult(FilterResult.ERROR, exception);
|
||||
|
@ -68,8 +72,9 @@ public class FilterLoader extends AsyncExecutor<FilterLoader.FilterParam, Filter
|
|||
public static class FilterParam {
|
||||
|
||||
public static final int RELOAD = 1;
|
||||
public static final int MUTE = 2;
|
||||
public static final int BLOCK = 3;
|
||||
public static final int MUTE_USER = 2;
|
||||
public static final int BLOCK_USER = 3;
|
||||
public static final int BLOCK_DOMAIN = 4;
|
||||
|
||||
final String name;
|
||||
final int mode;
|
||||
|
@ -91,9 +96,10 @@ public class FilterLoader extends AsyncExecutor<FilterLoader.FilterParam, Filter
|
|||
public static class FilterResult {
|
||||
|
||||
public static final int ERROR = -1;
|
||||
public static final int RELOAD = 4;
|
||||
public static final int MUTE = 5;
|
||||
public static final int BLOCK = 6;
|
||||
public static final int RELOAD = 5;
|
||||
public static final int MUTE_USER = 6;
|
||||
public static final int BLOCK_USER = 7;
|
||||
public static final int BLOCK_DOMAIN = 8;
|
||||
|
||||
public final int mode;
|
||||
@Nullable
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.nuclearfog.twidda.lists;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* represents a list of domain url
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class Domains extends LinkedList<String> {
|
||||
|
||||
private static final long serialVersionUID = 7642308259992697427L;
|
||||
|
||||
private long prevCursor, nextCursor;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Domains() {
|
||||
this(0L, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prevCursor cursor to the previous page
|
||||
* @param nextCursor cursor to the next page
|
||||
*/
|
||||
public Domains(long prevCursor, long nextCursor) {
|
||||
super();
|
||||
this.prevCursor = prevCursor;
|
||||
this.nextCursor = nextCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domains list to clone
|
||||
*/
|
||||
public Domains(Domains domains) {
|
||||
super(domains);
|
||||
prevCursor = domains.prevCursor;
|
||||
nextCursor = domains.nextCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* clone existing list
|
||||
*
|
||||
* @param domains list to clone
|
||||
*/
|
||||
public void replaceAll(Domains domains) {
|
||||
clear();
|
||||
addAll(domains);
|
||||
prevCursor = domains.prevCursor;
|
||||
nextCursor = domains.nextCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* add new items at specific index
|
||||
*
|
||||
* @param index index where to insert new items
|
||||
* @param list items to add
|
||||
*/
|
||||
public void addAll(int index, Domains list) {
|
||||
if (isEmpty()) {
|
||||
prevCursor = list.prevCursor;
|
||||
nextCursor = list.nextCursor;
|
||||
} else if (index == 0) {
|
||||
prevCursor = list.prevCursor;
|
||||
} else if (index == size() - 1) {
|
||||
nextCursor = list.nextCursor;
|
||||
}
|
||||
super.addAll(index, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* get cursor for next items
|
||||
*
|
||||
* @return cursor or 0L if not set
|
||||
*/
|
||||
public long getNextCursor() {
|
||||
return nextCursor;
|
||||
}
|
||||
}
|
|
@ -42,6 +42,9 @@ import org.nuclearfog.textviewtool.LinkAndScrollMovement;
|
|||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionException;
|
||||
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction.DomainParam;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction.DomainResult;
|
||||
import org.nuclearfog.twidda.backend.async.RelationLoader;
|
||||
import org.nuclearfog.twidda.backend.async.RelationLoader.RelationParam;
|
||||
import org.nuclearfog.twidda.backend.async.RelationLoader.RelationResult;
|
||||
|
@ -121,6 +124,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
private static final int SCROLL_THRESHOLD = 10;
|
||||
|
||||
private ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this);
|
||||
private AsyncCallback<DomainResult> domainCallback = this::setDomainResult;
|
||||
private AsyncCallback<RelationResult> relationCallback = this::setRelationResult;
|
||||
private AsyncCallback<UserResult> userCallback = this::setUserResult;
|
||||
private AsyncCallback<EmojiResult> usernameUpdate = this::onUsernameUpdate;
|
||||
|
@ -131,6 +135,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
private Picasso picasso;
|
||||
private ConfirmDialog confirmDialog;
|
||||
|
||||
private DomainAction domainAction;
|
||||
private RelationLoader relationLoader;
|
||||
private UserLoader userLoader;
|
||||
private TextEmojiLoader emojiLoader;
|
||||
|
@ -181,6 +186,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
viewPager = findViewById(R.id.profile_pager);
|
||||
|
||||
relationLoader = new RelationLoader(this);
|
||||
domainAction = new DomainAction(this);
|
||||
userLoader = new UserLoader(this);
|
||||
emojiLoader = new TextEmojiLoader(this);
|
||||
picasso = PicassoBuilder.get(this);
|
||||
|
@ -323,6 +329,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
boolean result = super.onPrepareOptionsMenu(m);
|
||||
if (user != null) {
|
||||
MenuItem listItem = m.findItem(R.id.profile_lists);
|
||||
MenuItem domainBlock = m.findItem(R.id.profile_block_domain);
|
||||
|
||||
switch (settings.getLogin().getConfiguration()) {
|
||||
case TWITTER1:
|
||||
|
@ -339,6 +346,8 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
case MASTODON:
|
||||
if (user.isCurrentUser()) {
|
||||
listItem.setVisible(true);
|
||||
} else {
|
||||
domainBlock.setVisible(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -472,6 +481,12 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
startActivity(usersIntent);
|
||||
return true;
|
||||
}
|
||||
// block user domain
|
||||
else if (item.getItemId() == R.id.profile_block_domain) {
|
||||
if (user != null) {
|
||||
confirmDialog.show(ConfirmDialog.DOMAIN_BLOCK_ADD);
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
@ -594,6 +609,12 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
RelationParam param = new RelationParam(user.getId(), RelationParam.MUTE);
|
||||
relationLoader.execute(param, relationCallback);
|
||||
}
|
||||
// confirmed domain block
|
||||
else if (type == ConfirmDialog.DOMAIN_BLOCK_ADD) {
|
||||
String url = Uri.parse(user.getProfileUrl()).getHost();
|
||||
DomainParam param = new DomainParam(DomainParam.MODE_BLOCK, 0, DomainParam.NO_CURSOR, url);
|
||||
domainAction.execute(param, domainCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -675,7 +696,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
private void setRelationResult(@NonNull RelationResult result) {
|
||||
switch (result.mode) {
|
||||
case RelationResult.BLOCK:
|
||||
Toast.makeText(getApplicationContext(), R.string.info_user_blocked, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getApplicationContext(), R.string.info_blocked, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
|
||||
case RelationResult.UNBLOCK:
|
||||
|
@ -709,6 +730,18 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set domain block result
|
||||
*/
|
||||
private void setDomainResult(DomainResult result) {
|
||||
if (result.mode == DomainResult.MODE_BLOCK) {
|
||||
Toast.makeText(getApplicationContext(), R.string.info_domain_blocked, Toast.LENGTH_SHORT).show();
|
||||
} else if (result.mode == DomainResult.ERROR) {
|
||||
String message = ErrorHandler.getErrorMessage(this, result.exception);
|
||||
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set User Information
|
||||
*
|
||||
|
|
|
@ -19,7 +19,6 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
|
@ -34,12 +33,10 @@ import org.nuclearfog.twidda.backend.async.UserlistManager.ListManagerResult;
|
|||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.User;
|
||||
import org.nuclearfog.twidda.model.UserList;
|
||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||
import org.nuclearfog.twidda.ui.fragments.UserFragment;
|
||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
||||
|
||||
|
@ -100,8 +97,6 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
|
|||
|
||||
@Nullable
|
||||
private UserList userList;
|
||||
@Nullable
|
||||
private User user;
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -283,13 +278,6 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
|
|||
listLoaderAsync.execute(param, userlistSet);
|
||||
}
|
||||
}
|
||||
// remove user from list
|
||||
else if (type == ConfirmDialog.LIST_REMOVE_USER) {
|
||||
if (listManagerAsync.isIdle() && userList != null && user != null) {
|
||||
ListManagerParam param = new ListManagerParam(ListManagerParam.REMOVE, userList.getId(), user.getScreenname());
|
||||
listManagerAsync.execute(param, userlistUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -322,19 +310,6 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* called from {@link org.nuclearfog.twidda.ui.fragments.UserFragment} when an user should be removed from a list
|
||||
*
|
||||
* @param user user to remove from the lsit
|
||||
*/
|
||||
public void onDelete(User user) {
|
||||
if (!confirmDialog.isShowing()) {
|
||||
confirmDialog.show(ConfirmDialog.LIST_REMOVE_USER);
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update userlist member
|
||||
*/
|
||||
|
@ -351,19 +326,6 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
|
|||
invalidateOptionsMenu();
|
||||
break;
|
||||
|
||||
case ListManagerResult.DEL_USER:
|
||||
if (user != null) {
|
||||
info = getString(R.string.info_user_removed, user.getScreenname());
|
||||
Toast.makeText(getApplicationContext(), info, Toast.LENGTH_SHORT).show();
|
||||
// remove user from list member page
|
||||
Fragment fragment = adapter.getItem(1);
|
||||
if (fragment instanceof UserFragment) {
|
||||
UserFragment callback = (UserFragment) fragment;
|
||||
callback.removeUser(user);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ListManagerResult.ERROR:
|
||||
String message = ErrorHandler.getErrorMessage(this, result.exception);
|
||||
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
|
||||
|
|
|
@ -2,7 +2,9 @@ package org.nuclearfog.twidda.ui.activities;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Patterns;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -170,11 +172,17 @@ public class UsersActivity extends AppCompatActivity implements OnTabSelectedLis
|
|||
break;
|
||||
|
||||
case USERS_EXCLUDED:
|
||||
adapter.setupMuteBlockPage();
|
||||
viewPager.setOffscreenPageLimit(2);
|
||||
if (settings.getLogin().getConfiguration() == Configuration.MASTODON) {
|
||||
adapter.setupBlockPage(true);
|
||||
viewPager.setOffscreenPageLimit(3);
|
||||
tabSelector.addTabIcons(R.array.user_domain_exclude_icons);
|
||||
} else {
|
||||
adapter.setupBlockPage(false);
|
||||
viewPager.setOffscreenPageLimit(2);
|
||||
tabSelector.addTabIcons(R.array.user_exclude_icons);
|
||||
}
|
||||
tabSelector.addViewPager(viewPager);
|
||||
tabSelector.addOnTabSelectedListener(this);
|
||||
tabSelector.addTabIcons(R.array.user_exclude_icons);
|
||||
toolbar.setTitle(R.string.menu_toolbar_excluded_users);
|
||||
break;
|
||||
|
||||
|
@ -230,6 +238,9 @@ public class UsersActivity extends AppCompatActivity implements OnTabSelectedLis
|
|||
} else if (viewPager.getCurrentItem() == 1) {
|
||||
String hint = getString(R.string.menu_hint_block_user);
|
||||
searchView.setQueryHint(hint);
|
||||
} else if (viewPager.getCurrentItem() == 2) {
|
||||
String hint = getString(R.string.menu_hint_block_domain);
|
||||
searchView.setQueryHint(hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -261,19 +272,33 @@ public class UsersActivity extends AppCompatActivity implements OnTabSelectedLis
|
|||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
if (USERNAME_PATTERN.matcher(query).matches()) {
|
||||
if (filterLoader.isIdle()) {
|
||||
if (viewPager.getCurrentItem() == 0) {
|
||||
FilterParam param = new FilterParam(FilterParam.MUTE, query);
|
||||
filterLoader.execute(param, this);
|
||||
} else if (viewPager.getCurrentItem() == 1) {
|
||||
FilterParam param = new FilterParam(FilterParam.BLOCK, query);
|
||||
filterLoader.execute(param, this);
|
||||
}
|
||||
if (!filterLoader.isIdle())
|
||||
return false;
|
||||
if (viewPager.getCurrentItem() == 0) {
|
||||
if (USERNAME_PATTERN.matcher(query).matches()) {
|
||||
FilterParam param = new FilterParam(FilterParam.MUTE_USER, query);
|
||||
filterLoader.execute(param, this);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), R.string.error_username_format, Toast.LENGTH_SHORT).show();
|
||||
} else if (viewPager.getCurrentItem() == 1) {
|
||||
if (USERNAME_PATTERN.matcher(query).matches()) {
|
||||
FilterParam param = new FilterParam(FilterParam.BLOCK_USER, query);
|
||||
filterLoader.execute(param, this);
|
||||
return true;
|
||||
}
|
||||
Toast.makeText(getApplicationContext(), R.string.error_username_format, Toast.LENGTH_SHORT).show();
|
||||
} else if (viewPager.getCurrentItem() == 2) {
|
||||
if (Patterns.WEB_URL.matcher(query).matches()) {
|
||||
FilterParam param;
|
||||
if (query.startsWith("https://"))
|
||||
param = new FilterParam(FilterParam.BLOCK_DOMAIN, Uri.parse(query).getHost());
|
||||
else
|
||||
param = new FilterParam(FilterParam.BLOCK_DOMAIN, query);
|
||||
filterLoader.execute(param, this);
|
||||
return true;
|
||||
}
|
||||
Toast.makeText(getApplicationContext(), R.string.error_domain_format, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -288,13 +313,14 @@ public class UsersActivity extends AppCompatActivity implements OnTabSelectedLis
|
|||
@Override
|
||||
public void onResult(@NonNull FilterResult result) {
|
||||
switch (result.mode) {
|
||||
case FilterResult.MUTE:
|
||||
case FilterResult.MUTE_USER:
|
||||
Toast.makeText(getApplicationContext(), R.string.info_user_muted, Toast.LENGTH_SHORT).show();
|
||||
invalidateOptionsMenu();
|
||||
break;
|
||||
|
||||
case FilterResult.BLOCK:
|
||||
Toast.makeText(getApplicationContext(), R.string.info_user_blocked, Toast.LENGTH_SHORT).show();
|
||||
case FilterResult.BLOCK_DOMAIN:
|
||||
case FilterResult.BLOCK_USER:
|
||||
Toast.makeText(getApplicationContext(), R.string.info_blocked, Toast.LENGTH_SHORT).show();
|
||||
invalidateOptionsMenu();
|
||||
break;
|
||||
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
package org.nuclearfog.twidda.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
import org.nuclearfog.twidda.ui.adapter.holder.DomainHolder;
|
||||
import org.nuclearfog.twidda.ui.adapter.holder.OnHolderClickListener;
|
||||
import org.nuclearfog.twidda.ui.adapter.holder.PlaceHolder;
|
||||
|
||||
/**
|
||||
* RecyclerView adapter for domain list
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class DomainAdapter extends Adapter<ViewHolder> implements OnHolderClickListener {
|
||||
|
||||
private static final int TYPE_ITEM = 0;
|
||||
private static final int TYPE_FOOTER = 1;
|
||||
|
||||
private static final int NO_LOADING = -1;
|
||||
public static final int NO_INDEX = -1;
|
||||
|
||||
private OnDomainClickListener listener;
|
||||
private GlobalSettings settings;
|
||||
|
||||
private Domains items = new Domains();
|
||||
private int loadingIndex = NO_LOADING;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public DomainAdapter(Context context, OnDomainClickListener listener) {
|
||||
settings = GlobalSettings.getInstance(context);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (viewType == TYPE_ITEM) {
|
||||
return new DomainHolder(parent, settings, this);
|
||||
} else {
|
||||
return new PlaceHolder(parent, settings, false, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
if (holder instanceof DomainHolder) {
|
||||
DomainHolder domainHolder = (DomainHolder) holder;
|
||||
domainHolder.setDomain(items.get(position));
|
||||
} else if (holder instanceof PlaceHolder) {
|
||||
PlaceHolder placeHolder = (PlaceHolder) holder;
|
||||
placeHolder.setLoading(loadingIndex == position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (items.get(position) != null)
|
||||
return TYPE_ITEM;
|
||||
return TYPE_FOOTER;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onItemClick(int position, int type, int... extras) {
|
||||
if (type == DOMAIN_REMOVE) {
|
||||
listener.onDomainRemove(items.get(position));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPlaceholderClick(int index) {
|
||||
return listener.onPlaceholderClick(index, items.getNextCursor());
|
||||
}
|
||||
|
||||
/**
|
||||
* replace old items with new items
|
||||
*
|
||||
* @param domains new items
|
||||
*/
|
||||
public void replaceItems(Domains domains) {
|
||||
disableLoading();
|
||||
items.replaceAll(domains);
|
||||
if (items.getNextCursor() != 0L && items.peekLast() != null) {
|
||||
items.add(null);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* insert new items at specific position
|
||||
*
|
||||
* @param domains new items
|
||||
* @param index index where to insert new items
|
||||
*/
|
||||
public void addItems(Domains domains, int index) {
|
||||
disableLoading();
|
||||
if (index < 0) {
|
||||
items.replaceAll(domains);
|
||||
if (items.getNextCursor() != 0L) {
|
||||
items.add(null);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
items.addAll(index, domains);
|
||||
if (items.getNextCursor() != 0L && items.peekLast() != null) {
|
||||
items.add(null);
|
||||
notifyItemRangeInserted(index, domains.size() + 1);
|
||||
} else if (items.getNextCursor() == 0L && items.peekLast() == null) {
|
||||
items.pollLast();
|
||||
notifyItemRangeInserted(index, domains.size() - 1);
|
||||
} else if (!domains.isEmpty()) {
|
||||
notifyItemRangeInserted(index, domains.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all items used in this adapter
|
||||
*
|
||||
* @return domain list
|
||||
*/
|
||||
public Domains getItems() {
|
||||
return new Domains(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove specific item from list
|
||||
*
|
||||
* @param item domain name item
|
||||
*/
|
||||
public void removeItem(String item) {
|
||||
int index = items.indexOf(item);
|
||||
if (index >= 0) {
|
||||
items.remove(index);
|
||||
notifyItemRemoved(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disable placeholder view loading animation
|
||||
*/
|
||||
public void disableLoading() {
|
||||
if (loadingIndex != NO_LOADING) {
|
||||
int oldIndex = loadingIndex;
|
||||
loadingIndex = NO_LOADING;
|
||||
notifyItemChanged(oldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* listener for list item
|
||||
*/
|
||||
public interface OnDomainClickListener {
|
||||
|
||||
/**
|
||||
* called on remove button click
|
||||
*
|
||||
* @param domain item of selected item
|
||||
*/
|
||||
void onDomainRemove(String domain);
|
||||
|
||||
/**
|
||||
* called on footer click
|
||||
*
|
||||
* @param index index of the footer
|
||||
* @param cursor cursor for the next items
|
||||
* @return true if click was performed
|
||||
*/
|
||||
boolean onPlaceholderClick(int index, long cursor);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
|
|||
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Account;
|
||||
import org.nuclearfog.twidda.ui.fragments.DomainFragment;
|
||||
import org.nuclearfog.twidda.ui.fragments.ListFragment;
|
||||
import org.nuclearfog.twidda.ui.fragments.MessageFragment;
|
||||
import org.nuclearfog.twidda.ui.fragments.NotificationFragment;
|
||||
|
@ -64,19 +65,6 @@ public class FragmentAdapter extends FragmentStateAdapter {
|
|||
return fragments.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* get fragment at index
|
||||
*
|
||||
* @param index index of the fragment
|
||||
* @return fragment
|
||||
*/
|
||||
public ListFragment getItem(int index) {
|
||||
if (index >= 0 && index < fragments.length) {
|
||||
return fragments[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if adapter is empty
|
||||
*
|
||||
|
@ -297,12 +285,17 @@ public class FragmentAdapter extends FragmentStateAdapter {
|
|||
/**
|
||||
* setup adapter for a page of muted / blocked users
|
||||
*/
|
||||
public void setupMuteBlockPage() {
|
||||
public void setupBlockPage(boolean enableDomainBlock) {
|
||||
Bundle paramMuteList = new Bundle();
|
||||
Bundle paramBlockList = new Bundle();
|
||||
paramMuteList.putInt(UserFragment.KEY_MODE, UserFragment.MODE_MUTES);
|
||||
paramBlockList.putInt(UserFragment.KEY_MODE, UserFragment.MODE_BLOCKS);
|
||||
fragments = new ListFragment[2];
|
||||
if (enableDomainBlock) {
|
||||
fragments = new ListFragment[3];
|
||||
fragments[2] = new DomainFragment();
|
||||
} else {
|
||||
fragments = new ListFragment[2];
|
||||
}
|
||||
fragments[0] = new UserFragment();
|
||||
fragments[1] = new UserFragment();
|
||||
fragments[0].setArguments(paramMuteList);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package org.nuclearfog.twidda.ui.adapter.holder;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
|
||||
/**
|
||||
* domain holder class for {@link org.nuclearfog.twidda.ui.adapter.DomainAdapter}
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class DomainHolder extends ViewHolder implements OnClickListener {
|
||||
|
||||
private TextView domain_name;
|
||||
|
||||
private OnHolderClickListener listener;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public DomainHolder(ViewGroup parent, GlobalSettings settings, OnHolderClickListener listener) {
|
||||
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_domain, parent, false));
|
||||
this.listener = listener;
|
||||
CardView card = (CardView) itemView;
|
||||
View deleteButton = itemView.findViewById(R.id.item_domain_delete);
|
||||
domain_name = itemView.findViewById(R.id.item_domain_name);
|
||||
domain_name.setTextColor(settings.getTextColor());
|
||||
domain_name.setTypeface(settings.getTypeFace());
|
||||
card.setCardBackgroundColor(settings.getCardColor());
|
||||
|
||||
deleteButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.item_domain_delete) {
|
||||
int position = getLayoutPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onItemClick(position, OnHolderClickListener.DOMAIN_REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set domain information
|
||||
*
|
||||
* @param name domain information
|
||||
*/
|
||||
public void setDomain(String name) {
|
||||
domain_name.setText(name);
|
||||
}
|
||||
}
|
|
@ -49,6 +49,8 @@ public interface OnHolderClickListener {
|
|||
|
||||
int EMOJI_CLICK = 20;
|
||||
|
||||
int DOMAIN_REMOVE = 21;
|
||||
|
||||
/**
|
||||
* called when an item was clicked
|
||||
*
|
||||
|
|
|
@ -127,6 +127,10 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
|
|||
*/
|
||||
public static final int NOTIFICATION_DISMISS = 623;
|
||||
|
||||
public static final int DOMAIN_BLOCK_ADD = 624;
|
||||
|
||||
public static final int DOMAIN_BLOCK_REMOVE = 625;
|
||||
|
||||
|
||||
private TextView title, message;
|
||||
private Button confirm, cancel;
|
||||
|
@ -236,6 +240,14 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
|
|||
messageRes = R.string.confirm_dismiss_notification;
|
||||
break;
|
||||
|
||||
case DOMAIN_BLOCK_ADD:
|
||||
messageRes = R.string.confirm_add_domain_block;
|
||||
break;
|
||||
|
||||
case DOMAIN_BLOCK_REMOVE:
|
||||
messageRes = R.string.confirm_remove_domain_block;
|
||||
break;
|
||||
|
||||
case PROFILE_UNFOLLOW:
|
||||
messageRes = R.string.confirm_unfollow;
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package org.nuclearfog.twidda.ui.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction.DomainParam;
|
||||
import org.nuclearfog.twidda.backend.async.DomainAction.DomainResult;
|
||||
import org.nuclearfog.twidda.lists.Domains;
|
||||
import org.nuclearfog.twidda.ui.adapter.DomainAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.DomainAdapter.OnDomainClickListener;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Fragment containing a list of domain names
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class DomainFragment extends ListFragment implements OnDomainClickListener, OnConfirmListener, AsyncCallback<DomainResult> {
|
||||
|
||||
private static final String KEY_DATA = "domain-data";
|
||||
|
||||
private DomainAction domainAction;
|
||||
private DomainAdapter adapter;
|
||||
private ConfirmDialog dialog;
|
||||
|
||||
private String selectedDomain = "";
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
adapter = new DomainAdapter(requireContext(), this);
|
||||
domainAction = new DomainAction(requireContext());
|
||||
dialog = new ConfirmDialog(requireContext());
|
||||
setAdapter(adapter);
|
||||
dialog.setConfirmListener(this);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
Serializable data = savedInstanceState.getSerializable(KEY_DATA);
|
||||
if (data instanceof Domains) {
|
||||
adapter.replaceItems((Domains) data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
load(DomainAdapter.NO_INDEX, DomainParam.NO_CURSOR);
|
||||
setRefresh(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putSerializable(KEY_DATA, adapter.getItems());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
domainAction.cancel();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onReload() {
|
||||
load(DomainAdapter.NO_INDEX, DomainParam.NO_CURSOR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
setRefresh(true);
|
||||
load(DomainAdapter.NO_INDEX, DomainParam.NO_CURSOR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDomainRemove(String domain) {
|
||||
if (!isRefreshing() && !dialog.isShowing()) {
|
||||
dialog.show(ConfirmDialog.DOMAIN_BLOCK_REMOVE);
|
||||
selectedDomain = domain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPlaceholderClick(int index, long cursor) {
|
||||
if (!isRefreshing()) {
|
||||
load(index, cursor);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResult(@NonNull DomainResult result) {
|
||||
setRefresh(false);
|
||||
if (result.mode == DomainResult.MODE_LOAD) {
|
||||
if (result.domains != null) {
|
||||
adapter.addItems(result.domains, result.index);
|
||||
}
|
||||
} else if (result.mode == DomainResult.MODE_UNBLOCK) {
|
||||
if (result.domain != null) {
|
||||
adapter.removeItem(result.domain);
|
||||
Toast.makeText(requireContext(), R.string.info_domain_removed, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConfirm(int type) {
|
||||
if (type == ConfirmDialog.DOMAIN_BLOCK_REMOVE) {
|
||||
DomainParam param = new DomainParam(DomainParam.MODE_UNBLOCK, DomainAdapter.NO_INDEX, DomainParam.NO_CURSOR, selectedDomain);
|
||||
domainAction.execute(param, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load domain list
|
||||
*
|
||||
* @param index index where to insert domains into the list
|
||||
* @param cursor cursor used to page through results
|
||||
*/
|
||||
private void load(int index, long cursor) {
|
||||
DomainParam param = new DomainParam(DomainParam.MODE_LOAD, index, cursor, null);
|
||||
domainAction.execute(param, this);
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@ public class NotificationFragment extends ListFragment implements OnNotification
|
|||
notificationAction = new NotificationAction(requireContext());
|
||||
adapter = new NotificationAdapter(requireContext(), this);
|
||||
setAdapter(adapter);
|
||||
confirmDialog.setConfirmListener(this);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
Serializable data = savedInstanceState.getSerializable(KEY_DATA);
|
||||
|
@ -75,7 +76,6 @@ public class NotificationFragment extends ListFragment implements OnNotification
|
|||
return;
|
||||
}
|
||||
}
|
||||
confirmDialog.setConfirmListener(this);
|
||||
load(0L, 0L, 0);
|
||||
setRefresh(true);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,10 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
|
||||
import org.nuclearfog.twidda.backend.async.UserlistManager;
|
||||
import org.nuclearfog.twidda.backend.async.UserlistManager.ListManagerResult;
|
||||
import org.nuclearfog.twidda.backend.async.UsersLoader;
|
||||
import org.nuclearfog.twidda.backend.async.UsersLoader.UserParam;
|
||||
import org.nuclearfog.twidda.backend.async.UsersLoader.UserResult;
|
||||
|
@ -20,9 +23,10 @@ import org.nuclearfog.twidda.lists.Users;
|
|||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.model.User;
|
||||
import org.nuclearfog.twidda.ui.activities.ProfileActivity;
|
||||
import org.nuclearfog.twidda.ui.activities.UserlistActivity;
|
||||
import org.nuclearfog.twidda.ui.adapter.UserAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.UserAdapter.UserClickListener;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
@ -31,7 +35,7 @@ import java.io.Serializable;
|
|||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class UserFragment extends ListFragment implements UserClickListener, AsyncCallback<UserResult>, ActivityResultCallback<ActivityResult> {
|
||||
public class UserFragment extends ListFragment implements UserClickListener, OnConfirmListener, AsyncCallback<UserResult>, ActivityResultCallback<ActivityResult> {
|
||||
|
||||
/**
|
||||
* key to set the type of user list to show
|
||||
|
@ -145,10 +149,14 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy
|
|||
|
||||
|
||||
private ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this);
|
||||
private AsyncCallback<ListManagerResult> userlistUpdate = this::updateUsers;
|
||||
|
||||
private ConfirmDialog confirmDialog;
|
||||
private UsersLoader userLoader;
|
||||
private UserlistManager userlistManager;
|
||||
private UserAdapter adapter;
|
||||
|
||||
private User selectedUser;
|
||||
private String search = "";
|
||||
private long id = 0;
|
||||
private int mode = 0;
|
||||
|
@ -158,8 +166,11 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy
|
|||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
userLoader = new UsersLoader(requireContext());
|
||||
userlistManager = new UserlistManager(requireContext());
|
||||
confirmDialog = new ConfirmDialog(requireContext());
|
||||
adapter = new UserAdapter(requireContext(), this);
|
||||
setAdapter(adapter);
|
||||
confirmDialog.setConfirmListener(this);
|
||||
|
||||
Bundle param = getArguments();
|
||||
if (param != null) {
|
||||
|
@ -244,10 +255,9 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy
|
|||
|
||||
@Override
|
||||
public void onDelete(User user) {
|
||||
if (getActivity() instanceof UserlistActivity) {
|
||||
// call parent activity to handle user delete
|
||||
UserlistActivity callback = (UserlistActivity) getActivity();
|
||||
callback.onDelete(user);
|
||||
if (!confirmDialog.isShowing()) {
|
||||
confirmDialog.show(ConfirmDialog.LIST_REMOVE_USER);
|
||||
this.selectedUser = user;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,15 +274,30 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy
|
|||
setRefresh(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove specific user from fragment list
|
||||
*
|
||||
* @param user user to remove
|
||||
*/
|
||||
public void removeUser(User user) {
|
||||
adapter.removeItem(user);
|
||||
|
||||
@Override
|
||||
public void onConfirm(int type) {
|
||||
// remove user from list
|
||||
if (type == ConfirmDialog.LIST_REMOVE_USER) {
|
||||
if (userlistManager.isIdle() && selectedUser != null) {
|
||||
UserlistManager.ListManagerParam param = new UserlistManager.ListManagerParam(UserlistManager.ListManagerParam.REMOVE, id, selectedUser.getScreenname());
|
||||
userlistManager.execute(param, userlistUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* callback for userlist changes
|
||||
*/
|
||||
private void updateUsers(ListManagerResult result) {
|
||||
if (result.mode == ListManagerResult.DEL_USER) {
|
||||
if (selectedUser != null) {
|
||||
String info = getString(R.string.info_user_removed, selectedUser.getScreenname());
|
||||
Toast.makeText(requireContext(), info, Toast.LENGTH_SHORT).show();
|
||||
adapter.removeItem(selectedUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load content into the list
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="m20.025,10.016c0,0.921 -0.08,1.665 -1.002,1.665h-7.332v7.325c0,0.919 -0.745,1.001 -1.667,1.001 -0.922,0 -1.667,-0.082 -1.667,-1.001V11.681H1.025c-0.92,0 -1.002,-0.744 -1.002,-1.665 0,-0.921 0.082,-1.665 1.002,-1.665H8.358V1.026c0,-0.921 0.745,-1.001 1.667,-1.001 0.922,0 1.667,0.08 1.667,1.001v7.325h7.332c0.922,0 1.002,0.744 1.002,1.665z"
|
||||
android:strokeWidth="1.66597"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/CardViewStyle">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_domain_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/item_domain_layout_margin"
|
||||
android:textSize="@dimen/item_domain_text_size"
|
||||
android:layout_weight="1"
|
||||
android:lines="1"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/item_domain_delete"
|
||||
android:layout_width="@dimen/item_domain_button_size"
|
||||
android:layout_height="@dimen/item_domain_button_size"
|
||||
android:padding="@dimen/item_domain_button_padding"
|
||||
android:layout_margin="@dimen/item_domain_layout_margin"
|
||||
android:contentDescription="@string/descr_remove_domain"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/cross"
|
||||
style="@style/RoundButton" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
|
@ -5,7 +5,7 @@
|
|||
<item
|
||||
android:id="@+id/menu_exclude_user"
|
||||
android:title="@string/menu_exclude_user"
|
||||
android:icon="@drawable/add_user"
|
||||
android:icon="@drawable/plus"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="ifRoom|collapseActionView" />
|
||||
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
android:title="@string/menu_block_user"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/profile_block_domain"
|
||||
android:title="@string/menu_block_domain"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/profile_mute"
|
||||
android:title="@string/menu_mute_user"
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
<string name="settings_background">Hintergrund</string>
|
||||
<string name="confirm_delete_status">Status löschen?</string>
|
||||
<string name="error_enter_code">PIN eingeben!</string>
|
||||
<string name="confirm_add_domain_block">Nutzerdomain sperren?</string>
|
||||
<string name="confirm_remove_domain_block">Domain von der Liste entfernen?</string>
|
||||
<string name="confirm_delete_database">App Daten Löschen?</string>
|
||||
<string name="error_limit_exceeded">"Anfragelimit erreicht! Entsperrung nach:"</string>
|
||||
<string name="info_status_sent">Status gesendet</string>
|
||||
|
@ -26,6 +28,7 @@
|
|||
<string name="info_user_repost">%1$s hat deinen Status geteilt</string>
|
||||
<string name="info_database_cleared">Datenbank gelöscht</string>
|
||||
<string name="info_notification_dismiss">Nachricht verworfen</string>
|
||||
<string name="info_domain_blocked">Domain gesperrt!</string>
|
||||
<string name="info_error">Fehler!</string>
|
||||
<string name="menu_status_edit">bearbeiten</string>
|
||||
<string name="menu_status_delete">löschen</string>
|
||||
|
@ -35,7 +38,7 @@
|
|||
<string name="menu_block_user">blocken</string>
|
||||
<string name="menu_user_unblock">entblocken</string>
|
||||
<string name="info_followed">gefolgt!</string>
|
||||
<string name="info_user_blocked">geblockt!</string>
|
||||
<string name="info_blocked">geblockt!</string>
|
||||
<string name="info_user_unblocked">entblockt!</string>
|
||||
<string name="info_status_removed">Status gelöscht!</string>
|
||||
<string name="status_sent_from">"gesendet von: "</string>
|
||||
|
@ -171,6 +174,7 @@
|
|||
<string name="info_user_added_to_list">%1$s wurde zur Liste hinzugefügt!</string>
|
||||
<string name="error_username_format">Nutzername falsch!</string>
|
||||
<string name="error_not_defined">Undefinierter Fehler!</string>
|
||||
<string name="error_domain_format">Falsches Domainformat!</string>
|
||||
<string name="error_cant_reply_to_status">Status ist nicht für Antworten freigegeben!</string>
|
||||
<string name="error_corrupt_api_key">Fehler, API Schlüssel ist fehlerhaft!</string>
|
||||
<string name="error_acc_update">Account Informationen konnten nicht aktualisiert werden! Bitte Eingaben überprüfen.</string>
|
||||
|
@ -200,6 +204,7 @@
|
|||
<string name="menu_hint_block_user">\@name blockieren</string>
|
||||
<string name="menu_exclude_user">Nutzer ausschließen</string>
|
||||
<string name="menu_search_filter">Ergebnisse filtern</string>
|
||||
<string name="menu_hint_block_domain">domain.namen eingeben</string>
|
||||
<string name="menu_refresh_exclude">Filterliste aktualisieren</string>
|
||||
<string name="info_refreshing_exclude_list">aktualisiere Filterliste</string>
|
||||
<string name="info_exclude_list_updated">Filterliste aktualisiert!</string>
|
||||
|
@ -229,6 +234,7 @@
|
|||
<string name="info_reply_hidden">Antwort wurde ausgeblendet</string>
|
||||
<string name="info_reply_unhidden">Antwort wurde eingeblendet</string>
|
||||
<string name="settings_description_enable_twitter_alt">öffne Twitter-Links mit Nitter</string>
|
||||
<string name="descr_remove_domain">entferne Domain von der Liste</string>
|
||||
<string name="confirm_unknown_error">Unbekannter Fehler!</string>
|
||||
<string name="error_invalid_media">Ungültige Mediendatei!</string>
|
||||
<string name="menu_follow_requests">Anfragen</string>
|
||||
|
@ -265,6 +271,7 @@
|
|||
<string name="info_poll_voted">Umfrage abgestimmt!</string>
|
||||
<string name="confirm_dismiss_notification">Nachricht verwerfen?</string>
|
||||
<string name="error_json_format">JSON Fehler!</string>
|
||||
<string name="menu_block_domain">Domain sperren</string>
|
||||
<string name="menu_bookmark_add">Lesezeichen hinzufügen</string>
|
||||
<string name="menu_bookmark_remove">Lesezeichen entfernen</string>
|
||||
<string name="notification_dismiss">Benachrichtigung verwerfen</string>
|
||||
|
@ -294,5 +301,6 @@
|
|||
<string name="menu_hashtag_follow">folge Hashtag</string>
|
||||
<string name="menu_hashtag_unfollow">entfolge Hashtag</string>
|
||||
<string name="info_hashtag_unfollowed">Hashtag entfolgt</string>
|
||||
<string name="info_domain_removed">Domain von der Liste entfernt</string>
|
||||
<string name="info_hashtag_followed">Hashtag gefolgt</string>
|
||||
</resources>
|
|
@ -109,7 +109,7 @@
|
|||
<string name="info_profile_updated">个人资料已更新</string>
|
||||
<string name="info_followed">已关注</string>
|
||||
<string name="info_unfollowed">已取消关注</string>
|
||||
<string name="info_user_blocked">已屏蔽</string>
|
||||
<string name="info_blocked">已屏蔽</string>
|
||||
<string name="info_user_unblocked">已取消屏蔽</string>
|
||||
<string name="info_status_removed">推文已删除</string>
|
||||
<string name="info_status_sent">推文已发送</string>
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
<item>@drawable/block</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="user_domain_exclude_icons">
|
||||
<item>@drawable/mute</item>
|
||||
<item>@drawable/block</item>
|
||||
<item>@drawable/link</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="user_hashtag_following">
|
||||
<item>@drawable/user</item>
|
||||
<item>@drawable/hash</item>
|
||||
|
|
|
@ -310,4 +310,10 @@
|
|||
<!--dimens of DescriptionView-->
|
||||
<dimen name="descriptionview_layout_padding">5dp</dimen>
|
||||
|
||||
<!--dimens of item_domain.xml-->
|
||||
<dimen name="item_domain_layout_margin">8dp</dimen>
|
||||
<dimen name="item_domain_text_size">20sp</dimen>
|
||||
<dimen name="item_domain_button_size">36dp</dimen>
|
||||
<dimen name="item_domain_button_padding">7dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -41,7 +41,7 @@
|
|||
<string name="info_profile_updated">Profile updated</string>
|
||||
<string name="info_followed">followed</string>
|
||||
<string name="info_unfollowed">unfollowed</string>
|
||||
<string name="info_user_blocked">blocked</string>
|
||||
<string name="info_blocked">blocked</string>
|
||||
<string name="info_user_unblocked">unblocked</string>
|
||||
<string name="info_status_removed">Status removed</string>
|
||||
<string name="info_status_sent">Status shared</string>
|
||||
|
@ -66,7 +66,9 @@
|
|||
<string name="info_database_cleared">Database cleared</string>
|
||||
<string name="info_notification_dismiss">Notification dismissed</string>
|
||||
<string name="info_hashtag_unfollowed">hashtag unfollowed</string>
|
||||
<string name="info_domain_removed">domain removed from the list</string>
|
||||
<string name="info_hashtag_followed">hashtag followed</string>
|
||||
<string name="info_domain_blocked">domain blocked!</string>
|
||||
<string name="info_error">Error</string>
|
||||
|
||||
<!-- toast messages for error information -->
|
||||
|
@ -97,6 +99,7 @@
|
|||
<string name="error_dm_length">Directmessage is too long!</string>
|
||||
<string name="error_list_title_empty">Empty list title!</string>
|
||||
<string name="error_username_format">Wrong username format!</string>
|
||||
<string name="error_domain_format">Wrong domain format!</string>
|
||||
<string name="error_cant_reply_to_status">You can\'t reply to this status!</string>
|
||||
<string name="error_corrupt_api_key">Error, corrupt API key!</string>
|
||||
<string name="error_acc_update">Account update failed! Please check your input!</string>
|
||||
|
@ -129,7 +132,8 @@
|
|||
<string name="menu_goto_lists">Userlists</string>
|
||||
<string name="menu_app_info">About</string>
|
||||
<string name="menu_licenses">Licenses</string>
|
||||
<string name="menu_block_user">block</string>
|
||||
<string name="menu_block_user">block user</string>
|
||||
<string name="menu_block_domain">block domain</string>
|
||||
<string name="menu_bookmark_add">bookmark</string>
|
||||
<string name="menu_bookmark_remove">remove bookmark</string>
|
||||
<string name="menu_unmute_user">unmute</string>
|
||||
|
@ -155,6 +159,7 @@
|
|||
<string name="menu_search_filter">filter results</string>
|
||||
<string name="menu_hint_mute_user">enter @name to mute</string>
|
||||
<string name="menu_hint_block_user">enter @name to block</string>
|
||||
<string name="menu_hint_block_domain">enter domain.name</string>
|
||||
<string name="menu_refresh_exclude">refresh exclude list</string>
|
||||
<string name="menu_select_account">Logins</string>
|
||||
<string name="menu_add_account">add account</string>
|
||||
|
@ -205,6 +210,8 @@
|
|||
<string name="settings_clear_data">clear app data</string>
|
||||
<string name="confirm_delete_status">delete status?</string>
|
||||
<string name="confirm_dismiss_notification">dismiss notification?</string>
|
||||
<string name="confirm_add_domain_block">block user domain?</string>
|
||||
<string name="confirm_remove_domain_block">remove domain from list?</string>
|
||||
<string name="confirm_delete_database">clear app data?</string>
|
||||
<string name="status_sent_from">"sent from: "</string>
|
||||
<string name="username">Username</string>
|
||||
|
@ -289,6 +296,7 @@
|
|||
<string name="update_list">update list</string>
|
||||
<string name="confirm_remove_user_from_list">remove user from list?</string>
|
||||
<string name="descr_remove_user">remove user from list</string>
|
||||
<string name="descr_remove_domain">remove domain from list</string>
|
||||
<string name="confirm_unknown_error">unknown error!</string>
|
||||
<string name="list_following_indicator">following list</string>
|
||||
<string name="descr_add_profile_image">change profile image</string>
|
||||
|
|
Loading…
Reference in New Issue