mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
move application specific logic from android-runtime to android-widgets
This commit is contained in:
@@ -52,9 +52,11 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
compile 'com.android.support:support-v4:+'
|
compile 'com.android.support:support-v4:+'
|
||||||
|
compile files('libs/android-runtime.jar')
|
||||||
|
compile files('libs/android-binding-generator.jar')
|
||||||
}
|
}
|
||||||
|
|
||||||
task cleanDistDir (type: Delete) {
|
task cleanDistDir (type: Delete) {
|
||||||
|
|||||||
BIN
widgets/libs/android-binding-generator.jar
Normal file
BIN
widgets/libs/android-binding-generator.jar
Normal file
Binary file not shown.
BIN
widgets/libs/android-runtime.jar
Normal file
BIN
widgets/libs/android-runtime.jar
Normal file
Binary file not shown.
414
widgets/src/main/java/com/tns/Async.java
Normal file
414
widgets/src/main/java/com/tns/Async.java
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
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.content.Context;
|
||||||
|
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 (bitmapOptions.outWidth > options.screenWidth || 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 (options.screenWidth < 0 || options.screenHeight < 0)
|
||||||
|
{
|
||||||
|
DisplayMetrics metrics = appContext.getResources().getDisplayMetrics();
|
||||||
|
options.screenWidth = metrics.widthPixels;
|
||||||
|
options.screenHeight = metrics.heightPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookieManager == null)
|
||||||
|
{
|
||||||
|
cookieManager = new CookieManager();
|
||||||
|
CookieHandler.setDefault(cookieManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
new HttpRequestTask(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Application appContext;
|
||||||
|
|
||||||
|
public static void setApplicationContext(Application app)
|
||||||
|
{
|
||||||
|
appContext = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
137
widgets/src/main/java/com/tns/DefaultExtractPolicy.java
Normal file
137
widgets/src/main/java/com/tns/DefaultExtractPolicy.java
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
|
||||||
|
import com.tns.Logger;
|
||||||
|
import com.tns.ExtractPolicy;
|
||||||
|
import com.tns.FileExtractor;
|
||||||
|
|
||||||
|
public class DefaultExtractPolicy implements ExtractPolicy
|
||||||
|
{
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
private final static String ASSETS_THUMB_FILENAME = "assetsThumb";
|
||||||
|
|
||||||
|
public DefaultExtractPolicy(Logger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldExtract(android.content.Context context)
|
||||||
|
{
|
||||||
|
String assetsThumb = generateAssetsThumb(context);
|
||||||
|
if (assetsThumb != null)
|
||||||
|
{
|
||||||
|
String assetsThumbFilePath = context.getFilesDir().getPath() + File.separatorChar + ASSETS_THUMB_FILENAME;
|
||||||
|
String oldAssetsThumb = getCachedAssetsThumb(assetsThumbFilePath);
|
||||||
|
if (oldAssetsThumb == null || !assetsThumb.equals(oldAssetsThumb))
|
||||||
|
{
|
||||||
|
saveNewAssetsThumb(assetsThumb, assetsThumbFilePath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean forceOverwrite()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileExtractor extractor()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateAssetsThumb(Context context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||||
|
int code = packageInfo.versionCode;
|
||||||
|
long updateTime = packageInfo.lastUpdateTime;
|
||||||
|
return String.valueOf(updateTime) + "-" + String.valueOf(code);
|
||||||
|
}
|
||||||
|
catch (PackageManager.NameNotFoundException e)
|
||||||
|
{
|
||||||
|
logger.write("Error while getting current assets thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCachedAssetsThumb(String assetsThumbFilePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File cachedThumbFile = new File(assetsThumbFilePath);
|
||||||
|
if (cachedThumbFile.exists())
|
||||||
|
{
|
||||||
|
FileInputStream in = new FileInputStream(cachedThumbFile);
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
String cachedThumb = reader.readLine();
|
||||||
|
reader.close();
|
||||||
|
in.close();
|
||||||
|
return cachedThumb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
logger.write("Error while getting current assets thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.write("Error while getting current asstes thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveNewAssetsThumb(String newThumb, String assetsThumbFile)
|
||||||
|
{
|
||||||
|
File cachedThumbFile = new File(assetsThumbFile);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileOutputStream out = new FileOutputStream(cachedThumbFile, false);
|
||||||
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
writer.write(newThumb);
|
||||||
|
writer.newLine();
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
writer.close();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
logger.write("Error while writting current assets thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.write("Error while writting current assets thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
197
widgets/src/main/java/com/tns/ErrorReport.java
Normal file
197
widgets/src/main/java/com/tns/ErrorReport.java
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.PendingIntent.CanceledException;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.text.method.ScrollingMovementMethod;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
class ErrorReport
|
||||||
|
{
|
||||||
|
public static final String ERROR_FILE_NAME = "hasError";
|
||||||
|
private final Activity activity;
|
||||||
|
|
||||||
|
private final static String EXTRA_NATIVESCRIPT_ERROR_REPORT = "NativeScriptErrorMessage";
|
||||||
|
private final static String EXTRA_ERROR_REPORT_MSG = "msg";
|
||||||
|
private final static int EXTRA_ERROR_REPORT_VALUE = 1;
|
||||||
|
|
||||||
|
public ErrorReport(Activity activity)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean startActivity(final Context context, String errorMessage)
|
||||||
|
{
|
||||||
|
final Intent intent = getIntent(context);
|
||||||
|
if (intent == null)
|
||||||
|
{
|
||||||
|
return false; // (if in release mode) don't do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
intent.putExtra(EXTRA_ERROR_REPORT_MSG, errorMessage);
|
||||||
|
|
||||||
|
createErrorFile(context);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
startPendingErrorActivity(context, intent);
|
||||||
|
}
|
||||||
|
catch (CanceledException e)
|
||||||
|
{
|
||||||
|
Log.d("ErrorReport", "Couldn't send pending intent! Exception: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
killProcess(context);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void killProcess(Context context)
|
||||||
|
{
|
||||||
|
// finish current activity and all below it first
|
||||||
|
if (context instanceof Activity)
|
||||||
|
{
|
||||||
|
((Activity) context).finishAffinity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// kill process
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void startPendingErrorActivity(Context context, Intent intent) throws CanceledException
|
||||||
|
{
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
|
||||||
|
pendingIntent.send(context, 0, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getErrorMessage(Throwable ex)
|
||||||
|
{
|
||||||
|
String content;
|
||||||
|
PrintStream ps = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ps = new PrintStream(baos);
|
||||||
|
ex.printStackTrace(ps);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
content = baos.toString("US-ASCII");
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
content = e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (ps != null)
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Intent getIntent(Context context)
|
||||||
|
{
|
||||||
|
Class<?> errorActivityClass = Platform.getErrorActivityClass(); // can be null or can be provided beforehand
|
||||||
|
|
||||||
|
// if in debug and errorActivityClass is not provided use ErrorReportActivity class
|
||||||
|
if (errorActivityClass == null && JsDebugger.isDebuggableApp(context))
|
||||||
|
{
|
||||||
|
errorActivityClass = ErrorReportActivity.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not in debug mode should return null and use the errorActivityClass implementation provided
|
||||||
|
if (errorActivityClass == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(context, errorActivityClass);
|
||||||
|
|
||||||
|
intent.putExtra(EXTRA_NATIVESCRIPT_ERROR_REPORT, EXTRA_ERROR_REPORT_VALUE);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean hasIntent(Intent intent)
|
||||||
|
{
|
||||||
|
int value = intent.getIntExtra(EXTRA_NATIVESCRIPT_ERROR_REPORT, 0);
|
||||||
|
|
||||||
|
return value == EXTRA_ERROR_REPORT_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildUI()
|
||||||
|
{
|
||||||
|
Context context = activity;
|
||||||
|
Intent intent = activity.getIntent();
|
||||||
|
final String msg = intent.getStringExtra(EXTRA_ERROR_REPORT_MSG);
|
||||||
|
|
||||||
|
// container
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
activity.setContentView(layout);
|
||||||
|
|
||||||
|
// header
|
||||||
|
TextView txtHeader = new TextView(context);
|
||||||
|
txtHeader.setText("Unhandled Exception");
|
||||||
|
|
||||||
|
// error + stacktrace
|
||||||
|
TextView txtErrorMsg = new TextView(context);
|
||||||
|
txtErrorMsg.setText(msg);
|
||||||
|
txtErrorMsg.setHeight(1000);
|
||||||
|
txtErrorMsg.setMovementMethod(new ScrollingMovementMethod());
|
||||||
|
|
||||||
|
// copy button
|
||||||
|
Button copyToClipboard = new Button(context);
|
||||||
|
copyToClipboard.setText("Copy to clipboard");
|
||||||
|
copyToClipboard.setOnClickListener(new OnClickListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onClick(View v)
|
||||||
|
{
|
||||||
|
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText("nsError", msg);
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
layout.addView(txtHeader);
|
||||||
|
layout.addView(txtErrorMsg);
|
||||||
|
layout.addView(copyToClipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createErrorFile(final Context context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File errFile = new File(context.getFilesDir(), ERROR_FILE_NAME);
|
||||||
|
errFile.createNewFile();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
Log.d("ErrorReport", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
widgets/src/main/java/com/tns/ErrorReportActivity.java
Normal file
21
widgets/src/main/java/com/tns/ErrorReportActivity.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class ErrorReportActivity extends Activity
|
||||||
|
{
|
||||||
|
public void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
new ErrorReport(this).buildUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause()
|
||||||
|
{
|
||||||
|
// the moment the error activity is not in the foreground we want to kill the process
|
||||||
|
super.onPause();
|
||||||
|
ErrorReport.killProcess(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
widgets/src/main/java/com/tns/FragmentClass.java
Normal file
71
widgets/src/main/java/com/tns/FragmentClass.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@JavaScriptImplementation(javaScriptFile = "app/tns_modules/ui/frme/frame.js")
|
||||||
|
public class FragmentClass extends android.app.Fragment implements com.tns.NativeScriptHashCodeProvider {
|
||||||
|
|
||||||
|
public FragmentClass()
|
||||||
|
{
|
||||||
|
com.tns.Platform.initInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onHiddenChanged(boolean hidden) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = hidden;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onHiddenChanged", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
|
||||||
|
java.lang.Object[] params = new Object[3];
|
||||||
|
params[0] = transit;
|
||||||
|
params[1] = enter;
|
||||||
|
params[2] = nextAnim;
|
||||||
|
return (Animator)com.tns.Platform.callJSMethod(this, "onCreateAnimator", Animator.class, params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = savedInstanceState;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onCreate", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
java.lang.Object[] params = new Object[3];
|
||||||
|
params[0] = inflater;
|
||||||
|
params[1] = container;
|
||||||
|
params[2] = savedInstanceState;
|
||||||
|
return (View)com.tns.Platform.callJSMethod(this, "onCreateView", View.class, params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = outState;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onSaveInstanceState", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroyView() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onDestroyView", void.class, params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroy() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onDestroy", void.class, params);
|
||||||
|
|
||||||
|
}
|
||||||
|
public boolean equals__super(java.lang.Object other) {
|
||||||
|
return super.equals(other);
|
||||||
|
}
|
||||||
|
public int hashCode__super() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
56
widgets/src/main/java/com/tns/LogcatLogger.java
Normal file
56
widgets/src/main/java/com/tns/LogcatLogger.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public final class LogcatLogger implements Logger
|
||||||
|
{
|
||||||
|
private final static String DEFAULT_LOG_TAG = "TNS.Java";
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
public LogcatLogger(boolean isEnabled, Context context)
|
||||||
|
{
|
||||||
|
this.enabled = isEnabled;
|
||||||
|
|
||||||
|
if (!isEnabled)
|
||||||
|
{
|
||||||
|
this.initLogging(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setEnabled(boolean isEnabled)
|
||||||
|
{
|
||||||
|
enabled = isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void write(String msg)
|
||||||
|
{
|
||||||
|
Log.d(DEFAULT_LOG_TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void write(String tag, String msg)
|
||||||
|
{
|
||||||
|
Log.d(tag, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLogging(Context context)
|
||||||
|
{
|
||||||
|
boolean isDebuggableApp = JsDebugger.isDebuggableApp(context);
|
||||||
|
|
||||||
|
if (isDebuggableApp)
|
||||||
|
{
|
||||||
|
String verboseLoggingProp = Util.readSystemProperty("nativescript.verbose.logging");
|
||||||
|
|
||||||
|
if (Util.isPositive(verboseLoggingProp))
|
||||||
|
{
|
||||||
|
setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
widgets/src/main/java/com/tns/NativeScriptActivity.java
Normal file
59
widgets/src/main/java/com/tns/NativeScriptActivity.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
@JavaScriptImplementation(javaScriptFile = "app/tns_modules/ui/frme/frame.js")
|
||||||
|
public class NativeScriptActivity extends android.app.Activity implements com.tns.NativeScriptHashCodeProvider {
|
||||||
|
|
||||||
|
public NativeScriptActivity()
|
||||||
|
{
|
||||||
|
com.tns.Platform.initInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onCreate(android.os.Bundle savedInstanceState) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = savedInstanceState;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onCreate", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onSaveInstanceState(android.os.Bundle outState) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = outState;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onSaveInstanceState", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStart() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onStart", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStop() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onStop", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDestroy() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onDestroy", void.class, params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBackPressed() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onBackPressed", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, android.content.Intent data) {
|
||||||
|
java.lang.Object[] params = new Object[3];
|
||||||
|
params[0] = requestCode;
|
||||||
|
params[1] = resultCode;
|
||||||
|
params[2] = data;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onActivityResult", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals__super(java.lang.Object other) {
|
||||||
|
return super.equals(other);
|
||||||
|
}
|
||||||
|
public int hashCode__super() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
widgets/src/main/java/com/tns/NativeScriptApplication.java
Normal file
52
widgets/src/main/java/com/tns/NativeScriptApplication.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
@JavaScriptImplementation(javaScriptFile = "app/tns_modules/application/application.js")
|
||||||
|
public class NativeScriptApplication extends android.app.Application implements com.tns.NativeScriptHashCodeProvider {
|
||||||
|
|
||||||
|
private static NativeScriptApplication thiz;
|
||||||
|
|
||||||
|
public NativeScriptApplication()
|
||||||
|
{
|
||||||
|
thiz = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void attachBaseContext(android.content.Context param_0) {
|
||||||
|
super.attachBaseContext(param_0);
|
||||||
|
|
||||||
|
new RuntimeHelper(this).initRuntime();
|
||||||
|
|
||||||
|
Platform.initInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void onCreate() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onCreate", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLowMemory() {
|
||||||
|
java.lang.Object[] params = null;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onLowMemory", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTrimMemory(int level) {
|
||||||
|
java.lang.Object[] params = new Object[1];
|
||||||
|
params[0] = level;
|
||||||
|
com.tns.Platform.callJSMethod(this, "onTrimMemory", void.class, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean equals__super(java.lang.Object other) {
|
||||||
|
return super.equals(other);
|
||||||
|
}
|
||||||
|
public int hashCode__super() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Application getInstance() {
|
||||||
|
return thiz;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
424
widgets/src/main/java/com/tns/NativeScriptSyncService.java
Normal file
424
widgets/src/main/java/com/tns/NativeScriptSyncService.java
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.net.LocalServerSocket;
|
||||||
|
import android.net.LocalSocket;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class NativeScriptSyncService
|
||||||
|
{
|
||||||
|
private static String SYNC_ROOT_SOURCE_DIR = "/data/local/tmp/";
|
||||||
|
private static final String SYNC_SOURCE_DIR = "/sync/";
|
||||||
|
private static final String FULL_SYNC_SOURCE_DIR = "/fullsync/";
|
||||||
|
private static final String REMOVED_SYNC_SOURCE_DIR = "/removedsync/";
|
||||||
|
|
||||||
|
private static Logger logger;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private final String syncPath;
|
||||||
|
private final String fullSyncPath;
|
||||||
|
private final String removedSyncPath;
|
||||||
|
private final File fullSyncDir;
|
||||||
|
private final File syncDir;
|
||||||
|
private final File removedSyncDir;
|
||||||
|
|
||||||
|
private LocalServerSocketThread localServerThread;
|
||||||
|
private Thread localServerJavaThread;
|
||||||
|
|
||||||
|
public NativeScriptSyncService(Logger logger, Context context)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
syncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + SYNC_SOURCE_DIR;
|
||||||
|
fullSyncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + FULL_SYNC_SOURCE_DIR;
|
||||||
|
removedSyncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + REMOVED_SYNC_SOURCE_DIR;
|
||||||
|
fullSyncDir = new File(fullSyncPath);
|
||||||
|
syncDir = new File(syncPath);
|
||||||
|
removedSyncDir = new File(removedSyncPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sync()
|
||||||
|
{
|
||||||
|
if (logger != null && logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Sync is enabled:");
|
||||||
|
logger.write("Sync path : " + syncPath);
|
||||||
|
logger.write("Full sync path : " + fullSyncPath);
|
||||||
|
logger.write("Removed files sync path: " + removedSyncPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullSyncDir.exists())
|
||||||
|
{
|
||||||
|
executeFullSync(context, fullSyncDir);
|
||||||
|
deleteRecursive(fullSyncDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncDir.exists())
|
||||||
|
{
|
||||||
|
executePartialSync(context, syncDir);
|
||||||
|
deleteRecursive(syncDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedSyncDir.exists())
|
||||||
|
{
|
||||||
|
executeRemovedSync(context, removedSyncDir);
|
||||||
|
deleteRecursive(removedSyncDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalServerSocketThread implements Runnable
|
||||||
|
{
|
||||||
|
private volatile boolean running;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private ListenerWorker commThread;
|
||||||
|
private LocalServerSocket serverSocket;
|
||||||
|
|
||||||
|
public LocalServerSocketThread(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
this.running = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverSocket.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
running = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverSocket = new LocalServerSocket(this.name);
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
LocalSocket socket = serverSocket.accept();
|
||||||
|
commThread = new ListenerWorker(socket);
|
||||||
|
new Thread(commThread).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ListenerWorker implements Runnable
|
||||||
|
{
|
||||||
|
private final DataInputStream input;
|
||||||
|
private Closeable socket;
|
||||||
|
private OutputStream output;
|
||||||
|
|
||||||
|
public ListenerWorker(LocalSocket socket) throws IOException
|
||||||
|
{
|
||||||
|
this.socket = socket;
|
||||||
|
input = new DataInputStream(socket.getInputStream());
|
||||||
|
output = socket.getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int length = input.readInt();
|
||||||
|
input.readFully(new byte[length]); // ignore the payload
|
||||||
|
executePartialSync(context, syncDir);
|
||||||
|
executeRemovedSync(context, removedSyncDir);
|
||||||
|
|
||||||
|
Platform.runScript(new File(NativeScriptSyncService.this.context.getFilesDir(), "internal/livesync.js"));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
output.write(1);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startServer()
|
||||||
|
{
|
||||||
|
localServerThread = new LocalServerSocketThread(context.getPackageName() + "-livesync");
|
||||||
|
localServerJavaThread = new Thread(localServerThread);
|
||||||
|
localServerJavaThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRecursive(File fileOrDirectory)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (fileOrDirectory.isDirectory())
|
||||||
|
{
|
||||||
|
for (File child : fileOrDirectory.listFiles())
|
||||||
|
{
|
||||||
|
deleteRecursive(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOrDirectory.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSyncEnabled(Context context)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
boolean shouldExecuteSync = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
flags = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.flags;
|
||||||
|
shouldExecuteSync = ((flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
|
||||||
|
}
|
||||||
|
catch (NameNotFoundException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldExecuteSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileFilter deletingFilesFilter = new FileFilter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname)
|
||||||
|
{
|
||||||
|
if (pathname.isDirectory())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = pathname.delete();
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
logger.write("Syncing: file not deleted: " + pathname.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void deleteDir(File directory)
|
||||||
|
{
|
||||||
|
File[] subDirectories = directory.listFiles(deletingFilesFilter);
|
||||||
|
if (subDirectories != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < subDirectories.length; i++)
|
||||||
|
{
|
||||||
|
File subDir = subDirectories[i];
|
||||||
|
deleteDir(subDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = directory.delete();
|
||||||
|
if (!success && directory.exists())
|
||||||
|
{
|
||||||
|
logger.write("Syncing: directory not deleted: " + directory.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveFiles(File sourceDir, String sourceRootAbsolutePath, String targetRootAbsolutePath)
|
||||||
|
{
|
||||||
|
File[] files = sourceDir.listFiles();
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Syncing total number of fiiles: " + files.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < files.length; i++)
|
||||||
|
{
|
||||||
|
File file = files[i];
|
||||||
|
if (file.isFile())
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Syncing: " + file.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetFilePath = file.getAbsolutePath().replace(sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||||
|
File targetFileDir = new File(targetFilePath);
|
||||||
|
|
||||||
|
File targetParent = targetFileDir.getParentFile();
|
||||||
|
if (targetParent != null)
|
||||||
|
{
|
||||||
|
targetParent.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = copyFile(file.getAbsolutePath(), targetFilePath);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
logger.write("Sync failed: " + file.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moveFiles(file, sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Can't move files. Source is empty.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this removes only the app directory from the device to preserve
|
||||||
|
// any existing files in /files directory on the device
|
||||||
|
private void executeFullSync(Context context, final File sourceDir)
|
||||||
|
{
|
||||||
|
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||||
|
final File appDir = new File(appPath);
|
||||||
|
|
||||||
|
if (appDir.exists())
|
||||||
|
{
|
||||||
|
deleteDir(appDir);
|
||||||
|
moveFiles(sourceDir, sourceDir.getAbsolutePath(), appDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executePartialSync(Context context, File sourceDir)
|
||||||
|
{
|
||||||
|
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||||
|
final File appDir = new File(appPath);
|
||||||
|
|
||||||
|
if (!appDir.exists())
|
||||||
|
{
|
||||||
|
Log.e("TNS", "Application dir does not exists. Partial Sync failed. appDir: " + appPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Syncing sourceDir " + sourceDir.getAbsolutePath() + " with " + appDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
moveFiles(sourceDir, sourceDir.getAbsolutePath(), appDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRemovedFiles(File sourceDir, String sourceRootAbsolutePath, String targetRootAbsolutePath)
|
||||||
|
{
|
||||||
|
if (!sourceDir.exists())
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Directory does not exist: " + sourceDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = sourceDir.listFiles();
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < files.length; i++)
|
||||||
|
{
|
||||||
|
File file = files[i];
|
||||||
|
if (file.isFile())
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Syncing removed file: " + file.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetFilePath = file.getAbsolutePath().replace(sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||||
|
File targetFile = new File(targetFilePath);
|
||||||
|
targetFile.delete();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deleteRemovedFiles(file, sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeRemovedSync(final Context context, final File sourceDir)
|
||||||
|
{
|
||||||
|
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||||
|
deleteRemovedFiles(sourceDir, sourceDir.getAbsolutePath(), appPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean copyFile(String sourceFile, String destinationFile)
|
||||||
|
{
|
||||||
|
FileInputStream fis = null;
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fis = new FileInputStream(sourceFile);
|
||||||
|
fos = new FileOutputStream(destinationFile, false);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
|
while ((read = fis.read(buffer)) != -1)
|
||||||
|
{
|
||||||
|
fos.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
logger.write("Error copying file " + sourceFile);
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.write("Error copying file " + sourceFile);
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (fis != null)
|
||||||
|
{
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
if (fos != null)
|
||||||
|
{
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public class NativeScriptUncaughtExceptionHandler implements UncaughtExceptionHandler
|
||||||
|
{
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private final UncaughtExceptionHandler defaultHandler;
|
||||||
|
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
public NativeScriptUncaughtExceptionHandler(Logger logger, Context context)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.context = context;
|
||||||
|
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable ex)
|
||||||
|
{
|
||||||
|
String errorMessage = ErrorReport.getErrorMessage(ex);
|
||||||
|
|
||||||
|
if (Platform.isInitialized())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
Platform.passUncaughtExceptionToJsNative(ex, errorMessage);
|
||||||
|
|
||||||
|
if (JsDebugger.isJsDebugerActive())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("Uncaught Exception Message=" + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ErrorReport.startActivity(context, errorMessage) && defaultHandler != null)
|
||||||
|
{
|
||||||
|
defaultHandler.uncaughtException(thread, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
widgets/src/main/java/com/tns/RuntimeHelper.java
Normal file
108
widgets/src/main/java/com/tns/RuntimeHelper.java
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class RuntimeHelper
|
||||||
|
{
|
||||||
|
private final Application app;
|
||||||
|
|
||||||
|
public RuntimeHelper(Application app)
|
||||||
|
{
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasErrorIntent tells you if there was an event (with an uncaught exception) raised from ErrorReport
|
||||||
|
public boolean hasErrorIntent()
|
||||||
|
{
|
||||||
|
boolean hasErrorIntent = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//empty file just to check if there was a raised uncaught error by ErrorReport
|
||||||
|
File errFile = new File(app.getFilesDir(), ErrorReport.ERROR_FILE_NAME);
|
||||||
|
|
||||||
|
if (errFile.exists())
|
||||||
|
{
|
||||||
|
errFile.delete();
|
||||||
|
hasErrorIntent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.d(logTag, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasErrorIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initRuntime()
|
||||||
|
{
|
||||||
|
System.loadLibrary("NativeScript");
|
||||||
|
|
||||||
|
Logger logger = new LogcatLogger(true, app);
|
||||||
|
|
||||||
|
boolean showErrorIntent = hasErrorIntent();
|
||||||
|
if (!showErrorIntent)
|
||||||
|
{
|
||||||
|
Thread.UncaughtExceptionHandler exHandler = new NativeScriptUncaughtExceptionHandler(logger, app);
|
||||||
|
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(exHandler);
|
||||||
|
|
||||||
|
Async.Http.setApplicationContext(this.app);
|
||||||
|
|
||||||
|
ExtractPolicy extractPolicy = new DefaultExtractPolicy(logger);
|
||||||
|
boolean skipAssetExtraction = Util.runPlugin(logger, app);
|
||||||
|
if (!skipAssetExtraction)
|
||||||
|
{
|
||||||
|
new AssetExtractor(null, logger).extractAssets(app, extractPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NativeScriptSyncService.isSyncEnabled(this.app))
|
||||||
|
{
|
||||||
|
NativeScriptSyncService syncService = new NativeScriptSyncService(logger, this.app);
|
||||||
|
|
||||||
|
syncService.sync();
|
||||||
|
syncService.startServer();
|
||||||
|
|
||||||
|
// preserve this instance as strong reference
|
||||||
|
// do not preserve in NativeScriptApplication field inorder to make the code more portable
|
||||||
|
Platform.getOrCreateJavaObjectID(syncService);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
{
|
||||||
|
logger.write("NativeScript LiveSync is not enabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String appName = app.getPackageName();
|
||||||
|
File rootDir = new File(app.getApplicationInfo().dataDir);
|
||||||
|
File appDir = app.getFilesDir();
|
||||||
|
|
||||||
|
ClassLoader classLoader = app.getClassLoader();
|
||||||
|
File dexDir = new File(rootDir, "code_cache/secondary-dexes");
|
||||||
|
String dexThumb = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dexThumb = Util.getDexThumb(app);
|
||||||
|
}
|
||||||
|
catch (NameNotFoundException e)
|
||||||
|
{
|
||||||
|
if (logger.isEnabled()) logger.write("Error while getting current proxy thumb");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ThreadScheduler workThreadScheduler = new WorkThreadScheduler(new Handler(Looper.getMainLooper()));
|
||||||
|
Platform.init(this.app, workThreadScheduler, logger, appName, null, rootDir, appDir, classLoader, dexDir, dexThumb);
|
||||||
|
Platform.runScript(new File(appDir, "internal/ts_helpers.js"));
|
||||||
|
Platform.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String logTag = "MyApp";
|
||||||
|
}
|
||||||
121
widgets/src/main/java/com/tns/Util.java
Normal file
121
widgets/src/main/java/com/tns/Util.java
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package com.tns;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import com.tns.internal.Plugin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public final class Util
|
||||||
|
{
|
||||||
|
private Util()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDexThumb(Context context) throws NameNotFoundException
|
||||||
|
{
|
||||||
|
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||||
|
int code = packageInfo.versionCode;
|
||||||
|
long updateTime = packageInfo.lastUpdateTime;
|
||||||
|
return String.valueOf(updateTime) + "-" + String.valueOf(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDebuggableApp(Context context)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
flags = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.flags;
|
||||||
|
}
|
||||||
|
catch (NameNotFoundException e)
|
||||||
|
{
|
||||||
|
flags = 0;
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isDebuggableApp = ((flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
|
||||||
|
return isDebuggableApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runPlugin(Logger logger, Context context)
|
||||||
|
{
|
||||||
|
boolean success = false;
|
||||||
|
String pluginClassName = "org.nativescript.livesync.LiveSyncPlugin";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
||||||
|
Bundle metadataBundle = ai.metaData;
|
||||||
|
if (metadataBundle != null)
|
||||||
|
{
|
||||||
|
pluginClassName = metadataBundle.getString("com.tns.internal.Plugin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class<?> liveSyncPluginClass = Class.forName(pluginClassName);
|
||||||
|
Plugin p = (Plugin) liveSyncPluginClass.newInstance();
|
||||||
|
success = p.execute(context);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (logger.isEnabled())
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readSystemProperty(String name)
|
||||||
|
{
|
||||||
|
InputStreamReader in = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process proc = Runtime.getRuntime().exec(new String[] { "/system/bin/getprop", name });
|
||||||
|
in = new InputStreamReader(proc.getInputStream());
|
||||||
|
reader = new BufferedReader(in);
|
||||||
|
return reader.readLine();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
silentClose(in);
|
||||||
|
silentClose(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void silentClose(Closeable closeable)
|
||||||
|
{
|
||||||
|
if (closeable == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isPositive(String value)
|
||||||
|
{
|
||||||
|
return (value.equals("true") || value.equals("TRUE") ||
|
||||||
|
value.equals("yes") || value.equals("YES") ||
|
||||||
|
value.equals("enabled") || value.equals("ENABLED"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.tns.internal;
|
||||||
|
|
||||||
|
import com.tns.ExtractPolicy;
|
||||||
|
|
||||||
|
public interface AppBuilderCallback
|
||||||
|
{
|
||||||
|
void onConfigurationChanged(android.content.Context context, android.content.res.Configuration newConfig);
|
||||||
|
|
||||||
|
void onCreate(android.content.Context context);
|
||||||
|
|
||||||
|
void onLowMemory(android.content.Context context);
|
||||||
|
|
||||||
|
void onTerminate(android.content.Context context);
|
||||||
|
|
||||||
|
void onTrimMemory(android.content.Context context, int level);
|
||||||
|
|
||||||
|
Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler();
|
||||||
|
|
||||||
|
ExtractPolicy getExtractPolicy();
|
||||||
|
|
||||||
|
boolean shouldEnableDebugging(android.content.Context context);
|
||||||
|
}
|
||||||
6
widgets/src/main/java/com/tns/internal/Plugin.java
Normal file
6
widgets/src/main/java/com/tns/internal/Plugin.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package com.tns.internal;
|
||||||
|
|
||||||
|
public interface Plugin
|
||||||
|
{
|
||||||
|
boolean execute(android.content.Context context) throws Exception;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user