Admin features + fix pagination with flag loading

This commit is contained in:
Thomas 2022-05-28 18:02:30 +02:00
parent f5fbb1b05e
commit 7e4c043711
13 changed files with 654 additions and 114 deletions

View file

@ -33,6 +33,7 @@ import com.google.gson.annotations.SerializedName;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityAdminActionsBinding; import app.fedilab.android.databinding.ActivityAdminActionsBinding;
import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding; import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding;
import app.fedilab.android.databinding.PopupAdminFilterReportsBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.admin.FragmentAdminAccount; import app.fedilab.android.ui.fragment.admin.FragmentAdminAccount;
@ -41,6 +42,7 @@ import app.fedilab.android.ui.fragment.admin.FragmentAdminReport;
public class AdminActionActivity extends BaseActivity { public class AdminActionActivity extends BaseActivity {
public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true; public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true;
public static Boolean resolved = false, reportLocal = true, reportRemote = true;
private ActivityAdminActionsBinding binding; private ActivityAdminActionsBinding binding;
private boolean canGoBack; private boolean canGoBack;
private FragmentAdminReport fragmentAdminReport; private FragmentAdminReport fragmentAdminReport;
@ -108,7 +110,7 @@ public class AdminActionActivity extends BaseActivity {
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (canGoBack && getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) { if (canGoBack) {
getMenuInflater().inflate(R.menu.menu_admin_account, menu); getMenuInflater().inflate(R.menu.menu_admin_account, menu);
} }
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
@ -120,97 +122,150 @@ public class AdminActionActivity extends BaseActivity {
onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_filter) { } else if (item.getItemId() == R.id.action_filter) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle()); if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) {
PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater()); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle());
alertDialogBuilder.setView(binding.getRoot()); PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater());
if (local != null && remote == null) { alertDialogBuilder.setView(binding.getRoot());
binding.locationLocal.setChecked(true); if (local != null && remote == null) {
} else if (remote != null && local == null) { binding.locationLocal.setChecked(true);
binding.locationRemote.setChecked(true); } else if (remote != null && local == null) {
} else { binding.locationRemote.setChecked(true);
binding.locationAll.setChecked(true); } else {
} binding.locationAll.setChecked(true);
binding.location.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.location_all) {
local = true;
remote = true;
} else if (checkedId == R.id.location_local) {
local = true;
remote = null;
} else if (checkedId == R.id.location_remote) {
local = null;
remote = true;
} }
}); binding.location.setOnCheckedChangeListener((group, checkedId) -> {
if (pending != null && suspended == null && active == null) { if (checkedId == R.id.location_all) {
binding.moderationPending.setChecked(true); local = true;
} else if (suspended != null && pending == null && active == null) { remote = true;
binding.moderationSuspended.setChecked(true); } else if (checkedId == R.id.location_local) {
} else if (active != null && pending == null && suspended == null) { local = true;
binding.moderationActive.setChecked(true); remote = null;
} else { } else if (checkedId == R.id.location_remote) {
binding.moderationAll.setChecked(true); local = null;
} remote = true;
binding.moderation.setOnCheckedChangeListener((group, checkedId) -> { }
if (checkedId == R.id.moderation_all) { });
active = true; if (pending != null && suspended == null && active == null) {
suspended = true; binding.moderationPending.setChecked(true);
pending = true; } else if (suspended != null && pending == null && active == null) {
} else if (checkedId == R.id.moderation_active) { binding.moderationSuspended.setChecked(true);
active = true; } else if (active != null && pending == null && suspended == null) {
suspended = null; binding.moderationActive.setChecked(true);
pending = null; } else {
} else if (checkedId == R.id.moderation_suspended) { binding.moderationAll.setChecked(true);
active = null;
suspended = true;
pending = null;
} else if (checkedId == R.id.moderation_pending) {
active = null;
suspended = null;
pending = true;
} }
}); binding.moderation.setOnCheckedChangeListener((group, checkedId) -> {
if (staff != null) { if (checkedId == R.id.moderation_all) {
binding.permissionsStaff.setChecked(true); active = true;
} else { suspended = true;
binding.permissionsAll.setChecked(true); pending = true;
} } else if (checkedId == R.id.moderation_active) {
binding.permissions.setOnCheckedChangeListener((group, checkedId) -> { active = true;
if (checkedId == R.id.permissions_all) { suspended = null;
staff = null; pending = null;
} else if (checkedId == R.id.permissions_staff) { } else if (checkedId == R.id.moderation_suspended) {
staff = true; active = null;
suspended = true;
pending = null;
} else if (checkedId == R.id.moderation_pending) {
active = null;
suspended = null;
pending = true;
}
});
if (staff != null) {
binding.permissionsStaff.setChecked(true);
} else {
binding.permissionsAll.setChecked(true);
} }
}); binding.permissions.setOnCheckedChangeListener((group, checkedId) -> {
if (orderByMostRecent != null) { if (checkedId == R.id.permissions_all) {
binding.orderByMostRecent.setChecked(true); staff = null;
} else { } else if (checkedId == R.id.permissions_staff) {
binding.orderByLastActive.setChecked(true); staff = true;
} }
binding.orderBy.setOnCheckedChangeListener((group, checkedId) -> { });
if (checkedId == R.id.order_by_most_recent) { if (orderByMostRecent != null) {
orderByMostRecent = true; binding.orderByMostRecent.setChecked(true);
} else if (checkedId == R.id.order_by_last_active) { } else {
orderByMostRecent = null; binding.orderByLastActive.setChecked(true);
} }
}); binding.orderBy.setOnCheckedChangeListener((group, checkedId) -> {
alertDialogBuilder.setPositiveButton(R.string.filter, (dialog, id) -> { if (checkedId == R.id.order_by_most_recent) {
final FragmentTransaction ft1 = getSupportFragmentManager().beginTransaction(); orderByMostRecent = true;
ft1.detach(fragmentAdminAccount); } else if (checkedId == R.id.order_by_last_active) {
ft1.commit(); orderByMostRecent = null;
final FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction(); }
ft2.attach(fragmentAdminAccount); });
ft2.commit(); alertDialogBuilder.setPositiveButton(R.string.filter, (dialog, id) -> {
dialog.dismiss(); final FragmentTransaction ft1 = getSupportFragmentManager().beginTransaction();
}); ft1.detach(fragmentAdminAccount);
alertDialogBuilder.setNegativeButton(R.string.reset, (dialog, id) -> { ft1.commit();
binding.locationAll.callOnClick(); final FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction();
binding.permissionsAll.callOnClick(); ft2.attach(fragmentAdminAccount);
binding.moderationAll.callOnClick(); ft2.commit();
binding.orderByMostRecent.callOnClick(); dialog.dismiss();
}); });
AlertDialog alert = alertDialogBuilder.create(); alertDialogBuilder.setNegativeButton(R.string.reset, (dialog, id) -> {
alert.show(); binding.locationAll.callOnClick();
binding.permissionsAll.callOnClick();
binding.moderationAll.callOnClick();
binding.orderByMostRecent.callOnClick();
});
AlertDialog alert = alertDialogBuilder.create();
alert.show();
} else {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle());
PopupAdminFilterReportsBinding binding = PopupAdminFilterReportsBinding.inflate(getLayoutInflater());
alertDialogBuilder.setView(binding.getRoot());
if (resolved == null) {
binding.statusUnresolved.setChecked(true);
} else {
binding.statusResolved.setChecked(true);
}
binding.status.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.status_resolved) {
resolved = true;
} else if (checkedId == R.id.status_unresolved) {
resolved = false;
}
});
if (reportLocal != null && reportRemote == null) {
binding.originLocal.setChecked(true);
} else if (reportRemote != null && reportLocal == null) {
binding.originRemote.setChecked(true);
} else {
binding.originAll.setChecked(true);
}
binding.origin.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.origin_all) {
reportLocal = true;
reportRemote = true;
} else if (checkedId == R.id.origin_local) {
reportLocal = true;
reportRemote = null;
} else if (checkedId == R.id.origin_remote) {
reportLocal = null;
reportRemote = true;
}
});
alertDialogBuilder.setPositiveButton(R.string.filter, (dialog, id) -> {
final FragmentTransaction ft1 = getSupportFragmentManager().beginTransaction();
ft1.detach(fragmentAdminReport);
ft1.commit();
final FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction();
ft2.attach(fragmentAdminReport);
ft2.commit();
dialog.dismiss();
});
alertDialogBuilder.setNegativeButton(R.string.reset, (dialog, id) -> {
binding.originAll.callOnClick();
binding.statusUnresolved.callOnClick();
});
AlertDialog alert = alertDialogBuilder.create();
alert.show();
}
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View file

@ -0,0 +1,415 @@
package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* 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.
*
* Fedilab 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 Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.ActivityAdminAccountBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty;
public class AdminReportActivity extends BaseActivity {
private AdminAccount adminAccount;
private Account account;
private ScheduledExecutorService scheduledExecutorService;
private ActivityAdminAccountBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityAdminAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
Bundle b = getIntent().getExtras();
if (b != null) {
adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
if (adminAccount != null) {
account = adminAccount.account;
}
}
postponeEnterTransition();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account != null) {
new Thread(() -> {
account = SpannableHelper.convertAccount(AdminReportActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish();
}
}
private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminReportActivity.this);
if (account == null) {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish();
return;
}
binding.title.setText(String.format(Locale.getDefault(), "@%s", account.acct));
// MastodonHelper.loadPPMastodon(binding.profilePicture, account);
binding.appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
if (Math.abs(verticalOffset) - binding.appBar.getTotalScrollRange() == 0) {
binding.profilePicture.setVisibility(View.VISIBLE);
binding.title.setVisibility(View.VISIBLE);
} else {
binding.profilePicture.setVisibility(View.GONE);
binding.title.setVisibility(View.GONE);
}
});
binding.username.setText(String.format(Locale.getDefault(), "@%s", adminAccount.username));
binding.domain.setText(adminAccount.domain);
binding.email.setText(adminAccount.email);
StringBuilder lastActive = new StringBuilder();
if (adminAccount.ips != null) {
for (AdminAccount.IP ip : adminAccount.ips) {
lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n");
}
}
if (lastActive.toString().trim().length() == 0) {
binding.lastActiveContainer.setVisibility(View.GONE);
}
if (adminAccount.email == null || adminAccount.email.trim().length() == 0) {
binding.emailContainer.setVisibility(View.GONE);
}
binding.lastActive.setText(lastActive.toString());
binding.disabled.setText(adminAccount.disabled ? R.string.yes : R.string.no);
binding.approved.setText(adminAccount.approved ? R.string.yes : R.string.no);
binding.silenced.setText(adminAccount.silenced ? R.string.yes : R.string.no);
binding.suspended.setText(adminAccount.suspended ? R.string.yes : R.string.no);
binding.disableAction.setText(adminAccount.disabled ? R.string.undisable : R.string.disable);
binding.approveAction.setText(adminAccount.approved ? R.string.reject : R.string.approve);
binding.silenceAction.setText(adminAccount.silenced ? R.string.unsilence : R.string.silence);
binding.suspendAction.setText(adminAccount.suspended ? R.string.unsuspend : R.string.suspend);
AdminVM adminVM = new ViewModelProvider(AdminReportActivity.this).get(AdminVM.class);
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
}
});
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
adminAccount.approved = false;
binding.approveAction.setText(R.string.approve);
binding.approved.setText(R.string.no);
});
} else {
adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account.id);
adminAccount.approved = true;
binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes);
}
});
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
adminAccount.silenced = false;
binding.silenceAction.setText(R.string.silence);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes);
}
});
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> {
adminAccount.suspended = false;
binding.suspendAction.setText(R.string.suspend);
binding.suspended.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes);
}
});
//Retrieve relationship with the connected account
List<String> accountListToCheck = new ArrayList<>();
accountListToCheck.add(account.id);
//Animate emojis
if (account.emojis != null && account.emojis.size() > 0) {
boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
if (!disableAnimatedEmoji) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(() -> binding.accountDn.invalidate(), 0, 130, TimeUnit.MILLISECONDS);
}
}
//Tablayout for timelines/following/followers
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.avatar_static : account.avatar;
Glide.with(AdminReportActivity.this)
.asDrawable()
.dontTransform()
.load(targetedUrl).into(
new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) {
binding.profilePicture.setImageDrawable(resource);
startPostponedEnterTransition();
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
binding.profilePicture.setImageResource(R.drawable.ic_person);
startPostponedEnterTransition();
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
}
);
//Load header
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density;
if (account.locked) {
Drawable img = ContextCompat.getDrawable(AdminReportActivity.this, R.drawable.ic_baseline_lock_24);
assert img != null;
img.setBounds(0, 0, (int) (16 * scale + 0.5f), (int) (16 * scale + 0.5f));
binding.accountUn.setCompoundDrawables(null, null, img, null);
} else {
binding.accountUn.setCompoundDrawables(null, null, null, null);
}
//Peertube account watched by a Mastodon account
//Bot account
if (account.bot) {
binding.accountBot.setVisibility(View.VISIBLE);
}
if (account.acct != null) {
setTitle(account.acct);
}
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(AdminReportActivity.this, R.color.cyanea_accent_reference)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//This account was moved to another one
if (account.moved != null) {
binding.accountMoved.setVisibility(View.VISIBLE);
Drawable imgTravel = ContextCompat.getDrawable(AdminReportActivity.this, R.drawable.ic_baseline_card_travel_24);
assert imgTravel != null;
imgTravel.setBounds(0, 0, (int) (20 * scale + 0.5f), (int) (20 * scale + 0.5f));
binding.accountMoved.setCompoundDrawables(imgTravel, null, null, null);
//Retrieves content and make account names clickable
SpannableString spannableString = SpannableHelper.moveToText(AdminReportActivity.this, account);
binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE);
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
}
binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String account_id = account.acct;
if (account_id.split("@").length == 1)
account_id += "@" + BaseMainActivity.currentInstance;
ClipData clip = ClipData.newPlainText("mastodon_account_id", "@" + account_id);
Toasty.info(AdminReportActivity.this, getString(R.string.account_id_clipbloard), Toast.LENGTH_SHORT).show();
assert clipboard != null;
clipboard.setPrimaryClip(clip);
return false;
});
MastodonHelper.loadPPMastodon(binding.accountPp, account);
binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(AdminReportActivity.this, MediaActivity.class);
Bundle b = new Bundle();
Attachment attachment = new Attachment();
attachment.description = account.acct;
attachment.preview_url = account.avatar;
attachment.url = account.avatar;
attachment.remote_url = account.avatar;
attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment);
b.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments);
b.putInt(Helper.ARG_MEDIA_POSITION, 1);
intent.putExtras(b);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(AdminReportActivity.this, binding.accountPp, attachment.url);
// start the new activity
startActivity(intent, options.toBundle());
});
binding.accountDate.setText(Helper.shortDateToString(account.created_at));
binding.accountDate.setVisibility(View.VISIBLE);
String[] accountInstanceArray = account.acct.split("@");
String accountInstance = BaseMainActivity.currentInstance;
if (accountInstanceArray.length > 1) {
accountInstance = accountInstanceArray[1];
}
NodeInfoVM nodeInfoVM = new ViewModelProvider(AdminReportActivity.this).get(NodeInfoVM.class);
String finalAccountInstance = accountInstance;
nodeInfoVM.getNodeInfo(accountInstance).observe(AdminReportActivity.this, nodeInfo -> {
if (nodeInfo != null && nodeInfo.software != null) {
binding.instanceInfo.setText(nodeInfo.software.name);
binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> {
Intent intent = new Intent(AdminReportActivity.this, InstanceProfileActivity.class);
Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
intent.putExtras(b);
startActivity(intent);
});
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
}
return true;
}
@Override
public void onDestroy() {
if (scheduledExecutorService != null) {
scheduledExecutorService.shutdownNow();
scheduledExecutorService = null;
}
super.onDestroy();
}
public enum action {
FOLLOW,
UNFOLLOW,
BLOCK,
UNBLOCK,
NOTHING,
MUTE,
UNMUTE,
REPORT,
BLOCK_DOMAIN
}
}

View file

@ -95,7 +95,7 @@ public class FollowRequestActivity extends BaseActivity {
} }
private void manageView(Accounts accounts) { private void manageView(Accounts accounts) {
flagLoading = false;
binding.loadingNextAccounts.setVisibility(View.GONE); binding.loadingNextAccounts.setVisibility(View.GONE);
if (accountList != null && accounts != null && accounts.accounts != null && accounts.accounts.size() > 0) { if (accountList != null && accounts != null && accounts.accounts != null && accounts.accounts.size() > 0) {
int startId = 0; int startId = 0;
@ -103,6 +103,7 @@ public class FollowRequestActivity extends BaseActivity {
if (accountList.size() > 0) { if (accountList.size() > 0) {
startId = accountList.size(); startId = accountList.size();
} }
flagLoading = accounts.pagination.max_id == null;
accountList.addAll(accounts.accounts); accountList.addAll(accounts.accounts);
max_id = accounts.pagination.max_id; max_id = accounts.pagination.max_id;
accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size()); accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size());

View file

@ -113,7 +113,6 @@ public class StatusInfoActivity extends BaseActivity {
} }
private void manageView(Accounts accounts) { private void manageView(Accounts accounts) {
flagLoading = false;
binding.loadingNextAccounts.setVisibility(View.GONE); binding.loadingNextAccounts.setVisibility(View.GONE);
if (accountList != null && accounts != null && accounts.accounts != null) { if (accountList != null && accounts != null && accounts.accounts != null) {
int startId = 0; int startId = 0;
@ -123,6 +122,7 @@ public class StatusInfoActivity extends BaseActivity {
} }
accountList.addAll(accounts.accounts); accountList.addAll(accounts.accounts);
max_id = accounts.pagination.max_id; max_id = accounts.pagination.max_id;
flagLoading = accounts.pagination.max_id == null;
accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size()); accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size());
} }
} }

View file

@ -135,7 +135,7 @@ public class FragmentAdminAccount extends Fragment {
this.adminAccounts = adminAccounts.adminAccounts; this.adminAccounts = adminAccounts.adminAccounts;
adminAccountAdapter = new AdminAccountAdapter(this.adminAccounts); adminAccountAdapter = new AdminAccountAdapter(this.adminAccounts);
flagLoading = (adminAccounts.adminAccounts.size() < MastodonHelper.accountsPerCall(requireActivity())); flagLoading = adminAccounts.pagination.max_id == null;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(adminAccountAdapter); binding.recyclerView.setAdapter(adminAccountAdapter);
@ -177,13 +177,12 @@ public class FragmentAdminAccount extends Fragment {
* @param adminAccounts AdminAccounts * @param adminAccounts AdminAccounts
*/ */
private void dealWithPagination(AdminAccounts adminAccounts) { private void dealWithPagination(AdminAccounts adminAccounts) {
flagLoading = false;
if (binding == null) { if (binding == null) {
return; return;
} }
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (this.adminAccounts != null && adminAccounts != null && adminAccounts.adminAccounts != null) { if (this.adminAccounts != null && adminAccounts != null && adminAccounts.adminAccounts != null) {
flagLoading = (adminAccounts.adminAccounts.size() < MastodonHelper.accountsPerCall(requireActivity())); flagLoading = adminAccounts.pagination.max_id == null;
int startId = 0; int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
if (this.adminAccounts.size() > 0) { if (this.adminAccounts.size() > 0) {
@ -195,8 +194,6 @@ public class FragmentAdminAccount extends Fragment {
max_id = adminAccounts.pagination.max_id; max_id = adminAccounts.pagination.max_id;
} }
adminAccountAdapter.notifyItemRangeInserted(startId, adminAccounts.adminAccounts.size()); adminAccountAdapter.notifyItemRangeInserted(startId, adminAccounts.adminAccounts.size());
} else {
flagLoading = true;
} }
} }

View file

@ -36,7 +36,6 @@ import app.fedilab.android.client.entities.api.AdminReport;
import app.fedilab.android.client.entities.api.AdminReports; import app.fedilab.android.client.entities.api.AdminReports;
import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.databinding.FragmentPaginationBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.ui.drawer.StatusAdapter;
import app.fedilab.android.viewmodel.mastodon.AdminVM; import app.fedilab.android.viewmodel.mastodon.AdminVM;
@ -117,7 +116,7 @@ public class FragmentAdminReport extends Fragment {
binding.noAction.setVisibility(View.VISIBLE); binding.noAction.setVisibility(View.VISIBLE);
return; return;
} }
flagLoading = (adminReports.adminReports.size() < MastodonHelper.statusesPerCall(requireActivity())); flagLoading = adminReports.pagination.max_id == null;
binding.recyclerView.setVisibility(View.VISIBLE); binding.recyclerView.setVisibility(View.VISIBLE);
if (statusAdapter != null && this.adminReports != null) { if (statusAdapter != null && this.adminReports != null) {
int size = this.adminReports.size(); int size = this.adminReports.size();
@ -179,13 +178,13 @@ public class FragmentAdminReport extends Fragment {
* @param admReports AdminReports * @param admReports AdminReports
*/ */
private void dealWithPagination(AdminReports admReports) { private void dealWithPagination(AdminReports admReports) {
flagLoading = false;
if (binding == null) { if (binding == null) {
return; return;
} }
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (adminReports != null && admReports != null && admReports.adminReports != null && admReports.adminReports.size() > 0) { if (adminReports != null && admReports != null && admReports.adminReports != null && admReports.adminReports.size() > 0) {
flagLoading = (admReports.adminReports.size() < MastodonHelper.statusesPerCall(requireActivity())); flagLoading = admReports.pagination.max_id == null;
//There are some adminReports present in the timeline //There are some adminReports present in the timeline
int startId = adminReports.size(); int startId = adminReports.size();
adminReports.addAll(admReports.adminReports); adminReports.addAll(admReports.adminReports);

View file

@ -198,7 +198,7 @@ public class FragmentMastodonAccount extends Fragment {
this.accounts = accounts.accounts; this.accounts = accounts.accounts;
accountAdapter = new AccountAdapter(this.accounts); accountAdapter = new AccountAdapter(this.accounts);
flagLoading = (accounts.accounts.size() < MastodonHelper.accountsPerCall(requireActivity())); flagLoading = accounts.pagination.max_id == null;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(accountAdapter); binding.recyclerView.setAdapter(accountAdapter);
@ -240,13 +240,13 @@ public class FragmentMastodonAccount extends Fragment {
* @param fetched_accounts Accounts * @param fetched_accounts Accounts
*/ */
private void dealWithPagination(Accounts fetched_accounts) { private void dealWithPagination(Accounts fetched_accounts) {
flagLoading = false;
if (binding == null) { if (binding == null) {
return; return;
} }
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (accounts != null && fetched_accounts != null && fetched_accounts.accounts != null) { if (accounts != null && fetched_accounts != null && fetched_accounts.accounts != null) {
flagLoading = (fetched_accounts.accounts.size() < MastodonHelper.accountsPerCall(requireActivity())); flagLoading = fetched_accounts.pagination.max_id == null;
int startId = 0; int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
if (accounts.size() > 0) { if (accounts.size() > 0) {

View file

@ -115,10 +115,10 @@ public class FragmentMastodonConversation extends Fragment {
binding.loadingNextElements.setVisibility(View.VISIBLE); binding.loadingNextElements.setVisibility(View.VISIBLE);
timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(FragmentMastodonConversation.this, fetched_conversations -> { .observe(FragmentMastodonConversation.this, fetched_conversations -> {
flagLoading = false;
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (currentFragment.conversations != null && fetched_conversations != null) { if (currentFragment.conversations != null && fetched_conversations != null) {
int startId = 0; int startId = 0;
flagLoading = fetched_conversations.pagination.max_id == null;
//There are some statuses present in the timeline //There are some statuses present in the timeline
if (currentFragment.conversations.size() > 0) { if (currentFragment.conversations.size() > 0) {
startId = currentFragment.conversations.size(); startId = currentFragment.conversations.size();
@ -126,8 +126,6 @@ public class FragmentMastodonConversation extends Fragment {
currentFragment.conversations.addAll(fetched_conversations.conversations); currentFragment.conversations.addAll(fetched_conversations.conversations);
max_id = fetched_conversations.pagination.max_id; max_id = fetched_conversations.pagination.max_id;
conversationAdapter.notifyItemRangeInserted(startId, fetched_conversations.conversations.size()); conversationAdapter.notifyItemRangeInserted(startId, fetched_conversations.conversations.size());
} else {
flagLoading = true;
} }
}); });
} }

View file

@ -192,7 +192,7 @@ public class FragmentMastodonNotification extends Fragment {
binding.noAction.setVisibility(View.GONE); binding.noAction.setVisibility(View.GONE);
binding.recyclerView.setVisibility(View.VISIBLE); binding.recyclerView.setVisibility(View.VISIBLE);
} }
flagLoading = notifications.notifications.size() < MastodonHelper.notificationsPerCall(requireActivity()); flagLoading = notifications.pagination.max_id == null;
if (aggregateNotification) { if (aggregateNotification) {
notifications.notifications = aggregateNotifications(notifications.notifications); notifications.notifications = aggregateNotifications(notifications.notifications);
} }
@ -277,10 +277,9 @@ public class FragmentMastodonNotification extends Fragment {
* @param fetched_notifications Notifications * @param fetched_notifications Notifications
*/ */
private void dealWithPagination(Notifications fetched_notifications) { private void dealWithPagination(Notifications fetched_notifications) {
flagLoading = false;
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (currentFragment.notifications != null && fetched_notifications != null && fetched_notifications.notifications != null) { if (currentFragment.notifications != null && fetched_notifications != null && fetched_notifications.notifications != null) {
flagLoading = fetched_notifications.notifications.size() < MastodonHelper.notificationsPerCall(requireActivity()); flagLoading = fetched_notifications.pagination.max_id == null;
if (aggregateNotification) { if (aggregateNotification) {
fetched_notifications.notifications = aggregateNotifications(fetched_notifications.notifications); fetched_notifications.notifications = aggregateNotifications(fetched_notifications.notifications);
} }

View file

@ -288,7 +288,7 @@ public class FragmentMastodonTimeline extends Fragment {
statuses.statuses = mediaStatuses; statuses.statuses = mediaStatuses;
} }
} }
flagLoading = (statuses.statuses.size() < MastodonHelper.statusesPerCall(requireActivity())); flagLoading = statuses.pagination.max_id == null;
binding.recyclerView.setVisibility(View.VISIBLE); binding.recyclerView.setVisibility(View.VISIBLE);
if (statusAdapter != null && this.statuses != null) { if (statusAdapter != null && this.statuses != null) {
int size = this.statuses.size(); int size = this.statuses.size();
@ -362,14 +362,13 @@ public class FragmentMastodonTimeline extends Fragment {
* @param fetched_statuses Statuses * @param fetched_statuses Statuses
*/ */
private void dealWithPagination(Statuses fetched_statuses, DIRECTION direction) { private void dealWithPagination(Statuses fetched_statuses, DIRECTION direction) {
flagLoading = false;
if (binding == null) { if (binding == null) {
return; return;
} }
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
if (statuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) { if (statuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
flagLoading = (direction == DIRECTION.BOTTOM && fetched_statuses.statuses.size() < MastodonHelper.statusesPerCall(requireActivity())); flagLoading = fetched_statuses.pagination.max_id == null;
if (timelineType == Timeline.TimeLineEnum.ART) { if (timelineType == Timeline.TimeLineEnum.ART) {
//We have to split media in different statuses //We have to split media in different statuses
List<Status> mediaStatuses = new ArrayList<>(); List<Status> mediaStatuses = new ArrayList<>();

View file

@ -13,6 +13,7 @@
<RadioGroup <RadioGroup
android:id="@+id/location" android:id="@+id/location"
android:layout_marginTop="5dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
@ -40,6 +41,7 @@
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/location" android:text="@string/location"
android:textSize="16sp" /> android:textSize="16sp" />
@ -47,7 +49,7 @@
android:id="@+id/moderation" android:id="@+id/moderation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="5dp"
android:orientation="horizontal"> android:orientation="horizontal">
<RadioButton <RadioButton
@ -79,6 +81,7 @@
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/permissions" android:text="@string/permissions"
android:textSize="16sp" /> android:textSize="16sp" />
@ -86,7 +89,7 @@
android:id="@+id/permissions" android:id="@+id/permissions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="5dp"
android:orientation="horizontal"> android:orientation="horizontal">
<RadioButton <RadioButton
@ -107,13 +110,14 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/order_by" android:text="@string/order_by"
android:layout_marginTop="10dp"
android:textSize="16sp" /> android:textSize="16sp" />
<RadioGroup <RadioGroup
android:id="@+id/order_by" android:id="@+id/order_by"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="5dp"
android:orientation="horizontal"> android:orientation="horizontal">
<RadioButton <RadioButton

View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/status"
android:textSize="16sp" />
<RadioGroup
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/status_unresolved"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/unresolved" />
<RadioButton
android:id="@+id/status_resolved"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/resolved" />
</RadioGroup>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/origin_report"
android:textSize="16sp" />
<RadioGroup
android:id="@+id/origin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/origin_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/all" />
<RadioButton
android:id="@+id/origin_local"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/local" />
<RadioButton
android:id="@+id/origin_remote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/remote" />
</RadioGroup>
</LinearLayout>

View file

@ -1610,6 +1610,9 @@
<string name="domain">Domain</string> <string name="domain">Domain</string>
<string name="approved">Approved</string> <string name="approved">Approved</string>
<string name="approve">Approve</string> <string name="approve">Approve</string>
<string name="origin_report">Origin of reported account</string>
<string name="status">Status</string>
<string name="resolved">Resolved</string>
</resources> </resources>