Merge branch 'admin_api' into develop

This commit is contained in:
tom79 2019-06-22 15:18:57 +02:00
commit 69177abf70
40 changed files with 4067 additions and 34 deletions

View File

@ -232,6 +232,16 @@
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
/>
<activity android:name="app.fedilab.android.activities.AdminActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
/>
<activity android:name="app.fedilab.android.activities.AccountReportActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
/>
<activity android:name="app.fedilab.android.activities.ReorderTimelinesActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="orientation|screenSize"

View File

@ -0,0 +1,427 @@
package app.fedilab.android.activities;
/* 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 <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import app.fedilab.android.R;
import app.fedilab.android.asynctasks.PostAdminActionAsyncTask;
import app.fedilab.android.client.API;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.client.Entities.AccountAdmin;
import app.fedilab.android.client.Entities.AdminAction;
import app.fedilab.android.client.Entities.Report;
import app.fedilab.android.client.Entities.Status;
import app.fedilab.android.drawers.StatusReportAdapter;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.interfaces.OnAdminActionInterface;
import es.dmoral.toasty.Toasty;
import static app.fedilab.android.client.API.adminAction.APPROVE;
import static app.fedilab.android.client.API.adminAction.DISABLE;
import static app.fedilab.android.client.API.adminAction.NONE;
import static app.fedilab.android.client.API.adminAction.REJECT;
import static app.fedilab.android.client.API.adminAction.SILENCE;
import static app.fedilab.android.client.API.adminAction.SUSPEND;
public class AccountReportActivity extends BaseActivity implements OnAdminActionInterface {
TextView permissions, username, email, email_status, login_status, joined, recent_ip, comment_label;
Button warn, disable, silence, suspend, allow, reject, assign, status;
private String account_id;
private CheckBox email_user;
private EditText comment;
private Report report;
private Group allow_reject_group;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
setTheme(R.style.AppAdminTheme);
report = null;
AccountAdmin targeted_account = null;
Bundle b = getIntent().getExtras();
if (b != null) {
account_id = b.getString("account_id", null);
targeted_account = b.getParcelable("targeted_account");
report = b.getParcelable("report");
}
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_report);
warn = findViewById(R.id.warn);
disable = findViewById(R.id.disable);
silence = findViewById(R.id.silence);
suspend = findViewById(R.id.suspend);
allow = findViewById(R.id.allow);
reject = findViewById(R.id.reject);
status = findViewById(R.id.status);
assign = findViewById(R.id.assign);
allow_reject_group = findViewById(R.id.allow_reject_group);
allow_reject_group.setVisibility(View.GONE);
allow.getBackground().setColorFilter(ContextCompat.getColor(getApplicationContext(), R.color.green_1), PorterDuff.Mode.MULTIPLY);
reject.getBackground().setColorFilter(ContextCompat.getColor(getApplicationContext(), R.color.red_1), PorterDuff.Mode.MULTIPLY);
comment_label = findViewById(R.id.comment_label);
permissions = findViewById(R.id.permissions);
username = findViewById(R.id.username);
email = findViewById(R.id.email);
email_status = findViewById(R.id.email_status);
login_status = findViewById(R.id.login_status);
joined = findViewById(R.id.joined);
recent_ip = findViewById(R.id.recent_ip);
email_user = findViewById(R.id.email_user);
comment = findViewById(R.id.comment);
if( account_id == null && report == null && targeted_account == null){
Toasty.error(getApplicationContext(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish();
}
assign.setVisibility(View.GONE);
status.setVisibility(View.GONE);
if( account_id != null){
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.GET_ONE_ACCOUNT, account_id, null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return;
}
if( report != null) {
targeted_account = report.getTarget_account();
RecyclerView lv_statuses = findViewById(R.id.lv_statuses);
ArrayList<String> contents = new ArrayList<>();
for(Status status: report.getStatuses()){
contents.add(status.getContent());
}
lv_statuses.setLayoutManager(new LinearLayoutManager(this));
StatusReportAdapter adapter = new StatusReportAdapter(this, contents);
lv_statuses.setAdapter(adapter);
Group statuses_group = findViewById(R.id.statuses_group);
statuses_group.setVisibility(View.VISIBLE);
}
account_id = targeted_account.getId();
fillReport(targeted_account);
}
@Override
public void onAdminAction(APIResponse apiResponse) {
if( apiResponse.getError() != null){
if( apiResponse.getError().getStatusCode() == 403){
AlertDialog.Builder builderInner;
builderInner = new AlertDialog.Builder(AccountReportActivity.this, R.style.AdminDialog);
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(AccountReportActivity.this, LoginActivity.class);
intent.putExtra("admin", true);
startActivity(intent);
}
});
builderInner.show();
}else{
Toasty.error(AccountReportActivity.this, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
}
return;
}
if( apiResponse.getReports() != null && apiResponse.getReports().size() > 0){
report = apiResponse.getReports().get(0);
fillReport(apiResponse.getAccountAdmins().get(0));
} else if( apiResponse.getAccountAdmins() != null && apiResponse.getAccountAdmins().size() > 0) {
fillReport(apiResponse.getAccountAdmins().get(0));
}
}
private void fillReport(AccountAdmin accountAdmin){
if( accountAdmin == null){
Toasty.error(getApplicationContext(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
return;
}
if(!accountAdmin.isApproved()){
allow_reject_group.setVisibility(View.VISIBLE);
}
reject.setOnClickListener(view->{
AdminAction adminAction = new AdminAction();
adminAction.setType(REJECT);
new PostAdminActionAsyncTask(getApplicationContext(), REJECT, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
allow.setOnClickListener(view->{
AdminAction adminAction = new AdminAction();
adminAction.setType(APPROVE);
new PostAdminActionAsyncTask(getApplicationContext(), APPROVE, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
warn.setOnClickListener(view->{
AdminAction adminAction = new AdminAction();
adminAction.setType(NONE);
adminAction.setSend_email_notification(email_user.isChecked());
adminAction.setText(comment.getText().toString().trim());
new PostAdminActionAsyncTask(getApplicationContext(), NONE, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
if( !accountAdmin.isSilenced() ) {
silence.setText(getString(R.string.silence));
}else{
silence.setText(getString(R.string.unsilence));
}
silence.setOnClickListener(view->{
if( !accountAdmin.isSilenced() ) {
AdminAction adminAction = new AdminAction();
adminAction.setType(SILENCE);
adminAction.setSend_email_notification(email_user.isChecked());
adminAction.setText(comment.getText().toString().trim());
new PostAdminActionAsyncTask(getApplicationContext(), SILENCE, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.UNSILENCE, account_id, null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
if( !accountAdmin.isDisabled() ) {
disable.setText(getString(R.string.disable));
}else{
disable.setText(getString(R.string.undisable));
}
disable.setOnClickListener(view->{
if( !accountAdmin.isDisabled()) {
AdminAction adminAction = new AdminAction();
adminAction.setType(DISABLE);
adminAction.setSend_email_notification(email_user.isChecked());
adminAction.setText(comment.getText().toString().trim());
new PostAdminActionAsyncTask(getApplicationContext(), DISABLE, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.ENABLE, account_id, null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
if( !accountAdmin.isSuspended() ) {
suspend.setText(getString(R.string.suspend));
}else{
suspend.setText(getString(R.string.unsuspend));
}
suspend.setOnClickListener(view->{
if( !accountAdmin.isSuspended() ){
AdminAction adminAction = new AdminAction();
adminAction.setType(SUSPEND);
adminAction.setSend_email_notification(email_user.isChecked());
adminAction.setText(comment.getText().toString().trim());
new PostAdminActionAsyncTask(getApplicationContext(), SUSPEND, account_id, adminAction, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.UNSUSPEND, account_id, null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
if( accountAdmin.getAction() != null) {
String message = null;
switch (accountAdmin.getAction()) {
case SILENCE:
message = getString(R.string.account_silenced);
break;
case UNSILENCE:
message = getString(R.string.account_unsilenced);
break;
case DISABLE:
message = getString(R.string.account_disabled);
break;
case ENABLE:
message = getString(R.string.account_undisabled);
break;
case SUSPEND:
message = getString(R.string.account_suspended);
break;
case UNSUSPEND:
message = getString(R.string.account_unsuspended);
break;
case NONE:
message = getString(R.string.account_warned);
break;
case APPROVE:
allow_reject_group.setVisibility(View.GONE);
message = getString(R.string.account_approved);
break;
case REJECT:
allow_reject_group.setVisibility(View.GONE);
message = getString(R.string.account_rejected);
break;
}
if( message != null){
Toasty.success(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
comment.setText("");
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(comment.getWindowToken(), 0);
}
if( accountAdmin.getRole() == null) {
return;
}
switch (accountAdmin.getRole()) {
case "user":
permissions.setText(getString(R.string.user));
break;
case "mod":
permissions.setText(getString(R.string.moderator));
break;
case "admin":
permissions.setText(getString(R.string.administrator));
break;
}
username.setText(String.format("@%s", accountAdmin.getAccount().getAcct()));
email.setText(accountAdmin.getEmail());
email_status.setText(accountAdmin.isConfirmed()?getString(R.string.confirmed):getString(R.string.unconfirmed));
if( accountAdmin.isDisabled()){
login_status.setText(getString(R.string.disabled));
}else if( accountAdmin.isSilenced()){
login_status.setText(getString(R.string.silenced));
}else if( accountAdmin.isSuspended()){
login_status.setText(getString(R.string.suspended));
}else{
login_status.setText(getString(R.string.active));
}
if( accountAdmin.getDomain() == null || accountAdmin.getDomain().equals("null")){
warn.setVisibility(View.VISIBLE);
email_user.setVisibility(View.VISIBLE);
comment_label.setVisibility(View.VISIBLE);
comment.setVisibility(View.VISIBLE);
recent_ip.setText(accountAdmin.getIp());
disable.setVisibility(View.VISIBLE);
suspend.setVisibility(View.VISIBLE);
}else{
warn.setVisibility(View.GONE);
email_user.setVisibility(View.GONE);
email_user.setChecked(false);
comment.setVisibility(View.GONE);
recent_ip.setText("-");
permissions.setText("-");
email.setText("-");
disable.setVisibility(View.GONE);
suspend.setVisibility(View.VISIBLE);
comment_label.setVisibility(View.GONE);
}
if( accountAdmin.getRole().equals("admin") || accountAdmin.getRole().equals("mod")){
warn.setVisibility(View.GONE);
suspend.setVisibility(View.GONE);
silence.setVisibility(View.GONE);
disable.setVisibility(View.GONE);
email_user.setVisibility(View.GONE);
email_user.setChecked(false);
comment.setVisibility(View.GONE);
comment_label.setVisibility(View.GONE);
}
joined.setText(Helper.dateToString(accountAdmin.getCreated_at()));
if( report != null){
assign.setVisibility(View.VISIBLE);
status.setVisibility(View.VISIBLE);
if( report.getAssigned_account() == null){
assign.setText(getString(R.string.assign_to_me));
}else{
assign.setText(getString(R.string.unassign));
}
assign.setOnClickListener(view ->{
if( report.getAssigned_account() == null){
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.ASSIGN_TO_SELF, report.getId(), null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.UNASSIGN, report.getId(), null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
if( report.isAction_taken()){
status.setText(getString(R.string.mark_unresolved));
}else{
status.setText(getString(R.string.mark_resolved));
}
status.setOnClickListener(view ->{
if( report.isAction_taken() ){
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.REOPEN, report.getId(), null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
new PostAdminActionAsyncTask(getApplicationContext(), API.adminAction.RESOLVE, report.getId(), null, AccountReportActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
}else{
assign.setVisibility(View.GONE);
status.setVisibility(View.GONE);
}
}
}

View File

@ -0,0 +1,363 @@
/* 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 <http://www.gnu.org/licenses>. */
package app.fedilab.android.activities;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.PopupMenu;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.fragment.app.FragmentTransaction;
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.fragments.DisplayStatusFragment;
import app.fedilab.android.helper.Helper;
import static app.fedilab.android.activities.BaseMainActivity.mPageReferenceMap;
/**
* Created by Thomas on 19/06/2019.
* Admin activity
*/
public class AdminActivity extends BaseActivity {
private boolean unresolved;
private boolean local, remote, active, pending, disabled, silenced, suspended;
private DisplayAdminReportsFragment displayAdminReportsFragment;
private DisplayAdminAccountsFragment displayAdminAccountsFragment;
@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);
unresolved = local = active = true;
remote = pending = disabled = silenced = suspended = false;
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)));
final LinearLayout tabStrip = (LinearLayout) admin_tablayout.getChildAt(0);
tabStrip.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
PopupMenu popup = new PopupMenu(AdminActivity.this, tabStrip.getChildAt(0));
popup.getMenuInflater()
.inflate(R.menu.option_filter_admin_reports, popup.getMenu());
Menu menu = popup.getMenu();
final MenuItem itemUnresolved = menu.findItem(R.id.action_unresolved_reports);
itemUnresolved.setChecked(unresolved);
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
fragTransaction.detach(displayAdminReportsFragment);
Bundle bundle = new Bundle();
bundle.putBoolean("unresolved",unresolved);
displayAdminReportsFragment.setArguments(bundle);
fragTransaction.attach(displayAdminReportsFragment);
fragTransaction.commit();
}
});
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(getApplicationContext()));
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return false;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
return false;
}
});
if (item.getItemId() == R.id.action_unresolved_reports) {
unresolved = !unresolved;
}
itemUnresolved.setChecked(unresolved);
return false;
}
});
popup.show();
return true;
}
});
tabStrip.getChildAt(1).setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
PopupMenu popup = new PopupMenu(AdminActivity.this, tabStrip.getChildAt(1));
popup.getMenuInflater()
.inflate(R.menu.option_filter_admin_accounts, popup.getMenu());
Menu menu = popup.getMenu();
final MenuItem itemLocal = menu.findItem(R.id.action_local);
final MenuItem itemRemote = menu.findItem(R.id.action_remote);
final MenuItem itemActive = menu.findItem(R.id.action_active);
final MenuItem itemPending = menu.findItem(R.id.action_pending);
final MenuItem itemDisabled = menu.findItem(R.id.action_disabled);
final MenuItem itemSilenced = menu.findItem(R.id.action_silenced);
final MenuItem itemSuspended = menu.findItem(R.id.action_suspended);
itemLocal.setChecked(local);
itemRemote.setChecked(remote);
itemActive.setChecked(active);
itemPending.setChecked(pending);
itemDisabled.setChecked(disabled);
itemSilenced.setChecked(silenced);
itemSuspended.setChecked(suspended);
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
fragTransaction.detach(displayAdminAccountsFragment);
Bundle bundle = new Bundle();
bundle.putBoolean("local",local);
bundle.putBoolean("remote",remote);
bundle.putBoolean("active",active);
bundle.putBoolean("pending",pending);
bundle.putBoolean("disabled",disabled);
bundle.putBoolean("silenced",silenced);
bundle.putBoolean("suspended",suspended);
displayAdminAccountsFragment.setArguments(bundle);
fragTransaction.attach(displayAdminAccountsFragment);
fragTransaction.commit();
}
});
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(getApplicationContext()));
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return false;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
return false;
}
});
switch (item.getItemId()){
case R.id.action_local:
if( !local) {
remote = false;
local = true;
}
break;
case R.id.action_remote:
if( !remote) {
remote = true;
local = false;
}
break;
case R.id.action_active:
if( !active) {
active = true;
pending = false;
disabled = false;
silenced = false;
suspended = false;
}
break;
case R.id.action_pending:
if( !pending) {
pending = true;
active = false;
disabled = false;
silenced = false;
suspended = false;
}
break;
case R.id.action_disabled:
if( !disabled) {
disabled = true;
active = false;
pending = false;
silenced = false;
suspended = false;
}
break;
case R.id.action_silenced:
if( !silenced) {
silenced = true;
active = false;
pending = false;
disabled = false;
suspended = false;
}
break;
case R.id.action_suspended:
if( !suspended) {
suspended = true;
active = false;
pending = false;
disabled = false;
silenced = false;
}
break;
}
itemLocal.setChecked(local);
itemRemote.setChecked(remote);
itemActive.setChecked(active);
itemPending.setChecked(pending);
itemDisabled.setChecked(disabled);
itemSilenced.setChecked(silenced);
itemSuspended.setChecked(suspended);
return false;
}
});
popup.show();
return true;
}
});
PagerAdapter mPagerAdapter = new AdminPagerAdapter(getSupportFragmentManager());
admin_viewpager.setAdapter(mPagerAdapter);
admin_viewpager.setOffscreenPageLimit(2);
admin_viewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(admin_tablayout));
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) fragment);
displayAdminReportsFragment.scrollToTop();
}
break;
case 1:
if( fragment != null) {
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 = new DisplayAdminReportsFragment();
bundle = new Bundle();
bundle.putBoolean("unresolved",unresolved);
displayAdminReportsFragment.setArguments(bundle);
return displayAdminReportsFragment;
case 1:
displayAdminAccountsFragment = new DisplayAdminAccountsFragment();
bundle = new Bundle();
bundle.putBoolean("local",local);
bundle.putBoolean("remote",remote);
bundle.putBoolean("active",active);
bundle.putBoolean("pending",pending);
bundle.putBoolean("disabled",disabled);
bundle.putBoolean("silenced",silenced);
bundle.putBoolean("suspended",suspended);
displayAdminAccountsFragment.setArguments(bundle);
return displayAdminAccountsFragment;
}
return null;
}
@Override
public int getCount() {
return 2;
}
}
}

View File

@ -74,8 +74,11 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.asynctasks.PostAdminActionAsyncTask;
import app.fedilab.android.client.API;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.client.Entities.Account;
import app.fedilab.android.client.Entities.AdminAction;
import app.fedilab.android.client.Entities.Filters;
import app.fedilab.android.client.Entities.ManageTimelines;
import app.fedilab.android.client.Entities.Results;
@ -83,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;
@ -103,6 +107,7 @@ import app.fedilab.android.fragments.WhoToFollowFragment;
import app.fedilab.android.helper.CrossActions;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MenuFloating;
import app.fedilab.android.interfaces.OnAdminActionInterface;
import app.fedilab.android.services.BackupStatusService;
import app.fedilab.android.services.LiveNotificationService;
import app.fedilab.android.sqlite.AccountDAO;
@ -279,7 +284,6 @@ public abstract class BaseMainActivity extends BaseActivity
viewPager = findViewById(R.id.viewpager);
display_timeline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -1081,7 +1085,20 @@ public abstract class BaseMainActivity extends BaseActivity
partnerShipItem.setVisible(false);
}
}
if( MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.MASTODON){
MenuItem adminItem = navigationView.getMenu().findItem(R.id.nav_administration);
if( adminItem != null){
adminItem.setVisible(false);
}
}else{
boolean display_admin_menu = sharedpreferences.getBoolean(Helper.SET_DISPLAY_ADMIN_MENU + userId + instance, false);
if( !display_admin_menu){
MenuItem adminItem = navigationView.getMenu().findItem(R.id.nav_administration);
if( adminItem != null){
adminItem.setVisible(false);
}
}
}
LinearLayout owner_container = headerLayout.findViewById(R.id.main_header_container);
owner_container.setOnClickListener(new View.OnClickListener() {
@Override
@ -1507,6 +1524,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);

View File

@ -114,12 +114,14 @@ public class LoginActivity extends BaseActivity {
private TextView instance_chosen;
private ImageView info_instance;
private final int PICK_IMPORT = 5557;
public static 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=")){
@ -547,7 +550,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);
}
@ -875,8 +882,14 @@ public class LoginActivity extends BaseActivity {
String queryString = Helper.CLIENT_ID + "="+ clientId;
queryString += "&" + Helper.REDIRECT_URI + "="+ Uri.encode(Helper.REDIRECT_CONTENT_WEB);
queryString += "&" + Helper.RESPONSE_TYPE +"=code";
if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED )
queryString += "&" + Helper.SCOPE +"=" + Helper.OAUTH_SCOPES;
if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED ) {
if( admin ) {
queryString += "&" + Helper.SCOPE + "=" + Helper.OAUTH_SCOPES_ADMIN;
}else{
queryString += "&" + Helper.SCOPE + "=" + Helper.OAUTH_SCOPES;
}
}
return Helper.instanceWithProtocol(context, instance) + Helper.EP_AUTHORIZE + "?" + queryString;
}

View File

@ -0,0 +1,76 @@
/* 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 <http://www.gnu.org/licenses>. */
package app.fedilab.android.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import app.fedilab.android.client.API;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.client.Entities.AdminAction;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.interfaces.OnAdminActionInterface;
/**
* Created by Thomas on 18/06/2019.
* Makes actions for post admin calls
*/
public class PostAdminActionAsyncTask extends AsyncTask<Void, Void, Void> {
private OnAdminActionInterface listener;
private API.adminAction action;
private String id;
private WeakReference<Context> contextReference;
private APIResponse apiResponse;
private AdminAction adminAction;
public PostAdminActionAsyncTask(Context context, API.adminAction action, String id, AdminAction adminAction, OnAdminActionInterface onAdminActionInterface){
this.contextReference = new WeakReference<>(context);
this.listener = onAdminActionInterface;
this.action = action;
this.id = id;
this.adminAction = adminAction;
}
@Override
protected Void doInBackground(Void... params) {
switch (action){
case GET_ACCOUNTS:
case GET_ONE_ACCOUNT:
case GET_REPORTS:
case GET_ONE_REPORT:
apiResponse = new API(contextReference.get()).adminGet(action, id, adminAction);
break;
default:
apiResponse = new API(contextReference.get()).adminDo(action, id, adminAction);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onAdminAction(apiResponse);
}
}

View File

@ -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;
@ -48,10 +49,13 @@ import java.util.Map;
import app.fedilab.android.R;
import app.fedilab.android.activities.LoginActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.asynctasks.PostAdminActionAsyncTask;
import app.fedilab.android.asynctasks.RetrieveOpenCollectiveAsyncTask;
import app.fedilab.android.asynctasks.UpdateAccountInfoAsyncTask;
import app.fedilab.android.client.Entities.Account;
import app.fedilab.android.client.Entities.AccountAdmin;
import app.fedilab.android.client.Entities.AccountCreation;
import app.fedilab.android.client.Entities.AdminAction;
import app.fedilab.android.client.Entities.Application;
import app.fedilab.android.client.Entities.Attachment;
import app.fedilab.android.client.Entities.Card;
@ -71,6 +75,7 @@ import app.fedilab.android.client.Entities.Peertube;
import app.fedilab.android.client.Entities.Poll;
import app.fedilab.android.client.Entities.PollOptions;
import app.fedilab.android.client.Entities.Relationship;
import app.fedilab.android.client.Entities.Report;
import app.fedilab.android.client.Entities.Results;
import app.fedilab.android.client.Entities.Schedule;
import app.fedilab.android.client.Entities.Status;
@ -114,6 +119,28 @@ public class API {
ACCOUNTS
}
public enum adminAction{
ENABLE,
APPROVE,
REJECT,
NONE,
SILENCE,
DISABLE,
UNSILENCE,
SUSPEND,
UNSUSPEND,
ASSIGN_TO_SELF,
UNASSIGN,
REOPEN,
RESOLVE,
GET_ACCOUNTS,
GET_ONE_ACCOUNT,
GET_REPORTS,
GET_ONE_REPORT
}
public enum StatusAction{
FAVOURITE,
UNFAVOURITE,
@ -149,12 +176,15 @@ public class API {
UPDATESERVERSCHEDULE,
DELETESCHEDULED,
REFRESHPOLL
}
public enum accountPrivacy {
PUBLIC,
LOCKED
}
public API(Context context) {
this.context = context;
if( context == null) {
@ -184,6 +214,246 @@ public class API {
APIError = null;
}
/**
* Execute admin get actions
* @param action type of the action
* @param id String can for an account or a status
* @return APIResponse
*/
public APIResponse adminGet(adminAction action, String id, AdminAction adminAction){
apiResponse = new APIResponse();
HashMap<String, String> params = null;
String endpoint = null;
switch (action){
case GET_ACCOUNTS:
params = new HashMap<>();
if( adminAction.isLocal())
params.put("local", String.valueOf(adminAction.isLocal()));
if( adminAction.isRemote() )
params.put("remote", String.valueOf(adminAction.isRemote()));
if( adminAction.isActive() )
params.put("active", String.valueOf(adminAction.isActive()));
if( adminAction.isPending() )
params.put("pending", String.valueOf(adminAction.isPending()));
if( adminAction.isDisabled() )
params.put("disabled", String.valueOf(adminAction.isDisabled()));
if( adminAction.isSilenced() )
params.put("silenced", String.valueOf(adminAction.isSilenced()));
if( adminAction.isSuspended() )
params.put("suspended", String.valueOf(adminAction.isSuspended()));
endpoint = "/admin/accounts";
break;
case GET_ONE_ACCOUNT:
endpoint = String.format("/admin/accounts/%s", id);
break;
case GET_REPORTS:
endpoint = "/admin/reports";
if( !adminAction.isUnresolved()) {
params = new HashMap<>();
params.put("resolved", "present");
}
break;
case GET_ONE_REPORT:
endpoint = String.format("/admin/reports/%s", id);
break;
}
try {
String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(endpoint), 60, params, prefKeyOauthTokenT);
switch (action){
case GET_ACCOUNTS:
List<AccountAdmin> accountAdmins = parseAccountAdminResponse(new JSONArray(response));
apiResponse.setAccountAdmins(accountAdmins);
break;
case GET_ONE_ACCOUNT:
AccountAdmin accountAdmin = parseAccountAdminResponse(context, new JSONObject(response));
accountAdmins = new ArrayList<>();
accountAdmins.add(accountAdmin);
apiResponse.setAccountAdmins(accountAdmins);
break;
case GET_REPORTS:
List<Report> reports = parseReportAdminResponse(new JSONArray(response));
apiResponse.setReports(reports);
break;
case GET_ONE_REPORT:
reports = new ArrayList<>();
Report report = parseReportAdminResponse(context, new JSONObject(response));
reports.add(report);
apiResponse.setReports(reports);
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return apiResponse;
}
/**
* Execute admin post actions
* @param action type of the action
* @param id String can for an account or a status
* @return APIResponse
*/
public APIResponse adminDo(adminAction action, String id, AdminAction adminAction){
apiResponse = new APIResponse();
String endpoint = null;
HashMap<String, String> params = null;
switch (action){
case ENABLE:
endpoint = String.format("/admin/accounts/%s/enable", id);
break;
case APPROVE:
endpoint = String.format("/admin/accounts/%s/approve", id);
break;
case REJECT:
endpoint = String.format("/admin/accounts/%s/reject", id);
break;
case UNSILENCE:
endpoint = String.format("/admin/accounts/%s/unsilence", id);
break;
case UNSUSPEND:
endpoint = String.format("/admin/accounts/%s/unsuspend", id);
break;
case ASSIGN_TO_SELF:
endpoint = String.format("/admin/reports/%s/assign_to_self", id);
break;
case UNASSIGN:
endpoint = String.format("/admin/reports/%s/unassign", id);
break;
case REOPEN:
endpoint = String.format("/admin/reports/%s/reopen", id);
break;
case RESOLVE:
endpoint = String.format("/admin/reports/%s/resolve", id);
break;
case NONE:
params = new HashMap<>();
params.put("type","none");
endpoint = String.format("/admin/accounts/%s/action", id);
params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
if( adminAction.getText() != null) {
params.put("text", adminAction.getText());
}
break;
case DISABLE:
params = new HashMap<>();
params.put("type","disable");
endpoint = String.format("/admin/accounts/%s/action", id);
params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
if( adminAction.getText() != null) {
params.put("text", adminAction.getText());
}
break;
case SILENCE:
params = new HashMap<>();
params.put("type","silence");
endpoint = String.format("/admin/accounts/%s/action", id);
params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
if( adminAction.getText() != null) {
params.put("text", adminAction.getText());
}
break;
case SUSPEND:
params = new HashMap<>();
params.put("type","suspend");
endpoint = String.format("/admin/accounts/%s/action", id);
params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
if( adminAction.getText() != null) {
params.put("text", adminAction.getText());
}
break;
}
try {
String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl(endpoint), 60, params, prefKeyOauthTokenT);
switch (action){
case ENABLE:
case APPROVE:
case REJECT:
case UNSILENCE:
// case UNDISABLE:
case UNSUSPEND:
List<AccountAdmin> accountAdmins = null;
try {
AccountAdmin accountAdmin = parseAccountAdminResponse(context, new JSONObject(response));
accountAdmin.setAction(action);
accountAdmins = new ArrayList<>();
accountAdmins.add(accountAdmin);
} catch (JSONException e) {
e.printStackTrace();
}
apiResponse.setAccountAdmins(accountAdmins);
break;
case ASSIGN_TO_SELF:
case UNASSIGN:
case REOPEN:
case RESOLVE:
List<Report> reports = null;
Report report;
try {
reports = new ArrayList<>();
report = parseReportAdminResponse(context, new JSONObject(response));
reports.add(report);
} catch (JSONException e) {
e.printStackTrace();
}
apiResponse.setReports(reports);
break;
case NONE:
accountAdmins = new ArrayList<>();
AccountAdmin accountAdmin = new AccountAdmin();
accountAdmin.setAction(action);
accountAdmins.add(accountAdmin);
apiResponse.setAccountAdmins(accountAdmins);
break;
case DISABLE:
accountAdmins = new ArrayList<>();
accountAdmin = new AccountAdmin();
accountAdmin.setDisabled(true);
accountAdmin.setAction(action);
accountAdmins.add(accountAdmin);
apiResponse.setAccountAdmins(accountAdmins);
break;
case SILENCE:
accountAdmins = new ArrayList<>();
accountAdmin = new AccountAdmin();
accountAdmin.setSilenced(true);
accountAdmin.setAction(action);
accountAdmins.add(accountAdmin);
apiResponse.setAccountAdmins(accountAdmins);
break;
case SUSPEND:
accountAdmins = new ArrayList<>();
accountAdmin = new AccountAdmin();
accountAdmin.setSuspended(true);
accountAdmin.setAction(action);
accountAdmins.add(accountAdmin);
apiResponse.setAccountAdmins(accountAdmins);
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
e.printStackTrace();
}
return apiResponse;
}
public InstanceNodeInfo getNodeInfo(String domain){
//Try to guess URL scheme for the onion instance
@ -327,6 +597,9 @@ public class API {
}
/***
* Get info on the current Instance *synchronously*
* @return APIResponse
@ -4814,6 +5087,151 @@ public class API {
return account;
}
/**
* Parse json response for list of reports for admins
* @param jsonArray JSONArray
* @return List<Report>
*/
private List<Report> parseReportAdminResponse(JSONArray jsonArray){
List<Report> reports = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length() ) {
JSONObject resobj = jsonArray.getJSONObject(i);
Report report = parseReportAdminResponse(context, resobj);
reports.add(report);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
return reports;
}
/**
* Parse json response an unique report for admins
* @param resobj JSONObject
* @return AccountAdmin
*/
private static Report parseReportAdminResponse(Context context, JSONObject resobj){
Report report = new Report();
try {
report.setId(resobj.getString("id"));
report.setAction_taken(resobj.getBoolean("action_taken"));
report.setComment(resobj.getString("comment"));
report.setCreated_at(Helper.mstStringToDate(context, resobj.getString("created_at")));
report.setUpdated_at(Helper.mstStringToDate(context, resobj.getString("updated_at")));
if( !resobj.isNull("account")) {
report.setAccount(parseAccountAdminResponse(context, resobj.getJSONObject("account")));
}
if( !resobj.isNull("target_account")) {
report.setTarget_account(parseAccountAdminResponse(context, resobj.getJSONObject("target_account")));
}
if( !resobj.isNull("assigned_account")) {
report.setAssigned_account(parseAccountAdminResponse(context, resobj.getJSONObject("assigned_account")));
}
if( !resobj.isNull("action_taken_by_account")) {
report.setAction_taken_by_account(parseAccountAdminResponse(context, resobj.getJSONObject("action_taken_by_account")));
}
report.setStatuses(parseStatuses(context, resobj.getJSONArray("statuses")));
}catch (Exception ignored){ignored.printStackTrace();}
return report;
}
/**
* Parse json response for list of accounts for admins
* @param jsonArray JSONArray
* @return List<AccountAdmin>
*/
private List<AccountAdmin> parseAccountAdminResponse(JSONArray jsonArray){
List<AccountAdmin> accountAdmins = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length() ) {
JSONObject resobj = jsonArray.getJSONObject(i);
AccountAdmin accountAdmin = parseAccountAdminResponse(context, resobj);
accountAdmins.add(accountAdmin);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
return accountAdmins;
}
/**
* Parse json response an unique account for admins
* @param resobj JSONObject
* @return Account
*/
private static AccountAdmin parseAccountAdminResponse(Context context, JSONObject resobj){
AccountAdmin accountAdmin = new AccountAdmin();
try {
accountAdmin.setId(resobj.get("id").toString());
accountAdmin.setUsername(resobj.getString("username"));
accountAdmin.setCreated_at(Helper.mstStringToDate(context, resobj.getString("created_at")));
accountAdmin.setEmail(resobj.getString("email"));
accountAdmin.setRole(resobj.getString("role"));
accountAdmin.setIp(resobj.getString("ip"));
accountAdmin.setDomain(resobj.getString("domain"));
accountAdmin.setAccount(parseAccountResponse(context, resobj.getJSONObject("account")));
if( !resobj.isNull("confirmed")) {
accountAdmin.setConfirmed(resobj.getBoolean("confirmed"));
}else{
accountAdmin.setConfirmed(true);
}
if( !resobj.isNull("suspended")) {
accountAdmin.setSuspended(resobj.getBoolean("suspended"));
}else{
accountAdmin.setSuspended(false);
}
if( !resobj.isNull("silenced")) {
accountAdmin.setSilenced(resobj.getBoolean("silenced"));
}else{
accountAdmin.setSilenced(false);
}
if( !resobj.isNull("disabled")) {
accountAdmin.setDisabled(resobj.getBoolean("disabled"));
}else{
accountAdmin.setDisabled(false);
}
if( !resobj.isNull("approved")) {
accountAdmin.setApproved(resobj.getBoolean("approved"));
}else{
accountAdmin.setApproved(false);
}
}catch (Exception ignored){}
return accountAdmin;
}
/**
* Parse json response for list of accounts
* @param jsonArray JSONArray
* @return List<Account>
*/
private List<Account> parseAccountResponse(JSONArray jsonArray){
List<Account> accounts = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length() ) {
JSONObject resobj = jsonArray.getJSONObject(i);
Account account = parseAccountResponse(context, resobj);
accounts.add(account);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
return accounts;
}
/**
* Parse json response an unique account
* @param resobj JSONObject
@ -5029,27 +5447,7 @@ public class API {
} catch (JSONException ignored) {}
return account;
}
/**
* Parse json response for list of accounts
* @param jsonArray JSONArray
* @return List<Account>
*/
private List<Account> parseAccountResponse(JSONArray jsonArray){
List<Account> accounts = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length() ) {
JSONObject resobj = jsonArray.getJSONObject(i);
Account account = parseAccountResponse(context, resobj);
accounts.add(account);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
return accounts;
}
/**

View File

@ -19,6 +19,7 @@ import android.content.Context;
import java.util.List;
import app.fedilab.android.client.Entities.Account;
import app.fedilab.android.client.Entities.AccountAdmin;
import app.fedilab.android.client.Entities.Conversation;
import app.fedilab.android.client.Entities.Emojis;
import app.fedilab.android.client.Entities.Error;
@ -31,6 +32,7 @@ import app.fedilab.android.client.Entities.Peertube;
import app.fedilab.android.client.Entities.PeertubeNotification;
import app.fedilab.android.client.Entities.Playlist;
import app.fedilab.android.client.Entities.Relationship;
import app.fedilab.android.client.Entities.Report;
import app.fedilab.android.client.Entities.Results;
import app.fedilab.android.client.Entities.Status;
import app.fedilab.android.client.Entities.StoredStatus;
@ -64,7 +66,8 @@ public class APIResponse {
private boolean fetchmore = false;
private List<String> playlistForVideos;
private List<InstanceReg> instanceRegs = null;
private List<AccountAdmin> accountAdmins = null;
private List<Report> reports = null;
public List<Account> getAccounts() {
return accounts;
@ -249,4 +252,20 @@ public class APIResponse {
public void setInstanceRegs(List<InstanceReg> instanceRegs) {
this.instanceRegs = instanceRegs;
}
public List<AccountAdmin> getAccountAdmins() {
return accountAdmins;
}
public void setAccountAdmins(List<AccountAdmin> accountAdmins) {
this.accountAdmins = accountAdmins;
}
public List<Report> getReports() {
return reports;
}
public void setReports(List<Report> reports) {
this.reports = reports;
}
}

View File

@ -120,6 +120,11 @@ public class Account implements Parcelable {
private String privacy = "public";
private boolean sensitive = false;
private String locale;
private String invite_request;
private String created_by_application_id;
private String invited_by_account_id;
@ -176,6 +181,10 @@ public class Account implements Parcelable {
dest.writeByte(this.isAdmin ? (byte) 1 : (byte) 0);
dest.writeString(this.privacy);
dest.writeByte(this.sensitive ? (byte) 1 : (byte) 0);
dest.writeString(this.locale);
dest.writeString(this.invite_request);
dest.writeString(this.created_by_application_id);
dest.writeString(this.invited_by_account_id);
}
public Account() {
@ -231,6 +240,10 @@ public class Account implements Parcelable {
this.isAdmin = in.readByte() != 0;
this.privacy = in.readString();
this.sensitive =in.readByte() != 0;
this.locale = in.readString();
this.invite_request = in.readString();
this.created_by_application_id = in.readString();
this.invited_by_account_id = in.readString();
}
public static final Creator<Account> CREATOR = new Creator<Account>() {
@ -406,6 +419,38 @@ public class Account implements Parcelable {
this.sensitive = sensitive;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getInvite_request() {
return invite_request;
}
public void setInvite_request(String invite_request) {
this.invite_request = invite_request;
}
public String getCreated_by_application_id() {
return created_by_application_id;
}
public void setCreated_by_application_id(String created_by_application_id) {
this.created_by_application_id = created_by_application_id;
}
public String getInvited_by_account_id() {
return invited_by_account_id;
}
public void setInvited_by_account_id(String invited_by_account_id) {
this.invited_by_account_id = invited_by_account_id;
}
public enum followAction{
FOLLOW,

View File

@ -0,0 +1,209 @@
package app.fedilab.android.client.Entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Date;
import app.fedilab.android.client.API;
public class AccountAdmin implements Parcelable {
private String id;
private String username;
private Date created_at;
private String email;
private String role;
private String ip;
private String domain;
private boolean confirmed;
private boolean suspended;
private boolean silenced;
private boolean disabled;
private Account account;
private API.adminAction action;
private boolean approved;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getCreated_at() {
return created_at;
}
public void setCreated_at(Date created_at) {
this.created_at = created_at;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public boolean isConfirmed() {
return confirmed;
}
public void setConfirmed(boolean confirmed) {
this.confirmed = confirmed;
}
public boolean isSuspended() {
return suspended;
}
public void setSuspended(boolean suspended) {
this.suspended = suspended;
}
public boolean isSilenced() {
return silenced;
}
public void setSilenced(boolean silenced) {
this.silenced = silenced;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public AccountAdmin() {
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public API.adminAction getAction() {
return action;
}
public void setAction(API.adminAction action) {
this.action = action;
}
public boolean isApproved() {
return approved;
}
public void setApproved(boolean approved) {
this.approved = approved;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.id);
dest.writeString(this.username);
dest.writeLong(this.created_at != null ? this.created_at.getTime() : -1);
dest.writeString(this.email);
dest.writeString(this.role);
dest.writeString(this.ip);
dest.writeString(this.domain);
dest.writeByte(this.confirmed ? (byte) 1 : (byte) 0);
dest.writeByte(this.suspended ? (byte) 1 : (byte) 0);
dest.writeByte(this.silenced ? (byte) 1 : (byte) 0);
dest.writeByte(this.disabled ? (byte) 1 : (byte) 0);
dest.writeParcelable(this.account, flags);
dest.writeInt(this.action == null ? -1 : this.action.ordinal());
dest.writeByte(this.approved ? (byte) 1 : (byte) 0);
}
protected AccountAdmin(Parcel in) {
this.id = in.readString();
this.username = in.readString();
long tmpCreated_at = in.readLong();
this.created_at = tmpCreated_at == -1 ? null : new Date(tmpCreated_at);
this.email = in.readString();
this.role = in.readString();
this.ip = in.readString();
this.domain = in.readString();
this.confirmed = in.readByte() != 0;
this.suspended = in.readByte() != 0;
this.silenced = in.readByte() != 0;
this.disabled = in.readByte() != 0;
this.account = in.readParcelable(Account.class.getClassLoader());
int tmpAction = in.readInt();
this.action = tmpAction == -1 ? null : API.adminAction.values()[tmpAction];
this.approved = in.readByte() != 0;
}
public static final Creator<AccountAdmin> CREATOR = new Creator<AccountAdmin>() {
@Override
public AccountAdmin createFromParcel(Parcel source) {
return new AccountAdmin(source);
}
@Override
public AccountAdmin[] newArray(int size) {
return new AccountAdmin[size];
}
};
}

View File

@ -1,5 +1,18 @@
package app.fedilab.android.client.Entities;
/* 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 <http://www.gnu.org/licenses>. */
public class AccountCreation {
private String username;

View File

@ -0,0 +1,121 @@
package app.fedilab.android.client.Entities;
import app.fedilab.android.client.API;
/* 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 <http://www.gnu.org/licenses>. */
public class AdminAction {
private API.adminAction type;
private boolean send_email_notification;
private String text;
private boolean unresolved;
private boolean local, remote, active, pending, disabled, silenced, suspended;
public boolean isLocal() {
return local;
}
public void setLocal(boolean local) {
this.local = local;
}
public boolean isRemote() {
return remote;
}
public void setRemote(boolean remote) {
this.remote = remote;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public boolean isPending() {
return pending;
}
public void setPending(boolean pending) {
this.pending = pending;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public boolean isSilenced() {
return silenced;
}
public void setSilenced(boolean silenced) {
this.silenced = silenced;
}
public boolean isSuspended() {
return suspended;
}
public void setSuspended(boolean suspended) {
this.suspended = suspended;
}
public boolean isUnresolved() {
return unresolved;
}
public void setUnresolved(boolean unresolved) {
this.unresolved = unresolved;
}
public boolean isSend_email_notification() {
return send_email_notification;
}
public void setSend_email_notification(boolean send_email_notification) {
this.send_email_notification = send_email_notification;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public API.adminAction getType() {
return type;
}
public void setType(API.adminAction type) {
this.type = type;
}
}

View File

@ -0,0 +1,168 @@
package app.fedilab.android.client.Entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Date;
import java.util.List;
public class Report implements Parcelable {
private String id;
private boolean action_taken;
private String comment;
private Date created_at;
private Date updated_at;
private AccountAdmin account;
private AccountAdmin target_account;
private AccountAdmin assigned_account;
private AccountAdmin action_taken_by_account;
private List<Status> statuses;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Date getCreated_at() {
return created_at;
}
public void setCreated_at(Date created_at) {
this.created_at = created_at;
}
public Date getUpdated_at() {
return updated_at;
}
public void setUpdated_at(Date updated_at) {
this.updated_at = updated_at;
}
public List<Status> getStatuses() {
return statuses;
}
public void setStatuses(List<Status> statuses) {
this.statuses = statuses;
}
public Report() {
}
public AccountAdmin getAccount() {
return account;
}
public void setAccount(AccountAdmin account) {
this.account = account;
}
public AccountAdmin getTarget_account() {
return target_account;
}
public void setTarget_account(AccountAdmin target_account) {
this.target_account = target_account;
}
public AccountAdmin getAssigned_account() {
return assigned_account;
}
public void setAssigned_account(AccountAdmin assigned_account) {
this.assigned_account = assigned_account;
}
public boolean isAction_taken() {
return action_taken;
}
public void setAction_taken(boolean action_taken) {
this.action_taken = action_taken;
}
public AccountAdmin getAction_taken_by_account() {
return action_taken_by_account;
}
public void setAction_taken_by_account(AccountAdmin action_taken_by_account) {
this.action_taken_by_account = action_taken_by_account;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.id);
dest.writeByte(this.action_taken ? (byte) 1 : (byte) 0);
dest.writeString(this.comment);
dest.writeLong(this.created_at != null ? this.created_at.getTime() : -1);
dest.writeLong(this.updated_at != null ? this.updated_at.getTime() : -1);
dest.writeParcelable(this.account, flags);
dest.writeParcelable(this.target_account, flags);
dest.writeParcelable(this.assigned_account, flags);
dest.writeParcelable(this.action_taken_by_account, flags);
dest.writeTypedList(this.statuses);
}
protected Report(Parcel in) {
this.id = in.readString();
this.action_taken = in.readByte() != 0;
this.comment = in.readString();
long tmpCreated_at = in.readLong();
this.created_at = tmpCreated_at == -1 ? null : new Date(tmpCreated_at);
long tmpUpdated_at = in.readLong();
this.updated_at = tmpUpdated_at == -1 ? null : new Date(tmpUpdated_at);
this.account = in.readParcelable(AccountAdmin.class.getClassLoader());
this.target_account = in.readParcelable(AccountAdmin.class.getClassLoader());
this.assigned_account = in.readParcelable(AccountAdmin.class.getClassLoader());
this.action_taken_by_account = in.readParcelable(AccountAdmin.class.getClassLoader());
this.statuses = in.createTypedArrayList(Status.CREATOR);
}
public static final Creator<Report> CREATOR = new Creator<Report>() {
@Override
public Report createFromParcel(Parcel source) {
return new Report(source);
}
@Override
public Report[] newArray(int size) {
return new Report[size];
}
};
}

View File

@ -0,0 +1,169 @@
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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
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.activities.AccountReportActivity;
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<AccountAdmin> accountAdmins;
private LayoutInflater layoutInflater;
private RetrieveAccountsAsyncTask.Type action;
private Context context;
private AccountsAdminListAdapter accountsAdminListAdapter;
private String targetedId;
public AccountsAdminListAdapter(Context context, List<AccountAdmin> 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());
Helper.loadGiF(context, account.getAvatar(), holder.account_pp);
holder.main_container.setOnClickListener(view ->{
Intent intent = new Intent(context, AccountReportActivity.class);
Bundle b = new Bundle();
b.putParcelable("targeted_account", accountAdmin);
intent.putExtras(b);
context.startActivity(intent);
});
}
@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 main_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);
main_container = itemView.findViewById(R.id.main_container);
}
}
}

View File

@ -179,12 +179,12 @@ public class AccountsListAdapter extends RecyclerView.Adapter implements OnPostA
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("@",""));
holder.account_dn.setText(account.getUsername().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()))
if( account.getUsername().equals(account.getAcct()))
holder.account_ac.setVisibility(View.GONE);
else
holder.account_ac.setVisibility(View.VISIBLE);

View File

@ -0,0 +1,176 @@
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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
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.activities.AccountReportActivity;
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<Report> reports;
private LayoutInflater layoutInflater;
private RetrieveAccountsAsyncTask.Type action;
private Context context;
private ReportsListAdapter reportsListAdapter;
private String targetedId;
public ReportsListAdapter(Context context, List<Report> 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.getAccount().getAccount();
Account target_account = report.getTarget_account().getAccount();
account.makeAccountNameEmoji(context, ReportsListAdapter.this, account);
target_account.makeAccountNameEmoji(context, ReportsListAdapter.this, target_account);
if( account.getdisplayNameSpan() == null || account.getdisplayNameSpan().toString().trim().equals("")) {
if( account.getDisplay_name() != null && !account.getDisplay_name().trim().equals(""))
holder.account_dn_reporter.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true));
else
holder.account_dn_reporter.setText(account.getUsername().replace("@",""));
}else
holder.account_dn_reporter.setText( account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE);
if( target_account.getdisplayNameSpan() == null || target_account.getdisplayNameSpan().toString().trim().equals("")) {
if( target_account.getDisplay_name() != null && !target_account.getDisplay_name().trim().equals(""))
holder.account_dn.setText(Helper.shortnameToUnicode(target_account.getDisplay_name(), true));
else
holder.account_dn.setText(target_account.getUsername().replace("@",""));
}else
holder.account_dn.setText( target_account.getdisplayNameSpan(), TextView.BufferType.SPANNABLE);
Helper.loadGiF(context, target_account.getAvatar(), holder.account_pp);
Helper.loadGiF(context, account.getAvatar(), holder.account_pp_reporter);
holder.account_ac.setText(target_account.getAcct());
holder.report_comment.setText(report.getComment());
if( report.getStatuses() != null){
holder.report_number_status.setText(String.valueOf(report.getStatuses().size()));
}else{
holder.report_number_status.setText("0");
}
holder.main_container.setOnClickListener(view ->{
Intent intent = new Intent(context, AccountReportActivity.class);
Bundle b = new Bundle();
b.putParcelable("report", report);
intent.putExtras(b);
context.startActivity(intent);
});
}
@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, account_pp_reporter;
TextView account_ac;
TextView account_dn, account_dn_reporter;
TextView report_comment, report_number_status;
LinearLayout main_container;
ViewHolder(View itemView) {
super(itemView);
account_pp = itemView.findViewById(R.id.account_pp);
account_pp_reporter = itemView.findViewById(R.id.account_pp_reporter);
account_dn = itemView.findViewById(R.id.account_dn);
account_dn_reporter = itemView.findViewById(R.id.account_dn_reporter);
account_ac = itemView.findViewById(R.id.account_ac);
report_comment = itemView.findViewById(R.id.report_comment);
main_container = itemView.findViewById(R.id.main_container);
report_number_status = itemView.findViewById(R.id.report_number_status);
}
}
}

View File

@ -97,6 +97,7 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.activities.AccountReportActivity;
import app.fedilab.android.client.API;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.client.Entities.Account;
@ -2361,7 +2362,14 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct
popup.getMenu().findItem(R.id.action_schedule_boost).setVisible(false);
popup.getMenu().findItem(R.id.action_mention).setVisible(false);
}
if( MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.MASTODON){
popup.getMenu().findItem(R.id.action_admin).setVisible(false);
}else{
boolean display_admin_statuses = sharedpreferences.getBoolean(Helper.SET_DISPLAY_ADMIN_STATUSES + userId + Helper.getLiveInstance(context), false);
if( !display_admin_statuses){
popup.getMenu().findItem(R.id.action_admin).setVisible(false);
}
}
boolean custom_sharing = sharedpreferences.getBoolean(Helper.SET_CUSTOM_SHARING, false);
if( custom_sharing && status.getVisibility().equals("public"))
@ -2393,7 +2401,14 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct
break;
case R.id.action_schedule_boost:
scheduleBoost(status);
return true;
case R.id.action_admin:
String account_id = status.getReblog() != null ? status.getReblog().getAccount().getId() : status.getAccount().getId();
Intent intent = new Intent(context, AccountReportActivity.class);
Bundle b = new Bundle();
b.putString("account_id", account_id);
intent.putExtras(b);
context.startActivity(intent);
return true;
case R.id.action_info:
tootInformation(status);

View File

@ -0,0 +1,99 @@
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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.os.Build;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import app.fedilab.android.R;
public class StatusReportAdapter extends RecyclerView.Adapter<StatusReportAdapter.ViewHolder> {
private List<String> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
public StatusReportAdapter(Context context, List<String> data) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
}
@NotNull
@Override
public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.drawer_status_report, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NotNull ViewHolder holder, int position) {
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(mData.get(position), Html.FROM_HTML_MODE_LEGACY).toString();
else
content = Html.fromHtml(mData.get(position)).toString();
holder.report_content.setText(content);
}
// total number of rows
@Override
public int getItemCount() {
return mData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView report_content;
ViewHolder(View itemView) {
super(itemView);
report_content = itemView.findViewById(R.id.report_content);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
String getItem(int id) {
return mData.get(id);
}
void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}

View File

@ -0,0 +1,259 @@
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 <http://www.gnu.org/licenses>. */
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.client.Entities.AdminAction;
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 accounts
*/
public class DisplayAdminAccountsFragment extends Fragment implements OnAdminActionInterface {
private boolean flag_loading;
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private AccountsAdminListAdapter accountsAdminListAdapter;
private String max_id;
private List<AccountAdmin> accountAdmins;
private RetrieveAccountsAsyncTask.Type type;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private boolean swiped;
private RecyclerView lv_admin_accounts;
private boolean local, remote, active, pending, disabled, silenced, suspended;
@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<>();
Bundle bundle = this.getArguments();
if (bundle != null) {
local = bundle.getBoolean("local", false);
remote = bundle.getBoolean("remote", false);
active = bundle.getBoolean("active", false);
pending = bundle.getBoolean("pending", false);
disabled = bundle.getBoolean("disabled", false);
silenced = bundle.getBoolean("silenced", false);
suspended = bundle.getBoolean("suspended", false);
}
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;
AdminAction adminAction = new AdminAction();
adminAction.setLocal(local);
adminAction.setRemote(remote);
adminAction.setActive(active);
adminAction.setPending(pending);
adminAction.setDisabled(disabled);
adminAction.setSilenced(silenced);
adminAction.setSuspended(suspended);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_ACCOUNTS, null, adminAction, 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;
AdminAction adminAction = new AdminAction();
adminAction.setLocal(local);
adminAction.setRemote(remote);
adminAction.setActive(active);
adminAction.setPending(pending);
adminAction.setDisabled(disabled);
adminAction.setSilenced(silenced);
adminAction.setSuspended(suspended);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_ACCOUNTS, null, adminAction, 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;
}
AdminAction adminAction = new AdminAction();
adminAction.setLocal(local);
adminAction.setRemote(remote);
adminAction.setActive(active);
adminAction.setPending(pending);
adminAction.setDisabled(disabled);
adminAction.setSilenced(silenced);
adminAction.setSuspended(suspended);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_ACCOUNTS, null, adminAction, DisplayAdminAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onCreate(Bundle saveInstance)
{
super.onCreate(saveInstance);
}
/**
* Refresh accounts in list
*/
public void refreshFilter(){
accountsAdminListAdapter.notifyDataSetChanged();
}
@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<AccountAdmin> 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;
}
}

View File

@ -0,0 +1,262 @@
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 <http://www.gnu.org/licenses>. */
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.AdminAction;
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<Void, Void, Void> asyncTask;
private ReportsListAdapter reportsListAdapter;
private String max_id;
private List<Report> reports;
private RetrieveAccountsAsyncTask.Type type;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private boolean swiped;
private RecyclerView lv_reports;
private boolean unresolved;
@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<>();
Bundle bundle = this.getArguments();
if (bundle != null) {
unresolved = bundle.getBoolean("unresolved", true);
}
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;
AdminAction adminAction = new AdminAction();
adminAction.setUnresolved(unresolved);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, adminAction, 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;
AdminAction adminAction = new AdminAction();
adminAction.setUnresolved(unresolved);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, adminAction, 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;
}
AdminAction adminAction = new AdminAction();
adminAction.setUnresolved(unresolved);
asyncTask = new PostAdminActionAsyncTask(context, API.adminAction.GET_REPORTS, null, adminAction, DisplayAdminReportsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onCreate(Bundle saveInstance)
{
super.onCreate(saveInstance);
}
/**
* Refresh report in list
*/
public void refreshFilter(){
reportsListAdapter.notifyDataSetChanged();
}
@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){
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.AdminDialog);
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();
}else{
Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
}
return;
}
flag_loading = (apiResponse.getMax_id() == null );
List<Report> 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;
}
}

View File

@ -342,6 +342,41 @@ public class SettingsFragment extends Fragment {
}
});
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
boolean display_admin_menu = sharedpreferences.getBoolean(Helper.SET_DISPLAY_ADMIN_MENU + userId + instance, false);
final CheckBox set_display_admin_menu = rootView.findViewById(R.id.set_display_admin_menu);
set_display_admin_menu.setChecked(display_admin_menu);
set_display_admin_menu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(Helper.SET_DISPLAY_ADMIN_MENU + userId + instance, set_display_admin_menu.isChecked());
editor.apply();
}
});
boolean display_admin_statuses = sharedpreferences.getBoolean(Helper.SET_DISPLAY_ADMIN_STATUSES + userId + instance, false);
final CheckBox set_display_admin_statuses = rootView.findViewById(R.id.set_display_admin_statuses);
set_display_admin_statuses.setChecked(display_admin_statuses);
set_display_admin_statuses.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(Helper.SET_DISPLAY_ADMIN_STATUSES + userId + instance, set_display_admin_statuses.isChecked());
editor.apply();
}
});
if( MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.MASTODON){
LinearLayout admin_container = rootView.findViewById(R.id.admin_container);
if( admin_container != null){
admin_container.setVisibility(View.GONE);
}
}
boolean show_media_urls = sharedpreferences.getBoolean(Helper.SET_MEDIA_URLS, false);
final CheckBox set_auto_add_media_url = rootView.findViewById(R.id.set_auto_add_media_url);
@ -1022,8 +1057,7 @@ public class SettingsFragment extends Fragment {
LinearLayout toot_visibility_container = rootView.findViewById(R.id.toot_visibility_container);
SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
final Account account = new AccountDAO(context, db).getUniqAccount(userId, instance);
final ImageView set_toot_visibility = rootView.findViewById(R.id.set_toot_visibility);
if( theme == Helper.THEME_DARK){

View File

@ -228,6 +228,7 @@ public class Helper {
public static final String TAG = "mastodon_etalab";
public static final String CLIENT_NAME_VALUE = "Fedilab";
public static final String OAUTH_SCOPES = "read write follow";
public static final String OAUTH_SCOPES_ADMIN = "read write follow admin:read admin:write admin";
public static final String OAUTH_SCOPES_PIXELFED = "write follow";
public static final String OAUTH_SCOPES_PEERTUBE = "user";
public static final String PREF_KEY_OAUTH_TOKEN = "oauth_token";
@ -348,6 +349,9 @@ public class Helper {
public static final String SET_DISPLAY_TIMELINE_IN_LIST = "set_display_timeline_in_list";
public static final String SET_ONION_SCHEME = "set_onion_scheme";
public static final String SET_REMEMBER_POSITION_HOME = "set_remember_position";
public static final String SET_DISPLAY_ADMIN_MENU = "set_display_admin_menu";
public static final String SET_DISPLAY_ADMIN_STATUSES = "set_display_admin_statuses";
public static final int S_NO = 0;
static final int S_512KO = 1;
public static final int S_1MO = 2;

View File

@ -0,0 +1,25 @@
/* 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 <http://www.gnu.org/licenses>. */
package app.fedilab.android.interfaces;
import app.fedilab.android.client.APIResponse;
/**
* Created by Thomas on 08/06/2019.
* Interface when an admin action is done GET/POST
*/
public interface OnAdminActionInterface {
void onAdminAction(APIResponse apiResponse);
}

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#14161B"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z"/>
</vector>

View File

@ -155,6 +155,54 @@
android:text="@string/set_display_news_from_fedilab"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/admin_container">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:background="?colorAccent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/administration"
android:textColor="?colorAccent"
android:textSize="16sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:background="?colorAccent" />
</LinearLayout>
<CheckBox
android:id="@+id/set_display_admin_menu"
android:layout_width="wrap_content"
android:layout_marginTop="@dimen/settings_checkbox_margin"
android:layout_marginBottom="@dimen/settings_checkbox_margin"
android:text="@string/set_display_admin_menu"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_admin_statuses"
android:layout_width="wrap_content"
android:layout_marginTop="@dimen/settings_checkbox_margin"
android:layout_marginBottom="@dimen/settings_checkbox_margin"
android:text="@string/set_display_admin_toot"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
tools:context="app.fedilab.android.activities.AdminActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<com.google.android.material.tabs.TabLayout
android:id="@+id/admin_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabSelectedTextColor="?attr/colorAccent"
app:tabTextColor="?attr/textColor"
app:tabMode="fixed"
app:tabGravity="fill"
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/admin_viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,390 @@
<?xml version="1.0" encoding="utf-8"?><!--
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 <http://www.gnu.org/licenses>.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:fitsSystemWindows="true"
tools:context="app.fedilab.android.activities.AdminActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<Button
android:id="@+id/assign"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/status"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reject"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="assign,status"
android:id="@+id/assign_status_buttons_barrier"/>
<TextView
android:id="@+id/permissions_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/permissions"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/assign_status_buttons_barrier"/>
<TextView
android:id="@+id/username_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/account"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/permissions_label" />
<TextView
android:id="@+id/email_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/email"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/username_label" />
<TextView
android:id="@+id/email_status_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/email_status"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/email_label" />
<TextView
android:id="@+id/login_status_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/login_status"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/email_status_label" />
<TextView
android:id="@+id/joined_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/joined"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/login_status_label" />
<TextView
android:id="@+id/recent_ip_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/recent_ip"
app:layout_constraintEnd_toStartOf="@id/stats_label_end_barrier"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/joined_label" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/stats_label_end_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="permissions_label,email_label,email_status_label,login_status_label,joined_label,recent_ip_label" />
<TextView
android:id="@+id/permissions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/permissions_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/permissions_label" />
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/username_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/username_label" />
<TextView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/email_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/email_label" />
<TextView
android:id="@+id/email_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/email_status_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/email_status_label" />
<TextView
android:id="@+id/login_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toBottomOf="@id/email_status"
app:layout_constraintTop_toTopOf="@id/login_status_label" />
<TextView
android:id="@+id/joined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/joined_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/joined_label" />
<TextView
android:id="@+id/recent_ip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@id/recent_ip_label"
app:layout_constraintStart_toEndOf="@id/stats_label_end_barrier"
app:layout_constraintTop_toTopOf="@id/recent_ip_label" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/stats_bottom_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="recent_ip,recent_ip_label" />
<Button
android:id="@+id/reject"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:text="@string/reject"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/allow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/stats_bottom_barrier" />
<Button
android:id="@+id/allow"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:text="@string/allow"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reject"
app:layout_constraintTop_toBottomOf="@id/stats_bottom_barrier" />
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="reject,allow"
android:id="@+id/allow_reject_group"/>
<androidx.constraintlayout.widget.Barrier
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="reject,allow"
android:id="@+id/allow_reject_buttons_barrier"/>
<Button
android:id="@+id/warn"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:text="@string/warn"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/disable"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/allow_reject_buttons_barrier" />
<Button
android:id="@+id/disable"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:text="@string/disable"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/warn"
app:layout_constraintTop_toBottomOf="@id/allow_reject_buttons_barrier" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/warn_disable_buttons_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="warn,disable" />
<Button
android:id="@+id/silence"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:text="@string/silence"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/suspend"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/warn_disable_buttons_barrier" />
<Button
android:id="@+id/suspend"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:text="@string/suspend"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/silence"
app:layout_constraintTop_toBottomOf="@id/warn_disable_buttons_barrier" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/buttons_bottom_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="warn,disable,silence" />
<CheckBox
android:id="@+id/email_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:checked="true"
android:text="@string/email_user"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/buttons_bottom_barrier" />
<TextView
android:id="@+id/comment_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:labelFor="@+id/comment"
android:text="@string/custom_warning"
app:layout_constraintTop_toBottomOf="@id/email_user" />
<EditText
android:id="@+id/comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minLines="3"
app:layout_constraintTop_toBottomOf="@id/comment_label" />
<TextView
android:id="@+id/lv_statuses_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/list_reported_statuses"
app:layout_constraintTop_toBottomOf="@id/comment" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_statuses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@null"
android:scrollbars="none"
app:layout_constraintTop_toBottomOf="@id/lv_statuses_label" />
<androidx.constraintlayout.widget.Group
android:id="@+id/statuses_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="lv_statuses_label,lv_statuses" />
<!--
<Button
android:id="@+id/action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/perform_action"
android:textSize="18sp" />
-->
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/main_container"
android:orientation="horizontal">
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/account_pp"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@string/profile_picture"/>
<LinearLayout
android:layout_marginStart="20dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/account_container"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/account_dn"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:id="@+id/account_un"
android:maxLines="1"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
/>
</LinearLayout>
<TextView
android:layout_marginTop="10dp"
android:visibility="gone"
android:id="@+id/account_ac"
android:textSize="16sp"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/report_action_taken"
android:textColor="?attr/colorAccent"
android:text="@string/request_sent"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_gravity="center"
android:gravity="center"
/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/main_container"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/account_pp"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@string/profile_picture"/>
<LinearLayout
android:layout_marginStart="20dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/account_container"
android:orientation="vertical">
<TextView
android:id="@+id/account_dn"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:layout_marginTop="10dp"
android:id="@+id/account_ac"
android:textSize="16sp"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/account_pp_reporter"
android:layout_width="40dp"
android:layout_height="40dp"
android:contentDescription="@string/profile_picture"/>
<TextView
android:layout_marginStart="10dp"
android:id="@+id/account_dn_reporter"
android:maxLines="1"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/report_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:src="@drawable/ic_mode_comment_white"
android:layout_width="20dp"
android:layout_height="20dp"
/>
<TextView
android:layout_marginStart="5dp"
android:id="@+id/report_number_status"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/report_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Listview Accounts -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:id="@+id/swipeContainer"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_admin_accounts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<RelativeLayout
android:id="@+id/no_action"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/no_action_text"
android:padding="10dp"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"
android:textStyle="italic|bold"
android:typeface="serif"
android:text="@string/no_accounts"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loader"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<!-- Loader for next accounts -->
<RelativeLayout
android:id="@+id/loading_next_accounts"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center_horizontal"
android:gravity="bottom|center_horizontal"
android:layout_height="20dp">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Listview Accounts -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:id="@+id/swipeContainer"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_reports"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<RelativeLayout
android:id="@+id/no_action"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/no_action_text"
android:padding="10dp"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"
android:textStyle="italic|bold"
android:typeface="serif"
android:text="@string/no_reports"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout
android:id="@+id/loader"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<!-- Loader for next accounts -->
<RelativeLayout
android:id="@+id/loading_next_accounts"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center_horizontal"
android:gravity="bottom|center_horizontal"
android:layout_height="20dp">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

View File

@ -158,6 +158,59 @@
android:text="@string/set_display_news_from_fedilab"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/admin_container">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:background="?colorAccent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/administration"
android:textColor="?colorAccent"
android:textSize="16sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:background="?colorAccent" />
</LinearLayout>
<CheckBox
android:id="@+id/set_display_admin_menu"
android:layout_width="wrap_content"
android:layout_marginTop="@dimen/settings_checkbox_margin"
android:layout_marginBottom="@dimen/settings_checkbox_margin"
android:text="@string/set_display_admin_menu"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_admin_statuses"
android:layout_width="wrap_content"
android:layout_marginTop="@dimen/settings_checkbox_margin"
android:layout_marginBottom="@dimen/settings_checkbox_margin"
android:text="@string/set_display_admin_toot"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -58,6 +58,10 @@
android:id="@+id/nav_drag_timelines"
android:icon="@drawable/ic_drag_handle_menu"
android:title="@string/reorder_timelines" />
<item
android:id="@+id/nav_administration"
android:icon="@drawable/ic_security_admin"
android:title="@string/administration" />
</group>
</menu>
</item>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_local"
android:checkable="true"
android:title="@string/local"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_remote"
android:checkable="true"
android:title="@string/remote"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_active"
android:checkable="true"
android:title="@string/active"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_pending"
android:checkable="true"
android:title="@string/pending"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_disabled"
android:checkable="true"
android:title="@string/disabled"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_silenced"
android:checkable="true"
android:title="@string/silenced"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/action_suspended"
android:checkable="true"
android:title="@string/suspended"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
</menu>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_unresolved_reports"
android:checkable="true"
android:checked="true"
android:title="@string/unresolved"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
</menu>

View File

@ -10,6 +10,10 @@
android:title="@string/bookmark_add"
android:actionLayout="@layout/bookmark_layout"
app:showAsAction="never" />
<item
android:id="@+id/action_admin"
android:title="@string/administration"
app:showAsAction="never" />
<item
android:id="@+id/action_info"
android:title="@string/information"

View File

@ -1028,6 +1028,57 @@
<b>Important</b>: If your instance required validation, you will receive an email once it is validated!
</string>
<string name="save_draft">Save the message in drafts?</string>
<string name="administration">Administration</string>
<string name="reports">Reports</string>
<string name="no_reports">No reports to display!</string>
<string name="reconnect_account">Reconnect the account</string>
<string name="reconnect_account_message">The application failed to access the admin features. You might need to reconnect the account for having the correct scope.</string>
<string name="unresolved">Unresolved</string>
<string name="resolved">Resolved</string>
<string name="remote">Remote</string>
<string name="active">Active</string>
<string name="pending">Pending</string>
<string name="disabled">Disabled</string>
<string name="silenced">Silenced</string>
<string name="suspended">Suspended</string>
<string name="permissions">Permissions</string>
<string name="email_status">Email status</string>
<string name="login_status">Login status</string>
<string name="joined">Joined</string>
<string name="recent_ip">Most recent IP</string>
<string name="warn">Warn</string>
<string name="disable">Disable</string>
<string name="silence">Silence</string>
<string name="email_user">Notify the user per e-mail</string>
<string name="perform_action">Perform action</string>
<string name="custom_warning">Custom warning</string>
<string name="user">User</string>
<string name="moderator">Moderator</string>
<string name="administrator">Administrator</string>
<string name="confirmed">Confirmed</string>
<string name="unconfirmed">Not confirmed</string>
<string name="list_reported_statuses">Reported statuses</string>
<string name="account">Account</string>
<string name="unsilence">Undo silence</string>
<string name="undisable">Undo disable</string>
<string name="suspend">Suspend</string>
<string name="unsuspend">Undo suspend</string>
<string name="account_silenced">The account is silenced!</string>
<string name="account_unsilenced">The account is no longer silenced!</string>
<string name="account_suspended">The account is suspended!</string>
<string name="account_unsuspended">The account is no longer suspended!</string>
<string name="account_disabled">The account is disabled!</string>
<string name="account_undisabled">The account is no longer disabled!</string>
<string name="account_warned">The account has been warned!</string>
<string name="set_display_admin_menu">Display the admin menu</string>
<string name="set_display_admin_toot">Display the admin feature in statuses</string>
<string name="allow">Allow</string>
<string name="account_approved">The account is approved!</string>
<string name="account_rejected">The account is rejected!</string>
<string name="assign_to_me">Assign to me</string>
<string name="unassign">Unassign</string>
<string name="mark_resolved">Mark as resolved</string>
<string name="mark_unresolved">Mark as unresolved</string>
<plurals name="number_of_vote">
<item quantity="one">%d vote</item>
<item quantity="other">%d votes</item>

View File

@ -392,4 +392,48 @@
<style name="Shape.Dark" parent="AppThemeDark">
<item name="android:background">@drawable/shape_border_bottom_settings_dark</item>
</style>
<!-- Light theme -->
<style name="AppAdminTheme" parent="Theme.AppCompat">
<item name="android:textColor">@color/dark_text</item>
<item name="colorPrimary">@color/mastodonC1</item>
<item name="colorPrimaryDark">@color/mastodonC1</item>
<item name="colorAccent">@color/mastodonC4</item>
<item name="cardBorder">@color/cardBorderLight</item>
<item name="textColor">@color/dark_text</item>
<item name="android:buttonStyle">@style/ButtonColor</item>
<item name="headerbg">@style/Header.Light</item>
<item name="android:windowBackground">@color/mastodonC1__</item>
<item name="shapeBorder">@style/Shape.Light</item>
<item name="imgbd">@style/Image.Border.Light</item>
<item name="borderless">@style/borderLessLightStyle</item>
<item name="borderlessColored">@style/borderlessColoredStyle</item>
<item name="windowActionModeOverlay">true</item>
<item name="popupOverlay">@style/AppThemeDark.PopupOverlay</item>
<item name="android:spinnerStyle">@style/SpinnerStyle</item>
<item name="android:spinnerItemStyle">@style/SpinnerItemStyle</item>
<item name="android:popupMenuStyle">@style/DarkPopupMenu</item>
<item name="android:itemBackground">@drawable/menu_selector_dark</item>
<item name="android:actionModeStyle">@style/DarkActionMode</item>
<item name="android:dropDownListViewStyle">@style/DarkdropDownListViewStyle</item>
<item name="actionBarTextColor">@color/dark_text</item>
<item name="color_in_account_header">@color/black</item>
<item name="colorBackgroundFloating">@color/mastodonC1__</item>
<item name="android:colorBackground">@color/mastodonC1__</item>
<item name="android:actionOverflowButtonStyle">@style/Widget.Admin.ActionButton.Overflow</item>
</style>
<style name="Widget.Admin.ActionButton.Overflow" parent="Base.Widget.AppCompat.ActionButton.Overflow">
<item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>
</style>
<style name="AdminAlertDialogDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:windowBackground">@color/mastodonC1__</item>
</style>
<style name="AdminDialog" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:windowBackground">@color/mastodonC1__</item>
<item name="colorAccent">@color/mastodonC4</item>
<item name="android:headerBackground">@color/mastodonC2</item>
<item name="colorControlNormal">@color/mastodonC2</item>
</style>
</resources>