/* * Copyright (c) 2015 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com> * All Rights Reserved. */ package me.zhanghai.android.douya.network; import android.net.Uri; import com.android.volley.AuthFailureError; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.RequestFuture; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * {@inheritDoc} */ public abstract class Request<T> extends com.android.volley.Request<T> { private UploadProgressListener mUploadProgressListener; private DownloadProgressListener mDownloadProgressListener; private Response.Listener<T> mListener; private Response.ErrorListener mErrorListener; // Take over control of url from super class. private final String mUrl; private Map<String, String> mHeaders = new HashMap<>(); private Map<String, String> mParams = new HashMap<>(); private String mParamsEncoding = Http.Charsets.UTF8; private String mContentType = Http.ContentTypes.FORM_UTF8; private Priority mPriority = Priority.NORMAL; /** * By directly setting listeners in constructor you cannot handle Activity recreation correctly. * Use {@link RequestFragment} instead. */ public Request(int method, String url) { super(method, null, null); mUrl = url; } public UploadProgressListener getUploadProgressListener() { return mUploadProgressListener; } public void setUploadProgressListener(UploadProgressListener uploadProgressListener) { mUploadProgressListener = uploadProgressListener; } public DownloadProgressListener getDownloadProgressListener() { return mDownloadProgressListener; } public void setDownloadProgressListener(DownloadProgressListener downloadProgressListener) { mDownloadProgressListener = downloadProgressListener; } public Response.Listener<T> getListener() { return mListener; } public Request<T> setListener(Response.Listener<T> listener) { mListener = listener; return this; } @Override public Response.ErrorListener getErrorListener() { return mErrorListener; } public Request<T> setErrorListener(Response.ErrorListener errorListener) { mErrorListener = errorListener; return this; } protected void deliverUploadProgress(int currentBytes, int totalBytes) { if (mUploadProgressListener != null) { mUploadProgressListener.onUploadProgress(currentBytes, totalBytes); } } protected void deliverDownloadProgress(int currentBytes, int totalBytes) { if (mDownloadProgressListener != null) { mDownloadProgressListener.onDownloadProgress(currentBytes, totalBytes); } } @Override protected void deliverResponse(T response) { if (mListener != null) { mListener.onResponse(response); } } @Override public void deliverError(VolleyError error) { if (mErrorListener != null) { mErrorListener.onErrorResponse(error); } } @Override public String getUrl() { int method = getMethod(); if ((method == Method.GET || method == Method.DELETE) && !mParams.isEmpty()) { return getUrlWithParams(); } else { return mUrl; } } @Override public String getCacheKey() { return getMethod() + ":" + mUrl; } public Map<String, String> getHeaders() { return mHeaders; } public Request<T> addHeader(String name, String value) { mHeaders.put(name, value); return this; } public Request<T> addHeader(Map.Entry<String, String> header) { return addHeader(header.getKey(), header.getValue()); } public Request<T> addHeaderAccept(String contentType) { return addHeader(Http.Headers.ACCEPT, contentType); } public Request<T> addHeaderAcceptWithCharset(String contentType, String charset) { return addHeaderAccept(Http.ContentTypes.withCharset(contentType, charset)); } public Request<T> addHeaderAcceptJson(String charset) { return addHeaderAcceptWithCharset(Http.ContentTypes.JSON, charset); } public Request<T> addHeaderAcceptJsonUtf8() { return addHeaderAcceptJson(Http.Charsets.UTF8); } public Request<T> addHeaderAcceptCharset(String charset) { return addHeader(Http.Headers.ACCEPT_CHARSET, charset); } public Request<T> addHeaderAcceptCharsetUtf8() { return addHeaderAcceptCharset(Http.Charsets.UTF8); } public Request<T> addHeaderAcceptEncoding(String encoding) { return addHeader(Http.Headers.ACCEPT_ENCODING, encoding); } /** * @deprecated Underlying network stack automatically adds accept gzip and handles compressed * response. If we do this explicitly we need to do the decompression ourselves. */ public Request<T> addHeaderAcceptEncodingGzip() { return addHeaderAcceptEncoding(Http.Encodings.GZIP); } public Request<T> addHeaderAuthorization(String authorization) { return addHeader(Http.Headers.AUTHORIZATION, authorization); } public Request<T> addHeaderAuthorizationBearer(String token) { return addHeaderAuthorization(Http.Headers.makeBearerAuthorization(token)); } public Request<T> addHeaderUserAgent(String userAgent) { return addHeader(Http.Headers.USER_AGENT, userAgent); } public Request<T> addHeaders(Map<String, String> headers) { mHeaders.putAll(headers); return this; } public Request<T> removeHeader(String name) { mHeaders.remove(name); return this; } public Request<T> clearHeaders() { mHeaders.clear(); return this; } public Request<T> setHeaders(Map<String, String> headers) { return clearHeaders().addHeaders(headers); } @Override public Map<String, String> getParams() { return mParams; } public Request<T> addParam(String name, String value) { mParams.put(name, value); return this; } public Request<T> addParam(Map.Entry<String, String> param) { return addParam(param.getKey(), param.getValue()); } public Request<T> addParams(Map<String, String> params) { mParams.putAll(params); return this; } public Request<T> removeParam(String name) { mParams.remove(name); return this; } public Request<T> clearParams() { mParams.clear(); return this; } public Request<T> setParams(Map<String, String> params) { return clearParams().addParams(params); } @Override public String getParamsEncoding() { return mParamsEncoding; } public void setParamsEncoding(String paramsEncoding) { mParamsEncoding = paramsEncoding; } public String getContentType() { return mContentType; } public Request<T> setContentType(String contentType) { mContentType = contentType; return this; } @Override public String getBodyContentType() { return getContentType(); } @Override public byte[] getBody() { int method = getMethod(); if ((method == Method.POST || method == Method.PUT) && !mParams.isEmpty()) { return encodeParams(); } return null; } @Override public Priority getPriority() { return mPriority; } public void setPriority(Priority priority) { mPriority = priority; } public void onPreparePerformRequest() throws AuthFailureError {} public T getResponse() throws InterruptedException, TimeoutException, ExecutionException { RequestFuture<T> future = RequestFuture.newFuture(); setListener(future); setErrorListener(future); Volley.getInstance().addToRequestQueue(this); return future.get(getTimeoutMs(), TimeUnit.MILLISECONDS); } private StringBuilder appendEncodedParams(StringBuilder builder) { for (Map.Entry<String, String> entry : mParams.entrySet()) { builder .append(Uri.encode(entry.getKey())) .append('=') .append(Uri.encode(entry.getValue())) .append('&'); } return builder; } // TODO: Cache this? private String getUrlWithParams() { StringBuilder builder = new StringBuilder(mUrl) .append('?'); return appendEncodedParams(builder) .toString(); } private byte[] encodeParams() { try { return appendEncodedParams(new StringBuilder()) .toString() .getBytes(mParamsEncoding); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public interface UploadProgressListener { void onUploadProgress(int currentBytes, int totalBytes); } public interface DownloadProgressListener { void onDownloadProgress(int currentBytes, int totalBytes); } }