summaryrefslogtreecommitdiff
path: root/platform/android/java/src/com
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-04-05 12:39:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-04-05 12:39:30 -0300
commit9f33134c93ecbadda70e8eefc50563e29b2eb7f2 (patch)
tree299ded94fe74a61bf8094935d0f3283f6f30e435 /platform/android/java/src/com
parent35b84d2c85fd152bee05d7d5a05e20a5f602a285 (diff)
-Support for changing fonts
-Detect when free() might crash the project and throw error -fixed 2D Bounce in physics (3d still broken) -renamed “on_top” property to “behind_parent”, which makes more sense, old on_top remains there for compatibility but is invisible. -large amount of fixes
Diffstat (limited to 'platform/android/java/src/com')
-rw-r--r--platform/android/java/src/com/android/godot/Godot.java3
-rw-r--r--platform/android/java/src/com/android/godot/GodotPaymentV3.java83
-rw-r--r--platform/android/java/src/com/android/godot/payments/ConsumeTask.java71
-rw-r--r--platform/android/java/src/com/android/godot/payments/HandlePurchaseTask.java79
-rw-r--r--platform/android/java/src/com/android/godot/payments/PaymentsCache.java42
-rw-r--r--platform/android/java/src/com/android/godot/payments/PaymentsManager.java151
-rw-r--r--platform/android/java/src/com/android/godot/payments/PurchaseTask.java121
-rw-r--r--platform/android/java/src/com/android/godot/payments/ValidateTask.java97
-rw-r--r--platform/android/java/src/com/android/godot/utils/Crypt.java39
-rw-r--r--platform/android/java/src/com/android/godot/utils/CustomSSLSocketFactory.java54
-rw-r--r--platform/android/java/src/com/android/godot/utils/HttpRequester.java206
-rw-r--r--platform/android/java/src/com/android/godot/utils/RequestParams.java58
-rw-r--r--platform/android/java/src/com/android/vending/billing/IInAppBillingService.aidl144
13 files changed, 1147 insertions, 1 deletions
diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java
index 276ba63b29..ddb2dcfa75 100644
--- a/platform/android/java/src/com/android/godot/Godot.java
+++ b/platform/android/java/src/com/android/godot/Godot.java
@@ -135,7 +135,7 @@ public class Godot extends Activity implements SensorEventListener
};
public ResultCallback result_callback;
- private PaymentsManager mPaymentsManager;
+ private PaymentsManager mPaymentsManager = null;
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if(requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE){
@@ -168,6 +168,7 @@ public class Godot extends Activity implements SensorEventListener
@Override protected void onCreate(Bundle icicle) {
+ System.out.printf("** GODOT ACTIVITY CREATED HERE ***\n");
super.onCreate(icicle);
_self = this;
diff --git a/platform/android/java/src/com/android/godot/GodotPaymentV3.java b/platform/android/java/src/com/android/godot/GodotPaymentV3.java
new file mode 100644
index 0000000000..9d2893cde6
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/GodotPaymentV3.java
@@ -0,0 +1,83 @@
+package com.android.godot;
+
+
+import android.app.Activity;
+
+
+public class GodotPaymentV3 extends Godot.SingletonBase {
+
+ private Godot activity;
+
+ private Integer purchaseCallbackId = 0;
+
+ private String accessToken;
+
+ private String purchaseValidationUrlPrefix;
+
+ public void purchase( String _sku) {
+ final String sku = _sku;
+ activity.getPaymentsManager().setBaseSingleton(this);
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ activity.getPaymentsManager().requestPurchase(sku);
+ }
+ });
+ };
+
+
+ static public Godot.SingletonBase initialize(Activity p_activity) {
+
+ return new GodotPaymentV3(p_activity);
+ }
+
+
+ public GodotPaymentV3(Activity p_activity) {
+
+ registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix"});
+ activity=(Godot) p_activity;
+ }
+
+
+
+ public void callbackSuccess(){
+ GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{});
+ }
+
+ public void callbackFail(){
+ GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{});
+ }
+
+ public void callbackCancel(){
+ GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{});
+ }
+
+ public int getPurchaseCallbackId() {
+ return purchaseCallbackId;
+ }
+
+ public void setPurchaseCallbackId(int purchaseCallbackId) {
+ this.purchaseCallbackId = purchaseCallbackId;
+ }
+
+
+
+ public String getPurchaseValidationUrlPrefix(){
+ return this.purchaseValidationUrlPrefix ;
+ }
+
+ public void setPurchaseValidationUrlPrefix(String url){
+ this.purchaseValidationUrlPrefix = url;
+ }
+
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+}
diff --git a/platform/android/java/src/com/android/godot/payments/ConsumeTask.java b/platform/android/java/src/com/android/godot/payments/ConsumeTask.java
new file mode 100644
index 0000000000..62b33c002a
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/ConsumeTask.java
@@ -0,0 +1,71 @@
+package com.android.godot.payments;
+
+import com.android.vending.billing.IInAppBillingService;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.Log;
+
+abstract public class ConsumeTask {
+
+ private Context context;
+
+ private IInAppBillingService mService;
+ public ConsumeTask(IInAppBillingService mService, Context context ){
+ this.context = context;
+ this.mService = mService;
+ }
+
+
+ public void consume(final String sku){
+// Log.d("XXX", "Consuming product " + sku);
+ PaymentsCache pc = new PaymentsCache(context);
+ Boolean isBlocked = pc.getConsumableFlag("block", sku);
+ String _token = pc.getConsumableValue("token", sku);
+// Log.d("XXX", "token " + _token);
+ if(!isBlocked && _token == null){
+// _token = "inapp:"+context.getPackageName()+":android.test.purchased";
+// Log.d("XXX", "Consuming product " + sku + " with token " + _token);
+ }else if(!isBlocked){
+// Log.d("XXX", "It is not blocked ¿?");
+ return;
+ }else if(_token == null){
+// Log.d("XXX", "No token available");
+ this.error("No token for sku:" + sku);
+ return;
+ }
+ final String token = _token;
+ new AsyncTask<String, String, String>(){
+
+ @Override
+ protected String doInBackground(String... params) {
+ try {
+// Log.d("XXX", "Requesting to release item.");
+ int response = mService.consumePurchase(3, context.getPackageName(), token);
+// Log.d("XXX", "release response code: " + response);
+ if(response == 0 || response == 8){
+ return null;
+ }
+ } catch (RemoteException e) {
+ return e.getMessage();
+
+ }
+ return "Some error";
+ }
+
+ protected void onPostExecute(String param){
+ if(param == null){
+ success();
+ }else{
+ error(param);
+ }
+ }
+
+ }.execute();
+ }
+
+ abstract protected void success();
+ abstract protected void error(String message);
+
+}
diff --git a/platform/android/java/src/com/android/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/com/android/godot/payments/HandlePurchaseTask.java
new file mode 100644
index 0000000000..08fc405183
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/HandlePurchaseTask.java
@@ -0,0 +1,79 @@
+package com.android.godot.payments;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.android.godot.GodotLib;
+import com.android.godot.utils.Crypt;
+import com.android.vending.billing.IInAppBillingService;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+abstract public class HandlePurchaseTask {
+
+ private Activity context;
+
+ public HandlePurchaseTask(Activity context ){
+ this.context = context;
+ }
+
+
+ public void handlePurchaseRequest(int resultCode, Intent data){
+// Log.d("XXX", "Handling purchase response");
+// int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
+ PaymentsCache pc = new PaymentsCache(context);
+
+ String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
+// String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
+
+ if (resultCode == Activity.RESULT_OK) {
+
+ try {
+ Log.d("SARLANGA", purchaseData);
+
+
+ JSONObject jo = new JSONObject(purchaseData);
+// String sku = jo.getString("productId");
+// alert("You have bought the " + sku + ". Excellent choice, aventurer!");
+// String orderId = jo.getString("orderId");
+// String packageName = jo.getString("packageName");
+ String productId = jo.getString("productId");
+// Long purchaseTime = jo.getLong("purchaseTime");
+// Integer state = jo.getInt("purchaseState");
+ String developerPayload = jo.getString("developerPayload");
+ String purchaseToken = jo.getString("purchaseToken");
+
+ if(! pc.getConsumableValue("validation_hash", productId).equals(developerPayload) ) {
+ error("Untrusted callback");
+ return;
+ }
+
+ pc.setConsumableValue("ticket", productId, purchaseData);
+ pc.setConsumableFlag("block", productId, true);
+ pc.setConsumableValue("token", productId, purchaseToken);
+
+ success(purchaseToken, productId);
+ return;
+ } catch (JSONException e) {
+ error(e.getMessage());
+ }
+ }else if( resultCode == Activity.RESULT_CANCELED){
+ canceled();
+ }
+ }
+
+ abstract protected void success(String purchaseToken, String sku);
+ abstract protected void error(String message);
+ abstract protected void canceled();
+
+
+}
diff --git a/platform/android/java/src/com/android/godot/payments/PaymentsCache.java b/platform/android/java/src/com/android/godot/payments/PaymentsCache.java
new file mode 100644
index 0000000000..ba84097732
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/PaymentsCache.java
@@ -0,0 +1,42 @@
+package com.android.godot.payments;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class PaymentsCache {
+
+ public Context context;
+
+ public PaymentsCache(Context context){
+ this.context = context;
+ }
+
+
+ public void setConsumableFlag(String set, String sku, Boolean flag){
+ SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putBoolean(sku, flag);
+ editor.commit();
+}
+
+ public boolean getConsumableFlag(String set, String sku){
+ SharedPreferences sharedPref = context.getSharedPreferences(
+ "consumables_" + set, Context.MODE_PRIVATE);
+ return sharedPref.getBoolean(sku, false);
+ }
+
+
+ public void setConsumableValue(String set, String sku, String value){
+ SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putString(sku, value);
+ editor.commit();
+ }
+
+ public String getConsumableValue(String set, String sku){
+ SharedPreferences sharedPref = context.getSharedPreferences(
+ "consumables_" + set, Context.MODE_PRIVATE);
+ return sharedPref.getString(sku, null);
+ }
+
+}
diff --git a/platform/android/java/src/com/android/godot/payments/PaymentsManager.java b/platform/android/java/src/com/android/godot/payments/PaymentsManager.java
new file mode 100644
index 0000000000..325e3a0751
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/PaymentsManager.java
@@ -0,0 +1,151 @@
+package com.android.godot.payments;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import com.android.godot.Godot;
+import com.android.godot.GodotPaymentV3;
+import com.android.vending.billing.IInAppBillingService;
+
+public class PaymentsManager {
+
+ public static final int BILLING_RESPONSE_RESULT_OK = 0;
+
+
+ public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
+
+
+ private Activity activity;
+ IInAppBillingService mService;
+
+
+ public void setActivity(Activity activity){
+ this.activity = activity;
+ }
+
+ public static PaymentsManager createManager(Activity activity){
+ PaymentsManager manager = new PaymentsManager(activity);
+ return manager;
+ }
+
+ private PaymentsManager(Activity activity){
+ this.activity = activity;
+ }
+
+ public PaymentsManager initService(){
+ activity.bindService(
+ new Intent("com.android.vending.billing.InAppBillingService.BIND"),
+ mServiceConn,
+ Context.BIND_AUTO_CREATE);
+ return this;
+ }
+
+ public void destroy(){
+ if (mService != null) {
+ activity.unbindService(mServiceConn);
+ }
+ }
+
+ ServiceConnection mServiceConn = new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name,
+ IBinder service) {
+ mService = IInAppBillingService.Stub.asInterface(service);
+ }
+ };
+
+ public void requestPurchase(String sku){
+ new PurchaseTask(mService, Godot.getInstance()) {
+
+ @Override
+ protected void error(String message) {
+ godotPaymentV3.callbackFail();
+
+ }
+
+ @Override
+ protected void canceled() {
+ godotPaymentV3.callbackCancel();
+ }
+ }.purchase(sku);
+
+ }
+
+ public void processPurchaseResponse(int resultCode, Intent data) {
+ new HandlePurchaseTask(activity){
+
+ @Override
+ protected void success(String purchaseToken, String sku) {
+ validatePurchase(purchaseToken, sku);
+ }
+
+ @Override
+ protected void error(String message) {
+ godotPaymentV3.callbackFail();
+
+ }
+
+ @Override
+ protected void canceled() {
+ godotPaymentV3.callbackCancel();
+
+ }}.handlePurchaseRequest(resultCode, data);
+ }
+
+ public void validatePurchase(String purchaseToken, final String sku){
+
+ new ValidateTask(activity, godotPaymentV3){
+
+ @Override
+ protected void success() {
+
+ new ConsumeTask(mService, activity) {
+
+ @Override
+ protected void success() {
+ godotPaymentV3.callbackSuccess();
+
+ }
+
+ @Override
+ protected void error(String message) {
+ godotPaymentV3.callbackFail();
+
+ }
+ }.consume(sku);
+
+ }
+
+ @Override
+ protected void error(String message) {
+ godotPaymentV3.callbackFail();
+
+ }
+
+ @Override
+ protected void canceled() {
+ godotPaymentV3.callbackCancel();
+
+ }
+ }.validatePurchase(sku);
+ }
+
+ private GodotPaymentV3 godotPaymentV3;
+
+ public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
+ this.godotPaymentV3 = godotPaymentV3;
+
+ }
+
+
+}
+
diff --git a/platform/android/java/src/com/android/godot/payments/PurchaseTask.java b/platform/android/java/src/com/android/godot/payments/PurchaseTask.java
new file mode 100644
index 0000000000..3b2cb78d27
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/PurchaseTask.java
@@ -0,0 +1,121 @@
+package com.android.godot.payments;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.android.godot.GodotLib;
+import com.android.godot.utils.Crypt;
+import com.android.vending.billing.IInAppBillingService;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+abstract public class PurchaseTask {
+
+ private Activity context;
+
+ private IInAppBillingService mService;
+ public PurchaseTask(IInAppBillingService mService, Activity context ){
+ this.context = context;
+ this.mService = mService;
+ }
+
+
+ private boolean isLooping = false;
+
+ public void purchase(final String sku){
+// Log.d("XXX", "Starting purchase");
+ PaymentsCache pc = new PaymentsCache(context);
+ Boolean isBlocked = pc.getConsumableFlag("block", sku);
+// if(isBlocked){
+// Log.d("XXX", "Is awaiting payment confirmation");
+// error("Awaiting payment confirmation");
+// return;
+// }
+ final String hash = Crypt.createRandomHash() + Crypt.createRandomHash();
+
+ Bundle buyIntentBundle;
+ try {
+ buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash );
+ } catch (RemoteException e) {
+// Log.d("XXX", "Error: " + e.getMessage());
+ error(e.getMessage());
+ return;
+ }
+ Object rc = buyIntentBundle.get("RESPONSE_CODE");
+ int responseCode = 0;
+ if(rc == null){
+ responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK;
+ }else if( rc instanceof Integer){
+ responseCode = ((Integer)rc).intValue();
+ }else if( rc instanceof Long){
+ responseCode = (int)((Long)rc).longValue();
+ }
+// Log.d("XXX", "Buy intent response code: " + responseCode);
+ if(responseCode == 1 || responseCode == 3 || responseCode == 4){
+ canceled();
+ return ;
+ }
+ if(responseCode == 7){
+ new ConsumeTask(mService, context) {
+
+ @Override
+ protected void success() {
+// Log.d("XXX", "Product was erroniously purchased!");
+ if(isLooping){
+// Log.d("XXX", "It is looping");
+ error("Error while purchasing product");
+ return;
+ }
+ isLooping=true;
+ PurchaseTask.this.purchase(sku);
+
+ }
+
+ @Override
+ protected void error(String message) {
+ PurchaseTask.this.error(message);
+
+ }
+ }.consume(sku);
+ return;
+ }
+
+
+ PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
+ pc.setConsumableValue("validation_hash", sku, hash);
+ try {
+ if(context == null){
+// Log.d("XXX", "No context!");
+ }
+ if(pendingIntent == null){
+// Log.d("XXX", "No pending intent");
+ }
+// Log.d("XXX", "Starting activity for purchase!");
+ context.startIntentSenderForResult(
+ pendingIntent.getIntentSender(),
+ PaymentsManager.REQUEST_CODE_FOR_PURCHASE,
+ new Intent(),
+ Integer.valueOf(0), Integer.valueOf(0),
+ Integer.valueOf(0));
+ } catch (SendIntentException e) {
+ error(e.getMessage());
+ }
+
+
+
+ }
+
+ abstract protected void error(String message);
+ abstract protected void canceled();
+
+
+}
diff --git a/platform/android/java/src/com/android/godot/payments/ValidateTask.java b/platform/android/java/src/com/android/godot/payments/ValidateTask.java
new file mode 100644
index 0000000000..6ea415e8a9
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/payments/ValidateTask.java
@@ -0,0 +1,97 @@
+package com.android.godot.payments;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.android.godot.Godot;
+import com.android.godot.GodotLib;
+import com.android.godot.GodotPaymentV3;
+import com.android.godot.utils.Crypt;
+import com.android.godot.utils.HttpRequester;
+import com.android.godot.utils.RequestParams;
+import com.android.vending.billing.IInAppBillingService;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+abstract public class ValidateTask {
+
+ private Activity context;
+ private GodotPaymentV3 godotPaymentsV3;
+ public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3){
+ this.context = context;
+ this.godotPaymentsV3 = godotPaymentsV3;
+ }
+
+ public void validatePurchase(final String sku){
+ new AsyncTask<String, String, String>(){
+
+
+ private ProgressDialog dialog;
+
+ @Override
+ protected void onPreExecute(){
+ dialog = ProgressDialog.show(context, null, "Please wait...");
+ }
+
+ @Override
+ protected String doInBackground(String... params) {
+ PaymentsCache pc = new PaymentsCache(context);
+ String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
+ RequestParams param = new RequestParams();
+ param.setUrl(url);
+ param.put("ticket", pc.getConsumableValue("ticket", sku));
+ param.put("purchaseToken", pc.getConsumableValue("token", sku));
+ param.put("sku", sku);
+// Log.d("XXX", "Haciendo request a " + url);
+// Log.d("XXX", "ticket: " + pc.getConsumableValue("ticket", sku));
+// Log.d("XXX", "purchaseToken: " + pc.getConsumableValue("token", sku));
+// Log.d("XXX", "sku: " + sku);
+ param.put("package", context.getApplicationContext().getPackageName());
+ HttpRequester requester = new HttpRequester();
+ String jsonResponse = requester.post(param);
+// Log.d("XXX", "Validation response:\n"+jsonResponse);
+ return jsonResponse;
+ }
+
+ @Override
+ protected void onPostExecute(String response){
+ if(dialog != null){
+ dialog.dismiss();
+ }
+ JSONObject j;
+ try {
+ j = new JSONObject(response);
+ if(j.getString("status").equals("OK")){
+ success();
+ return;
+ }else if(j.getString("status") != null){
+ error(j.getString("message"));
+ }else{
+ error("Connection error");
+ }
+ } catch (JSONException e) {
+ error(e.getMessage());
+ }catch (Exception e){
+ error(e.getMessage());
+ }
+
+
+ }
+
+ }.execute();
+ }
+ abstract protected void success();
+ abstract protected void error(String message);
+ abstract protected void canceled();
+
+
+}
diff --git a/platform/android/java/src/com/android/godot/utils/Crypt.java b/platform/android/java/src/com/android/godot/utils/Crypt.java
new file mode 100644
index 0000000000..7801f474b9
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/utils/Crypt.java
@@ -0,0 +1,39 @@
+package com.android.godot.utils;
+
+import java.security.MessageDigest;
+import java.util.Random;
+
+public class Crypt {
+
+ public static String md5(String input){
+ try {
+ // Create MD5 Hash
+ MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+ digest.update(input.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ // Create Hex String
+ StringBuffer hexString = new StringBuffer();
+ for (int i=0; i<messageDigest.length; i++)
+ hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+ return hexString.toString();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static String createRandomHash(){
+ return md5(Long.toString(createRandomLong()));
+ }
+
+ public static long createAbsRandomLong(){
+ return Math.abs(createRandomLong());
+ }
+
+ public static long createRandomLong(){
+ Random r = new Random();
+ return r.nextLong();
+ }
+}
diff --git a/platform/android/java/src/com/android/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/com/android/godot/utils/CustomSSLSocketFactory.java
new file mode 100644
index 0000000000..5f2b44fc8c
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/utils/CustomSSLSocketFactory.java
@@ -0,0 +1,54 @@
+package com.android.godot.utils;
+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.conn.ssl.SSLSocketFactory;
+
+
+/**
+ *
+ * @author Luis Linietsky <luis.linietsky@gmail.com>
+ */
+public class CustomSSLSocketFactory extends SSLSocketFactory {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+
+ public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+ super(truststore);
+
+ TrustManager tm = new X509TrustManager() {
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+
+ sslContext.init(null, new TrustManager[] { tm }, null);
+ }
+
+ @Override
+ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
+ return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return sslContext.getSocketFactory().createSocket();
+ }
+} \ No newline at end of file
diff --git a/platform/android/java/src/com/android/godot/utils/HttpRequester.java b/platform/android/java/src/com/android/godot/utils/HttpRequester.java
new file mode 100644
index 0000000000..7de77881d0
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/utils/HttpRequester.java
@@ -0,0 +1,206 @@
+package com.android.godot.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+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.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+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.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+/**
+ *
+ * @author Luis Linietsky <luis.linietsky@gmail.com>
+ */
+public class HttpRequester {
+
+ private Context context;
+ private static final int TTL = 600000; // 10 minutos
+ private long cttl=0;
+
+ public HttpRequester(){
+// Log.d("XXX", "Creando http request sin contexto");
+ }
+
+ public HttpRequester(Context context){
+ this.context=context;
+// Log.d("XXX", "Creando http request con contexto");
+ }
+
+ public String post(RequestParams params){
+ HttpPost httppost = new HttpPost(params.getUrl());
+ try {
+ httppost.setEntity(new UrlEncodedFormEntity(params.toPairsList()));
+ return request(httppost);
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ public String get(RequestParams params){
+ String response = getResponseFromCache(params.getUrl());
+ if(response == null){
+// Log.d("XXX", "Cache miss!");
+ HttpGet httpget = new HttpGet(params.getUrl());
+ long timeInit = new Date().getTime();
+ response = request(httpget);
+ long delay = new Date().getTime() - timeInit;
+ Log.d("com.app11tt.android.utils.HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay/1000.0f) + " seconds");
+ if(response == null || response.length() == 0){
+ response = "";
+ }else{
+ saveResponseIntoCache(params.getUrl(), response);
+ }
+ }
+ Log.d("XXX", "Req: " + params.getUrl());
+ Log.d("XXX", "Resp: " + response);
+ return response;
+ }
+
+ private String request(HttpUriRequest request){
+// Log.d("XXX", "Haciendo request a: " + request.getURI() );
+ Log.d("PPP", "Haciendo request a: " + request.getURI() );
+ long init = new Date().getTime();
+ HttpClient httpclient = getNewHttpClient();
+ HttpParams httpParameters = httpclient.getParams();
+ HttpConnectionParams.setConnectionTimeout(httpParameters, 0);
+ HttpConnectionParams.setSoTimeout(httpParameters, 0);
+ HttpConnectionParams.setTcpNoDelay(httpParameters, true);
+ try {
+ HttpResponse response = httpclient.execute(request);
+ Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI() );
+// Log.d("XXX1", "Status:" + response.getStatusLine().toString());
+ if(response.getStatusLine().getStatusCode() == 200){
+ String strResponse = EntityUtils.toString(response.getEntity());
+// Log.d("XXX2", strResponse);
+ return strResponse;
+ }else{
+ Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity()));
+ return null;
+ }
+
+ } catch (ClientProtocolException e) {
+ Log.d("XXX3", e.getMessage());
+ } catch (IOException e) {
+ Log.d("XXX4", e.getMessage());
+ }
+ return null;
+ }
+
+ private HttpClient getNewHttpClient() {
+ try {
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, null);
+
+ SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
+ sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ HttpParams params = new BasicHttpParams();
+ HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+ HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
+
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ registry.register(new Scheme("https", sf, 443));
+
+ ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
+
+ return new DefaultHttpClient(ccm, params);
+ } catch (Exception e) {
+ return new DefaultHttpClient();
+ }
+ }
+
+ private static String convertStreamToString(InputStream is) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ try {
+ while ((line = reader.readLine()) != null) {
+ sb.append((line + "\n"));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return sb.toString();
+ }
+
+ public void saveResponseIntoCache(String request, String response){
+ if(context == null){
+// Log.d("XXX", "No context, cache failed!");
+ return;
+ }
+ SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putString("request_" + Crypt.md5(request), response);
+ editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl());
+ editor.commit();
+ }
+
+
+ public String getResponseFromCache(String request){
+ if(context == null){
+ Log.d("XXX", "No context, cache miss");
+ return null;
+ }
+ SharedPreferences sharedPref = context.getSharedPreferences( "http_get_cache", Context.MODE_PRIVATE);
+ long ttl = getResponseTtl(request);
+ if(ttl == 0l || (new Date().getTime() - ttl) > 0l){
+ Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime());
+ return null;
+ }
+ return sharedPref.getString("request_" + Crypt.md5(request), null);
+ }
+
+ public long getResponseTtl(String request){
+ SharedPreferences sharedPref = context.getSharedPreferences(
+ "http_get_cache", Context.MODE_PRIVATE);
+ return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l);
+ }
+
+ public long getTtl() {
+ return cttl > 0 ? cttl : TTL;
+ }
+
+ public void setTtl(long ttl) {
+ this.cttl = (ttl*1000) + new Date().getTime();
+ }
+
+}
diff --git a/platform/android/java/src/com/android/godot/utils/RequestParams.java b/platform/android/java/src/com/android/godot/utils/RequestParams.java
new file mode 100644
index 0000000000..31bf1940ad
--- /dev/null
+++ b/platform/android/java/src/com/android/godot/utils/RequestParams.java
@@ -0,0 +1,58 @@
+package com.android.godot.utils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+
+/**
+ *
+ * @author Luis Linietsky <luis.linietsky@gmail.com>
+ */
+public class RequestParams {
+
+ private HashMap<String,String> params;
+ private String url;
+
+ public RequestParams(){
+ params = new HashMap<String,String>();
+ }
+
+ public void put(String key, String value){
+ params.put(key, value);
+ }
+
+ public String get(String key){
+ return params.get(key);
+ }
+
+ public void remove(Object key){
+ params.remove(key);
+ }
+
+ public boolean has(String key){
+ return params.containsKey(key);
+ }
+
+ public List<NameValuePair> toPairsList(){
+ List<NameValuePair> fields = new ArrayList<NameValuePair>();
+
+ for(String key : params.keySet()){
+ fields.add(new BasicNameValuePair(key, this.get(key)));
+ }
+ return fields;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+
+}
diff --git a/platform/android/java/src/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/src/com/android/vending/billing/IInAppBillingService.aidl
new file mode 100644
index 0000000000..2a492f7845
--- /dev/null
+++ b/platform/android/java/src/com/android/vending/billing/IInAppBillingService.aidl
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.vending.billing;
+
+import android.os.Bundle;
+
+/**
+ * InAppBillingService is the service that provides in-app billing version 3 and beyond.
+ * This service provides the following features:
+ * 1. Provides a new API to get details of in-app items published for the app including
+ * price, type, title and description.
+ * 2. The purchase flow is synchronous and purchase information is available immediately
+ * after it completes.
+ * 3. Purchase information of in-app purchases is maintained within the Google Play system
+ * till the purchase is consumed.
+ * 4. An API to consume a purchase of an inapp item. All purchases of one-time
+ * in-app items are consumable and thereafter can be purchased again.
+ * 5. An API to get current purchases of the user immediately. This will not contain any
+ * consumed purchases.
+ *
+ * All calls will give a response code with the following possible values
+ * RESULT_OK = 0 - success
+ * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
+ * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
+ * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
+ * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
+ * RESULT_ERROR = 6 - Fatal error during the API action
+ * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
+ * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
+ */
+interface IInAppBillingService {
+ /**
+ * Checks support for the requested billing API version, package and in-app type.
+ * Minimum API version supported by this interface is 3.
+ * @param apiVersion the billing version which the app is using
+ * @param packageName the package name of the calling app
+ * @param type type of the in-app item being purchased "inapp" for one-time purchases
+ * and "subs" for subscription.
+ * @return RESULT_OK(0) on success, corresponding result code on failures
+ */
+ int isBillingSupported(int apiVersion, String packageName, String type);
+
+ /**
+ * Provides details of a list of SKUs
+ * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
+ * with a list JSON strings containing the productId, price, title and description.
+ * This API can be called with a maximum of 20 SKUs.
+ * @param apiVersion billing API version that the Third-party is using
+ * @param packageName the package name of the calling app
+ * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
+ * @return Bundle containing the following key-value pairs
+ * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
+ * failure as listed above.
+ * "DETAILS_LIST" with a StringArrayList containing purchase information
+ * in JSON format similar to:
+ * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
+ * "title : "Example Title", "description" : "This is an example description" }'
+ */
+ Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
+
+ /**
+ * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
+ * the type, a unique purchase token and an optional developer payload.
+ * @param apiVersion billing API version that the app is using
+ * @param packageName package name of the calling app
+ * @param sku the SKU of the in-app item as published in the developer console
+ * @param type the type of the in-app item ("inapp" for one-time purchases
+ * and "subs" for subscription).
+ * @param developerPayload optional argument to be sent back with the purchase information
+ * @return Bundle containing the following key-value pairs
+ * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
+ * failure as listed above.
+ * "BUY_INTENT" - PendingIntent to start the purchase flow
+ *
+ * The Pending intent should be launched with startIntentSenderForResult. When purchase flow
+ * has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
+ * If the purchase is successful, the result data will contain the following key-value pairs
+ * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
+ * failure as listed above.
+ * "INAPP_PURCHASE_DATA" - String in JSON format similar to
+ * '{"orderId":"12999763169054705758.1371079406387615",
+ * "packageName":"com.example.app",
+ * "productId":"exampleSku",
+ * "purchaseTime":1345678900000,
+ * "purchaseToken" : "122333444455555",
+ * "developerPayload":"example developer payload" }'
+ * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
+ * was signed with the private key of the developer
+ * TODO: change this to app-specific keys.
+ */
+ Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
+ String developerPayload);
+
+ /**
+ * Returns the current SKUs owned by the user of the type and package name specified along with
+ * purchase information and a signature of the data to be validated.
+ * This will return all SKUs that have been purchased in V3 and managed items purchased using
+ * V1 and V2 that have not been consumed.
+ * @param apiVersion billing API version that the app is using
+ * @param packageName package name of the calling app
+ * @param type the type of the in-app items being requested
+ * ("inapp" for one-time purchases and "subs" for subscription).
+ * @param continuationToken to be set as null for the first call, if the number of owned
+ * skus are too many, a continuationToken is returned in the response bundle.
+ * This method can be called again with the continuation token to get the next set of
+ * owned skus.
+ * @return Bundle containing the following key-value pairs
+ * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
+ * failure as listed above.
+ * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
+ * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
+ * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
+ * of the purchase information
+ * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
+ * next set of in-app purchases. Only set if the
+ * user has more owned skus than the current list.
+ */
+ Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
+
+ /**
+ * Consume the last purchase of the given SKU. This will result in this item being removed
+ * from all subsequent responses to getPurchases() and allow re-purchase of this item.
+ * @param apiVersion billing API version that the app is using
+ * @param packageName package name of the calling app
+ * @param purchaseToken token in the purchase information JSON that identifies the purchase
+ * to be consumed
+ * @return 0 if consumption succeeded. Appropriate error values for failures.
+ */
+ int consumePurchase(int apiVersion, String packageName, String purchaseToken);
+}