You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
-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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
}
|
||||
39
platform/android/java/src/com/android/godot/utils/Crypt.java
Normal file
39
platform/android/java/src/com/android/godot/utils/Crypt.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user