修改命令实现
This commit is contained in:
@ -23,4 +23,4 @@ dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
}
|
||||
|
||||
apply from: 'jcenter.gradle'
|
||||
//apply from: 'jcenter.gradle'
|
@ -1,6 +1,7 @@
|
||||
package com.arialyy.downloadutil.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
@ -16,10 +17,6 @@ public class DownloadTarget extends IDownloadTarget {
|
||||
private Context mContext;
|
||||
|
||||
public static DownloadTarget getInstance(Context context) {
|
||||
// if (INSTANCE == null) {
|
||||
// Log.e(TAG, "请在Application中调用DownloadTarget.init()方法注册下载器");
|
||||
// return null;
|
||||
// }
|
||||
if (INSTANCE == null) {
|
||||
synchronized (LOCK) {
|
||||
INSTANCE = new DownloadTarget(context.getApplicationContext());
|
||||
@ -27,19 +24,6 @@ public class DownloadTarget extends IDownloadTarget {
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 初始化下载器
|
||||
// *
|
||||
// * @param context 全局Context
|
||||
// */
|
||||
// public static void init(Context context) {
|
||||
// if (INSTANCE == null) {
|
||||
// synchronized (LOCK) {
|
||||
// INSTANCE = new DownloadTarget(context.getApplicationContext());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private DownloadTarget() {
|
||||
super();
|
||||
@ -81,46 +65,46 @@ public class DownloadTarget extends IDownloadTarget {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTask(String downloadUrl, String downloadPath) {
|
||||
DownloadEntity entity = new DownloadEntity();
|
||||
entity.setDownloadUrl(downloadUrl);
|
||||
entity.setDownloadPath(downloadPath);
|
||||
public Task createTask(DownloadEntity entity) {
|
||||
Task task = TaskFactory.getInstance().createTask(mContext, entity, mTaskHandler);
|
||||
mCachePool.putTask(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task getTask(String downloadUrl) {
|
||||
Task task = mCachePool.getTask(downloadUrl);
|
||||
if (task == null) {
|
||||
task = mExecutePool.getTask(downloadUrl);
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskState(String downloadUrl) {
|
||||
Task task = getTask(downloadUrl);
|
||||
public Task getTask(DownloadEntity entity) {
|
||||
Task task = mExecutePool.getTask(entity.getDownloadUrl());
|
||||
if (task == null) {
|
||||
Log.e(TAG, "没有找到下载链接为【" + downloadUrl + "】的下载任务");
|
||||
task = mCachePool.getTask(entity.getDownloadUrl());
|
||||
}
|
||||
if (task == null){
|
||||
task = createTask(entity);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskState(DownloadEntity entity) {
|
||||
Task task = getTask(entity);
|
||||
if (task == null) {
|
||||
Log.e(TAG, "没有找到下载链接为【" + entity.getDownloadUrl() + "】的下载任务");
|
||||
return -1;
|
||||
}
|
||||
return task.getDownloadEntity().getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTask(String downloadUrl) {
|
||||
Task task = mCachePool.getTask(downloadUrl);
|
||||
public void removeTask(DownloadEntity entity) {
|
||||
Task task = mCachePool.getTask(entity.getDownloadUrl());
|
||||
if (task != null) {
|
||||
Log.d(TAG, "任务删除" + (mCachePool.removeTask(task) ? "成功" : "失败"));
|
||||
} else {
|
||||
task = mExecutePool.getTask(downloadUrl);
|
||||
task = mExecutePool.getTask(entity.getDownloadUrl());
|
||||
}
|
||||
if (task != null) {
|
||||
Log.d(TAG, "任务删除" + (mCachePool.removeTask(task) ? "成功" : "失败"));
|
||||
} else {
|
||||
Log.w(TAG, "没有找到下载链接为【" + downloadUrl + "】的任务");
|
||||
Log.w(TAG, "没有找到下载链接为【" + entity.getDownloadUrl() + "】的任务");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ public abstract class IDownloadTarget implements IDownloader, ITask {
|
||||
*/
|
||||
private void callback(int state, DownloadEntity entity) {
|
||||
if (target.mTargetListener != null) {
|
||||
Task task = target.getTask(entity.getDownloadUrl());
|
||||
Task task = target.getTask(entity);
|
||||
switch (state) {
|
||||
case START:
|
||||
target.mTargetListener.onTaskStart(task);
|
||||
@ -205,7 +205,7 @@ public abstract class IDownloadTarget implements IDownloader, ITask {
|
||||
*/
|
||||
private void handleFailTask(DownloadEntity entity) {
|
||||
if (entity.getFailNum() <= target.mFailNum) {
|
||||
Task task = target.getTask(entity.getDownloadUrl());
|
||||
Task task = target.getTask(entity);
|
||||
target.reTryStart(task);
|
||||
} else {
|
||||
startNextTask(entity);
|
||||
@ -218,7 +218,7 @@ public abstract class IDownloadTarget implements IDownloader, ITask {
|
||||
* @param entity 通过Handler传递的下载实体
|
||||
*/
|
||||
private void startNextTask(DownloadEntity entity) {
|
||||
target.removeTask(entity.getDownloadUrl());
|
||||
target.removeTask(entity);
|
||||
Task newTask = target.getNextTask();
|
||||
if (newTask == null) {
|
||||
Log.e(TAG, "没有下一任务");
|
||||
|
@ -18,11 +18,11 @@ import java.net.HttpURLConnection;
|
||||
public class Task {
|
||||
public static final String TAG = "Task";
|
||||
|
||||
DownloadEntity downloadEntity;
|
||||
IDownloadListener listener;
|
||||
Handler outHandler;
|
||||
Context context;
|
||||
DownLoadUtil util;
|
||||
private DownloadEntity downloadEntity;
|
||||
private IDownloadListener listener;
|
||||
private Handler outHandler;
|
||||
private Context context;
|
||||
private DownLoadUtil util;
|
||||
|
||||
private Task() {
|
||||
util = new DownLoadUtil();
|
||||
@ -72,7 +72,7 @@ public class Task {
|
||||
/**
|
||||
* 下载监听类
|
||||
*/
|
||||
static class DownloadListener extends DownLoadUtil.DownloadListener {
|
||||
private static class DownloadListener extends DownLoadUtil.DownloadListener {
|
||||
Handler outHandler;
|
||||
Context context;
|
||||
Intent sendIntent;
|
||||
|
@ -1,30 +1,20 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/8/22.
|
||||
* 添加任务的命令
|
||||
*/
|
||||
public class AddCommand extends IDownloadCommand {
|
||||
String mDownloadUrl, mDownloadPath;
|
||||
class AddCommand extends IDownloadCommand {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target 下载调度器
|
||||
* @param downloadUrl 下载链接
|
||||
* @param downloadPath 文件保存地址
|
||||
*/
|
||||
public AddCommand(@NonNull IDownloadTarget target, String downloadUrl, String downloadPath) {
|
||||
super(target);
|
||||
mDownloadUrl = downloadUrl;
|
||||
mDownloadPath = downloadPath;
|
||||
AddCommand(Context context, DownloadEntity entity) {
|
||||
super(context, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeComment() {
|
||||
target.createTask(mDownloadUrl, mDownloadPath);
|
||||
target.createTask(mEntity);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/9/20.
|
||||
* 取消命令
|
||||
*/
|
||||
public class CancelCommand extends IDownloadCommand{
|
||||
public CancelCommand(IDownloadTarget target) {
|
||||
super(target);
|
||||
}
|
||||
public class CancelCommand extends IDownloadCommand {
|
||||
|
||||
@Override
|
||||
public void executeComment() {
|
||||
protected CancelCommand(Context context, DownloadEntity entity) {
|
||||
super(context, entity);
|
||||
}
|
||||
|
||||
}
|
||||
@Override public void executeComment() {
|
||||
target.cancelTask(target.getTask(mEntity));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,115 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by Lyy on 2016/9/23.
|
||||
* 命令工厂
|
||||
*/
|
||||
public class CommandFactory {
|
||||
/**
|
||||
* 创建任务
|
||||
*/
|
||||
public static final int TASK_CREATE = 0x122;
|
||||
/**
|
||||
* 启动任务
|
||||
*/
|
||||
public static final int TASK_START = 0x123;
|
||||
/**
|
||||
* 取消任务
|
||||
*/
|
||||
public static final int TASK_CANCEL = 0x124;
|
||||
/**
|
||||
* 停止任务
|
||||
*/
|
||||
public static final int TASK_STOP = 0x125;
|
||||
/**
|
||||
* 获取任务状态
|
||||
*/
|
||||
public static final int TASK_STATE = 0x126;
|
||||
|
||||
private static final Object LOCK = new Object();
|
||||
private static volatile CommandFactory INSTANCE = null;
|
||||
|
||||
private CommandFactory() {
|
||||
|
||||
}
|
||||
|
||||
public static CommandFactory getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (LOCK) {
|
||||
INSTANCE = new CommandFactory();
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context context
|
||||
* @param entity 下载实体
|
||||
* @param type 命令类型{@link #TASK_CREATE}、{@link #TASK_START}、{@link #TASK_CANCEL}、{@link
|
||||
* #TASK_STOP}、{@link #TASK_STATE}
|
||||
*/
|
||||
public IDownloadCommand createCommand(Context context, DownloadEntity entity, int type) {
|
||||
switch (type) {
|
||||
case TASK_CREATE:
|
||||
return createAddCommand(context, entity);
|
||||
case TASK_START:
|
||||
return createStartCommand(context, entity);
|
||||
case TASK_CANCEL:
|
||||
return createCancelCommand(context, entity);
|
||||
case TASK_STOP:
|
||||
return createStopCommand(context, entity);
|
||||
case TASK_STATE:
|
||||
return createStateCommand(context, entity);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建获取任务状态的命令
|
||||
*
|
||||
* @return {@link StateCommand}
|
||||
*/
|
||||
private StateCommand createStateCommand(Context context, DownloadEntity entity) {
|
||||
return new StateCommand(context, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建停止命令
|
||||
*
|
||||
* @return {@link StopCommand}
|
||||
*/
|
||||
private StopCommand createStopCommand(Context context, DownloadEntity entity) {
|
||||
return new StopCommand(context, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建下载任务命令
|
||||
*
|
||||
* @return {@link AddCommand}
|
||||
*/
|
||||
private AddCommand createAddCommand(Context context, DownloadEntity entity) {
|
||||
return new AddCommand(context, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建启动下载命令
|
||||
*
|
||||
* @return {@link StartCommand}
|
||||
*/
|
||||
private StartCommand createStartCommand(Context context, DownloadEntity entity) {
|
||||
return new StartCommand(context, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 取消下载的命令
|
||||
*
|
||||
* @return {@link CancelCommand}
|
||||
*/
|
||||
private CancelCommand createCancelCommand(Context context, DownloadEntity entity) {
|
||||
return new CancelCommand(context, entity);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/9/20.
|
||||
* 获取下载状态的命令
|
||||
*/
|
||||
public class GetStateCommand extends IDownloadCommand {
|
||||
public GetStateCommand(IDownloadTarget target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeComment() {
|
||||
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.arialyy.downloadutil.core.DownloadTarget;
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
import com.arialyy.downloadutil.help.CheckHelp;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -13,9 +16,24 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class IDownloadCommand {
|
||||
protected IDownloadTarget target;
|
||||
protected Context mContext;
|
||||
protected DownloadEntity mEntity;
|
||||
|
||||
public IDownloadCommand(@NonNull IDownloadTarget target) {
|
||||
this.target = target;
|
||||
/**
|
||||
* @param context context
|
||||
* @param entity 下载实体
|
||||
*/
|
||||
protected IDownloadCommand(Context context, DownloadEntity entity){
|
||||
if (!CheckHelp.checkDownloadEntity(entity)){
|
||||
return;
|
||||
}
|
||||
target = DownloadTarget.getInstance(context);
|
||||
mContext = context;
|
||||
mEntity = entity;
|
||||
}
|
||||
|
||||
public Context getContext(){
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,21 +1,20 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
import com.arialyy.downloadutil.core.Task;
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/8/22.
|
||||
* 开始命令
|
||||
*/
|
||||
public class StartCommand extends IDownloadCommand{
|
||||
public StartCommand(@NonNull IDownloadTarget target) {
|
||||
super(target);
|
||||
class StartCommand extends IDownloadCommand{
|
||||
|
||||
StartCommand(Context context, DownloadEntity entity) {
|
||||
super(context, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeComment() {
|
||||
// target.startTask();
|
||||
target.startTask(target.getTask(mEntity));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/9/20.
|
||||
* 获取下载状态的命令
|
||||
*/
|
||||
public class StateCommand extends IDownloadCommand {
|
||||
|
||||
/**
|
||||
* @param context context
|
||||
* @param entity 下载实体
|
||||
*/
|
||||
protected StateCommand(Context context, DownloadEntity entity) {
|
||||
super(context, entity);
|
||||
}
|
||||
|
||||
@Override public void executeComment() {
|
||||
target.getTaskState(mEntity);
|
||||
}
|
||||
}
|
@ -1,18 +1,23 @@
|
||||
package com.arialyy.downloadutil.core.command;
|
||||
|
||||
import com.arialyy.downloadutil.core.IDownloadTarget;
|
||||
import android.content.Context;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/9/20.
|
||||
* 停止命令
|
||||
*/
|
||||
public class StopCommand extends IDownloadCommand {
|
||||
public StopCommand(IDownloadTarget target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeComment() {
|
||||
/**
|
||||
* @param context context
|
||||
* @param entity 下载实体
|
||||
*/
|
||||
protected StopCommand(Context context, DownloadEntity entity) {
|
||||
super(context, entity);
|
||||
}
|
||||
|
||||
}
|
||||
@Override public void executeComment() {
|
||||
target.stopTask(target.getTask(mEntity));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.arialyy.downloadutil.core.inf;
|
||||
|
||||
import com.arialyy.downloadutil.core.Task;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/8/16.
|
||||
@ -11,33 +12,32 @@ public interface ITask {
|
||||
/**
|
||||
* 创建一个新的下载任务,创建时只是将新任务存储到缓存池
|
||||
*
|
||||
* @param downloadUrl 下载链接
|
||||
* @param downloadPath 保存路径
|
||||
*/
|
||||
public void createTask(String downloadUrl, String downloadPath);
|
||||
|
||||
/**
|
||||
* 通过下载链接从缓存池或任务池搜索下载任务
|
||||
*
|
||||
* @param downloadUrl 下载链接
|
||||
* @param entity 下载实体{@link DownloadEntity}
|
||||
* @return {@link Task}
|
||||
*/
|
||||
public Task getTask(String downloadUrl);
|
||||
public Task createTask(DownloadEntity entity);
|
||||
|
||||
/**
|
||||
* 通过下载链接从缓存池或任务池搜索下载任务,如果缓存池或任务池都没有任务,则创建新任务
|
||||
*
|
||||
* @param entity 下载实体{@link DownloadEntity}
|
||||
* @return {@link Task}
|
||||
*/
|
||||
public Task getTask(DownloadEntity entity);
|
||||
|
||||
/**
|
||||
* 通过下载链接搜索下载任务
|
||||
*
|
||||
* @param downloadUrl 下载链接
|
||||
* @param entity 下载实体{@link DownloadEntity}
|
||||
* @return {@code -1 ==> 错误},{@link com.arialyy.downloadutil.entity.DownloadEntity#STATE_FAIL}
|
||||
*/
|
||||
public int getTaskState(String downloadUrl);
|
||||
public int getTaskState(DownloadEntity entity);
|
||||
|
||||
/**
|
||||
* 通过下载链接删除任务
|
||||
*
|
||||
* @param downloadUrl 下载链接
|
||||
* @param entity 下载实体{@link DownloadEntity}
|
||||
*/
|
||||
public void removeTask(String downloadUrl);
|
||||
public void removeTask(DownloadEntity entity);
|
||||
|
||||
/**
|
||||
* 获取缓存池的下一个任务
|
||||
|
@ -41,6 +41,8 @@ public class DownloadEntity extends DbEntity implements Parcelable, Cloneable {
|
||||
|
||||
private String downloadUrl; //下载路径
|
||||
private String downloadPath; //保存路径
|
||||
private String fileName; //文件名
|
||||
private String str; //其它字段
|
||||
private long completeTime; //完成时间
|
||||
private long fileSize = 1;
|
||||
private int state = STATE_WAIT;
|
||||
@ -48,6 +50,22 @@ public class DownloadEntity extends DbEntity implements Parcelable, Cloneable {
|
||||
private long currentProgress = 0; //当前下载进度
|
||||
private int failNum = 0;
|
||||
|
||||
public String getStr() {
|
||||
return str;
|
||||
}
|
||||
|
||||
public void setStr(String str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getFailNum() {
|
||||
return failNum;
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.arialyy.downloadutil.help;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.arialyy.downloadutil.R;
|
||||
import com.arialyy.downloadutil.entity.DownloadEntity;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by Lyy on 2016/9/23.
|
||||
* 检查帮助类
|
||||
*/
|
||||
public class CheckHelp {
|
||||
private static final String TAG = "CheckHelp";
|
||||
|
||||
/**
|
||||
* 检测下载实体是否合法
|
||||
*
|
||||
* @param entity 下载实体
|
||||
* @return 合法(true)
|
||||
*/
|
||||
public static boolean checkDownloadEntity(DownloadEntity entity) {
|
||||
if (entity == null) {
|
||||
Log.w(TAG, Resources.getSystem().getString(R.string.error_entity_null));
|
||||
return false;
|
||||
} else if (TextUtils.isEmpty(entity.getDownloadUrl())) {
|
||||
Log.w(TAG, Resources.getSystem().getString(R.string.error_download_url_null));
|
||||
return false;
|
||||
} else if (TextUtils.isEmpty(entity.getFileName())){
|
||||
Log.w(TAG, Resources.getSystem().getString(R.string.error_file_name_null));
|
||||
return false;
|
||||
} else if (TextUtils.isEmpty(entity.getDownloadPath())){
|
||||
Log.w(TAG, Resources.getSystem().getString(R.string.error_file_name_null));
|
||||
return false;
|
||||
}
|
||||
String fileName = entity.getFileName();
|
||||
if (fileName.contains(" ")){
|
||||
fileName = fileName.replace(" ", "_");
|
||||
}
|
||||
String dPath = entity.getDownloadPath();
|
||||
File file = new File(dPath);
|
||||
if (file.isDirectory()){
|
||||
dPath += fileName;
|
||||
entity.setDownloadPath(dPath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,3 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">DownloadUtil</string>
|
||||
|
||||
<string name="error_entity_null">下载实体不能为空</string>
|
||||
<string name="error_download_url_null">下载链接不能为空</string>
|
||||
<string name="error_download_path_null">存储地址不能为空</string>
|
||||
<string name="error_file_name_null">文件名不能为空</string>
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user