mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Move the Async.java implementation from the Android Runtime project template here.
This commit is contained in:
@@ -0,0 +1,400 @@
|
|||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.net.CookieHandler;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import java.net.CookieManager;
|
||||||
|
|
||||||
|
public class Async
|
||||||
|
{
|
||||||
|
public interface CompleteCallback
|
||||||
|
{
|
||||||
|
void onComplete(Object result, Object context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DownloadImage(String url, CompleteCallback callback, Object context)
|
||||||
|
{
|
||||||
|
new ImageDownloadTask(callback, context).execute(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Http
|
||||||
|
{
|
||||||
|
private static final String DeleteMethod = "DELETE";
|
||||||
|
private static final String GetMethod = "GET";
|
||||||
|
private static CookieManager cookieManager;
|
||||||
|
|
||||||
|
public static class KeyValuePair
|
||||||
|
{
|
||||||
|
public String key;
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
public KeyValuePair(String key, String value)
|
||||||
|
{
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RequestOptions
|
||||||
|
{
|
||||||
|
public String url;
|
||||||
|
public String method;
|
||||||
|
public ArrayList<KeyValuePair> headers;
|
||||||
|
public String content;
|
||||||
|
public int timeout = -1;
|
||||||
|
public int screenWidth = -1;
|
||||||
|
public int screenHeight = -1;
|
||||||
|
|
||||||
|
public void addHeaders(HttpURLConnection connection)
|
||||||
|
{
|
||||||
|
if (this.headers == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeyValuePair pair : this.headers)
|
||||||
|
{
|
||||||
|
connection.addRequestProperty(pair.key.toString(), pair.value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeContent(HttpURLConnection connection, Stack<Closeable> openedStreams) throws IOException
|
||||||
|
{
|
||||||
|
if (this.content == null || this.content.getClass() != String.class)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream outStream = connection.getOutputStream();
|
||||||
|
openedStreams.push(outStream);
|
||||||
|
|
||||||
|
OutputStreamWriter writer = new java.io.OutputStreamWriter(outStream);
|
||||||
|
openedStreams.push(writer);
|
||||||
|
|
||||||
|
writer.write((String) this.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RequestResult
|
||||||
|
{
|
||||||
|
public static final class ByteArrayOutputStream2 extends java.io.ByteArrayOutputStream
|
||||||
|
{
|
||||||
|
public ByteArrayOutputStream2()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayOutputStream2(int size)
|
||||||
|
{
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the internal buffer of this ByteArrayOutputStream, without copying. */
|
||||||
|
public synchronized byte[] buf()
|
||||||
|
{
|
||||||
|
return this.buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayOutputStream raw;
|
||||||
|
public ArrayList<KeyValuePair> headers = new ArrayList<KeyValuePair>();
|
||||||
|
public int statusCode;
|
||||||
|
public String responseAsString;
|
||||||
|
public Bitmap responseAsImage;
|
||||||
|
public Exception error;
|
||||||
|
|
||||||
|
public void getHeaders(HttpURLConnection connection)
|
||||||
|
{
|
||||||
|
Map<String, List<String>> headers = connection.getHeaderFields();
|
||||||
|
if (headers == null)
|
||||||
|
{
|
||||||
|
// no headers, this may happen if there is no internet connection currently available
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = headers.size();
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size - 1; i++)
|
||||||
|
{
|
||||||
|
String key = connection.getHeaderFieldKey(i);
|
||||||
|
String value = connection.getHeaderField(key);
|
||||||
|
this.headers.add(new KeyValuePair(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readResponseStream(HttpURLConnection connection, Stack<Closeable> openedStreams, RequestOptions options) throws IOException
|
||||||
|
{
|
||||||
|
this.statusCode = connection.getResponseCode();
|
||||||
|
|
||||||
|
int contentLength = connection.getContentLength();
|
||||||
|
|
||||||
|
InputStream inStream;
|
||||||
|
if (this.statusCode >= 400)
|
||||||
|
{
|
||||||
|
inStream = connection.getErrorStream();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inStream = connection.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inStream == null)
|
||||||
|
{
|
||||||
|
// inStream is null when receiving status code 401 or 407
|
||||||
|
// see this thread for more information http://stackoverflow.com/a/24986433
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openedStreams.push(inStream);
|
||||||
|
|
||||||
|
BufferedInputStream buffer = new java.io.BufferedInputStream(inStream, 4096);
|
||||||
|
openedStreams.push(buffer);
|
||||||
|
|
||||||
|
ByteArrayOutputStream2 responseStream = contentLength != -1 ? new ByteArrayOutputStream2(contentLength) : new ByteArrayOutputStream2();
|
||||||
|
openedStreams.push(responseStream);
|
||||||
|
|
||||||
|
byte[] buff = new byte[4096];
|
||||||
|
int read = -1;
|
||||||
|
while ((read = buffer.read(buff, 0, buff.length)) != -1)
|
||||||
|
{
|
||||||
|
responseStream.write(buff, 0, read);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.raw = responseStream;
|
||||||
|
buff = null;
|
||||||
|
|
||||||
|
// make the byte array conversion here, not in the JavaScript
|
||||||
|
// world for better performance
|
||||||
|
// since we do not have some explicit way to determine whether
|
||||||
|
// the content-type is image
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO: Generally this approach will not work for very
|
||||||
|
// large files
|
||||||
|
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
|
||||||
|
bitmapOptions.inJustDecodeBounds = true;
|
||||||
|
|
||||||
|
// check the size of the bitmap first
|
||||||
|
BitmapFactory.decodeByteArray(responseStream.buf(), 0, responseStream.size(), bitmapOptions);
|
||||||
|
if (bitmapOptions.outWidth > 0 && bitmapOptions.outHeight > 0)
|
||||||
|
{
|
||||||
|
int scale = 1;
|
||||||
|
final int height = bitmapOptions.outHeight;
|
||||||
|
final int width = bitmapOptions.outWidth;
|
||||||
|
|
||||||
|
if ((options.screenWidth > 0 && bitmapOptions.outWidth > options.screenWidth) ||
|
||||||
|
(options.screenHeight > 0 && bitmapOptions.outHeight > options.screenHeight))
|
||||||
|
{
|
||||||
|
final int halfHeight = height / 2;
|
||||||
|
final int halfWidth = width / 2;
|
||||||
|
|
||||||
|
// scale down the image since it is larger than the
|
||||||
|
// screen resolution
|
||||||
|
while ((halfWidth / scale) > options.screenWidth && (halfHeight / scale) > options.screenHeight)
|
||||||
|
{
|
||||||
|
scale *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmapOptions.inJustDecodeBounds = false;
|
||||||
|
bitmapOptions.inSampleSize = scale;
|
||||||
|
this.responseAsImage = BitmapFactory.decodeByteArray(responseStream.buf(), 0, responseStream.size(), bitmapOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// bitmap decoding failed, the stream is not an image
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.responseAsImage == null)
|
||||||
|
{
|
||||||
|
// convert to string
|
||||||
|
this.responseAsString = responseStream.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MakeRequest(RequestOptions options, CompleteCallback callback, Object context)
|
||||||
|
{
|
||||||
|
if (cookieManager == null)
|
||||||
|
{
|
||||||
|
cookieManager = new CookieManager();
|
||||||
|
CookieHandler.setDefault(cookieManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
new HttpRequestTask(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HttpRequestTask extends AsyncTask<RequestOptions, Void, RequestResult>
|
||||||
|
{
|
||||||
|
private CompleteCallback callback;
|
||||||
|
private Object context;
|
||||||
|
|
||||||
|
public HttpRequestTask(CompleteCallback callback, Object context)
|
||||||
|
{
|
||||||
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RequestResult doInBackground(RequestOptions... params)
|
||||||
|
{
|
||||||
|
RequestResult result = new RequestResult();
|
||||||
|
Stack<Closeable> openedStreams = new Stack<Closeable>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RequestOptions options = params[0];
|
||||||
|
URL url = new URL(options.url);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
// set the request method
|
||||||
|
String requestMethod = options.method != null ? options.method.toUpperCase() : GetMethod;
|
||||||
|
connection.setRequestMethod(requestMethod);
|
||||||
|
|
||||||
|
// add the headers
|
||||||
|
options.addHeaders(connection);
|
||||||
|
|
||||||
|
// apply timeout
|
||||||
|
if (options.timeout > 0)
|
||||||
|
{
|
||||||
|
connection.setConnectTimeout(options.timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not attempt to write the content (body) for DELETE method, Java will throw directly
|
||||||
|
if (requestMethod != DeleteMethod)
|
||||||
|
{
|
||||||
|
options.writeContent(connection, openedStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the opened streams (saves copy-paste implementation
|
||||||
|
// in each method that throws IOException)
|
||||||
|
this.closeOpenedStreams(openedStreams);
|
||||||
|
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
// build the result
|
||||||
|
result.getHeaders(connection);
|
||||||
|
result.readResponseStream(connection, openedStreams, options);
|
||||||
|
|
||||||
|
// close the opened streams (saves copy-paste implementation
|
||||||
|
// in each method that throws IOException)
|
||||||
|
this.closeOpenedStreams(openedStreams);
|
||||||
|
|
||||||
|
connection.disconnect();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e) // TODO: Catch all exceptions?
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
result.error = e;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.closeOpenedStreams(openedStreams);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
// TODO: Java rules - what to do here???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(final RequestResult result)
|
||||||
|
{
|
||||||
|
this.callback.onComplete(result, this.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeOpenedStreams(Stack<Closeable> streams) throws IOException
|
||||||
|
{
|
||||||
|
while (streams.size() > 0)
|
||||||
|
{
|
||||||
|
Closeable stream = streams.pop();
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ImageDownloadTask extends AsyncTask<String, Void, Bitmap>
|
||||||
|
{
|
||||||
|
private CompleteCallback callback;
|
||||||
|
private Object context;
|
||||||
|
|
||||||
|
public ImageDownloadTask(CompleteCallback callback, Object context)
|
||||||
|
{
|
||||||
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Bitmap doInBackground(String... params)
|
||||||
|
{
|
||||||
|
InputStream stream = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new java.net.URL(params[0]).openStream();
|
||||||
|
Bitmap bmp = BitmapFactory.decodeStream(stream);
|
||||||
|
return bmp;
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(final Bitmap result)
|
||||||
|
{
|
||||||
|
this.callback.onComplete(result, this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user