From 31ce3c5fd0300aac1e86bced1efc5f9ec94bdb6b Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Thu, 13 Mar 2014 22:57:24 -0300 Subject: -fix bug in cache for atlas import/export -fix some menus -fixed bug in out transition curves -detect and remove file:/// in collada -remove multiscript for now -remove dependencies on mouse in OS, moved to Input -avoid fscache from screwing up (fix might make it slower, but it works) -funcref was missing, it's there now --- .../android/java/src/com/android/godot/Godot.java | 44 ++++++++++++++++++---- .../java/src/com/android/godot/GodotIO.java | 42 ++++++++++++++++++++- .../java/src/com/android/godot/GodotLib.java | 6 +-- 3 files changed, 80 insertions(+), 12 deletions(-) (limited to 'platform/android/java/src') diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java index cf1545df82..9868e62957 100644 --- a/platform/android/java/src/com/android/godot/Godot.java +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -49,15 +49,19 @@ import android.media.*; import android.hardware.*; import android.content.*; +import android.net.Uri; +import android.media.MediaPlayer; + import java.lang.reflect.Method; import java.util.List; import java.util.ArrayList; +import com.android.godot.payments.PaymentsManager; +import java.io.IOException; import android.provider.Settings.Secure; public class Godot extends Activity implements SensorEventListener -{ - +{ static public class SingletonBase { protected void registerClass(String p_name, String[] p_methods) { @@ -131,8 +135,12 @@ public class Godot extends Activity implements SensorEventListener }; public ResultCallback result_callback; + private PaymentsManager mPaymentsManager; + @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { - if (result_callback != null) { + if(requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE){ + mPaymentsManager.processPurchaseResponse(resultCode, data); + }else if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); result_callback = null; }; @@ -152,13 +160,17 @@ public class Godot extends Activity implements SensorEventListener } + private static Godot _self; + + public static Godot getInstance(){ + return Godot._self; + } + @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - - - + _self = this; Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -172,12 +184,20 @@ public class Godot extends Activity implements SensorEventListener mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); result_callback = null; - + + mPaymentsManager = PaymentsManager.createManager(this).initService(); + // instanceSingleton( new GodotFacebook(this) ); } + @Override protected void onDestroy(){ + + if(mPaymentsManager != null ) mPaymentsManager.destroy(); + super.onDestroy(); + } + @Override protected void onPause() { super.onPause(); mView.onPause(); @@ -291,7 +311,15 @@ public class Godot extends Activity implements SensorEventListener @Override public boolean onKeyDown(int keyCode, KeyEvent event) { GodotLib.key(event.getUnicodeChar(0), true); return super.onKeyDown(keyCode, event); - }; + } + + public PaymentsManager getPaymentsManager() { + return mPaymentsManager; + } + +// public void setPaymentsManager(PaymentsManager mPaymentsManager) { +// this.mPaymentsManager = mPaymentsManager; +// }; // Audio diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java index 1f3967cb8f..4b6a44335c 100644 --- a/platform/android/java/src/com/android/godot/GodotIO.java +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -57,6 +57,9 @@ public class GodotIO { AssetManager am; Activity activity; + Context applicationContext; + MediaPlayer mediaPlayer; + final int SCREEN_LANDSCAPE=0; final int SCREEN_PORTRAIT=1; final int SCREEN_REVERSE_LANDSCAPE=2; @@ -326,7 +329,7 @@ public class GodotIO { activity=p_activity; streams=new HashMap(); dirs=new HashMap(); - + applicationContext = activity.getApplicationContext(); } @@ -502,6 +505,43 @@ public class GodotIO { } }; + public void playVideo(String p_path) + { + Uri filePath = Uri.parse(p_path); + mediaPlayer = new MediaPlayer(); + + try { + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setDataSource(applicationContext, filePath); + mediaPlayer.prepare(); + mediaPlayer.start(); + } + catch(IOException e) + { + System.out.println("IOError while playing video"); + } + } + + public boolean isVideoPlaying() { + if (mediaPlayer != null) { + return mediaPlayer.isPlaying(); + } + return false; + } + + public void pauseVideo() { + if (mediaPlayer != null) { + mediaPlayer.pause(); + } + } + + public void stopVideo() { + if (mediaPlayer != null) { + mediaPlayer.release(); + mediaPlayer = null; + } + } + protected static final String PREFS_FILE = "device_id.xml"; protected static final String PREFS_DEVICE_ID = "device_id"; diff --git a/platform/android/java/src/com/android/godot/GodotLib.java b/platform/android/java/src/com/android/godot/GodotLib.java index f0ec3e97c6..ffa3f306f1 100644 --- a/platform/android/java/src/com/android/godot/GodotLib.java +++ b/platform/android/java/src/com/android/godot/GodotLib.java @@ -51,14 +51,14 @@ public class GodotLib { public static native void step(); public static native void touch(int what,int pointer,int howmany, int[] arr); public static native void accelerometer(float x, float y, float z); - public static native void key(int p_unicode_char, boolean p_pressed); + public static native void key(int p_unicode_char, boolean p_pressed); public static native void focusin(); public static native void focusout(); public static native void audio(); public static native void singleton(String p_name,Object p_object); public static native void method(String p_sname,String p_name,String p_ret,String[] p_params); public static native String getGlobal(String p_key); - public static native void callobject(int p_ID, String p_method, Object[] p_params); - public static native void calldeferred(int p_ID, String p_method, Object[] p_params); + public static native void callobject(int p_ID, String p_method, Object[] p_params); + public static native void calldeferred(int p_ID, String p_method, Object[] p_params); } -- cgit v1.2.3 From bdb5d68e77a0b2d8b06c469ee91d0d09cd4fb8db Mon Sep 17 00:00:00 2001 From: reduz Date: Thu, 13 Mar 2014 20:26:38 -0700 Subject: Update GodotLib.java --- platform/android/java/src/com/android/godot/GodotLib.java | 4 ---- 1 file changed, 4 deletions(-) (limited to 'platform/android/java/src') diff --git a/platform/android/java/src/com/android/godot/GodotLib.java b/platform/android/java/src/com/android/godot/GodotLib.java index e496d81356..459c6c1222 100644 --- a/platform/android/java/src/com/android/godot/GodotLib.java +++ b/platform/android/java/src/com/android/godot/GodotLib.java @@ -51,11 +51,7 @@ public class GodotLib { public static native void step(); public static native void touch(int what,int pointer,int howmany, int[] arr); public static native void accelerometer(float x, float y, float z); -<<<<<<< HEAD - public static native void key(int p_unicode_char, boolean p_pressed); -======= public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed); ->>>>>>> 81757d2e977d959e6b0bc26f9fa990417ca91de9 public static native void focusin(); public static native void focusout(); public static native void audio(); -- cgit v1.2.3 From 9f33134c93ecbadda70e8eefc50563e29b2eb7f2 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sat, 5 Apr 2014 12:39:30 -0300 Subject: =?UTF-8?q?-Support=20for=20changing=20fonts=20-Detect=20when=20fr?= =?UTF-8?q?ee()=20might=20crash=20the=20project=20and=20throw=20error=20-f?= =?UTF-8?q?ixed=202D=20Bounce=20in=20physics=20(3d=20still=20broken)=20-re?= =?UTF-8?q?named=20=E2=80=9Con=5Ftop=E2=80=9D=20property=20to=20=E2=80=9Cb?= =?UTF-8?q?ehind=5Fparent=E2=80=9D,=20which=20makes=20more=20sense,=20old?= =?UTF-8?q?=20on=5Ftop=20remains=20there=20for=20compatibility=20but=20is?= =?UTF-8?q?=20invisible.=20-large=20amount=20of=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/java/src/com/android/godot/Godot.java | 3 +- .../java/src/com/android/godot/GodotPaymentV3.java | 83 +++++++++ .../com/android/godot/payments/ConsumeTask.java | 71 +++++++ .../android/godot/payments/HandlePurchaseTask.java | 79 ++++++++ .../com/android/godot/payments/PaymentsCache.java | 42 +++++ .../android/godot/payments/PaymentsManager.java | 151 +++++++++++++++ .../com/android/godot/payments/PurchaseTask.java | 121 ++++++++++++ .../com/android/godot/payments/ValidateTask.java | 97 ++++++++++ .../java/src/com/android/godot/utils/Crypt.java | 39 ++++ .../godot/utils/CustomSSLSocketFactory.java | 54 ++++++ .../src/com/android/godot/utils/HttpRequester.java | 206 +++++++++++++++++++++ .../src/com/android/godot/utils/RequestParams.java | 58 ++++++ .../vending/billing/IInAppBillingService.aidl | 144 ++++++++++++++ 13 files changed, 1147 insertions(+), 1 deletion(-) create mode 100644 platform/android/java/src/com/android/godot/GodotPaymentV3.java create mode 100644 platform/android/java/src/com/android/godot/payments/ConsumeTask.java create mode 100644 platform/android/java/src/com/android/godot/payments/HandlePurchaseTask.java create mode 100644 platform/android/java/src/com/android/godot/payments/PaymentsCache.java create mode 100644 platform/android/java/src/com/android/godot/payments/PaymentsManager.java create mode 100644 platform/android/java/src/com/android/godot/payments/PurchaseTask.java create mode 100644 platform/android/java/src/com/android/godot/payments/ValidateTask.java create mode 100644 platform/android/java/src/com/android/godot/utils/Crypt.java create mode 100644 platform/android/java/src/com/android/godot/utils/CustomSSLSocketFactory.java create mode 100644 platform/android/java/src/com/android/godot/utils/HttpRequester.java create mode 100644 platform/android/java/src/com/android/godot/utils/RequestParams.java create mode 100644 platform/android/java/src/com/android/vending/billing/IInAppBillingService.aidl (limited to 'platform/android/java/src') 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(){ + + @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(){ + + + 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 + */ +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 + */ +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 + */ +public class RequestParams { + + private HashMap params; + private String url; + + public RequestParams(){ + params = new HashMap(); + } + + 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 toPairsList(){ + List fields = new ArrayList(); + + 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); +} -- cgit v1.2.3 From f103b67326376b76100ddb6373c5962378d44119 Mon Sep 17 00:00:00 2001 From: sikakraa Date: Sun, 6 Apr 2014 02:13:36 +0300 Subject: Fixed Android crash by adding safety to the hideKeyboard() -function. --- platform/android/java/src/com/android/godot/GodotIO.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'platform/android/java/src') diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java index 4b6a44335c..605e7ae369 100644 --- a/platform/android/java/src/com/android/godot/GodotIO.java +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -472,8 +472,13 @@ public class GodotIO { public void hideKeyboard() { - InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + View v = activity.getCurrentFocus(); + if (v != null) { + inputMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } else { + inputMgr.hideSoftInputFromWindow(new View(activity).getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } }; public void setScreenOrientation(int p_orientation) { -- cgit v1.2.3