diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f69f8a04d..a63593396 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -232,6 +232,11 @@ android:configChanges="orientation|screenSize" android:label="@string/app_name" /> + . */ +package app.fedilab.android.activities; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.appcompat.app.ActionBar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import com.google.android.material.tabs.TabLayout; +import app.fedilab.android.R; +import app.fedilab.android.fragments.DisplayAdminAccountsFragment; +import app.fedilab.android.fragments.DisplayAdminReportsFragment; +import app.fedilab.android.helper.Helper; + + +/** + * Created by Thomas on 19/06/2019. + * Admin activity + */ + +public class AdminActivity extends BaseActivity { + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + + setTheme(R.style.AppAdminTheme); + + if( getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if( actionBar != null ) { + LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE); + assert inflater != null; + @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.simple_bar, null); + actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + ImageView toolbar_close = actionBar.getCustomView().findViewById(R.id.toolbar_close); + TextView toolbar_title = actionBar.getCustomView().findViewById(R.id.toolbar_title); + toolbar_close.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + toolbar_title.setText(String.format(getString(R.string.administration)+ " %s", Helper.getLiveInstance(getApplicationContext()))); + } + setContentView(R.layout.activity_admin); + + + ViewPager admin_viewpager = findViewById(R.id.admin_viewpager); + + TabLayout admin_tablayout = findViewById(R.id.admin_tablayout); + admin_tablayout.addTab(admin_tablayout.newTab().setText(getString(R.string.reports))); + admin_tablayout.addTab(admin_tablayout.newTab().setText(getString(R.string.accounts))); + + + PagerAdapter mPagerAdapter = new AdminPagerAdapter(getSupportFragmentManager()); + admin_viewpager.setAdapter(mPagerAdapter); + + admin_viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + TabLayout.Tab tab = admin_tablayout.getTabAt(position); + if( tab != null) + tab.select(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + admin_tablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + admin_viewpager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + Fragment fragment = null; + if( admin_viewpager.getAdapter() != null) + fragment = (Fragment) admin_viewpager.getAdapter().instantiateItem(admin_viewpager, tab.getPosition()); + switch (tab.getPosition()){ + case 0: + if( fragment != null) { + DisplayAdminReportsFragment displayAdminReportsFragment = ((DisplayAdminReportsFragment) fragment); + displayAdminReportsFragment.scrollToTop(); + } + break; + case 1: + if( fragment != null) { + DisplayAdminAccountsFragment displayAdminAccountsFragment = ((DisplayAdminAccountsFragment) fragment); + displayAdminAccountsFragment.scrollToTop(); + } + break; + + } + } + }); + + } + + + private class AdminPagerAdapter extends FragmentStatePagerAdapter { + + AdminPagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + Bundle bundle = new Bundle(); + switch (position){ + case 0: + DisplayAdminReportsFragment displayAdminReportsFragment = new DisplayAdminReportsFragment(); + return displayAdminReportsFragment; + case 1: + DisplayAdminAccountsFragment displayAdminAccountsFragment = new DisplayAdminAccountsFragment(); + return displayAdminAccountsFragment; + } + return null; + } + + + @Override + public int getCount() { + return 2; + } + } + +} diff --git a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java index 67495f241..c35974af2 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java @@ -86,6 +86,7 @@ import app.fedilab.android.client.Entities.Status; import app.fedilab.android.client.Entities.TagTimeline; import app.fedilab.android.client.Entities.Version; import app.fedilab.android.fragments.DisplayAccountsFragment; +import app.fedilab.android.fragments.DisplayAdminReportsFragment; import app.fedilab.android.fragments.DisplayBookmarksFragment; import app.fedilab.android.fragments.DisplayDraftsFragment; import app.fedilab.android.fragments.DisplayFavoritesPeertubeFragment; @@ -284,6 +285,7 @@ public abstract class BaseMainActivity extends BaseActivity viewPager = findViewById(R.id.viewpager); new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.GET_ACCOUNTS, null, null, BaseMainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.GET_REPORTS, null, null, BaseMainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); display_timeline.setOnClickListener(new View.OnClickListener() { @Override @@ -1512,6 +1514,10 @@ public abstract class BaseMainActivity extends BaseActivity Intent intent = new Intent(getApplicationContext(), ReorderTimelinesActivity.class); startActivity(intent); return false; + }else if(id == R.id.nav_administration){ + Intent intent = new Intent(getApplicationContext(), AdminActivity.class); + startActivity(intent); + return false; } else if( id == R.id.nav_about) { Intent intent = new Intent(getApplicationContext(), AboutActivity.class); startActivity(intent); diff --git a/app/src/main/java/app/fedilab/android/activities/LoginActivity.java b/app/src/main/java/app/fedilab/android/activities/LoginActivity.java index 19aff3d16..6f59e6ce0 100644 --- a/app/src/main/java/app/fedilab/android/activities/LoginActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/LoginActivity.java @@ -114,12 +114,14 @@ public class LoginActivity extends BaseActivity { private TextView instance_chosen; private ImageView info_instance; private final int PICK_IMPORT = 5557; + private boolean admin; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle b = getIntent().getExtras(); socialNetwork = UpdateAccountInfoAsyncTask.SOCIAL.MASTODON; + admin = false; if(b != null) { autofilledInstance = b.getString("instance", null); social = b.getString("social", null); @@ -136,6 +138,7 @@ public class LoginActivity extends BaseActivity { break; } } + admin = b.getBoolean("admin", false); } if( getIntent() != null && getIntent().getData() != null && getIntent().getData().toString().contains("mastalab://backtomastalab?code=")){ @@ -551,7 +554,11 @@ public class LoginActivity extends BaseActivity { parameters.put(Helper.CLIENT_NAME, Helper.CLIENT_NAME_VALUE); parameters.put(Helper.REDIRECT_URIS, client_id_for_webview?Helper.REDIRECT_CONTENT_WEB:Helper.REDIRECT_CONTENT); if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { - parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES); + if( admin ) { + parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES_ADMIN); + }else{ + parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES); + } }else { parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES_PEERTUBE); } diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index 6748d4c8c..52780aa58 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -19,6 +19,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; +import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -234,6 +235,7 @@ public class API { } try { String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(endpoint), 60, params, prefKeyOauthTokenT); + Log.v(Helper.TAG,response); switch (action){ case GET_ACCOUNTS: List accountAdmins = parseAccountAdminResponse(new JSONArray(response)); diff --git a/app/src/main/java/app/fedilab/android/drawers/AccountsAdminListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/AccountsAdminListAdapter.java new file mode 100644 index 000000000..7acede193 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/drawers/AccountsAdminListAdapter.java @@ -0,0 +1,157 @@ +package app.fedilab.android.drawers; +/* Copyright 2019 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.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.asynctasks.RetrieveAccountsAsyncTask; +import app.fedilab.android.client.Entities.Account; +import app.fedilab.android.client.Entities.AccountAdmin; +import app.fedilab.android.client.Entities.Report; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnRetrieveEmojiAccountInterface; + + +/** + * Created by Thomas on 19/06/2019. + * Adapter for account admins + */ +public class AccountsAdminListAdapter extends RecyclerView.Adapter implements OnRetrieveEmojiAccountInterface { + + private List accountAdmins; + private LayoutInflater layoutInflater; + private RetrieveAccountsAsyncTask.Type action; + private Context context; + private AccountsAdminListAdapter accountsAdminListAdapter; + private String targetedId; + + public AccountsAdminListAdapter(Context context, List accountAdmins){ + this.context = context; + this.accountAdmins = accountAdmins; + layoutInflater = LayoutInflater.from(context); + this.accountsAdminListAdapter = this; + } + + + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(layoutInflater.inflate(R.layout.drawer_account_admin, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + final AccountsAdminListAdapter.ViewHolder holder = (AccountsAdminListAdapter.ViewHolder) viewHolder; + AccountAdmin accountAdmin = accountAdmins.get(position); + Account account = accountAdmin.getAccount(); + + + account.makeAccountNameEmoji(context, AccountsAdminListAdapter.this, account); + if( account.getdisplayNameSpan() == null || account.getdisplayNameSpan().toString().trim().equals("")) { + if( account.getDisplay_name() != null && !account.getDisplay_name().trim().equals("")) + holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true)); + else + holder.account_dn.setText(account.getDisplay_name().replace("@","")); + }else + holder.account_dn.setText( account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE); + + if( account.getdisplayNameSpan() == null || account.getdisplayNameSpan().toString().trim().equals("")) { + if( account.getDisplay_name() != null && !account.getDisplay_name().trim().equals("")) + holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true)); + else + holder.account_dn.setText(account.getDisplay_name().replace("@","")); + }else + holder.account_dn.setText( account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE); + holder.account_un.setText(String.format("@%s",account.getUsername())); + holder.account_ac.setText(account.getAcct()); + if( account.getDisplay_name().equals(account.getAcct())) + holder.account_ac.setVisibility(View.GONE); + else + holder.account_ac.setVisibility(View.VISIBLE); + + holder.report_action_taken.setText(accountAdmin.getIp()); + + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return accountAdmins.size(); + } + + private AccountAdmin getItemAt(int position){ + if( accountAdmins.size() > position) + return accountAdmins.get(position); + else + return null; + } + + @Override + public void onRetrieveEmojiAccount(Account account) { + notifyAccountChanged(account); + } + + private void notifyAccountChanged(Account account){ + for (int i = 0; i < accountsAdminListAdapter.getItemCount(); i++) { + //noinspection ConstantConditions + if (accountsAdminListAdapter.getItemAt(i) != null && accountsAdminListAdapter.getItemAt(i).getAccount().getId().equals(account.getId())) { + try { + accountsAdminListAdapter.notifyItemChanged(i); + } catch (Exception ignored) { + } + } + } + } + + + private class ViewHolder extends RecyclerView.ViewHolder{ + ImageView account_pp; + TextView account_ac; + TextView account_dn; + TextView account_un; + TextView report_action_taken; + + LinearLayout account_container; + + ViewHolder(View itemView) { + super(itemView); + account_pp = itemView.findViewById(R.id.account_pp); + account_dn = itemView.findViewById(R.id.account_dn); + account_ac = itemView.findViewById(R.id.account_ac); + account_un = itemView.findViewById(R.id.account_un); + report_action_taken = itemView.findViewById(R.id.report_action_taken); + account_container = itemView.findViewById(R.id.account_container); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/drawers/ReportsListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/ReportsListAdapter.java new file mode 100644 index 000000000..3a3f5e1ce --- /dev/null +++ b/app/src/main/java/app/fedilab/android/drawers/ReportsListAdapter.java @@ -0,0 +1,160 @@ +package app.fedilab.android.drawers; +/* Copyright 2019 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.Context; +import android.os.Build; +import android.text.Html; +import android.text.util.Linkify; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import java.util.List; +import app.fedilab.android.R; +import app.fedilab.android.asynctasks.RetrieveAccountsAsyncTask; +import app.fedilab.android.client.Entities.Account; +import app.fedilab.android.client.Entities.Report; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnRetrieveEmojiAccountInterface; + + +/** + * Created by Thomas on 19/06/2019. + * Adapter for reports + */ +public class ReportsListAdapter extends RecyclerView.Adapter implements OnRetrieveEmojiAccountInterface { + + private List reports; + private LayoutInflater layoutInflater; + private RetrieveAccountsAsyncTask.Type action; + private Context context; + private ReportsListAdapter reportsListAdapter; + private String targetedId; + + public ReportsListAdapter(Context context, List reports){ + this.context = context; + this.reports = reports; + layoutInflater = LayoutInflater.from(context); + this.reportsListAdapter = this; + } + + + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(layoutInflater.inflate(R.layout.drawer_report, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + final ReportsListAdapter.ViewHolder holder = (ReportsListAdapter.ViewHolder) viewHolder; + Report report = reports.get(position); + Account account = report.getStatuses().get(0).getAccount(); + + + account.makeAccountNameEmoji(context, ReportsListAdapter.this, account); + if( account.getdisplayNameSpan() == null || account.getdisplayNameSpan().toString().trim().equals("")) { + if( account.getDisplay_name() != null && !account.getDisplay_name().trim().equals("")) + holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true)); + else + holder.account_dn.setText(account.getDisplay_name().replace("@","")); + }else + holder.account_dn.setText( account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE); + + if( account.getdisplayNameSpan() == null || account.getdisplayNameSpan().toString().trim().equals("")) { + if( account.getDisplay_name() != null && !account.getDisplay_name().trim().equals("")) + holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true)); + else + holder.account_dn.setText(account.getDisplay_name().replace("@","")); + }else + holder.account_dn.setText( account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE); + holder.account_un.setText(String.format("@%s",account.getUsername())); + holder.account_ac.setText(account.getAcct()); + if( account.getDisplay_name().equals(account.getAcct())) + holder.account_ac.setVisibility(View.GONE); + else + holder.account_ac.setVisibility(View.VISIBLE); + + holder.report_action_taken.setText(report.getAction_taken()); + + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return reports.size(); + } + + private Report getItemAt(int position){ + if( reports.size() > position) + return reports.get(position); + else + return null; + } + + @Override + public void onRetrieveEmojiAccount(Account account) { + notifyAccountChanged(account); + } + + private void notifyAccountChanged(Account account){ + for (int i = 0; i < reportsListAdapter.getItemCount(); i++) { + //noinspection ConstantConditions + if (reportsListAdapter.getItemAt(i) != null && reportsListAdapter.getItemAt(i).getStatuses().get(0).getAccount().getId().equals(account.getId())) { + try { + reportsListAdapter.notifyItemChanged(i); + } catch (Exception ignored) { + } + } + } + } + + + private class ViewHolder extends RecyclerView.ViewHolder{ + ImageView account_pp; + TextView account_ac; + TextView account_dn; + TextView account_un; + TextView report_action_taken; + + LinearLayout account_container; + + ViewHolder(View itemView) { + super(itemView); + account_pp = itemView.findViewById(R.id.account_pp); + account_dn = itemView.findViewById(R.id.account_dn); + account_ac = itemView.findViewById(R.id.account_ac); + account_un = itemView.findViewById(R.id.account_un); + report_action_taken = itemView.findViewById(R.id.report_action_taken); + account_container = itemView.findViewById(R.id.account_container); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayAdminAccountsFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayAdminAccountsFragment.java new file mode 100644 index 000000000..1fe0bacb4 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/fragments/DisplayAdminAccountsFragment.java @@ -0,0 +1,215 @@ +package app.fedilab.android.fragments; +/* Copyright 2019 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.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.asynctasks.PostAdminActionAsyncTask; +import app.fedilab.android.asynctasks.RetrieveAccountsAsyncTask; +import app.fedilab.android.client.API; +import app.fedilab.android.client.APIResponse; +import app.fedilab.android.client.Entities.AccountAdmin; +import app.fedilab.android.drawers.AccountsAdminListAdapter; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnAdminActionInterface; +import es.dmoral.toasty.Toasty; + + +/** + * Created by Thomas on 19/06/2019. + * Fragment to display content related to reports + */ +public class DisplayAdminAccountsFragment extends Fragment implements OnAdminActionInterface { + + private boolean flag_loading; + private Context context; + private AsyncTask asyncTask; + private AccountsAdminListAdapter accountsAdminListAdapter; + private String max_id; + private List accountAdmins; + private RetrieveAccountsAsyncTask.Type type; + private RelativeLayout mainLoader, nextElementLoader, textviewNoAction; + private boolean firstLoad; + private SwipeRefreshLayout swipeRefreshLayout; + private boolean swiped; + private RecyclerView lv_admin_accounts; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.fragment_admin_accounts, container, false); + + context = getContext(); + + accountAdmins = new ArrayList<>(); + + max_id = null; + firstLoad = true; + flag_loading = true; + swiped = false; + + swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer); + lv_admin_accounts = rootView.findViewById(R.id.lv_admin_accounts); + lv_admin_accounts.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); + mainLoader = rootView.findViewById(R.id.loader); + nextElementLoader = rootView.findViewById(R.id.loading_next_accounts); + textviewNoAction = rootView.findViewById(R.id.no_action); + mainLoader.setVisibility(View.VISIBLE); + nextElementLoader.setVisibility(View.GONE); + accountsAdminListAdapter = new AccountsAdminListAdapter(context, this.accountAdmins); + lv_admin_accounts.setAdapter(accountsAdminListAdapter); + + final LinearLayoutManager mLayoutManager; + mLayoutManager = new LinearLayoutManager(context); + lv_admin_accounts.setLayoutManager(mLayoutManager); + lv_admin_accounts.addOnScrollListener(new RecyclerView.OnScrollListener() { + public void onScrolled(RecyclerView recyclerView, int dx, int dy) + { + if(dy > 0) { + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); + if (firstVisibleItem + visibleItemCount == totalItemCount) { + if (!flag_loading) { + flag_loading = true; + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + nextElementLoader.setVisibility(View.VISIBLE); + } + } else { + nextElementLoader.setVisibility(View.GONE); + } + } + } + }); + + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + max_id = null; + accountAdmins = new ArrayList<>(); + firstLoad = true; + flag_loading = true; + swiped = true; + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + switch (theme){ + case Helper.THEME_LIGHT: + swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4, + R.color.mastodonC2, + R.color.mastodonC3); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.white)); + break; + case Helper.THEME_DARK: + swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4__, + R.color.mastodonC4, + R.color.mastodonC4); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.mastodonC1_)); + break; + case Helper.THEME_BLACK: + swipeRefreshLayout.setColorSchemeResources(R.color.dark_icon, + R.color.mastodonC2, + R.color.mastodonC3); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.black_3)); + break; + } + + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return rootView; + } + + @Override + public void onCreate(Bundle saveInstance) + { + super.onCreate(saveInstance); + } + + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + public void onDestroy() { + super.onDestroy(); + if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING) + asyncTask.cancel(true); + } + + public void scrollToTop(){ + if( lv_admin_accounts != null) + lv_admin_accounts.setAdapter(accountsAdminListAdapter); + } + + + @Override + public void onAdminAction(APIResponse apiResponse) { + mainLoader.setVisibility(View.GONE); + nextElementLoader.setVisibility(View.GONE); + if( apiResponse.getError() != null){ + Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show(); + swipeRefreshLayout.setRefreshing(false); + swiped = false; + flag_loading = false; + return; + } + flag_loading = (apiResponse.getMax_id() == null ); + List accountAdmins = apiResponse.getAccountAdmins(); + + if( !swiped && firstLoad && (accountAdmins == null || accountAdmins.size() == 0)) + textviewNoAction.setVisibility(View.VISIBLE); + else + textviewNoAction.setVisibility(View.GONE); + + max_id = apiResponse.getMax_id(); + + if( swiped ){ + accountsAdminListAdapter = new AccountsAdminListAdapter(context, this.accountAdmins); + lv_admin_accounts.setAdapter(accountsAdminListAdapter); + swiped = false; + } + if( accountAdmins != null && accountAdmins.size() > 0) { + int currentPosition = this.accountAdmins.size(); + this.accountAdmins.addAll(accountAdmins); + accountsAdminListAdapter.notifyItemRangeChanged(currentPosition,accountAdmins.size()); + } + swipeRefreshLayout.setRefreshing(false); + firstLoad = false; + } +} diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayAdminReportsFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayAdminReportsFragment.java new file mode 100644 index 000000000..811217076 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/fragments/DisplayAdminReportsFragment.java @@ -0,0 +1,245 @@ +package app.fedilab.android.fragments; +/* Copyright 2019 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.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.activities.LoginActivity; +import app.fedilab.android.activities.PeertubeActivity; +import app.fedilab.android.asynctasks.PostActionAsyncTask; +import app.fedilab.android.asynctasks.PostAdminActionAsyncTask; +import app.fedilab.android.asynctasks.RetrieveAccountsAsyncTask; +import app.fedilab.android.client.API; +import app.fedilab.android.client.APIResponse; +import app.fedilab.android.client.Entities.Report; +import app.fedilab.android.drawers.ReportsListAdapter; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnAdminActionInterface; +import es.dmoral.toasty.Toasty; + + +/** + * Created by Thomas on 19/06/2019. + * Fragment to display content related to reports + */ +public class DisplayAdminReportsFragment extends Fragment implements OnAdminActionInterface { + + private boolean flag_loading; + private Context context; + private AsyncTask asyncTask; + private ReportsListAdapter reportsListAdapter; + private String max_id; + private List reports; + private RetrieveAccountsAsyncTask.Type type; + private RelativeLayout mainLoader, nextElementLoader, textviewNoAction; + private boolean firstLoad; + private SwipeRefreshLayout swipeRefreshLayout; + private boolean swiped; + private RecyclerView lv_reports; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.fragment_admin_reports, container, false); + + context = getContext(); + + reports = new ArrayList<>(); + + max_id = null; + firstLoad = true; + flag_loading = true; + swiped = false; + + swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer); + lv_reports = rootView.findViewById(R.id.lv_reports); + lv_reports.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); + mainLoader = rootView.findViewById(R.id.loader); + nextElementLoader = rootView.findViewById(R.id.loading_next_accounts); + textviewNoAction = rootView.findViewById(R.id.no_action); + mainLoader.setVisibility(View.VISIBLE); + nextElementLoader.setVisibility(View.GONE); + reportsListAdapter = new ReportsListAdapter(context, this.reports); + lv_reports.setAdapter(reportsListAdapter); + + final LinearLayoutManager mLayoutManager; + mLayoutManager = new LinearLayoutManager(context); + lv_reports.setLayoutManager(mLayoutManager); + lv_reports.addOnScrollListener(new RecyclerView.OnScrollListener() { + public void onScrolled(RecyclerView recyclerView, int dx, int dy) + { + if(dy > 0) { + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); + if (firstVisibleItem + visibleItemCount == totalItemCount) { + if (!flag_loading) { + flag_loading = true; + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminReportsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + nextElementLoader.setVisibility(View.VISIBLE); + } + } else { + nextElementLoader.setVisibility(View.GONE); + } + } + } + }); + + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + max_id = null; + reports = new ArrayList<>(); + firstLoad = true; + flag_loading = true; + swiped = true; + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminReportsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + switch (theme){ + case Helper.THEME_LIGHT: + swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4, + R.color.mastodonC2, + R.color.mastodonC3); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.white)); + break; + case Helper.THEME_DARK: + swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4__, + R.color.mastodonC4, + R.color.mastodonC4); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.mastodonC1_)); + break; + case Helper.THEME_BLACK: + swipeRefreshLayout.setColorSchemeResources(R.color.dark_icon, + R.color.mastodonC2, + R.color.mastodonC3); + swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.black_3)); + break; + } + + asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, null, DisplayAdminReportsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return rootView; + } + + @Override + public void onCreate(Bundle saveInstance) + { + super.onCreate(saveInstance); + } + + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + public void onDestroy() { + super.onDestroy(); + if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING) + asyncTask.cancel(true); + } + + public void scrollToTop(){ + if( lv_reports != null) + lv_reports.setAdapter(reportsListAdapter); + } + + + @Override + public void onAdminAction(APIResponse apiResponse) { + mainLoader.setVisibility(View.GONE); + nextElementLoader.setVisibility(View.GONE); + if( apiResponse.getError() != null){ + Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show(); + swipeRefreshLayout.setRefreshing(false); + swiped = false; + flag_loading = false; + //Admin right not granted through the API? + if( apiResponse.getError().getStatusCode() == 403){ + AlertDialog.Builder builderInner; + builderInner = new AlertDialog.Builder(context, R.style.AppAdminTheme); + builderInner.setTitle(R.string.reconnect_account); + builderInner.setMessage(R.string.reconnect_account_message); + builderInner.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int which) { + dialog.dismiss(); + } + }); + builderInner.setPositiveButton(R.string.validate, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int which) { + Intent intent = new Intent(context, LoginActivity.class); + intent.putExtra("admin", true); + context.startActivity(intent); + } + }); + builderInner.show(); + } + return; + } + flag_loading = (apiResponse.getMax_id() == null ); + List reports = apiResponse.getReports(); + + if( !swiped && firstLoad && (reports == null || reports.size() == 0)) + textviewNoAction.setVisibility(View.VISIBLE); + else + textviewNoAction.setVisibility(View.GONE); + + max_id = apiResponse.getMax_id(); + + if( swiped ){ + reportsListAdapter = new ReportsListAdapter(context, this.reports); + lv_reports.setAdapter(reportsListAdapter); + swiped = false; + } + if( reports != null && reports.size() > 0) { + int currentPosition = this.reports.size(); + this.reports.addAll(reports); + reportsListAdapter.notifyItemRangeChanged(currentPosition,reports.size()); + } + swipeRefreshLayout.setRefreshing(false); + firstLoad = false; + } +} diff --git a/app/src/main/res/drawable/ic_security_admin.xml b/app/src/main/res/drawable/ic_security_admin.xml new file mode 100644 index 000000000..d66f92794 --- /dev/null +++ b/app/src/main/res/drawable/ic_security_admin.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_admin.xml b/app/src/main/res/layout/activity_admin.xml new file mode 100644 index 000000000..16e7279f9 --- /dev/null +++ b/app/src/main/res/layout/activity_admin.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_account_admin.xml b/app/src/main/res/layout/drawer_account_admin.xml new file mode 100644 index 000000000..17ae31a2b --- /dev/null +++ b/app/src/main/res/layout/drawer_account_admin.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_report.xml b/app/src/main/res/layout/drawer_report.xml new file mode 100644 index 000000000..17ae31a2b --- /dev/null +++ b/app/src/main/res/layout/drawer_report.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_admin_accounts.xml b/app/src/main/res/layout/fragment_admin_accounts.xml new file mode 100644 index 000000000..e9bc00da4 --- /dev/null +++ b/app/src/main/res/layout/fragment_admin_accounts.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_admin_reports.xml b/app/src/main/res/layout/fragment_admin_reports.xml new file mode 100644 index 000000000..1e6b84a18 --- /dev/null +++ b/app/src/main/res/layout/fragment_admin_reports.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 5ebb902f6..5dce65bb0 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -58,6 +58,10 @@ android:id="@+id/nav_drag_timelines" android:icon="@drawable/ic_drag_handle_menu" android:title="@string/reorder_timelines" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d17082264..228e2639a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1028,6 +1028,11 @@ Important: If your instance required validation, you will receive an email once it is validated! Save the message in drafts? + Administration + Reports + No reports to display! + Reconnect the account + The application failed to access the admin features. You might need to reconnect the account for having the correct scope. %d vote %d votes diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index da3485a32..8d8981bc8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -392,4 +392,39 @@ + + + + + +