From 26e6fab5d989369015c7fcce996b4a149885f290 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 28 May 2022 09:31:19 +0200 Subject: [PATCH] Moderation - Display accounts and filter them --- app/src/main/AndroidManifest.xml | 5 + .../app/fedilab/android/BaseMainActivity.java | 7 + .../activities/AdminActionActivity.java | 256 ++++++++++++++++++ .../android/activities/SettingsActivity.java | 7 - .../endpoints/MastodonAdminService.java | 4 +- .../client/entities/api/AdminAccount.java | 5 +- .../client/entities/api/AdminAccounts.java | 24 ++ .../client/entities/api/AdminReport.java | 23 +- .../client/entities/api/AdminReports.java | 24 ++ .../android/helper/RecyclerViewThreadLines.kt | 4 +- .../ui/drawer/AdminAccountAdapter.java | 97 +++++++ .../fragment/admin/FragmentAdminAccount.java | 216 +++++++++++++++ .../fragment/admin/FragmentAdminReport.java | 227 ++++++++++++++++ .../FragmentAdministrationSettings.java | 63 ----- .../timeline/FragmentMastodonContext.java | 10 +- .../android/viewmodel/mastodon/AdminVM.java | 81 +++--- .../ic_baseline_admin_panel_settings_24.xml | 13 + .../res/layout/activity_admin_actions.xml | 52 ++++ .../main/res/layout/drawer_admin_account.xml | 139 ++++++++++ .../main/res/layout/drawer_admin_report.xml | 55 ++++ .../layout/drawer_admin_report_account.xml | 51 ++++ .../layout/popup_admin_filter_accounts.xml | 132 +++++++++ .../main/res/menu/activity_main_drawer.xml | 5 + app/src/main/res/menu/menu_admin_account.xml | 9 + app/src/main/res/values/strings.xml | 5 + app/src/main/res/xml/pref_administration.xml | 7 - 26 files changed, 1385 insertions(+), 136 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/activities/AdminActionActivity.java create mode 100644 app/src/main/java/app/fedilab/android/client/entities/api/AdminAccounts.java create mode 100644 app/src/main/java/app/fedilab/android/client/entities/api/AdminReports.java create mode 100644 app/src/main/java/app/fedilab/android/ui/drawer/AdminAccountAdapter.java create mode 100644 app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java create mode 100644 app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java delete mode 100644 app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentAdministrationSettings.java create mode 100644 app/src/main/res/drawable/ic_baseline_admin_panel_settings_24.xml create mode 100644 app/src/main/res/layout/activity_admin_actions.xml create mode 100644 app/src/main/res/layout/drawer_admin_account.xml create mode 100644 app/src/main/res/layout/drawer_admin_report.xml create mode 100644 app/src/main/res/layout/drawer_admin_report_account.xml create mode 100644 app/src/main/res/layout/popup_admin_filter_accounts.xml create mode 100644 app/src/main/res/menu/menu_admin_account.xml delete mode 100644 app/src/main/res/xml/pref_administration.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9299f85f5..3b4ec64f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -100,6 +100,11 @@ android:configChanges="keyboardHidden|orientation|screenSize" android:label="@string/interactions" android:theme="@style/AppThemeBar" /> + . */ + +import static app.fedilab.android.activities.AdminActionActivity.AdminEnum.REPORT; + +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +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.helper.Helper; +import app.fedilab.android.helper.ThemeHelper; +import app.fedilab.android.ui.fragment.admin.FragmentAdminAccount; +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; + private ActivityAdminActionsBinding binding; + private boolean canGoBack; + private FragmentAdminReport fragmentAdminReport; + private FragmentAdminAccount fragmentAdminAccount; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ThemeHelper.applyThemeBar(this); + binding = ActivityAdminActionsBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary))); + } + canGoBack = false; + binding.reports.setOnClickListener(v -> displayTimeline(REPORT)); + binding.accounts.setOnClickListener(v -> displayTimeline(AdminEnum.ACCOUNT)); + } + + private void displayTimeline(AdminEnum type) { + canGoBack = true; + if (type == REPORT) { + + ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { + fragmentAdminReport = new FragmentAdminReport(); + Bundle bundle = new Bundle(); + bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); + bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + type.getValue()); + fragmentAdminReport.setArguments(bundle); + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = + fragmentManager.beginTransaction(); + fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport); + fragmentTransaction.commit(); + }); + + } else { + + ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { + fragmentAdminAccount = new FragmentAdminAccount(); + Bundle bundle = new Bundle(); + bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); + bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + type.getValue()); + fragmentAdminAccount.setArguments(bundle); + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = + fragmentManager.beginTransaction(); + fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount); + fragmentTransaction.commit(); + }); + + } + switch (type) { + case REPORT: + setTitle(R.string.reports); + break; + case ACCOUNT: + setTitle(R.string.accounts); + break; + } + invalidateOptionsMenu(); + } + + @Override + public boolean onCreateOptionsMenu(@NonNull Menu menu) { + if (canGoBack && getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) { + getMenuInflater().inflate(R.menu.menu_admin_account, menu); + } + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + 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 (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; + } + }); + 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; + } + }); + 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; + } + }); + 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(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + if (canGoBack) { + canGoBack = false; + ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> { + if (fragmentAdminReport != null) { + fragmentAdminReport.onDestroyView(); + } + if (fragmentAdminAccount != null) { + fragmentAdminAccount.onDestroyView(); + } + setTitle(R.string.administration); + invalidateOptionsMenu(); + }); + } else { + super.onBackPressed(); + } + + } + + public enum AdminEnum { + @SerializedName("REPORT") + REPORT("REPORT"), + @SerializedName("ACCOUNT") + ACCOUNT("ACCOUNT"); + + private final String value; + + AdminEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + +} diff --git a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java index 8946d0a23..af9f268ab 100644 --- a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java @@ -32,7 +32,6 @@ import java.util.Locale; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivitySettingsBinding; import app.fedilab.android.helper.ThemeHelper; -import app.fedilab.android.ui.fragment.settings.FragmentAdministrationSettings; import app.fedilab.android.ui.fragment.settings.FragmentComposeSettings; import app.fedilab.android.ui.fragment.settings.FragmentInterfaceSettings; import app.fedilab.android.ui.fragment.settings.FragmentLanguageSettings; @@ -120,12 +119,6 @@ public class SettingsActivity extends BaseActivity { currentFragment = fragmentThemingSettings; category = getString(R.string.theming); break; - case ADMINISTRATION: - FragmentAdministrationSettings fragmentAdministrationSettings = new FragmentAdministrationSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentAdministrationSettings); - currentFragment = fragmentAdministrationSettings; - category = getString(R.string.administration); - break; case LANGUAGE: FragmentLanguageSettings fragmentLanguageSettings = new FragmentLanguageSettings(); fragmentTransaction.replace(R.id.fragment_container, fragmentLanguageSettings); diff --git a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonAdminService.java b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonAdminService.java index 11a3efc26..746462db0 100644 --- a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonAdminService.java +++ b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonAdminService.java @@ -109,7 +109,9 @@ public interface MastodonAdminService { @Header("Authorization") String token, @Field("resolved") Boolean resolved, @Field("account_id") String account_id, - @Field("target_account_id") String target_account_id + @Field("target_account_id") String target_account_id, + @Field("max_id") String max_id, + @Field("limit") int limit ); @FormUrlEncoded diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccount.java b/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccount.java index 8faf845d7..0dcaf923e 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccount.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccount.java @@ -16,10 +16,11 @@ package app.fedilab.android.client.entities.api; import com.google.gson.annotations.SerializedName; +import java.io.Serializable; import java.util.Date; import java.util.List; -public class AdminAccount { +public class AdminAccount implements Serializable { @SerializedName("id") public String id; @@ -59,7 +60,7 @@ public class AdminAccount { public String invited_by_account_id; - public final class IP { + public static class IP implements Serializable { @SerializedName("ip") public String ip; @SerializedName("used_at") diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccounts.java b/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccounts.java new file mode 100644 index 000000000..eed98dcdb --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/entities/api/AdminAccounts.java @@ -0,0 +1,24 @@ +package app.fedilab.android.client.entities.api; +/* Copyright 2021 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 java.util.List; + +public class AdminAccounts { + + public Pagination pagination = new Pagination(); + public List adminAccounts; +} diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/AdminReport.java b/app/src/main/java/app/fedilab/android/client/entities/api/AdminReport.java index 34502cc23..3ecc41340 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/AdminReport.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/AdminReport.java @@ -16,29 +16,34 @@ package app.fedilab.android.client.entities.api; import com.google.gson.annotations.SerializedName; +import java.io.Serializable; import java.util.Date; import java.util.List; -public class AdminReport { +public class AdminReport implements Serializable { @SerializedName("id") public String id; + @SerializedName("account") + public Account account; @SerializedName("action_taken") public String action_taken; + @SerializedName("action_taken_by_account") + public String action_taken_by_account; + @SerializedName("assigned_account") + public Account assigned_account; + @SerializedName("category") + public String category; @SerializedName("comment") public String comment; @SerializedName("created_at") public Date created_at; - @SerializedName("updated_at") - public Date updated_at; - @SerializedName("account") - public Account account; @SerializedName("target_account") public Account target_account; - @SerializedName("assigned_account") - public Account assigned_account; - @SerializedName("action_taken_by_account") - public String action_taken_by_account; @SerializedName("statuses") public List statuses; + @SerializedName("rules") + public List rules; + @SerializedName("updated_at") + public Date updated_at; } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/AdminReports.java b/app/src/main/java/app/fedilab/android/client/entities/api/AdminReports.java new file mode 100644 index 000000000..e7674d3ae --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/entities/api/AdminReports.java @@ -0,0 +1,24 @@ +package app.fedilab.android.client.entities.api; +/* Copyright 2021 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 java.util.List; + +public class AdminReports { + + public Pagination pagination = new Pagination(); + public List adminReports; +} diff --git a/app/src/main/java/app/fedilab/android/helper/RecyclerViewThreadLines.kt b/app/src/main/java/app/fedilab/android/helper/RecyclerViewThreadLines.kt index f8e258e58..bfad6bf8c 100644 --- a/app/src/main/java/app/fedilab/android/helper/RecyclerViewThreadLines.kt +++ b/app/src/main/java/app/fedilab/android/helper/RecyclerViewThreadLines.kt @@ -43,7 +43,7 @@ class RecyclerViewThreadLines(context: Context, private val lineInfoList: List= lineInfoList.size) return val level = lineInfoList[position].level val startMargin = margin * level + margin * fontScale if (parent.layoutDirection == View.LAYOUT_DIRECTION_LTR) outRect.left = startMargin else outRect.right = startMargin @@ -54,7 +54,7 @@ class RecyclerViewThreadLines(context: Context, private val lineInfoList: List= lineInfoList.size) return val lineInfo = lineInfoList[position] val level = lineInfo.level diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AdminAccountAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AdminAccountAdapter.java new file mode 100644 index 000000000..55ed0b514 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AdminAccountAdapter.java @@ -0,0 +1,97 @@ +package app.fedilab.android.ui.drawer; +/* 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.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; +import java.util.Locale; + +import app.fedilab.android.client.entities.api.AdminAccount; +import app.fedilab.android.databinding.DrawerAdminAccountBinding; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.helper.MastodonHelper; + + +public class AdminAccountAdapter extends RecyclerView.Adapter { + + private final List adminAccountList; + + public AdminAccountAdapter(List adminAccountList) { + this.adminAccountList = adminAccountList; + } + + public int getCount() { + return adminAccountList.size(); + } + + public AdminAccount getItem(int position) { + return adminAccountList.get(position); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + DrawerAdminAccountBinding itemBinding = DrawerAdminAccountBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new AccountAdminViewHolder(itemBinding); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + AdminAccount adminAccount = adminAccountList.get(position); + AccountAdminViewHolder holder = (AccountAdminViewHolder) viewHolder; + MastodonHelper.loadPPMastodon(holder.binding.pp, adminAccount.account); + holder.binding.username.setText(adminAccount.account.display_name); + holder.binding.acct.setText(String.format(Locale.getDefault(), "@%s", adminAccount.account.acct)); + holder.binding.postCount.setText(String.valueOf(adminAccount.account.statuses_count)); + holder.binding.followersCount.setText(String.valueOf(adminAccount.account.followers_count)); + holder.binding.email.setText(adminAccount.email); + if (adminAccount.ip != null) { + holder.binding.lastActive.setText(Helper.shortDateToString(adminAccount.ip.used_at)); + holder.binding.ip.setText(adminAccount.ip.ip); + } else if (adminAccount.ips != null && adminAccount.ips.size() > 0) { + holder.binding.lastActive.setText(Helper.shortDateToString(adminAccount.ips.get(0).used_at)); + holder.binding.ip.setText(adminAccount.ips.get(0).ip); + } else { + holder.binding.lastActive.setText(Helper.shortDateToString(adminAccount.created_at)); + } + + } + + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return adminAccountList.size(); + } + + + public static class AccountAdminViewHolder extends RecyclerView.ViewHolder { + DrawerAdminAccountBinding binding; + + AccountAdminViewHolder(DrawerAdminAccountBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..230c8c362 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminAccount.java @@ -0,0 +1,216 @@ +package app.fedilab.android.ui.fragment.admin; +/* 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.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.R; +import app.fedilab.android.activities.AdminActionActivity; +import app.fedilab.android.client.entities.api.AdminAccount; +import app.fedilab.android.client.entities.api.AdminAccounts; +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.AdminAccountAdapter; +import app.fedilab.android.viewmodel.mastodon.AdminVM; + + +public class FragmentAdminAccount extends Fragment { + + + String byDomain, username, displayName, email, ip; + private FragmentPaginationBinding binding; + private AdminVM adminVM; + private boolean flagLoading; + private List adminAccounts; + private String max_id; + private AdminAccountAdapter adminAccountAdapter; + private String viewModelKey; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + if (getArguments() != null) { + viewModelKey = getArguments().getString(Helper.ARG_VIEW_MODEL_KEY, ""); + } + flagLoading = false; + binding = FragmentPaginationBinding.inflate(inflater, container, false); + binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity())); + return binding.getRoot(); + } + + private void fetchAccount(Callback callback) { + adminVM.getAccounts( + BaseMainActivity.currentInstance, BaseMainActivity.currentToken, + AdminActionActivity.local, + AdminActionActivity.remote, + byDomain, + AdminActionActivity.active, + AdminActionActivity.pending, + AdminActionActivity.disabled, + AdminActionActivity.silenced, + AdminActionActivity.suspended, + username, displayName, email, ip, + AdminActionActivity.staff, max_id, null, + MastodonHelper.statusesPerCall(requireActivity())) + .observe(requireActivity(), callback::accountFetched); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + int c1 = getResources().getColor(R.color.cyanea_accent_reference); + binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.cyanea_primary_reference)); + binding.swipeContainer.setColorSchemeColors( + c1, c1, c1 + ); + binding.loader.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + adminVM = new ViewModelProvider(FragmentAdminAccount.this).get(viewModelKey, AdminVM.class); + max_id = null; + fetchAccount(this::initializeAccountCommonView); + } + + public void scrollToTop() { + binding.recyclerView.setAdapter(adminAccountAdapter); + } + + /** + * Intialize the view for accounts + * + * @param adminAccounts {@link AdminAccounts} + */ + private void initializeAccountCommonView(final AdminAccounts adminAccounts) { + if (binding == null) { + return; + } + binding.loader.setVisibility(View.GONE); + binding.noAction.setVisibility(View.GONE); + binding.swipeContainer.setRefreshing(false); + binding.swipeContainer.setOnRefreshListener(() -> { + binding.swipeContainer.setRefreshing(true); + flagLoading = false; + max_id = null; + fetchAccount(this::initializeAccountCommonView); + }); + if (adminAccounts == null || adminAccounts.adminAccounts == null || adminAccounts.adminAccounts.size() == 0) { + binding.noAction.setVisibility(View.VISIBLE); + binding.noActionText.setText(R.string.no_accounts); + return; + } + binding.recyclerView.setVisibility(View.VISIBLE); + if (adminAccountAdapter != null && this.adminAccounts != null) { + int size = this.adminAccounts.size(); + this.adminAccounts.clear(); + this.adminAccounts = new ArrayList<>(); + adminAccountAdapter.notifyItemRangeRemoved(0, size); + } + + this.adminAccounts = adminAccounts.adminAccounts; + adminAccountAdapter = new AdminAccountAdapter(this.adminAccounts); + flagLoading = (adminAccounts.adminAccounts.size() < MastodonHelper.accountsPerCall(requireActivity())); + LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(mLayoutManager); + binding.recyclerView.setAdapter(adminAccountAdapter); + //Fetch the relationship + if (max_id == null || (adminAccounts.pagination.max_id != null && adminAccounts.pagination.max_id.compareTo(max_id) < 0)) { + max_id = adminAccounts.pagination.max_id; + } + binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (requireActivity() instanceof BaseMainActivity) { + if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility()) + ((BaseMainActivity) requireActivity()).manageFloatingButton(true); + if (dy > 0 && ((BaseMainActivity) requireActivity()).getFloatingVisibility()) + ((BaseMainActivity) requireActivity()).manageFloatingButton(false); + } + int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); + if (dy > 0) { + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + if (firstVisibleItem + visibleItemCount == totalItemCount) { + if (!flagLoading) { + flagLoading = true; + binding.loadingNextElements.setVisibility(View.VISIBLE); + fetchAccount(adminAccounts1 -> dealWithPagination(adminAccounts1)); + } + } else { + binding.loadingNextElements.setVisibility(View.GONE); + } + } + + } + }); + } + + /** + * Update view and pagination when scrolling down + * + * @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())); + int startId = 0; + //There are some statuses present in the timeline + if (this.adminAccounts.size() > 0) { + startId = this.adminAccounts.size(); + } + int position = this.adminAccounts.size(); + this.adminAccounts.addAll(adminAccounts.adminAccounts); + if (max_id == null || (adminAccounts.pagination.max_id != null && adminAccounts.pagination.max_id.compareTo(max_id) < 0)) { + max_id = adminAccounts.pagination.max_id; + } + adminAccountAdapter.notifyItemRangeInserted(startId, adminAccounts.adminAccounts.size()); + } else { + flagLoading = true; + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (binding != null) { + binding.recyclerView.setAdapter(null); + } + adminAccountAdapter = null; + binding = null; + } + + interface Callback { + void accountFetched(AdminAccounts adminAccounts); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..299725cd0 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/ui/fragment/admin/FragmentAdminReport.java @@ -0,0 +1,227 @@ +package app.fedilab.android.ui.fragment.admin; +/* 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.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.R; +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; + + +public class FragmentAdminReport extends Fragment { + + + private FragmentPaginationBinding binding; + private AdminVM adminVM; + private boolean flagLoading; + private List adminReports; + private String max_id, min_id; + private StatusAdapter statusAdapter; + private LinearLayoutManager mLayoutManager; + private String viewModelKey; + + public void scrollToTop() { + if (binding != null) { + binding.recyclerView.scrollToPosition(0); + } + } + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + + if (getArguments() != null) { + viewModelKey = getArguments().getString(Helper.ARG_VIEW_MODEL_KEY, ""); + } + + binding = FragmentPaginationBinding.inflate(inflater, container, false); + binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity())); + + int c1 = getResources().getColor(R.color.cyanea_accent_reference); + binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.cyanea_primary_reference)); + binding.swipeContainer.setColorSchemeColors( + c1, c1, c1 + ); + + adminVM = new ViewModelProvider(FragmentAdminReport.this).get(viewModelKey, AdminVM.class); + + binding.loader.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + flagLoading = false; + adminVM.getReports( + BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, null) + .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + /** + * Intialize the common view for statuses on different timelines + * + * @param adminReports {@link AdminReports} + */ + private void initializeStatusesCommonView(final AdminReports adminReports) { + if (binding == null) { + return; + } + binding.loader.setVisibility(View.GONE); + binding.noAction.setVisibility(View.GONE); + binding.swipeContainer.setRefreshing(false); + binding.swipeContainer.setOnRefreshListener(() -> { + binding.swipeContainer.setRefreshing(true); + max_id = null; + flagLoading = false; + adminVM.getReports( + BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, null) + .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); + }); + + if (adminReports == null || adminReports.adminReports == null || adminReports.adminReports.size() == 0) { + binding.noAction.setVisibility(View.VISIBLE); + return; + } + flagLoading = (adminReports.adminReports.size() < MastodonHelper.statusesPerCall(requireActivity())); + binding.recyclerView.setVisibility(View.VISIBLE); + if (statusAdapter != null && this.adminReports != null) { + int size = this.adminReports.size(); + this.adminReports.clear(); + this.adminReports = new ArrayList<>(); + statusAdapter.notifyItemRangeRemoved(0, size); + } + if (this.adminReports == null) { + this.adminReports = new ArrayList<>(); + } + + this.adminReports.addAll(adminReports.adminReports); + + if (max_id == null || (adminReports.pagination.max_id != null && adminReports.pagination.max_id.compareTo(max_id) < 0)) { + max_id = adminReports.pagination.max_id; + } + if (min_id == null || (adminReports.pagination.max_id != null && adminReports.pagination.min_id.compareTo(min_id) > 0)) { + min_id = adminReports.pagination.min_id; + } + + // statusAdapter = new StatusAdapter(this.statuses, timelineType, minified); + + mLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(mLayoutManager); + binding.recyclerView.setAdapter(statusAdapter); + + binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (requireActivity() instanceof BaseMainActivity) { + if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility()) + ((BaseMainActivity) requireActivity()).manageFloatingButton(true); + if (dy > 0 && ((BaseMainActivity) requireActivity()).getFloatingVisibility()) + ((BaseMainActivity) requireActivity()).manageFloatingButton(false); + } + int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); + if (dy > 0) { + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + if (firstVisibleItem + visibleItemCount == totalItemCount) { + if (!flagLoading) { + flagLoading = true; + binding.loadingNextElements.setVisibility(View.VISIBLE); + adminVM.getReports( + BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, max_id) + .observe(getViewLifecycleOwner(), adminReports1 -> dealWithPagination(adminReports1)); + } + } else { + binding.loadingNextElements.setVisibility(View.GONE); + } + } + } + }); + } + + /** + * Update view and pagination when scrolling down + * + * @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())); + //There are some adminReports present in the timeline + int startId = adminReports.size(); + adminReports.addAll(admReports.adminReports); + statusAdapter.notifyItemRangeInserted(startId, admReports.adminReports.size()); + if (max_id == null || (admReports.pagination.max_id != null && admReports.pagination.max_id.compareTo(max_id) < 0)) { + max_id = admReports.pagination.max_id; + } + if (min_id == null || (admReports.pagination.min_id != null && admReports.pagination.min_id.compareTo(min_id) > 0)) { + min_id = admReports.pagination.min_id; + } + } + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onDestroyView() { + if (binding != null) { + binding.recyclerView.setAdapter(null); + } + statusAdapter = null; + binding = null; + super.onDestroyView(); + } + + + /** + * Refresh status in list + */ + public void refreshAllAdapters() { + if (statusAdapter != null && adminReports != null) { + statusAdapter.notifyItemRangeChanged(0, adminReports.size()); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentAdministrationSettings.java b/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentAdministrationSettings.java deleted file mode 100644 index 1a51d9492..000000000 --- a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentAdministrationSettings.java +++ /dev/null @@ -1,63 +0,0 @@ -package app.fedilab.android.ui.fragment.settings; -/* 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.SharedPreferences; -import android.os.Bundle; - -import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.PreferenceManager; - -import app.fedilab.android.R; - -public class FragmentAdministrationSettings extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.pref_administration); - createPref(); - } - - private void createPref() { - - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (getActivity() != null) { - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()); - SharedPreferences.Editor editor = sharedpreferences.edit(); - - editor.apply(); - } - } - - @Override - public void onResume() { - super.onResume(); - - getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onPause() { - super.onPause(); - getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); - } - - -} diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index 03e8c033e..5eebb5690 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -16,7 +16,6 @@ package app.fedilab.android.ui.fragment.timeline; import static app.fedilab.android.activities.ContextActivity.displayCW; import static app.fedilab.android.activities.ContextActivity.expand; -import static app.fedilab.android.helper.RecyclerViewThreadLinesKt.getThreadDecorationInfo; import android.content.BroadcastReceiver; import android.content.Intent; @@ -44,9 +43,8 @@ import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; +import app.fedilab.android.helper.DividerDecoration; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.RecyclerViewThreadLines; -import app.fedilab.android.helper.RecyclerViewThreadLines.LineInfo; import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; @@ -60,7 +58,6 @@ public class FragmentMastodonContext extends Fragment { private StatusesVM statusesVM; private List statuses; private StatusAdapter statusAdapter; - private RecyclerViewThreadLines recyclerViewThreadLines; //Handle actions that can be done in other fragments private final BroadcastReceiver receive_action = new BroadcastReceiver() { @Override @@ -252,9 +249,7 @@ public class FragmentMastodonContext extends Fragment { binding.recyclerView.removeItemDecorationAt(i); } } - List threadDecorationInfo = getThreadDecorationInfo(context); - recyclerViewThreadLines = new RecyclerViewThreadLines(requireContext(), threadDecorationInfo); - binding.recyclerView.addItemDecoration(recyclerViewThreadLines); + binding.recyclerView.addItemDecoration(new DividerDecoration(requireActivity(), statuses)); binding.swipeContainer.setRefreshing(false); binding.recyclerView.scrollToPosition(statusPosition); } @@ -262,7 +257,6 @@ public class FragmentMastodonContext extends Fragment { @Override public void onDestroyView() { binding.recyclerView.setAdapter(null); - binding.recyclerView.removeItemDecoration(recyclerViewThreadLines); statusAdapter = null; binding = null; LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AdminVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AdminVM.java index ba145b856..a3bb52a3d 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AdminVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AdminVM.java @@ -29,8 +29,11 @@ import java.util.concurrent.TimeUnit; import app.fedilab.android.client.endpoints.MastodonAdminService; import app.fedilab.android.client.entities.api.AdminAccount; +import app.fedilab.android.client.entities.api.AdminAccounts; import app.fedilab.android.client.entities.api.AdminReport; +import app.fedilab.android.client.entities.api.AdminReports; import app.fedilab.android.helper.Helper; +import app.fedilab.android.helper.MastodonHelper; import okhttp3.OkHttpClient; import retrofit2.Call; import retrofit2.Response; @@ -46,9 +49,9 @@ public class AdminVM extends AndroidViewModel { .proxy(Helper.getProxy(getApplication().getApplicationContext())) .build(); private MutableLiveData adminAccountMutableLiveData; - private MutableLiveData> adminAccountListMutableLiveData; + private MutableLiveData adminAccountsListMutableLiveData; private MutableLiveData adminReportMutableLiveData; - private MutableLiveData> adminReportListMutableLiveData; + private MutableLiveData adminReporstListMutableLiveData; public AdminVM(@NonNull Application application) { super(application); @@ -83,47 +86,48 @@ public class AdminVM extends AndroidViewModel { * @param staff Filter for staff accounts? * @return {@link LiveData} containing a {@link List} of {@link AdminAccount}s */ - public LiveData> getAccounts(@NonNull String instance, - String token, - Boolean local, - Boolean remote, - String byDomain, - Boolean active, - Boolean pending, - Boolean disabled, - Boolean silenced, - Boolean suspended, - String username, - String displayName, - String email, - String ip, - Boolean staff, - String maxId, - String sinceId, - Integer limit) { + public LiveData getAccounts(@NonNull String instance, + String token, + Boolean local, + Boolean remote, + String byDomain, + Boolean active, + Boolean pending, + Boolean disabled, + Boolean silenced, + Boolean suspended, + String username, + String displayName, + String email, + String ip, + Boolean staff, + String maxId, + String sinceId, + Integer limit) { MastodonAdminService mastodonAdminService = init(instance); - adminAccountListMutableLiveData = new MutableLiveData<>(); + adminAccountsListMutableLiveData = new MutableLiveData<>(); new Thread(() -> { - List adminAccountList = null; Call> getAccountsCall = mastodonAdminService.getAccounts( token, local, remote, byDomain, active, pending, disabled, silenced, suspended, username, displayName, email, ip, staff, maxId, sinceId, limit); + AdminAccounts adminAccounts = new AdminAccounts(); if (getAccountsCall != null) { try { Response> getAccountsResponse = getAccountsCall.execute(); + if (getAccountsResponse.isSuccessful()) { - adminAccountList = getAccountsResponse.body(); + adminAccounts.adminAccounts = getAccountsResponse.body(); + adminAccounts.pagination = MastodonHelper.getPagination(getAccountsResponse.headers()); } } catch (IOException e) { e.printStackTrace(); } } Handler mainHandler = new Handler(Looper.getMainLooper()); - List finalAdminAccountList = adminAccountList; - Runnable myRunnable = () -> adminAccountListMutableLiveData.setValue(finalAdminAccountList); + Runnable myRunnable = () -> adminAccountsListMutableLiveData.setValue(adminAccounts); mainHandler.post(myRunnable); }).start(); - return adminAccountListMutableLiveData; + return adminAccountsListMutableLiveData; } /** @@ -358,32 +362,35 @@ public class AdminVM extends AndroidViewModel { * @param token Access token of the active account * @return {@link LiveData} containing a {@link List} of {@link AdminReport}s */ - public LiveData> getReports(@NonNull String instance, - String token, - Boolean resolved, - String accountId, - String targetAccountId) { + public LiveData getReports(@NonNull String instance, + String token, + Boolean resolved, + String accountId, + String targetAccountId, + String max_id) { MastodonAdminService mastodonAdminService = init(instance); - adminReportListMutableLiveData = new MutableLiveData<>(); + adminReporstListMutableLiveData = new MutableLiveData<>(); new Thread(() -> { - List adminReportList = null; - Call> getReportsCall = mastodonAdminService.getReports(token, resolved, accountId, targetAccountId); + List adminReportList; + Call> getReportsCall = mastodonAdminService.getReports(token, resolved, accountId, targetAccountId, max_id, MastodonHelper.statusesPerCall(getApplication())); + AdminReports adminReports = new AdminReports(); if (getReportsCall != null) { try { Response> getReportsResponse = getReportsCall.execute(); if (getReportsResponse.isSuccessful()) { adminReportList = getReportsResponse.body(); + adminReports.adminReports = adminReportList; + adminReports.pagination = MastodonHelper.getPagination(getReportsResponse.headers()); } } catch (IOException e) { e.printStackTrace(); } } Handler mainHandler = new Handler(Looper.getMainLooper()); - List finalAdminReportList = adminReportList; - Runnable myRunnable = () -> adminReportListMutableLiveData.setValue(finalAdminReportList); + Runnable myRunnable = () -> adminReporstListMutableLiveData.setValue(adminReports); mainHandler.post(myRunnable); }).start(); - return adminReportListMutableLiveData; + return adminReporstListMutableLiveData; } /** diff --git a/app/src/main/res/drawable/ic_baseline_admin_panel_settings_24.xml b/app/src/main/res/drawable/ic_baseline_admin_panel_settings_24.xml new file mode 100644 index 000000000..6542cf6b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_admin_panel_settings_24.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/layout/activity_admin_actions.xml b/app/src/main/res/layout/activity_admin_actions.xml new file mode 100644 index 000000000..f6d7b1021 --- /dev/null +++ b/app/src/main/res/layout/activity_admin_actions.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/drawer_admin_account.xml b/app/src/main/res/layout/drawer_admin_account.xml new file mode 100644 index 000000000..95e2072e1 --- /dev/null +++ b/app/src/main/res/layout/drawer_admin_account.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/drawer_admin_report.xml b/app/src/main/res/layout/drawer_admin_report.xml new file mode 100644 index 000000000..8e62fb16f --- /dev/null +++ b/app/src/main/res/layout/drawer_admin_report.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_admin_report_account.xml b/app/src/main/res/layout/drawer_admin_report_account.xml new file mode 100644 index 000000000..284cff2f0 --- /dev/null +++ b/app/src/main/res/layout/drawer_admin_report_account.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_admin_filter_accounts.xml b/app/src/main/res/layout/popup_admin_filter_accounts.xml new file mode 100644 index 000000000..4101693d6 --- /dev/null +++ b/app/src/main/res/layout/popup_admin_filter_accounts.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index b100d34e8..c37cf29b2 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -37,6 +37,11 @@ android:icon="@drawable/ic_baseline_group_add_24" android:title="@string/follow_request" android:visible="false" /> + + + + \ 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 8962f7cfd..1c870dd33 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1602,6 +1602,11 @@ "Also favourited by: " Also boosted by: I am a moderator + Last active + Location + Staff + Most recent + Filter diff --git a/app/src/main/res/xml/pref_administration.xml b/app/src/main/res/xml/pref_administration.xml deleted file mode 100644 index 5a5d2cdc9..000000000 --- a/app/src/main/res/xml/pref_administration.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file