Blocked domains

This commit is contained in:
stom79 2018-09-26 18:30:08 +02:00
parent c33aae346f
commit 01e53f8e48
11 changed files with 672 additions and 0 deletions

View File

@ -97,6 +97,7 @@ import fr.gouv.etalab.mastodon.fragments.DisplayDraftsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayFiltersFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayListsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayMutedInstanceFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayScheduledTootsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplaySearchFragment;
@ -1487,6 +1488,12 @@ public abstract class BaseMainActivity extends BaseActivity
fragmentTag = "BLOCKS";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, accountsFragment, fragmentTag).commit();
}else if (id == R.id.nav_blocked_domains) {
toot.hide();
DisplayMutedInstanceFragment displayMutedInstanceFragment = new DisplayMutedInstanceFragment();
fragmentTag = "BLOCKED_DOMAINS";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, displayMutedInstanceFragment, fragmentTag).commit();
}else if (id == R.id.nav_muted) {
toot.hide();
accountsFragment = new DisplayAccountsFragment();

View File

@ -0,0 +1,58 @@
/* Copyright 2018 Thomas Schneider
*
* This file is a part of Mastalab
*
* 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.
*
* Mastalab 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 Mastalab; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveDomainsInterface;
/**
* Created by Thomas on 26/09/2018.
* Retrieves blocked instance
*/
public class RetrieveDomainsAsyncTask extends AsyncTask<Void, Void, Void> {
private OnRetrieveDomainsInterface listener;
private WeakReference<Context> contextReference;
private APIResponse apiResponse;
private String max_id;
public RetrieveDomainsAsyncTask(Context context, String max_id, OnRetrieveDomainsInterface onRetrieveDomainsInterface){
this.contextReference = new WeakReference<>(context);
this.listener = onRetrieveDomainsInterface;
this.max_id = max_id;
}
@Override
protected Void doInBackground(Void... params) {
API api = new API(this.contextReference.get());
apiResponse = api.getBlockedDomain(max_id);
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveDomains(apiResponse);
}
}

View File

@ -64,6 +64,7 @@ public class API {
private String prefKeyOauthTokenT;
private APIResponse apiResponse;
private Error APIError;
private List<String> domains;
public enum StatusAction{
FAVOURITE,
@ -775,6 +776,95 @@ public class API {
}
/**
* Retrieves blocked domains for the authenticated account *synchronously*
* @param max_id String id max
* @return APIResponse
*/
@SuppressWarnings("SameParameterValue")
public APIResponse getBlockedDomain(String max_id){
HashMap<String, String> params = new HashMap<>();
if( max_id != null )
params.put("max_id", max_id);
params.put("limit","80");
domains = new ArrayList<>();
try {
HttpsConnection httpsConnection = new HttpsConnection(context);
String response = httpsConnection.get(getAbsoluteUrl("/domain_blocks"), 60, params, prefKeyOauthTokenT);
apiResponse.setSince_id(httpsConnection.getSince_id());
apiResponse.setMax_id(httpsConnection.getMax_id());
domains = parseDomains(new JSONArray(response));
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
apiResponse.setDomains(domains);
return apiResponse;
}
/**
* Add a blocked domains for the authenticated account *synchronously*
* @param domain String domain name
*/
@SuppressWarnings("SameParameterValue")
public int addBlockedDomain(String domain){
HashMap<String, String> params = new HashMap<>();
params.put("domain",domain);
domains = new ArrayList<>();
HttpsConnection httpsConnection;
try {
httpsConnection = new HttpsConnection(context);
httpsConnection.post(getAbsoluteUrl("/domain_blocks"), 60, params, prefKeyOauthTokenT);
actionCode = httpsConnection.getActionCode();
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return actionCode;
}
/**
* Delete a blocked domains for the authenticated account *synchronously*
* @param domain String domain name
*/
@SuppressWarnings("SameParameterValue")
public int deleteBlockedDomain(String domain){
HashMap<String, String> params = new HashMap<>();
params.put("domain",domain);
domains = new ArrayList<>();
HttpsConnection httpsConnection;
try {
httpsConnection = new HttpsConnection(context);
httpsConnection.delete(getAbsoluteUrl("/domain_blocks"), 60, params, prefKeyOauthTokenT);
actionCode = httpsConnection.getActionCode();
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return actionCode;
}
/**
* Retrieves follow requests for the authenticated account *synchronously*
* @param max_id String id max
@ -2033,6 +2123,22 @@ public class API {
}
/**
* Parse Domains
* @param jsonArray JSONArray
* @return List<String> of domains
*/
private List<String> parseDomains(JSONArray jsonArray){
List<String> list_tmp = new ArrayList<>();
for(int i = 0; i < jsonArray.length(); i++){
try {
list_tmp.add(jsonArray.getString(i));
} catch (JSONException ignored) {}
}
return list_tmp;
}
/**
* Parse Tags
* @param jsonArray JSONArray

View File

@ -31,6 +31,7 @@ public class APIResponse {
private List<Notification> notifications = null;
private List<Relationship> relationships = null;
private List<Filters> filters = null;
private List<String> domains = null;
private List<fr.gouv.etalab.mastodon.client.Entities.List> lists = null;
private List<Emojis> emojis = null;
private fr.gouv.etalab.mastodon.client.Entities.Error error = null;
@ -132,4 +133,12 @@ public class APIResponse {
public void setFilters(List<Filters> filters) {
this.filters = filters;
}
public List<String> getDomains() {
return domains;
}
public void setDomains(List<String> domains) {
this.domains = domains;
}
}

View File

@ -0,0 +1,126 @@
package fr.gouv.etalab.mastodon.drawers;
/* Copyright 2018 Thomas Schneider
*
* This file is a part of Mastalab
*
* 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.
*
* Mastalab 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 Mastalab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.List;
import fr.gouv.etalab.mastodon.R;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveDomainsInterface;
/**
* Created by Thomas on 26/09/2018.
* Adapter for domains
*/
public class DomainsListAdapter extends RecyclerView.Adapter implements OnRetrieveDomainsInterface {
private List<String> domains;
private LayoutInflater layoutInflater;
private Context context;
private DomainsListAdapter domainsListAdapter;
private RelativeLayout textviewNoAction;
public DomainsListAdapter(Context context, List<String> domains, RelativeLayout textviewNoAction){
this.context = context;
layoutInflater = LayoutInflater.from(context);
this.domains = domains;
this.domainsListAdapter = this;
this.textviewNoAction = textviewNoAction;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(layoutInflater.inflate(R.layout.drawer_domain, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final DomainsListAdapter.ViewHolder holder = (DomainsListAdapter.ViewHolder) viewHolder;
final String domain = domains.get(position);
holder.domain_name.setText(domain);
holder.domain_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(context.getString(R.string.unblock_domain_confirm_message, domain));
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.unblock_domain_confirm)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
domains.remove(domain);
domainsListAdapter.notifyItemRemoved(holder.getAdapterPosition());
if( domains.size() == 0 && textviewNoAction != null && textviewNoAction.getVisibility() == View.GONE)
textviewNoAction.setVisibility(View.VISIBLE);
dialog.dismiss();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
});
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return domains.size();
}
@Override
public void onRetrieveDomains(APIResponse apiResponse) {
}
private class ViewHolder extends RecyclerView.ViewHolder{
TextView domain_name;
ImageView domain_delete;
ViewHolder(View itemView) {
super(itemView);
domain_name = itemView.findViewById(R.id.domain_name);
domain_delete = itemView.findViewById(R.id.domain_delete);
}
}
}

View File

@ -0,0 +1,205 @@
package fr.gouv.etalab.mastodon.fragments;
/* Copyright 2018 Thomas Schneider
*
* This file is a part of Mastalab
*
* 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.
*
* Mastalab 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 Mastalab; 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.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.R;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveDomainsAsyncTask;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.drawers.DomainsListAdapter;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveDomainsInterface;
/**
* Created by Thomas on 26/09/2018.
* Fragment to display muted instances
*/
public class DisplayMutedInstanceFragment extends Fragment implements OnRetrieveDomainsInterface {
private boolean flag_loading;
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private DomainsListAdapter domainsListAdapter;
private String max_id;
private List<String> domains;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private boolean swiped;
private RecyclerView lv_domains;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_muted_instances, container, false);
context = getContext();
Bundle bundle = this.getArguments();
domains = new ArrayList<>();
max_id = null;
firstLoad = true;
flag_loading = true;
swiped = false;
swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer);
lv_domains = rootView.findViewById(R.id.lv_domains);
lv_domains.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
mainLoader = rootView.findViewById(R.id.loader);
nextElementLoader = rootView.findViewById(R.id.loading_next_domains);
textviewNoAction = rootView.findViewById(R.id.no_action);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
domainsListAdapter = new DomainsListAdapter(context, this.domains, textviewNoAction);
lv_domains.setAdapter(domainsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(context);
lv_domains.setLayoutManager(mLayoutManager);
lv_domains.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if(dy > 0) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (firstVisibleItem + visibleItemCount == totalItemCount) {
if (!flag_loading) {
flag_loading = true;
asyncTask = new RetrieveDomainsAsyncTask(context, max_id, DisplayMutedInstanceFragment.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;
domains = new ArrayList<>();
firstLoad = true;
flag_loading = true;
swiped = true;
asyncTask = new RetrieveDomainsAsyncTask(context, max_id, DisplayMutedInstanceFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
});
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
switch (theme){
case Helper.THEME_LIGHT:
swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4,
R.color.mastodonC2,
R.color.mastodonC3);
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.white));
break;
case Helper.THEME_DARK:
swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4__,
R.color.mastodonC4,
R.color.mastodonC4);
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.mastodonC1_));
break;
case Helper.THEME_BLACK:
swipeRefreshLayout.setColorSchemeResources(R.color.dark_icon,
R.color.mastodonC2,
R.color.mastodonC3);
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(context, R.color.black_3));
break;
}
asyncTask = new RetrieveDomainsAsyncTask(context, max_id, DisplayMutedInstanceFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onCreate(Bundle saveInstance)
{
super.onCreate(saveInstance);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
public void onDestroy() {
super.onDestroy();
if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
asyncTask.cancel(true);
}
public void scrollToTop(){
if( lv_domains != null)
lv_domains.setAdapter(domainsListAdapter);
}
@Override
public void onRetrieveDomains(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if( apiResponse.getError() != null){
Toast.makeText(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
swiped = false;
flag_loading = false;
return;
}
flag_loading = (apiResponse.getMax_id() == null );
List<String> domains = apiResponse.getDomains();
if( !swiped && firstLoad && (domains == null || domains.size() == 0))
textviewNoAction.setVisibility(View.VISIBLE);
else
textviewNoAction.setVisibility(View.GONE);
max_id = apiResponse.getMax_id();
if( swiped ){
domainsListAdapter = new DomainsListAdapter(context, this.domains, textviewNoAction);
lv_domains.setAdapter(domainsListAdapter);
swiped = false;
}
if( domains != null && domains.size() > 0) {
this.domains.addAll(domains);
domainsListAdapter.notifyDataSetChanged();
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
}
}

View File

@ -0,0 +1,26 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* 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.
*
* Mastalab 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 Mastalab; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.interfaces;
import fr.gouv.etalab.mastodon.client.APIResponse;
/**
* Created by Thomas on 26/09/2018.
* Interface when blocked domains have been retrieved
*/
public interface OnRetrieveDomainsInterface {
void onRetrieveDomains(APIResponse apiResponse);
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Thomas Schneider
This file is a part of Mastalab
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.
Mastalab 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 Mastalab; if not,
see <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/domain_container"
android:layout_marginTop="10dp"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<TextView
android:id="@+id/domain_name"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_width="0dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/domain_delete"
android:src="@drawable/ic_cancel"
android:layout_gravity="center_vertical"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
tools:ignore="ContentDescription" />
</LinearLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Thomas Schneider
This file is a part of Mastalab
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.
Mastalab 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 Mastalab; 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 -->
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:id="@+id/swipeContainer"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/lv_domains"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"
/>
</android.support.v4.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_blocked_domain"
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_domains"
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

@ -67,6 +67,10 @@
android:id="@+id/nav_muted"
android:icon="@drawable/ic_volume_mute"
android:title="@string/muted_menu" />
<item
android:id="@+id/nav_blocked_domains"
android:icon="@drawable/ic_block"
android:title="@string/blocked_domains" />
</group>
</menu>
</item>

View File

@ -622,6 +622,10 @@
<string name="set_enable_time_slot">Enable time slot</string>
<string name="how_to_videos">How To Videos</string>
<string name="retrieve_remote_conversation">Fetching remote thread!</string>
<string name="no_blocked_domain">No blocked domains!</string>
<string name="unblock_domain_confirm">Unblock domain</string>
<string name="unblock_domain_confirm_message">Are you sure to unblock %s?</string>
<string name="blocked_domains">Blocked domains</string>
<string-array name="filter_expire">
<item>Never</item>