diff --git a/ApkEditorTranslate.iml b/ApkEditorTranslate.iml
new file mode 100644
index 0000000..4f92904
--- /dev/null
+++ b/ApkEditorTranslate.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/apkEditorTranslate.iml b/apkEditorTranslate/apkEditorTranslate.iml
new file mode 100644
index 0000000..55892c4
--- /dev/null
+++ b/apkEditorTranslate/apkEditorTranslate.iml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/build.gradle b/apkEditorTranslate/build.gradle
new file mode 100644
index 0000000..21c47f1
--- /dev/null
+++ b/apkEditorTranslate/build.gradle
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "25.0.2"
+ useLibrary 'org.apache.http.legacy'
+
+ defaultConfig {
+ applicationId "apkeditor.translate"
+ minSdkVersion 12
+ targetSdkVersion 13
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+
+dependencies {
+ compile files('libs/rhino-1.7.7.jar')
+}
diff --git a/apkEditorTranslate/libs/rhino-1.7.7.jar b/apkEditorTranslate/libs/rhino-1.7.7.jar
new file mode 100644
index 0000000..422b5f2
Binary files /dev/null and b/apkEditorTranslate/libs/rhino-1.7.7.jar differ
diff --git a/apkEditorTranslate/lint.xml b/apkEditorTranslate/lint.xml
new file mode 100644
index 0000000..8423c0e
--- /dev/null
+++ b/apkEditorTranslate/lint.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/AndroidManifest.xml b/apkEditorTranslate/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..422d68c
--- /dev/null
+++ b/apkEditorTranslate/src/main/AndroidManifest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/CookieInfo.java b/apkEditorTranslate/src/main/java/apkeditor/translate/CookieInfo.java
new file mode 100644
index 0000000..ee22425
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/CookieInfo.java
@@ -0,0 +1,44 @@
+package apkeditor.translate;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class CookieInfo implements Serializable {
+ private static final long serialVersionUID = -1906450774713846916L;
+ private String domain;
+ private Date expiryDate;
+ private String name;
+ private String value;
+
+ public Date getCookieDate() {
+ return this.expiryDate;
+ }
+
+ public String getCookieDomain() {
+ return this.domain;
+ }
+
+ public String getCookieName() {
+ return this.name;
+ }
+
+ public String getCookieValue() {
+ return this.value;
+ }
+
+ public void setCookieDate(Date paramDate) {
+ this.expiryDate = paramDate;
+ }
+
+ public void setCookieDomain(String paramString) {
+ this.domain = paramString;
+ }
+
+ public void setCookieName(String paramString) {
+ this.name = paramString;
+ }
+
+ public void setCookieValue(String paramString) {
+ this.value = paramString;
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/Debug.java b/apkEditorTranslate/src/main/java/apkeditor/translate/Debug.java
new file mode 100644
index 0000000..c6bb158
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/Debug.java
@@ -0,0 +1,19 @@
+package apkeditor.translate;
+
+import android.util.Log;
+
+public class Debug {
+ public static void dumpClassName(Object paramObject) {
+ Object[] arrayOfObject = new Object[1];
+ arrayOfObject[0] = paramObject.getClass().getName();
+ log("** Class Name: %", arrayOfObject);
+ }
+
+ public static void log(String paramString, Object... args) {
+ //Log.d("DEBUG", String.format(paramString, args) + "\n");
+ }
+
+ public static void dump(String name, String content) {
+ //Log.d("DEBUG", name + ": ****\n" + content);
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/MainActivity.java b/apkEditorTranslate/src/main/java/apkeditor/translate/MainActivity.java
new file mode 100644
index 0000000..32e1f9c
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/MainActivity.java
@@ -0,0 +1,52 @@
+package apkeditor.translate;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+
+public class MainActivity extends Activity implements View.OnClickListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+
+ setContentView(R.layout.activity_main);
+
+ initView();
+ }
+
+ private void initView() {
+ findViewById(R.id.btn_view_free).setOnClickListener(this);
+ findViewById(R.id.btn_view_pro).setOnClickListener(this);
+ }
+
+ private void viewInMarket(String pkgName) {
+ Uri uri = Uri.parse("market://details?id=" + pkgName);
+ Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
+ try {
+ startActivity(goToMarket);
+ } catch (ActivityNotFoundException e) {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri
+ .parse("http://play.google.com/store/apps/details?id=" + pkgName)));
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ int id = view.getId();
+ switch (id) {
+ case R.id.btn_view_free:
+ viewInMarket("com.gmail.heagoo.apkeditor");
+ break;
+ case R.id.btn_view_pro:
+ viewInMarket("com.gmail.heagoo.apkeditor.pro");
+ break;
+ }
+ }
+}
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/MyCookieStore.java b/apkEditorTranslate/src/main/java/apkeditor/translate/MyCookieStore.java
new file mode 100644
index 0000000..932d50d
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/MyCookieStore.java
@@ -0,0 +1,129 @@
+package apkeditor.translate;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.client.CookieStore;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.cookie.BasicClientCookie;
+
+public class MyCookieStore implements CookieStore {
+
+ private List cookieList = (List) getCookieFile("CookieFile");
+ File file = null;
+
+ public MyCookieStore() {
+ if (this.cookieList == null)
+ this.cookieList = new ArrayList();
+ }
+
+ private List getCookieFile(String filename) {
+ return new ArrayList();
+ }
+
+ @SuppressWarnings("unused")
+ private void dumpCookieList() {
+ Iterator localIterator = this.cookieList.iterator();
+ while (true) {
+ if (!localIterator.hasNext())
+ return;
+ CookieInfo localCookieInfo = (CookieInfo) localIterator.next();
+ Debug.log("\tName: " + localCookieInfo.getCookieName(),
+ new Object[0]);
+ Debug.log("\tValue: " + localCookieInfo.getCookieValue(),
+ new Object[0]);
+ Debug.log("\tDomain: " + localCookieInfo.getCookieDomain(),
+ new Object[0]);
+ Debug.log("\tDate: " + localCookieInfo.getCookieDate(),
+ new Object[0]);
+ }
+ }
+
+ // Update the cookie
+ private void updateCookieList(CookieInfo cookieInfo) {
+
+ String name = cookieInfo.getCookieName();
+
+ for (CookieInfo ci : this.cookieList) {
+ // The same name already exists
+ if (ci.getCookieName().equals(name)) {
+ ci.setCookieDate(cookieInfo.getCookieDate());
+ ci.setCookieDomain(cookieInfo.getCookieDomain());
+ ci.setCookieName(cookieInfo.getCookieName());
+ ci.setCookieValue(cookieInfo.getCookieValue());
+ return;
+ }
+ }
+
+ this.cookieList.add(cookieInfo);
+ }
+
+ // Add one cookie to the store
+ public void addCookie(Cookie cookie) {
+ if (cookie == null)
+ return;
+
+ if (this.cookieList == null) {
+ this.cookieList = new ArrayList();
+ }
+
+ CookieInfo cookieInfo = new CookieInfo();
+ if (cookie.getExpiryDate() != null)
+ cookieInfo.setCookieDate(cookie.getExpiryDate());
+ cookieInfo.setCookieName(cookie.getName());
+ cookieInfo.setCookieValue(cookie.getValue());
+ cookieInfo.setCookieDomain(cookie.getDomain());
+
+ updateCookieList(cookieInfo);
+ }
+
+ public void addCookies(List cookies) {
+ Iterator it = cookies.iterator();
+ while (it.hasNext()) {
+ addCookie((Cookie) it.next());
+ }
+ }
+
+ public void clear() {
+ }
+
+ public boolean clearExpired(Date paramDate) {
+ return false;
+ }
+
+ public Cookie convertCookie(CookieInfo paramCookieInfo) {
+ BasicClientCookie localBasicClientCookie = new BasicClientCookie(
+ paramCookieInfo.getCookieName(),
+ paramCookieInfo.getCookieValue());
+ localBasicClientCookie.setDomain(paramCookieInfo.getCookieDomain());
+ localBasicClientCookie.setExpiryDate(paramCookieInfo.getCookieDate());
+ return localBasicClientCookie;
+ }
+
+ public List getCookies() {
+ ArrayList ret = new ArrayList();
+
+ if (this.cookieList != null) {
+ for (int i = 0; i < this.cookieList.size(); i++) {
+ CookieInfo cookie = (CookieInfo) cookieList.get(i);
+ if (cookie != null) {
+ ret.add(convertCookie(cookie));
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public void saveCookies() {
+ if (this.cookieList != null)
+ putCookie("CookieFile", this.cookieList);
+ }
+
+ private void putCookie(String string, List cookieList2) {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/Pair.java b/apkEditorTranslate/src/main/java/apkeditor/translate/Pair.java
new file mode 100644
index 0000000..f9173bd
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/Pair.java
@@ -0,0 +1,13 @@
+package apkeditor.translate;
+
+
+public class Pair {
+
+ public final T1 m1;
+ public T2 m2;
+
+ public Pair(T1 t1, T2 t2) {
+ this.m1 = t1;
+ this.m2 = t2;
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/SSLSocketFactoryEx.java b/apkEditorTranslate/src/main/java/apkeditor/translate/SSLSocketFactoryEx.java
new file mode 100644
index 0000000..f1cd247
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/SSLSocketFactoryEx.java
@@ -0,0 +1,87 @@
+package apkeditor.translate;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.http.HttpVersion;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpProtocolParams;
+
+public class SSLSocketFactoryEx extends org.apache.http.conn.ssl.SSLSocketFactory {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+
+ public SSLSocketFactoryEx(KeyStore paramKeyStore)
+ throws NoSuchAlgorithmException, KeyManagementException,
+ KeyStoreException, UnrecoverableKeyException {
+ super(paramKeyStore);
+ TrustManager local1 = new X509TrustManager() {
+ public void checkClientTrusted(
+ X509Certificate[] paramArrayOfX509Certificate,
+ String paramString) throws CertificateException {
+ }
+
+ public void checkServerTrusted(
+ X509Certificate[] paramArrayOfX509Certificate,
+ String paramString) throws CertificateException {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+ this.sslContext.init(null, new TrustManager[] { local1 }, null);
+ }
+
+ public static DefaultHttpClient getNewHttpClient() {
+ try {
+ KeyStore localKeyStore = KeyStore.getInstance(KeyStore
+ .getDefaultType());
+ localKeyStore.load(null, null);
+ SSLSocketFactoryEx localSSLSocketFactoryEx = new SSLSocketFactoryEx(
+ localKeyStore);
+ localSSLSocketFactoryEx
+ .setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ BasicHttpParams localBasicHttpParams = new BasicHttpParams();
+ HttpProtocolParams.setVersion(localBasicHttpParams,
+ HttpVersion.HTTP_1_1);
+ HttpProtocolParams.setContentCharset(localBasicHttpParams, "UTF-8");
+ SchemeRegistry localSchemeRegistry = new SchemeRegistry();
+ localSchemeRegistry.register(new Scheme("http", PlainSocketFactory
+ .getSocketFactory(), 80));
+ localSchemeRegistry.register(new Scheme("https",
+ localSSLSocketFactoryEx, 443));
+ DefaultHttpClient localDefaultHttpClient = new DefaultHttpClient(
+ new ThreadSafeClientConnManager(localBasicHttpParams,
+ localSchemeRegistry), localBasicHttpParams);
+ return localDefaultHttpClient;
+ } catch (Exception localException) {
+ }
+ return new DefaultHttpClient();
+ }
+
+ public Socket createSocket() throws IOException {
+ return this.sslContext.getSocketFactory().createSocket();
+ }
+
+ public Socket createSocket(Socket paramSocket, String paramString,
+ int paramInt, boolean paramBoolean) throws IOException,
+ UnknownHostException {
+ return this.sslContext.getSocketFactory().createSocket(paramSocket,
+ paramString, paramInt, paramBoolean);
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateActivity.java b/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateActivity.java
new file mode 100644
index 0000000..cdf8561
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateActivity.java
@@ -0,0 +1,340 @@
+package apkeditor.translate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.gmail.heagoo.apkeditor.translate.TranslateItem;
+import com.gmail.heagoo.common.ActivityUtil;
+import com.gmail.heagoo.common.IOUtils;
+
+public class TranslateActivity extends Activity implements OnClickListener {
+
+ // Standard code like "-zh-rCN"
+ private String targetLanguageCode;
+ private String translatedFilePath;
+ private LinearLayout stringLayout;
+
+ // All the string views
+ private Map etMap = new HashMap();
+ private LinearLayout translatingLayout;
+ private LinearLayout translatedLayout;
+ private TextView translatingMsg;
+ private TextView translatedMsg;
+ private Button stopOrSaveBtn;
+
+ private boolean translateFinished = false;
+ private boolean translationSaved = false;
+
+ // Translate list includes items already translated
+ private List translatedList;
+ private List untranslatedList;
+ private TranslateTask translatingTask;
+
+ // How many items need to be translated
+ private int numToTranslate = 0;
+ private int numSucceed = 0;
+ private int numFailed = 0;
+
+ // Style control
+ private boolean isFullScreen;
+ private boolean isDark;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+
+ this.isFullScreen = false;
+ this.isDark = false;
+ Intent intent = getIntent();
+ if (intent.getExtras() != null) {
+ isFullScreen = ActivityUtil.getBoolParam(intent, "isFullScreen");
+ isDark = ActivityUtil.getBoolParam(intent, "isDark");
+ }
+
+ if (isFullScreen) {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+
+ if (isDark) {
+ setTheme(android.R.style.Theme_Black_NoTitleBar);
+ setContentView(R.layout.activity_autotranslate_dark);
+ } else {
+ setContentView(R.layout.activity_autotranslate);
+ }
+
+ initData(intent);
+
+ initView(translatedList);
+
+ // Start the translating
+ this.startTranslating();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initData(Intent intent) {
+ if (intent.getExtras() != null) {
+ this.targetLanguageCode = ActivityUtil.getParam(intent,
+ "targetLanguageCode");
+ this.translatedFilePath = ActivityUtil.getParam(intent,
+ "translatedList_file");
+ this.translatedList = (List) IOUtils
+ .readObjectFromFile(translatedFilePath);
+ String path = ActivityUtil
+ .getParam(intent, "untranslatedList_file");
+ this.untranslatedList = (List) IOUtils
+ .readObjectFromFile(path);
+ }
+ }
+
+ private void initView(List strings) {
+ this.stringLayout = (LinearLayout) this
+ .findViewById(R.id.strings_layout);
+
+ this.translatingLayout = (LinearLayout) this
+ .findViewById(R.id.translating_layout);
+ this.translatedLayout = (LinearLayout) this
+ .findViewById(R.id.translated_layout);
+ this.translatingMsg = (TextView) translatingLayout
+ .findViewById(R.id.translating_msg);
+ this.translatedMsg = (TextView) translatedLayout
+ .findViewById(R.id.translated_msg);
+
+ if (strings != null) {
+ for (int i = 0; i < strings.size(); i++) {
+ TranslateItem item = strings.get(i);
+ View strView = createStringView(item);
+ stringLayout.addView(strView);
+ }
+ }
+
+ this.stopOrSaveBtn = (Button) this.findViewById(R.id.btn_stop_or_save);
+ stopOrSaveBtn.setOnClickListener(this);
+ }
+
+ // Transfer modified files to parent activity
+ private void setResult(List stringValues) {
+ Intent intent = new Intent();
+ intent.putExtra("targetLanguageCode", this.targetLanguageCode);
+ IOUtils.writeObjectToFile(this.translatedFilePath, stringValues);
+ intent.putExtra("translatedList_file", this.translatedFilePath);
+
+ this.setResult(RESULT_OK, intent);
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btn_stop_or_save) {
+ if (this.translateFinished) {
+ saveStringAsResource();
+ this.translationSaved = true;
+ this.finish();
+ } else {
+ stopTranslating();
+ }
+ }
+ }
+
+ private void showSaveDialog(final boolean bStartNewTranslation) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.save_translation_title);
+ builder.setMessage(R.string.save_translation_msg);
+ builder.setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // First save the translation and then do the
+ // translate
+ saveStringAsResource();
+ TranslateActivity.this.translationSaved = true;
+ if (bStartNewTranslation) {
+ // newTranslationTask();
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.no,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // Show "x strings are discarded" and start a
+ // new translation
+ String msg = getResources().getString(
+ R.string.string_notsaved_msg);
+ msg = String.format(msg, numSucceed);
+ Toast.makeText(TranslateActivity.this, msg,
+ Toast.LENGTH_LONG).show();
+ // newTranslationTask();
+ }
+ });
+ builder.show();
+ }
+
+ // private void newTranslationTask() {
+ // this.hide();
+ //
+ // ApkInfoActivity activity = activityRef.get();
+ // activity.startNewTranslation();
+ // }
+
+ // Forcely stop it
+ private void stopTranslating() {
+ translatingTask.cancel(true);
+ translateCompleted();
+ }
+
+ // To save the translation result
+ private void saveStringAsResource() {
+ // Prepare translated values (collect from the edit text)
+ List stringValues = new ArrayList();
+ for (Map.Entry entry : etMap.entrySet()) {
+ String name = entry.getKey();
+ String translatedVal = entry.getValue().getText().toString();
+ if (!"".equals(translatedVal)) {
+ stringValues.add(new TranslateItem(name, null, translatedVal));
+ }
+ }
+
+ // No translated string
+ if (stringValues.isEmpty()) {
+ // Toast.makeText(this, R.string.error_no_string_tosave,
+ // Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ setResult(stringValues);
+ }
+
+ @SuppressLint("InflateParams")
+ protected View createStringView(TranslateItem item) {
+
+ int itemLayoutId = this.isDark ? R.layout.item_stringvalue_translate_dark
+ : R.layout.item_stringvalue_translate;
+ View stringView = getLayoutInflater()
+ .inflate(itemLayoutId, null, false);
+
+ TextView tv = (TextView) stringView.findViewById(R.id.string_name);
+ tv.setText(item.name);
+
+ tv = (TextView) stringView.findViewById(R.id.origin_value);
+ tv.setText(item.originValue);
+
+ EditText et = (EditText) stringView.findViewById(R.id.translated_value);
+ etMap.put(item.name, et); // Record it
+ if (item.translatedValue != null) {
+ et.setText(item.translatedValue);
+ }
+
+ return stringView;
+ }
+
+ // Update views
+ @SuppressLint("DefaultLocale")
+ public void updateView(List items) {
+ for (TranslateItem item : items) {
+ View v = this.createStringView(item);
+ this.stringLayout.addView(v);
+
+ if (item.translatedValue != null) {
+ this.numSucceed += 1;
+ } else {
+ this.numFailed += 1;
+ }
+ }
+
+ String strTranslated = this.getString(R.string.translated);
+ int total = this.numToTranslate - this.numFailed;
+ String msg = String.format("%d / %d " + strTranslated, this.numSucceed,
+ total);
+ this.translatingMsg.setText(msg);
+ }
+
+ // Naturally completed, not by force stop
+ public void translateCompleted() {
+ this.translateFinished = true;
+ this.translatingLayout.setVisibility(View.GONE);
+ this.translatedLayout.setVisibility(View.VISIBLE);
+
+ String msg = null;
+ if (numToTranslate == 0) {
+ msg = getString(R.string.error_no_string_totranslate);
+ } else {
+ msg = String.format(getString(R.string.translated_format),
+ numSucceed);
+ }
+
+ // Failed number
+ if (numFailed > 0) {
+ msg += String.format(", " + getString(R.string.failed_format),
+ numFailed);
+ }
+
+ // Untranslated number
+ int untranslatedNum = numToTranslate - numSucceed - numFailed;
+ if (untranslatedNum > 0) {
+ msg += String.format(
+ ", " + getString(R.string.untranslated_format),
+ untranslatedNum);
+ }
+
+ this.translatedMsg.setText(msg);
+
+ // Change the button text
+ if (numSucceed > 0) {
+ stopOrSaveBtn.setText(R.string.save_and_close);
+ } else {
+ stopOrSaveBtn.setText(R.string.close);
+ }
+ }
+
+ private void startTranslating() {
+
+ // Update the view
+ this.translatingMsg.setText(R.string.translating);
+ this.translatingLayout.setVisibility(View.VISIBLE);
+ this.translatedLayout.setVisibility(View.GONE);
+ this.stopOrSaveBtn.setText(R.string.stop);
+
+ this.translationSaved = false;
+ this.translateFinished = false;
+ this.numToTranslate = (untranslatedList != null ? untranslatedList.size() : 0);
+ this.numSucceed = 0;
+ this.numFailed = 0;
+
+ if (numToTranslate > 0) {
+ this.translatingTask = new TranslateTask(untranslatedList, this);
+ translatingTask.execute();
+ } else {
+ translateCompleted();
+ }
+ }
+
+ // Get the google language code
+ // Convert -zh-rCN to zh-CN
+ public String getGoogleLangCode() {
+ String code = this.targetLanguageCode.substring(1);
+ int pos = code.indexOf("-");
+ if (pos != -1) {
+ code = code.substring(0, pos + 1) + code.substring(pos + 2);
+ }
+ return code;
+ }
+
+}
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateTask.java b/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateTask.java
new file mode 100644
index 0000000..4312dfa
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/TranslateTask.java
@@ -0,0 +1,152 @@
+package apkeditor.translate;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import com.gmail.heagoo.apkeditor.translate.TranslateItem;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+public class TranslateTask extends
+ AsyncTask, Boolean> {
+
+ private List untranslated;
+ private WeakReference activityRef;
+
+ Random r = new Random();
+ private String userAgent;
+
+ public TranslateTask(List untranslated,
+ TranslateActivity activity) {
+ this.untranslated = untranslated;
+ this.activityRef = new WeakReference(activity);
+ }
+
+ @Override
+ protected void onPreExecute() {
+ this.userAgent = getUserAgent(activityRef.get());
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ activityRef.get().translateCompleted();
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ Random r = new Random(System.currentTimeMillis());
+ final int maxItems = 10 + r.nextInt(5);
+ final int maxChars = 900;
+
+ String code = activityRef.get().getGoogleLangCode();
+ Translator translator = new Translator(userAgent, code);
+
+ List todo = new ArrayList();
+ for (TranslateItem item : untranslated) {
+ if (this.isCancelled()) {
+ return false;
+ }
+
+ // Multiple lines
+ if (item.originValue.contains("\n")) {
+ if (!todo.isEmpty()) {
+ doTranslate(translator, todo);
+ }
+
+ todo.add(item);
+ doTranslate(translator, todo);
+ } else if (todo.size() >= maxItems
+ || (getTotalCharaters(todo) + item.originValue.length()) > maxChars) {
+ doTranslate(translator, todo);
+ todo.add(item);
+ } else {
+ todo.add(item);
+ }
+ }
+
+ if (this.isCancelled()) {
+ return todo.isEmpty();
+ }
+
+ // Remaining
+ if (!todo.isEmpty()) {
+ doTranslate(translator, todo);
+ }
+
+ return true;
+ }
+
+ // Compute how many chars
+ private int getTotalCharaters(List todo) {
+ int totalLen = 0;
+ for (TranslateItem item : todo) {
+ totalLen += item.originValue.length();
+ }
+ return totalLen;
+ }
+
+ private void doTranslate(Translator translator, List todo) {
+ // No string need to be translated
+ if (todo.isEmpty()) {
+ return;
+ }
+
+ // Manually sleep awhile, so that I am not taken as a robot
+ try {
+ Thread.sleep(300 + r.nextInt(500));
+ } catch (InterruptedException e) {
+ }
+
+ if (this.isCancelled()) {
+ return;
+ }
+
+ translator.translate(todo);
+ checkResult(todo);
+ todo.clear();
+ }
+
+ // Check how many items are successfully translated
+ @SuppressWarnings("unchecked")
+ private void checkResult(List translatedItems) {
+ ArrayList copied = new ArrayList();
+ copied.addAll(translatedItems);
+ this.publishProgress(copied);
+ }
+
+ @Override
+ protected void onProgressUpdate(ArrayList... args) {
+ ArrayList translated = args[0];
+ activityRef.get().updateView(translated);
+ }
+
+ private static String getUserAgent(Context ctx) {
+ String ua = null;
+
+ WebView webview = new WebView(ctx);
+ webview.layout(0, 0, 0, 0);
+ WebSettings settings = webview.getSettings();
+ if (settings != null) {
+ ua = settings.getUserAgentString();
+ }
+ if (ua != null) {
+ String strNT = " NX";
+ StringBuffer sb = new StringBuffer();
+ sb.append("Mozilla/5.0");
+ sb.append(" (Windows");
+ strNT = strNT.replace('X', 'T');
+ sb.append(strNT.concat(" 6.1; WOW64) "));
+ sb.append("AppleWebKit/537.11 (KHTML, like Gecko) ");
+ sb.append("Chrome/23.0.1271.97 ");
+ sb.append("Safari/537.11");
+ ua = sb.toString();
+ }
+
+ return ua;
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/Translator.java b/apkEditorTranslate/src/main/java/apkeditor/translate/Translator.java
new file mode 100644
index 0000000..8704260
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/Translator.java
@@ -0,0 +1,314 @@
+package apkeditor.translate;
+
+import java.net.URLEncoder;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONTokener;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.Scriptable;
+
+import com.gmail.heagoo.apkeditor.translate.TranslateItem;
+
+public class Translator {
+
+ private WebBrowser browser;
+ private String tkk;
+ private String startUrl;
+
+ // This is a temporary var
+ private int jsonIndex = 0;
+
+ private String targetLangCode;
+
+ public Translator(String userAgent, String _target) {
+ this.browser = new WebBrowser(userAgent);
+ this.startUrl = "https://translate.google.com/m/translate";
+ this.targetLangCode = _target;
+
+ String mainPageContent = browser.get("main", startUrl, null);
+
+ // Can not get the content of the main page
+ if (mainPageContent == null) {
+ return;
+ }
+
+ final String keyword = "tkk:'";
+ int startPos = mainPageContent.indexOf(keyword);
+ // ERROR
+ if (startPos == -1) {
+ return;
+ }
+
+ // Extract the tkk
+ startPos += keyword.length();
+ int endPos = mainPageContent.indexOf("'", startPos);
+ this.tkk = mainPageContent.substring(startPos, endPos);
+ }
+
+ // javaScriptCode is like: ((function(){var a\x3d3253064315;var b\x3d-757794981;return 415855+\x27.\x27+(a+b)})())
+ private static String unescapeTkkScript(String javaScriptCode) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < javaScriptCode.length(); i++) {
+ char c = javaScriptCode.charAt(i);
+ if (c == '\\' && (i + 3 < javaScriptCode.length())
+ && javaScriptCode.charAt(i + 1) == 'x') {
+ char dc = decodeChar(javaScriptCode.charAt(i + 2),
+ javaScriptCode.charAt(i + 3));
+ sb.append(dc);
+ i += 3;
+ } else {
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private static char decodeChar(char c1, char c2) {
+ int i1 = hex2Int(c1);
+ int i2 = hex2Int(c2);
+ return (char)(i1 * 16 + i2);
+ }
+
+ private static int hex2Int(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else {
+ return c - 'A' + 10;
+ }
+ }
+
+ protected String encode(String str) {
+ try {
+ return URLEncoder.encode(str, "UTF-8");
+ } catch (Exception e) {
+ return str;
+ }
+ }
+
+ protected static String getToken(String tkk, String q) {
+ String tkkScript = unescapeTkkScript(tkk);
+
+ String javaScriptCode = "var Wl=function(a, b) {"
+ + "for (var c = 0; c < b.length - 2; c += 3) {"
+ + "var d = b.charAt(c + 2)"
+ + " , d = \"a\" <= d ? d.charCodeAt(0) - 87 : Number(d)"
+ + " , d = \"+\" == b.charAt(c + 1) ? a >>> d : a << d;"
+ + "a = \"+\" == b.charAt(c) ? a + d & 4294967295 : a ^ d"
+ + "}"
+ + "return a"
+ + "},"
+
+ + "Yl = function(b,a) {"
+ + "var c,d;"
+ + "d = b.split(\".\");"
+ + "b = Number(d[0]) || 0;"
+ + "for (var e = [], f = 0, g = 0; g < a.length; g++) {"
+ + "var k = a.charCodeAt(g);"
+ + "128 > k ? e[f++] = k : (2048 > k ? e[f++] = k >> 6 | 192 : (55296 == (k & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (k = 65536 + ((k & 1023) << 10) + (a.charCodeAt(++g) & 1023),"
+ + "e[f++] = k >> 18 | 240,"
+ + "e[f++] = k >> 12 & 63 | 128) : e[f++] = k >> 12 | 224,"
+ + "e[f++] = k >> 6 & 63 | 128),"
+ + "e[f++] = k & 63 | 128)"
+ + "}"
+ + "a = b;"
+ + "for (f = 0; f < e.length; f++)"
+ + "a += e[f],"
+ + "a = Wl(a, \"+-a^+6\");"
+ + "a = Wl(a, \"+-3^+b+-f\");"
+ + "a ^= Number(d[1]) || 0;"
+ + "0 > a && (a = (a & 2147483647) + 2147483648);"
+ + "a = a % 1E6;"
+ + "return a.toString() + \".\" +"
+ + "(a ^ b)"
+ +"};";
+
+ // Every Rhino VM begins with the enter()
+ // This Context is not Android's Context
+ Context rhino = Context.enter();
+
+ // Turn off optimization to make Rhino Android compatible
+ rhino.setOptimizationLevel(-1);
+ try {
+ Scriptable scope = rhino.initStandardObjects();
+
+ // Note the forth argument is 1, which means the JavaScript source
+ // has
+ // been compressed to only one line using something like YUI
+ Object ret = rhino.evaluateString(scope, tkkScript,
+ "JavaScript", 1, null);
+
+ rhino.evaluateString(scope, javaScriptCode,
+ "JavaScript", 1, null);
+
+ // Get the functionName defined in JavaScriptCode
+ Object obj = scope.get("Yl", scope);
+
+ if (obj instanceof Function) {
+ Function jsFunction = (Function) obj;
+
+ // Call the function with params
+ Object jsResult = jsFunction.call(rhino, scope, scope, new Object[] {ret.toString(), q});
+
+ // Parse the jsResult object to a String
+ String result = Context.toString(jsResult);
+ return result;
+ }
+ } finally {
+ Context.exit();
+ }
+
+ return null;
+ }
+
+// protected static void getToken_Old(String tkk, String a) {
+// String originStr = decodeTkk(tkk);
+// String[] d = new String[2];
+// int dotPos = originStr.charAt('.');
+// if (dotPos != -1) {
+// d[0] = originStr.substring(0, dotPos);
+// d[1] = originStr.substring(dotPos + 1);
+// }
+//
+// int c = Integer.valueOf(tkk);
+// int b = Integer.valueOf(d[0]);
+// int g = 0;
+// long new_c = 0;
+// StringBuffer d = new StringBuffer();
+// for (; g < a.length(); g++) {
+// int k = (int) a.charAt(g);
+// if (k < 128) {
+// d.append((char) k);
+// } else {
+// if (k < 2048) {
+// d.append((char) ((k >> 6) | 192));
+// } else {
+// d.append((char) ((k >> 12) | 224));
+// d.append((char) ((k >> 6) & 63 | 128));
+// }
+// d.append((char) (k & 63 | 128));
+// }
+// }
+// for (int i = 0; i < d.length(); i++) {
+// c += (int) d.charAt(i);
+// c += c << 10;
+// c ^= c >>> 6;
+// }
+// c += c << 3;
+// c ^= c >>> 11;
+// c = c + (c << 15) & 0xffffffff;
+// if (c < 0) {
+// new_c = (c & 0x7fffffff) + 0x80000000L;
+// } else {
+// new_c = c;
+// }
+// new_c %= 1E6;
+// return String.valueOf(new_c) + "|" + String.valueOf(new_c ^ b);
+// }
+
+ public void translate(List items) {
+ // This may be caused by main page loading fail
+ if (tkk == null) {
+ return;
+ }
+
+ // Concat the string
+ StringBuffer queryBuf = new StringBuffer();
+ for (TranslateItem item : items) {
+ queryBuf.append(item.originValue);
+ queryBuf.append('\n');
+ }
+ queryBuf.deleteCharAt(queryBuf.length() - 1);
+ String q = queryBuf.toString();
+
+ String url = "https://translate.google.com/translate_a/single?"
+ + "client=webapp&sl=auto&tl="
+ + targetLangCode
+ + "&hl=en&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&otf=1&ssel=0&tsel=0&kc=1&tk="
+ + encode(getToken(tkk, q)) + "&q=" + encode(q);
+ Debug.log("url=%s", url);
+ String content = browser.get("translate", url, startUrl);
+
+ // Succeed
+ if (content != null && content.startsWith("[[[\"")) {
+ int position = content.indexOf("]]");
+ if (position != -1) {
+ parseContent(content.substring(1, position + 2), items);
+ }
+ }
+ }
+
+ // Parse the content and save result to items
+ private void parseContent(String str, List items) {
+ try {
+ JSONTokener jsonParser = new JSONTokener(str);
+ JSONArray values = (JSONArray) jsonParser.nextValue();
+ // Only one item, all the translation content should save to it
+ if (items.size() == 1) {
+ items.get(0).translatedValue = extractAllValueFromJson(values);
+// Log.d("DEBUG",
+// items.get(0).originValue + " ---> "
+// + items.get(0).translatedValue);
+ } else {
+ int srcNum = items.size();
+ this.jsonIndex = 0;
+ for (int i = 0; i < srcNum; i++) {
+ TranslateItem item = items.get(i);
+ item.translatedValue = extractOneItemFromJson(values);
+// Log.d("DEBUG", item.originValue + " ---> "
+// + item.translatedValue);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // In Json, one item may be splited into several arrays
+ // jsonIndex will be used
+ private String extractOneItemFromJson(JSONArray values)
+ throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ while (jsonIndex < values.length() - 1) {
+ JSONArray oneArray = (JSONArray) values.get(jsonIndex++);
+ String curVal = (String) oneArray.get(0);
+ if (curVal == null) {
+ break;
+ }
+
+ sb.append(curVal);
+ if (curVal.endsWith("\n")) {
+ sb.deleteCharAt(sb.length() - 1);
+ break;
+ }
+ }
+ if (sb.length() > 0) {
+ return sb.toString();
+ }
+ return null;
+ }
+
+ private String extractAllValueFromJson(JSONArray values)
+ throws JSONException {
+ int jsonIndex = 0;
+ StringBuffer sb = new StringBuffer();
+ while (jsonIndex < values.length() - 1) {
+ JSONArray oneArray = (JSONArray) values.get(jsonIndex++);
+ String curVal = (String) oneArray.get(0);
+ if (curVal == null) {
+ break;
+ }
+
+ sb.append(curVal);
+ }
+ if (sb.length() > 0) {
+ return sb.toString();
+ }
+ return null;
+ }
+}
diff --git a/apkEditorTranslate/src/main/java/apkeditor/translate/WebBrowser.java b/apkEditorTranslate/src/main/java/apkeditor/translate/WebBrowser.java
new file mode 100644
index 0000000..a94465b
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/apkeditor/translate/WebBrowser.java
@@ -0,0 +1,190 @@
+package apkeditor.translate;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import android.util.Log;
+
+public class WebBrowser {
+ private MyCookieStore cookieStore = new MyCookieStore();
+ // HttpHost proxy = new HttpHost("proxy-shz.intel.com", 911, "http");
+ HttpHost proxy = new HttpHost("xxx", 911, "http");
+ private boolean useProxy = false;
+ private boolean useSSL = false;
+
+ private String userAgent = null;
+
+ public WebBrowser() {
+ this(false);
+ }
+
+ public WebBrowser(String userAgent) {
+ this.userAgent = userAgent;
+ }
+
+ public WebBrowser(boolean paramBoolean) {
+ this.useSSL = paramBoolean;
+ }
+
+ private DefaultHttpClient getHttpClient() {
+ if (this.useSSL)
+ return SSLSocketFactoryEx.getNewHttpClient();
+ return new DefaultHttpClient();
+ }
+
+ private String getString(InputStream input) {
+ int bufferSize = 256 * 1024;
+ int readSize = 0;
+
+ try {
+ byte[] buffer = new byte[bufferSize];
+ int maxStrSize = bufferSize - 1;
+ while (readSize < maxStrSize) {
+ int ret = input.read(buffer, readSize, maxStrSize - readSize);
+ if (ret <= 0) {
+ break;
+ }
+ readSize += ret;
+ }
+
+ if (readSize > 0) {
+ String str = new String(buffer, 0, readSize, "UTF-8");
+ return str;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return "";
+ }
+
+ public String get(String tag, String strUrl, String referUrl) {
+ String str = null;
+ try {
+ HttpGet httpGet = new HttpGet(strUrl);
+ httpGet.getParams().setParameter("http.protocol.cookie-policy",
+ "compatibility");
+ DefaultHttpClient httpClient = getHttpClient();
+ if (userAgent != null)
+ httpGet.addHeader("User-Agent", userAgent);
+ if (referUrl != null) {
+ httpGet.addHeader("Referer", referUrl);
+ }
+ MyCookieStore cookieStore = this.cookieStore;
+ str = null;
+ if (cookieStore != null) {
+ httpClient.setCookieStore(this.cookieStore);
+ }
+ if (this.useProxy) {
+ httpClient.getParams().setParameter("http.route.default-proxy",
+ this.proxy);
+ }
+ httpClient.getParams().setParameter("http.connection.timeout",
+ Integer.valueOf(15000));
+ httpClient.getParams().setParameter("http.socket.timeout",
+ Integer.valueOf(15000));
+ str = getString(httpClient.execute(httpGet).getEntity()
+ .getContent());
+ // Debug.dump(tag + ".html", str);
+ List newCookies = httpClient.getCookieStore().getCookies();
+ this.cookieStore.addCookies(newCookies);
+ return str;
+ } catch (Exception e) {
+ e.printStackTrace();
+ Debug.dump(tag + ".error", e.getMessage());
+ }
+ return str;
+ }
+
+ public MyCookieStore getCookieStore() {
+ return this.cookieStore;
+ }
+
+ public String post(String tag, String uri, List paramList) {
+ HttpPost httpPost = new HttpPost(uri);
+ // httpPost.addHeader("Origin", "http://home.guahao.cn");
+ httpPost.addHeader("Content-Type",
+ "application/x-www-form-urlencoded; charset=\"UTF-8\"");
+ httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
+ httpPost.addHeader(
+ "User-Agent",
+ "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari/537.36");
+ httpPost.addHeader("Accept",
+ "application/json, text/javascript, */*; q=0.01");
+ // httpPost.addHeader("Referer",
+ // "http://home.guahao.cn/expert/97bf49b0-ca4c-476d-b780-d2f0c2d4b126");
+ httpPost.addHeader("Accept-Encoding", "gzip,deflate,sdch");
+ httpPost.addHeader("Accept-Language", "en-US,en;q=0.8");
+ httpPost.addHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
+ return post(tag, httpPost, paramList);
+ }
+
+ public String post(String tag, HttpPost httpPost,
+ List paramList) {
+ String str = null;
+ try {
+ int k = paramList.size();
+ str = null;
+ if (k > 0)
+ httpPost.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
+ if (!httpPost.containsHeader("Content-Type"))
+ httpPost.addHeader("Content-Type",
+ "application/x-www-form-urlencoded; charset=\"UTF-8\"");
+ if (!httpPost.containsHeader("User-Agent") && userAgent != null) {
+ httpPost.addHeader("User-Agent", userAgent);
+ }
+ httpPost.getParams().setParameter("http.protocol.cookie-policy",
+ "compatibility");
+ DefaultHttpClient localDefaultHttpClient = getHttpClient();
+ MyCookieStore localMyCookieStore = this.cookieStore;
+ str = null;
+ if (localMyCookieStore != null)
+ localDefaultHttpClient.setCookieStore(this.cookieStore);
+ boolean bool = this.useProxy;
+ str = null;
+ if (bool)
+ localDefaultHttpClient.getParams().setParameter(
+ "http.route.default-proxy", this.proxy);
+ localDefaultHttpClient.getParams().setParameter(
+ "http.connection.timeout", Integer.valueOf(15000));
+ localDefaultHttpClient.getParams().setParameter(
+ "http.socket.timeout", Integer.valueOf(15000));
+ //Log.d("DEBUG", "Will call httpClient.execute");
+ HttpResponse localHttpResponse = localDefaultHttpClient
+ .execute(httpPost);
+ //Log.d("DEBUG", "After call httpClient.execute");
+ str = getString(localHttpResponse.getEntity().getContent());
+ //Log.d("DEBUG", "Response got!");
+ Header[] arrayOfHeader = localHttpResponse.getAllHeaders();
+ StringBuffer localStringBuffer = new StringBuffer();
+ int i = arrayOfHeader.length;
+ for (int j = 0;; j++) {
+ if (j >= i) {
+ Debug.dump(tag + ".header", localStringBuffer.toString());
+ Debug.dump(tag + ".html", str);
+ List newCookies = localDefaultHttpClient
+ .getCookieStore().getCookies();
+ this.cookieStore.addCookies(newCookies);
+ return str;
+ }
+ Header localHeader = arrayOfHeader[j];
+ localStringBuffer.append(localHeader.getName() + ": "
+ + localHeader.getValue() + "\n");
+ }
+ } catch (Exception localException) {
+ Debug.dump(tag + ".error", localException.getMessage());
+ }
+ return str;
+ }
+}
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/java/com/gmail/heagoo/apkeditor/translate/TranslateItem.java b/apkEditorTranslate/src/main/java/com/gmail/heagoo/apkeditor/translate/TranslateItem.java
new file mode 100644
index 0000000..612cd10
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/com/gmail/heagoo/apkeditor/translate/TranslateItem.java
@@ -0,0 +1,22 @@
+package com.gmail.heagoo.apkeditor.translate;
+
+import java.io.Serializable;
+
+public class TranslateItem implements Serializable {
+
+ private static final long serialVersionUID = -3101805950698159689L;
+ public String name;
+ public String originValue;
+ public String translatedValue;
+
+ public TranslateItem(String _n, String _o) {
+ this.name = _n;
+ this.originValue = _o;
+ }
+
+ public TranslateItem(String _n, String _o, String _t) {
+ this.name = _n;
+ this.originValue = _o;
+ this.translatedValue = _t;
+ }
+}
diff --git a/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/ActivityUtil.java b/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/ActivityUtil.java
new file mode 100644
index 0000000..03442b9
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/ActivityUtil.java
@@ -0,0 +1,126 @@
+package com.gmail.heagoo.common;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ActivityUtil {
+
+ public static Bundle attachParam(Intent intent, String key, String value) {
+ Bundle bundle = new Bundle();
+ bundle.putString(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static Bundle attachParam(Intent intent, String key, boolean value) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static Bundle attachParam(Intent intent, String key, ArrayList value) {
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static Bundle attachParam2(Intent intent, String key, ArrayList value) {
+ Bundle bundle = new Bundle();
+ bundle.putIntegerArrayList(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static Bundle attachParam(Intent intent, String key, int value) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static Bundle attachBoolParam(Intent intent, String key, boolean value) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(key, value);
+ intent.putExtras(bundle);
+ return bundle;
+ }
+
+ public static String getParam(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ return bundle.getString(key);
+ }
+ return null;
+ }
+
+ public static boolean getBoolParam(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ return bundle.getBoolean(key, false);
+ }
+ return false;
+ }
+
+ public static int getIntParam(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ return bundle.getInt(key, 0);
+ }
+ return 0;
+ }
+
+ public static ArrayList getStringArray(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ return bundle.getStringArrayList(key);
+ }
+ return null;
+ }
+
+ public static ArrayList getIntArray(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ return bundle.getIntegerArrayList(key);
+ }
+ return null;
+ }
+
+ public static void attachParam(Intent intent, String key,
+ Map mapValue) {
+ Bundle bundle = new Bundle();
+ ArrayList keyList = new ArrayList();
+ ArrayList valueList = new ArrayList();
+ for (String strKey : mapValue.keySet()) {
+ String strVal = mapValue.get(strKey);
+ keyList.add(strKey);
+ valueList.add(strVal);
+ }
+ bundle.putStringArrayList(key + "_keys", keyList);
+ bundle.putStringArrayList(key + "_values", valueList);
+ intent.putExtras(bundle);
+ }
+
+ public static Map getMapParam(Intent intent, String key) {
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ Map mapVal = new HashMap();
+ ArrayList strKeys = bundle
+ .getStringArrayList(key + "_keys");
+ ArrayList strValues = bundle.getStringArrayList(key
+ + "_values");
+ if (strKeys != null && strValues != null) {
+ for (int i = 0; i < strKeys.size(); i++) {
+ mapVal.put(strKeys.get(i), strValues.get(i));
+ }
+ return mapVal;
+ }
+ }
+ return null;
+ }
+}
diff --git a/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/IOUtils.java b/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/IOUtils.java
new file mode 100644
index 0000000..8f0993b
--- /dev/null
+++ b/apkEditorTranslate/src/main/java/com/gmail/heagoo/common/IOUtils.java
@@ -0,0 +1,115 @@
+package com.gmail.heagoo.common;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+public class IOUtils {
+
+ public static void copy(InputStream in, OutputStream out)
+ throws IOException {
+ byte[] buffer = new byte[4096];
+ int count = 0;
+ while ((count = in.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+ }
+
+ public static byte[] toByteArray(InputStream in) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy(in, output);
+ return output.toByteArray();
+ }
+
+ public static void writeZero(OutputStream out, int size) throws IOException {
+ int blocks = size / 1024;
+ int remain = size % 1024;
+
+ byte[] buffer = new byte[1024];
+ for (int i = 0; i < 1024; i++) {
+ buffer[i] = 0;
+ }
+
+ for (int i = 0; i < blocks; i++) {
+ out.write(buffer);
+ }
+
+ if (remain > 0) {
+ out.write(buffer, 0, remain);
+ }
+ }
+
+ public static void readFully(InputStream is, byte[] buf) throws IOException {
+ int read = 0;
+ while (read < buf.length) {
+ int ret = is.read(buf, read, buf.length - read);
+ if (ret != -1) {
+ read += ret;
+ } else {
+ break;
+ }
+ }
+ }
+
+ public static void writeObjectToFile(String filePath, Object obj) {
+ File file = new File(filePath);
+ ObjectOutputStream objOut = null;
+ try {
+ objOut = new ObjectOutputStream(new FileOutputStream(file));
+ objOut.writeObject(obj);
+ objOut.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ closeWithoutThrow(objOut);
+ }
+ }
+
+ public static Object readObjectFromFile(String filePath) {
+ Object result = null;
+ File file = new File(filePath);
+ ObjectInputStream objIn = null;
+ try {
+ objIn = new ObjectInputStream(new FileInputStream(file));
+ result = objIn.readObject();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ closeWithoutThrow(objIn);
+ }
+ return result;
+ }
+
+ public static String readString(InputStream input) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader br = new BufferedReader(new InputStreamReader(input));
+ String line = br.readLine();
+ while (line != null) {
+ sb.append(line);
+ sb.append("\n");
+ line = br.readLine();
+ }
+ return sb.toString();
+
+ }
+
+ private static void closeWithoutThrow(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+}
diff --git a/apkEditorTranslate/src/main/res/drawable/applistutil_btn_custom.xml b/apkEditorTranslate/src/main/res/drawable/applistutil_btn_custom.xml
new file mode 100644
index 0000000..5b5dc98
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/applistutil_btn_custom.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/applistutil_button.9.png b/apkEditorTranslate/src/main/res/drawable/applistutil_button.9.png
new file mode 100644
index 0000000..e7da9e4
Binary files /dev/null and b/apkEditorTranslate/src/main/res/drawable/applistutil_button.9.png differ
diff --git a/apkEditorTranslate/src/main/res/drawable/applistutil_button_hilight.9.png b/apkEditorTranslate/src/main/res/drawable/applistutil_button_hilight.9.png
new file mode 100644
index 0000000..0787270
Binary files /dev/null and b/apkEditorTranslate/src/main/res/drawable/applistutil_button_hilight.9.png differ
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_button_style.xml b/apkEditorTranslate/src/main/res/drawable/bg_button_style.xml
new file mode 100644
index 0000000..304a7ce
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_button_style.xml
@@ -0,0 +1,26 @@
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext.xml
new file mode 100644
index 0000000..cd828ad
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext_dark.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext_dark.xml
new file mode 100644
index 0000000..57ea8da
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext_dark.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused.xml
new file mode 100644
index 0000000..043fb2f
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused_dark.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused_dark.xml
new file mode 100644
index 0000000..1d3ce64
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext_focused_dark.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal.xml
new file mode 100644
index 0000000..5b11198
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal_dark.xml b/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal_dark.xml
new file mode 100644
index 0000000..b9b25f1
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/bg_edittext_normal_dark.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/drawable/blue.png b/apkEditorTranslate/src/main/res/drawable/blue.png
new file mode 100644
index 0000000..3abcf46
Binary files /dev/null and b/apkEditorTranslate/src/main/res/drawable/blue.png differ
diff --git a/apkEditorTranslate/src/main/res/drawable/commonutil_progressbar.xml b/apkEditorTranslate/src/main/res/drawable/commonutil_progressbar.xml
new file mode 100644
index 0000000..aed3665
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/drawable/commonutil_progressbar.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/layout/activity_autotranslate.xml b/apkEditorTranslate/src/main/res/layout/activity_autotranslate.xml
new file mode 100644
index 0000000..a4635e9
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/layout/activity_autotranslate.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/layout/activity_autotranslate_dark.xml b/apkEditorTranslate/src/main/res/layout/activity_autotranslate_dark.xml
new file mode 100644
index 0000000..0514392
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/layout/activity_autotranslate_dark.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/layout/activity_main.xml b/apkEditorTranslate/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..49aee5e
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/layout/activity_main.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate.xml b/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate.xml
new file mode 100644
index 0000000..d1c91b3
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate_dark.xml b/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate_dark.xml
new file mode 100644
index 0000000..af578d6
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/layout/item_stringvalue_translate_dark.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apkEditorTranslate/src/main/res/values/strings.xml b/apkEditorTranslate/src/main/res/values/strings.xml
new file mode 100644
index 0000000..1d13f28
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/values/strings.xml
@@ -0,0 +1,33 @@
+
+
+
+ #ff5997c4
+ #ff303030
+ #ff000000
+ #ff1122cc
+ #ffe9f2fc
+
+ Translate Plugin
+
+ Auto Translation
+ "Select a language to translate to"
+ Stop
+ Save
+ Close
+ Save & Close
+ Translating …
+ Translated
+ %d strings translated
+ %d strings failed
+ %d strings untranslated
+ No string needs to be translated.
+ No translated string to save.
+ Error: %s
+ %d strings saved.
+ Save the translation?
+ The translation is not saved yet. Save it before further operation?
+ %d strings are discarded.
+
+ APK Editor
+ APK Editor Pro
+
diff --git a/apkEditorTranslate/src/main/res/values/styles.xml b/apkEditorTranslate/src/main/res/values/styles.xml
new file mode 100644
index 0000000..7dc3a6e
--- /dev/null
+++ b/apkEditorTranslate/src/main/res/values/styles.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1758e50
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,15 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.0'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..475c4ed
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':apkEditorTranslate'