mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-17 07:30:16 -05:00
MessageView performance improvlements by debauchedsloth++ (This includes all changes from Issue 285's MessageView.java patch)
This commit is contained in:
parent
4b444b2bcf
commit
2dda469255
@ -29,6 +29,7 @@ import android.os.Handler;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.util.Regex;
|
import android.text.util.Regex;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.util.Config;
|
import android.util.Config;
|
||||||
@ -43,6 +44,7 @@ import android.view.View.OnClickListener;
|
|||||||
import android.webkit.CacheManager;
|
import android.webkit.CacheManager;
|
||||||
import android.webkit.UrlInterceptHandler;
|
import android.webkit.UrlInterceptHandler;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
import android.webkit.CacheManager.CacheResult;
|
import android.webkit.CacheManager.CacheResult;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -71,6 +73,9 @@ import com.android.email.mail.store.LocalStore.LocalAttachmentBody;
|
|||||||
import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart;
|
import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart;
|
||||||
import com.android.email.mail.store.LocalStore.LocalMessage;
|
import com.android.email.mail.store.LocalStore.LocalMessage;
|
||||||
import com.android.email.provider.AttachmentProvider;
|
import com.android.email.provider.AttachmentProvider;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class MessageView extends Activity
|
public class MessageView extends Activity
|
||||||
implements UrlInterceptHandler, OnClickListener {
|
implements UrlInterceptHandler, OnClickListener {
|
||||||
@ -79,6 +84,7 @@ public class MessageView extends Activity
|
|||||||
private static final String EXTRA_MESSAGE = "com.android.email.MessageView_message";
|
private static final String EXTRA_MESSAGE = "com.android.email.MessageView_message";
|
||||||
private static final String EXTRA_FOLDER_UIDS = "com.android.email.MessageView_folderUids";
|
private static final String EXTRA_FOLDER_UIDS = "com.android.email.MessageView_folderUids";
|
||||||
private static final String EXTRA_NEXT = "com.android.email.MessageView_next";
|
private static final String EXTRA_NEXT = "com.android.email.MessageView_next";
|
||||||
|
private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 120000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
|
||||||
|
|
||||||
|
|
||||||
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
|
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
|
||||||
@ -93,6 +99,10 @@ public class MessageView extends Activity
|
|||||||
private LinearLayout mAttachments;
|
private LinearLayout mAttachments;
|
||||||
private View mAttachmentIcon;
|
private View mAttachmentIcon;
|
||||||
private View mShowPicturesSection;
|
private View mShowPicturesSection;
|
||||||
|
View next;
|
||||||
|
View next_scrolling;
|
||||||
|
View previous;
|
||||||
|
View previous_scrolling;
|
||||||
|
|
||||||
private Account mAccount;
|
private Account mAccount;
|
||||||
private String mFolder;
|
private String mFolder;
|
||||||
@ -123,7 +133,7 @@ public class MessageView extends Activity
|
|||||||
dateFormat = new java.text.SimpleDateFormat(Email.BACKUP_DATE_FORMAT);
|
dateFormat = new java.text.SimpleDateFormat(Email.BACKUP_DATE_FORMAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dateFormat;
|
return dateFormat;
|
||||||
}
|
}
|
||||||
private DateFormat getTimeFormat()
|
private DateFormat getTimeFormat()
|
||||||
{
|
{
|
||||||
@ -134,12 +144,12 @@ public class MessageView extends Activity
|
|||||||
boolean b24 = !(timeFormatS == null || timeFormatS.equals("12"));
|
boolean b24 = !(timeFormatS == null || timeFormatS.equals("12"));
|
||||||
timeFormat = new java.text.SimpleDateFormat(b24 ? Email.TIME_FORMAT_24 : Email.TIME_FORMAT_12);
|
timeFormat = new java.text.SimpleDateFormat(b24 ? Email.TIME_FORMAT_24 : Email.TIME_FORMAT_12);
|
||||||
}
|
}
|
||||||
return timeFormat;
|
return timeFormat;
|
||||||
}
|
}
|
||||||
private void clearFormats()
|
private void clearFormats()
|
||||||
{
|
{
|
||||||
dateFormat = null;
|
dateFormat = null;
|
||||||
timeFormat = null;
|
timeFormat = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Listener mListener = new Listener();
|
private Listener mListener = new Listener();
|
||||||
@ -171,10 +181,10 @@ public class MessageView extends Activity
|
|||||||
}
|
}
|
||||||
return true; }
|
return true; }
|
||||||
|
|
||||||
case KeyEvent.KEYCODE_H: {
|
case KeyEvent.KEYCODE_H: {
|
||||||
Toast toast = Toast.makeText(this, R.string.message_help_key, Toast.LENGTH_LONG);
|
Toast toast = Toast.makeText(this, R.string.message_help_key, Toast.LENGTH_LONG);
|
||||||
toast.show();
|
toast.show();
|
||||||
return true; }
|
return true; }
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
@ -372,6 +382,7 @@ public class MessageView extends Activity
|
|||||||
mSubjectView = (TextView)findViewById(R.id.subject);
|
mSubjectView = (TextView)findViewById(R.id.subject);
|
||||||
mDateView = (TextView)findViewById(R.id.date);
|
mDateView = (TextView)findViewById(R.id.date);
|
||||||
mMessageContentView = (WebView)findViewById(R.id.message_content);
|
mMessageContentView = (WebView)findViewById(R.id.message_content);
|
||||||
|
mMessageContentView.setWebViewClient(new MessageWebViewClient());
|
||||||
mAttachments = (LinearLayout)findViewById(R.id.attachments);
|
mAttachments = (LinearLayout)findViewById(R.id.attachments);
|
||||||
mAttachmentIcon = findViewById(R.id.attachment);
|
mAttachmentIcon = findViewById(R.id.attachment);
|
||||||
mShowPicturesSection = findViewById(R.id.show_pictures_section);
|
mShowPicturesSection = findViewById(R.id.show_pictures_section);
|
||||||
@ -409,28 +420,17 @@ public class MessageView extends Activity
|
|||||||
mMessageUid = intent.getStringExtra(EXTRA_MESSAGE);
|
mMessageUid = intent.getStringExtra(EXTRA_MESSAGE);
|
||||||
mFolderUids = intent.getStringArrayListExtra(EXTRA_FOLDER_UIDS);
|
mFolderUids = intent.getStringArrayListExtra(EXTRA_FOLDER_UIDS);
|
||||||
|
|
||||||
View next = findViewById(R.id.next);
|
next = findViewById(R.id.next);
|
||||||
View previous = findViewById(R.id.previous);
|
previous = findViewById(R.id.previous);
|
||||||
|
|
||||||
findSurroundingMessagesUid();
|
|
||||||
|
|
||||||
setOnClickListener(R.id.next);
|
setOnClickListener(R.id.next);
|
||||||
setOnClickListener(R.id.previous);
|
setOnClickListener(R.id.previous);
|
||||||
|
|
||||||
next.setEnabled(mNextMessageUid != null );
|
next_scrolling = findViewById(R.id.next_scrolling);
|
||||||
previous.setEnabled(mPreviousMessageUid != null);
|
|
||||||
|
|
||||||
View next_scrolling = findViewById(R.id.next_scrolling);
|
|
||||||
|
|
||||||
if (next_scrolling != null) {
|
|
||||||
next_scrolling.setEnabled(mNextMessageUid != null );
|
|
||||||
}
|
|
||||||
|
|
||||||
View previous_scrolling = findViewById(R.id.previous_scrolling);
|
previous_scrolling = findViewById(R.id.previous_scrolling);
|
||||||
if (previous_scrolling != null) {
|
|
||||||
previous_scrolling.setEnabled(mPreviousMessageUid != null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean goNext = intent.getBooleanExtra(EXTRA_NEXT, false);
|
boolean goNext = intent.getBooleanExtra(EXTRA_NEXT, false);
|
||||||
if (goNext) {
|
if (goNext) {
|
||||||
@ -439,6 +439,7 @@ public class MessageView extends Activity
|
|||||||
|
|
||||||
Account.HideButtons hideButtons = mAccount.getHideMessageViewButtons();
|
Account.HideButtons hideButtons = mAccount.getHideMessageViewButtons();
|
||||||
|
|
||||||
|
MessagingController.getInstance(getApplication()).addListener(mListener);
|
||||||
if (Account.HideButtons.ALWAYS == hideButtons)
|
if (Account.HideButtons.ALWAYS == hideButtons)
|
||||||
{
|
{
|
||||||
hideButtons();
|
hideButtons();
|
||||||
@ -459,22 +460,37 @@ public class MessageView extends Activity
|
|||||||
showButtons();
|
showButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
displayMessage(mMessageUid);
|
||||||
MessagingController.getInstance(getApplication()).addListener(mListener);
|
}
|
||||||
new Thread() {
|
|
||||||
|
Thread loaderThread = new Thread() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// TODO this is a spot that should be eventually handled by a MessagingController
|
// TODO this is a spot that should be eventually handled by a MessagingController
|
||||||
// thread pool. We want it in a thread but it can't be blocked by the normal
|
// thread pool. We want it in a thread but it can't be blocked by the normal
|
||||||
// synchronization stuff in MC.
|
// synchronization stuff in MC.
|
||||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
|
||||||
MessagingController.getInstance(getApplication()).loadMessageForView(
|
MessagingController.getInstance(getApplication()).loadMessageForView(
|
||||||
mAccount,
|
mAccount,
|
||||||
mFolder,
|
mFolder,
|
||||||
mMessageUid,
|
mMessageUid,
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
}.start();
|
};
|
||||||
|
|
||||||
|
private void displayMessage(String uid)
|
||||||
|
{
|
||||||
|
mMessageUid = uid;
|
||||||
|
mAttachments.removeAllViews();
|
||||||
|
findSurroundingMessagesUid();
|
||||||
|
next.setEnabled(mNextMessageUid != null );
|
||||||
|
previous.setEnabled(mPreviousMessageUid != null);
|
||||||
|
if (next_scrolling != null)
|
||||||
|
next_scrolling.setEnabled(mNextMessageUid != null );
|
||||||
|
if (previous_scrolling != null)
|
||||||
|
previous_scrolling.setEnabled(mPreviousMessageUid != null);
|
||||||
|
threadPool.execute(loaderThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void showButtons()
|
private void showButtons()
|
||||||
{
|
{
|
||||||
View buttons = findViewById(R.id.scrolling_buttons);
|
View buttons = findViewById(R.id.scrolling_buttons);
|
||||||
@ -501,19 +517,14 @@ public class MessageView extends Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void findSurroundingMessagesUid() {
|
private void findSurroundingMessagesUid() {
|
||||||
for (int i = 0, count = mFolderUids.size(); i < count; i++) {
|
mNextMessageUid = mPreviousMessageUid = null;
|
||||||
String messageUid = mFolderUids.get(i);
|
int i = mFolderUids.indexOf(mMessageUid);
|
||||||
if (messageUid.equals(mMessageUid)) {
|
if(i < 0)
|
||||||
if (i != 0) {
|
return;
|
||||||
mNextMessageUid = mFolderUids.get(i - 1);
|
if(i != 0)
|
||||||
}
|
mNextMessageUid = mFolderUids.get(i - 1);
|
||||||
|
if(i != (mFolderUids.size() - 1))
|
||||||
if (i != count - 1) {
|
mPreviousMessageUid = mFolderUids.get(i + 1);
|
||||||
mPreviousMessageUid = mFolderUids.get(i + 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
@ -533,10 +544,11 @@ public class MessageView extends Activity
|
|||||||
String folderForDelete = mFolder;
|
String folderForDelete = mFolder;
|
||||||
Account accountForDelete = mAccount;
|
Account accountForDelete = mAccount;
|
||||||
|
|
||||||
|
findSurroundingMessagesUid();
|
||||||
|
|
||||||
// Remove this message's Uid locally
|
// Remove this message's Uid locally
|
||||||
mFolderUids.remove(messageToDelete.getUid());
|
mFolderUids.remove(messageToDelete.getUid());
|
||||||
|
|
||||||
findSurroundingMessagesUid();
|
|
||||||
|
|
||||||
MessagingController.getInstance(getApplication()).deleteMessage(
|
MessagingController.getInstance(getApplication()).deleteMessage(
|
||||||
accountForDelete,
|
accountForDelete,
|
||||||
@ -551,8 +563,6 @@ public class MessageView extends Activity
|
|||||||
} else {
|
} else {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,35 +677,27 @@ public class MessageView extends Activity
|
|||||||
|
|
||||||
private void onSendAlternate() {
|
private void onSendAlternate() {
|
||||||
if (mMessage != null) {
|
if (mMessage != null) {
|
||||||
MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage);
|
MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNext() {
|
private void onNext() {
|
||||||
if (mNextMessageUid == null)
|
if (mNextMessageUid == null) {
|
||||||
{
|
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(this,
|
return;
|
||||||
getString(R.string.end_of_folder),
|
}
|
||||||
Toast.LENGTH_SHORT).show();
|
displayMessage(mNextMessageUid);
|
||||||
return;
|
next.requestFocus();
|
||||||
}
|
|
||||||
Bundle extras = new Bundle(1);
|
|
||||||
extras.putBoolean(EXTRA_NEXT, true);
|
|
||||||
MessageView.actionView(this, mAccount, mFolder, mNextMessageUid, mFolderUids, extras);
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPrevious() {
|
private void onPrevious() {
|
||||||
if (mPreviousMessageUid == null)
|
if (mPreviousMessageUid == null) {
|
||||||
{
|
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(this,
|
return;
|
||||||
getString(R.string.end_of_folder),
|
}
|
||||||
Toast.LENGTH_SHORT).show();
|
displayMessage(mPreviousMessageUid);
|
||||||
return;
|
previous.requestFocus();
|
||||||
}
|
|
||||||
MessageView.actionView(this, mAccount, mFolder, mPreviousMessageUid, mFolderUids);
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMarkAsUnread() {
|
private void onMarkAsUnread() {
|
||||||
@ -1054,72 +1056,68 @@ public class MessageView extends Activity
|
|||||||
@Override
|
@Override
|
||||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||||
Message message) {
|
Message message) {
|
||||||
SpannableString markup;
|
Spannable markup;
|
||||||
MessageView.this.mMessage = message;
|
MessageView.this.mMessage = message;
|
||||||
try {
|
try {
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(mMessage, "text/html");
|
Part part = MimeUtility.findFirstPartByMimeType(mMessage, "text/html");
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(mMessage, "text/plain");
|
part = MimeUtility.findFirstPartByMimeType(mMessage, "text/plain");
|
||||||
}
|
|
||||||
if (part != null) {
|
|
||||||
String text = MimeUtility.getTextFromPart(part);
|
|
||||||
if (part.getMimeType().equalsIgnoreCase("text/html")) {
|
|
||||||
text = text.replaceAll("cid:", "http://cid/");
|
|
||||||
} else {
|
|
||||||
Matcher m = Regex.WEB_URL_PATTERN.matcher(text);
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
/*
|
|
||||||
* Convert plain text to HTML by replacing
|
|
||||||
* \r?\n with <br> and adding a html/body wrapper as well as escaping & < >
|
|
||||||
*/
|
|
||||||
text = text.replaceAll("&", "&");
|
|
||||||
text = text.replaceAll("<", "<");
|
|
||||||
text = text.replaceAll(">", ">");
|
|
||||||
text = text.replaceAll("\r?\n", "<br>");
|
|
||||||
|
|
||||||
while (m.find()) {
|
|
||||||
int start = m.start();
|
|
||||||
if (start == 0 || (start != 0 && text.charAt(start - 1) != '@')) {
|
|
||||||
m.appendReplacement(sb, "<a href=\"$0\">$0</a>");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m.appendReplacement(sb, "$0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.appendTail(sb);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
text = "<html><body>" + text + "</body></html>";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO this should be smarter, change to regex for img, but consider how to
|
|
||||||
* get background images and a million other things that HTML allows.
|
|
||||||
*/
|
|
||||||
if (text.contains("<img")) {
|
|
||||||
mHandler.showShowPictures(true);
|
|
||||||
}
|
|
||||||
markup = new SpannableString(text);
|
|
||||||
Linkify.addLinks(markup, Linkify.ALL);
|
|
||||||
mMessageContentView.loadDataWithBaseURL("email://", markup.toString(), "text/html",
|
|
||||||
"utf-8", null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mMessageContentView.loadUrl("file:///android_asset/empty.html");
|
|
||||||
}
|
|
||||||
renderAttachments(mMessage, 0);
|
|
||||||
}
|
}
|
||||||
|
if (part != null) {
|
||||||
|
String text = MimeUtility.getTextFromPart(part);
|
||||||
|
if (part.getMimeType().equalsIgnoreCase("text/html")) {
|
||||||
|
markup = new SpannableString(text.replaceAll("cid:", "http://cid/"));
|
||||||
|
Linkify.addLinks(markup, Linkify.ALL);
|
||||||
|
text = markup.toString();
|
||||||
|
} else {
|
||||||
|
if(text.length() != 0) {
|
||||||
|
/*
|
||||||
|
* Convert plain text to HTML by replacing
|
||||||
|
* \r?\n with <br> and adding a html/body wrapper.
|
||||||
|
*/
|
||||||
|
text = text.replaceAll("&", "&");
|
||||||
|
text = text.replaceAll("<", "<");
|
||||||
|
text = text.replaceAll(">", ">");
|
||||||
|
text = text.replaceAll("\r?\n", "<br/>");
|
||||||
|
Matcher m = Regex.WEB_URL_PATTERN.matcher(text);
|
||||||
|
StringBuffer sb = new StringBuffer(text.length() + 512);
|
||||||
|
sb.append("<html><body>");
|
||||||
|
while (m.find()) {
|
||||||
|
int start = m.start();
|
||||||
|
if (start == 0 || (start != 0 && text.charAt(start - 1) != '@')) {
|
||||||
|
m.appendReplacement(sb, "<a href=\"$0\">$0</a>");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m.appendReplacement(sb, "$0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.appendTail(sb);
|
||||||
|
sb.append("</body></html>");
|
||||||
|
markup = new SpannableStringBuilder(sb, 0, sb.length());
|
||||||
|
Linkify.addLinks(markup, Linkify.ALL);
|
||||||
|
text = markup.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
text = "<html><body></body></html>";
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* TODO this should be smarter, change to regex for img, but consider how to
|
||||||
|
* get background images and a million other things that HTML allows.
|
||||||
|
*/
|
||||||
|
mHandler.showShowPictures(text.contains("<img"));
|
||||||
|
mMessageContentView.loadDataWithBaseURL("email://", text, "text/html", "utf-8", null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mMessageContentView.loadUrl("file:///android_asset/empty.html");
|
||||||
|
renderAttachments(mMessage, 0);
|
||||||
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
if (Config.LOGV) {
|
if (Config.LOGV) {
|
||||||
Log.v(Email.LOG_TAG, "loadMessageForViewBodyAvailable", e);
|
Log.v(Email.LOG_TAG, "loadMessageForViewBodyAvailable", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadMessageForViewFailed(Account account, String folder, String uid,
|
public void loadMessageForViewFailed(Account account, String folder, String uid,
|
||||||
final String message) {
|
final String message) {
|
||||||
@ -1238,4 +1236,16 @@ public class MessageView extends Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MessageWebViewClient extends WebViewClient
|
||||||
|
{
|
||||||
|
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||||
|
{
|
||||||
|
Log.e(Email.LOG_TAG, "WebView: url '"+failingUrl+"' error "+description);
|
||||||
|
String error = String.format(getString(R.string.message_web_view_error).toString(), description);
|
||||||
|
Toast.makeText(MessageView.this, error, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user