From 7e4c04371185aa90e7e4f6d51805f80a3c71a8b4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 28 May 2022 18:02:30 +0200 Subject: [PATCH] Admin features + fix pagination with flag loading --- .../activities/AdminActionActivity.java | 231 ++++++---- .../activities/AdminReportActivity.java | 415 ++++++++++++++++++ .../activities/FollowRequestActivity.java | 3 +- .../activities/StatusInfoActivity.java | 2 +- .../fragment/admin/FragmentAdminAccount.java | 7 +- .../fragment/admin/FragmentAdminReport.java | 7 +- .../timeline/FragmentMastodonAccount.java | 6 +- .../FragmentMastodonConversation.java | 4 +- .../FragmentMastodonNotification.java | 5 +- .../timeline/FragmentMastodonTimeline.java | 5 +- .../layout/popup_admin_filter_accounts.xml | 10 +- .../res/layout/popup_admin_filter_reports.xml | 70 +++ app/src/main/res/values/strings.xml | 3 + 13 files changed, 654 insertions(+), 114 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java create mode 100644 app/src/main/res/layout/popup_admin_filter_reports.xml diff --git a/app/src/main/java/app/fedilab/android/activities/AdminActionActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminActionActivity.java index 1a14d8dd7..fa5304832 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminActionActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminActionActivity.java @@ -33,6 +33,7 @@ import com.google.gson.annotations.SerializedName; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityAdminActionsBinding; import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding; +import app.fedilab.android.databinding.PopupAdminFilterReportsBinding; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.ThemeHelper; 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 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 boolean canGoBack; private FragmentAdminReport fragmentAdminReport; @@ -108,7 +110,7 @@ public class AdminActionActivity extends BaseActivity { @Override 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); } return super.onCreateOptionsMenu(menu); @@ -120,97 +122,150 @@ public class AdminActionActivity extends BaseActivity { onBackPressed(); return true; } else if (item.getItemId() == R.id.action_filter) { - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle()); - PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater()); - alertDialogBuilder.setView(binding.getRoot()); - if (local != null && remote == null) { - binding.locationLocal.setChecked(true); - } else if (remote != null && local == null) { - binding.locationRemote.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; + if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle()); + PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater()); + alertDialogBuilder.setView(binding.getRoot()); + if (local != null && remote == null) { + binding.locationLocal.setChecked(true); + } else if (remote != null && local == null) { + binding.locationRemote.setChecked(true); + } else { + binding.locationAll.setChecked(true); } - }); - if (pending != null && suspended == null && active == null) { - binding.moderationPending.setChecked(true); - } else if (suspended != null && pending == null && active == null) { - binding.moderationSuspended.setChecked(true); - } else if (active != null && pending == null && suspended == null) { - binding.moderationActive.setChecked(true); - } else { - binding.moderationAll.setChecked(true); - } - binding.moderation.setOnCheckedChangeListener((group, checkedId) -> { - if (checkedId == R.id.moderation_all) { - active = true; - suspended = true; - pending = true; - } else if (checkedId == R.id.moderation_active) { - active = true; - suspended = null; - pending = null; - } else if (checkedId == R.id.moderation_suspended) { - active = null; - suspended = true; - pending = null; - } else if (checkedId == R.id.moderation_pending) { - active = null; - suspended = null; - pending = 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; + } + }); + if (pending != null && suspended == null && active == null) { + binding.moderationPending.setChecked(true); + } else if (suspended != null && pending == null && active == null) { + binding.moderationSuspended.setChecked(true); + } else if (active != null && pending == null && suspended == null) { + binding.moderationActive.setChecked(true); + } else { + binding.moderationAll.setChecked(true); } - }); - if (staff != null) { - binding.permissionsStaff.setChecked(true); - } else { - binding.permissionsAll.setChecked(true); - } - binding.permissions.setOnCheckedChangeListener((group, checkedId) -> { - if (checkedId == R.id.permissions_all) { - staff = null; - } else if (checkedId == R.id.permissions_staff) { - staff = true; + binding.moderation.setOnCheckedChangeListener((group, checkedId) -> { + if (checkedId == R.id.moderation_all) { + active = true; + suspended = true; + pending = true; + } else if (checkedId == R.id.moderation_active) { + active = true; + suspended = null; + pending = null; + } else if (checkedId == R.id.moderation_suspended) { + 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); } - }); - if (orderByMostRecent != null) { - binding.orderByMostRecent.setChecked(true); - } else { - binding.orderByLastActive.setChecked(true); - } - binding.orderBy.setOnCheckedChangeListener((group, checkedId) -> { - if (checkedId == R.id.order_by_most_recent) { - orderByMostRecent = true; - } else if (checkedId == R.id.order_by_last_active) { - orderByMostRecent = null; + binding.permissions.setOnCheckedChangeListener((group, checkedId) -> { + if (checkedId == R.id.permissions_all) { + staff = null; + } else if (checkedId == R.id.permissions_staff) { + staff = true; + } + }); + if (orderByMostRecent != null) { + binding.orderByMostRecent.setChecked(true); + } else { + binding.orderByLastActive.setChecked(true); } - }); - alertDialogBuilder.setPositiveButton(R.string.filter, (dialog, id) -> { - final FragmentTransaction ft1 = getSupportFragmentManager().beginTransaction(); - ft1.detach(fragmentAdminAccount); - ft1.commit(); - final FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction(); - ft2.attach(fragmentAdminAccount); - ft2.commit(); - dialog.dismiss(); - }); - alertDialogBuilder.setNegativeButton(R.string.reset, (dialog, id) -> { - binding.locationAll.callOnClick(); - binding.permissionsAll.callOnClick(); - binding.moderationAll.callOnClick(); - binding.orderByMostRecent.callOnClick(); - }); - AlertDialog alert = alertDialogBuilder.create(); - alert.show(); + binding.orderBy.setOnCheckedChangeListener((group, checkedId) -> { + if (checkedId == R.id.order_by_most_recent) { + orderByMostRecent = true; + } else if (checkedId == R.id.order_by_last_active) { + orderByMostRecent = null; + } + }); + alertDialogBuilder.setPositiveButton(R.string.filter, (dialog, id) -> { + final FragmentTransaction ft1 = getSupportFragmentManager().beginTransaction(); + ft1.detach(fragmentAdminAccount); + ft1.commit(); + final FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction(); + ft2.attach(fragmentAdminAccount); + ft2.commit(); + dialog.dismiss(); + }); + alertDialogBuilder.setNegativeButton(R.string.reset, (dialog, id) -> { + 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); } diff --git a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java new file mode 100644 index 000000000..b32e6c1df --- /dev/null +++ b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java @@ -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 . */ + + +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 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() { + @Override + public void onResourceReady(@NonNull final Drawable resource, Transition 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 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 + } + + +} diff --git a/app/src/main/java/app/fedilab/android/activities/FollowRequestActivity.java b/app/src/main/java/app/fedilab/android/activities/FollowRequestActivity.java index f869073ad..54414374b 100644 --- a/app/src/main/java/app/fedilab/android/activities/FollowRequestActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/FollowRequestActivity.java @@ -95,7 +95,7 @@ public class FollowRequestActivity extends BaseActivity { } private void manageView(Accounts accounts) { - flagLoading = false; + binding.loadingNextAccounts.setVisibility(View.GONE); if (accountList != null && accounts != null && accounts.accounts != null && accounts.accounts.size() > 0) { int startId = 0; @@ -103,6 +103,7 @@ public class FollowRequestActivity extends BaseActivity { if (accountList.size() > 0) { startId = accountList.size(); } + flagLoading = accounts.pagination.max_id == null; accountList.addAll(accounts.accounts); max_id = accounts.pagination.max_id; accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size()); diff --git a/app/src/main/java/app/fedilab/android/activities/StatusInfoActivity.java b/app/src/main/java/app/fedilab/android/activities/StatusInfoActivity.java index 9ccbcb548..45003cdec 100644 --- a/app/src/main/java/app/fedilab/android/activities/StatusInfoActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/StatusInfoActivity.java @@ -113,7 +113,6 @@ public class StatusInfoActivity extends BaseActivity { } private void manageView(Accounts accounts) { - flagLoading = false; binding.loadingNextAccounts.setVisibility(View.GONE); if (accountList != null && accounts != null && accounts.accounts != null) { int startId = 0; @@ -123,6 +122,7 @@ public class StatusInfoActivity extends BaseActivity { } accountList.addAll(accounts.accounts); max_id = accounts.pagination.max_id; + flagLoading = accounts.pagination.max_id == null; accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size()); } } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java index 230c8c362..e199a0882 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java @@ -135,7 +135,7 @@ public class FragmentAdminAccount extends Fragment { this.adminAccounts = adminAccounts.adminAccounts; adminAccountAdapter = new AdminAccountAdapter(this.adminAccounts); - flagLoading = (adminAccounts.adminAccounts.size() < MastodonHelper.accountsPerCall(requireActivity())); + flagLoading = adminAccounts.pagination.max_id == null; LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setAdapter(adminAccountAdapter); @@ -177,13 +177,12 @@ public class FragmentAdminAccount extends Fragment { * @param adminAccounts AdminAccounts */ private void dealWithPagination(AdminAccounts adminAccounts) { - flagLoading = false; if (binding == null) { return; } binding.loadingNextElements.setVisibility(View.GONE); 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; //There are some statuses present in the timeline if (this.adminAccounts.size() > 0) { @@ -195,8 +194,6 @@ public class FragmentAdminAccount extends Fragment { max_id = adminAccounts.pagination.max_id; } adminAccountAdapter.notifyItemRangeInserted(startId, adminAccounts.adminAccounts.size()); - } else { - flagLoading = true; } } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java index 299725cd0..9bd9fd2eb 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java @@ -36,7 +36,6 @@ import app.fedilab.android.client.entities.api.AdminReport; import app.fedilab.android.client.entities.api.AdminReports; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.AdminVM; @@ -117,7 +116,7 @@ public class FragmentAdminReport extends Fragment { binding.noAction.setVisibility(View.VISIBLE); return; } - flagLoading = (adminReports.adminReports.size() < MastodonHelper.statusesPerCall(requireActivity())); + flagLoading = adminReports.pagination.max_id == null; binding.recyclerView.setVisibility(View.VISIBLE); if (statusAdapter != null && this.adminReports != null) { int size = this.adminReports.size(); @@ -179,13 +178,13 @@ public class FragmentAdminReport extends Fragment { * @param admReports AdminReports */ private void dealWithPagination(AdminReports admReports) { - flagLoading = false; + if (binding == null) { return; } binding.loadingNextElements.setVisibility(View.GONE); 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 int startId = adminReports.size(); adminReports.addAll(admReports.adminReports); diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonAccount.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonAccount.java index 9f57c2f24..369c73fb6 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonAccount.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonAccount.java @@ -198,7 +198,7 @@ public class FragmentMastodonAccount extends Fragment { this.accounts = accounts.accounts; accountAdapter = new AccountAdapter(this.accounts); - flagLoading = (accounts.accounts.size() < MastodonHelper.accountsPerCall(requireActivity())); + flagLoading = accounts.pagination.max_id == null; LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setAdapter(accountAdapter); @@ -240,13 +240,13 @@ public class FragmentMastodonAccount extends Fragment { * @param fetched_accounts Accounts */ private void dealWithPagination(Accounts fetched_accounts) { - flagLoading = false; + if (binding == null) { return; } binding.loadingNextElements.setVisibility(View.GONE); 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; //There are some statuses present in the timeline if (accounts.size() > 0) { diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java index bc036bc28..b7613ccd8 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java @@ -115,10 +115,10 @@ public class FragmentMastodonConversation extends Fragment { binding.loadingNextElements.setVisibility(View.VISIBLE); timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(FragmentMastodonConversation.this, fetched_conversations -> { - flagLoading = false; binding.loadingNextElements.setVisibility(View.GONE); if (currentFragment.conversations != null && fetched_conversations != null) { int startId = 0; + flagLoading = fetched_conversations.pagination.max_id == null; //There are some statuses present in the timeline if (currentFragment.conversations.size() > 0) { startId = currentFragment.conversations.size(); @@ -126,8 +126,6 @@ public class FragmentMastodonConversation extends Fragment { currentFragment.conversations.addAll(fetched_conversations.conversations); max_id = fetched_conversations.pagination.max_id; conversationAdapter.notifyItemRangeInserted(startId, fetched_conversations.conversations.size()); - } else { - flagLoading = true; } }); } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java index f91c3663d..55bc6dda0 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java @@ -192,7 +192,7 @@ public class FragmentMastodonNotification extends Fragment { binding.noAction.setVisibility(View.GONE); binding.recyclerView.setVisibility(View.VISIBLE); } - flagLoading = notifications.notifications.size() < MastodonHelper.notificationsPerCall(requireActivity()); + flagLoading = notifications.pagination.max_id == null; if (aggregateNotification) { notifications.notifications = aggregateNotifications(notifications.notifications); } @@ -277,10 +277,9 @@ public class FragmentMastodonNotification extends Fragment { * @param fetched_notifications Notifications */ private void dealWithPagination(Notifications fetched_notifications) { - flagLoading = false; binding.loadingNextElements.setVisibility(View.GONE); 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) { fetched_notifications.notifications = aggregateNotifications(fetched_notifications.notifications); } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index d86041143..fe44a914e 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -288,7 +288,7 @@ public class FragmentMastodonTimeline extends Fragment { statuses.statuses = mediaStatuses; } } - flagLoading = (statuses.statuses.size() < MastodonHelper.statusesPerCall(requireActivity())); + flagLoading = statuses.pagination.max_id == null; binding.recyclerView.setVisibility(View.VISIBLE); if (statusAdapter != null && this.statuses != null) { int size = this.statuses.size(); @@ -362,14 +362,13 @@ public class FragmentMastodonTimeline extends Fragment { * @param fetched_statuses Statuses */ private void dealWithPagination(Statuses fetched_statuses, DIRECTION direction) { - flagLoading = false; if (binding == null) { return; } binding.loadingNextElements.setVisibility(View.GONE); 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) { //We have to split media in different statuses List mediaStatuses = new ArrayList<>(); diff --git a/app/src/main/res/layout/popup_admin_filter_accounts.xml b/app/src/main/res/layout/popup_admin_filter_accounts.xml index 4101693d6..68b2ab9b1 100644 --- a/app/src/main/res/layout/popup_admin_filter_accounts.xml +++ b/app/src/main/res/layout/popup_admin_filter_accounts.xml @@ -13,6 +13,7 @@ @@ -40,6 +41,7 @@ @@ -47,7 +49,7 @@ android:id="@+id/moderation" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" + android:layout_marginTop="5dp" android:orientation="horizontal"> @@ -86,7 +89,7 @@ android:id="@+id/permissions" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" + android:layout_marginTop="5dp" android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 396d364aa..935512306 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1610,6 +1610,9 @@ Domain Approved Approve + Origin of reported account + Status + Resolved