我正在尝试使用 cocos2d-JS 提供的 plugin-x 加入 googleIAP,每当我尝试调用 payForProduct 时,都会出现一个弹出窗口,提示要继续进行,或者如果设置错误,则会出现一些错误。
当我尝试返回或取消弹出窗口时,它第一次正确返回,但从下一次开始,如果我尝试单击调用 payForProduct,它不会显示弹出窗口,实际上它不会调用任何本机 UI 弹出窗口。这里的主类和其他 util 类类似于 google 为 android IAP 提供的示例
public class IAPGooglePlay implements InterfaceIAP, PluginListener {
// Debug tag, for logging
static final String TAG = "IAPGooglePlay";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;
// The helper object
IabHelper mHelper;
static boolean bDebug = false;
Context mContext;
static InterfaceIAP mAdapter;
protected static void LogE(String msg, Exception e) {
Log.e(TAG, msg, e);
e.printStackTrace();
}
protected static void LogD(String msg) {
if (bDebug) {
Log.d(TAG, msg);
}
}
public IAPGooglePlay(Context context) {
mContext = context;
mAdapter = this;
}
private Context getContext() {
return mContext;
}
private Activity getActivity() {
return (Activity) mContext;
}
@Override
public void configDeveloperInfo(Hashtable<String, String> cpInfo) {
LogD("initDeveloperInfo invoked " + cpInfo.toString());
try {
//String appId = cpInfo.get("GooglePlayAppId");
final String appKey = cpInfo.get("GooglePlayAppKey");
PluginWrapper.runOnMainThread(new Runnable() {
@Override
public void run() {
initWithKey(appKey);
}
});
} catch (Exception e) {
LogE("Developer info is wrong!", e);
}
}
@Override
public void payForProduct(Hashtable<String, String> info) {
LogD("payForProduct invoked " + info.toString());
if (! networkReachable()) {
payResult(IAPWrapper.PAYRESULT_FAIL, "Network Unreachable");
return;
}
final Hashtable<String, String> productInfo = info;
PluginWrapper.runOnMainThread(new Runnable() {
@Override
public void run() {
String iapId = productInfo.get("IAPId");
String iapSecKey = productInfo.get("IAPSecKey");
try{
mHelper.launchPurchaseFlow(getActivity(), iapId, RC_REQUEST, mPurchaseFinishedListener, iapSecKey);
}
catch(IllegalStateException ex){
LogD("Please retry in a few seconds.");
mHelper.flagEndAsync();
}
}
});
}
@Override
public void setDebugMode(boolean debug) {
//TODO: fix this
//It's possible setDebug don't work at the first time because init was happening on another thread
bDebug = debug;
if (mHelper != null) {
mHelper.enableDebugLogging(debug);
}
}
@Override
public String getSDKVersion() {
return "IAPv3Jan2014";
}
private boolean networkReachable() {
boolean bRet = false;
try {
ConnectivityManager conn = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = conn.getActiveNetworkInfo();
bRet = (null == netInfo) ? false : netInfo.isAvailable();
} catch (Exception e) {
LogE("Fail to check network status", e);
}
LogD("NetWork reachable : " + bRet);
return bRet;
}
private static void payResult(int ret, String msg) {
IAPWrapper.onPayResult(mAdapter, ret, msg);
LogD("GooglePlay result : " + ret + " msg : " + msg);
}
@Override
public String getPluginVersion() {
return "0.3.0";
}
/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
* (that you got from the Google Play developer console). This is not your
* developer public key, it's the *app-specific* public key.
*
* Instead of just storing the entire literal string here embedded in the
* program, construct the key at runtime from pieces or
* use bit manipulation (for example, XOR with some other string) to hide
* the actual key. The key itself is not secret information, but we don't
* want to make it easy for an attacker to replace the public key with one
* of their own and then fake messages from the server.
*/
public void initWithKey(String base64EncodedPublicKey) {
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
//mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper = new IabHelper(getContext(), base64EncodedPublicKey);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.e(TAG,"Problem setting up in-app billing: " + result);
return;
}
// Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
PluginWrapper.addListener(this);
}
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
Log.e(TAG, "Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
//start. you can add you own query code here for flushing if you wish
//end
}
};
public void refreshPurchases() {
Log.e(TAG, "TODO implement refreshPurchases");
}
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
/*
* TODO: verify that the developer payload of the purchase is correct. It will be
* the same one that you sent when initiating the purchase.
*
* WARNING: Locally generating a random string when starting a purchase and
* verifying it here might seem like a good approach, but this will fail in the
* case where the user purchases an item on one device and then uses your app on
* a different device, because on the other device you will not have access to the
* random string you originally generated.
*
* So a good developer payload has these characteristics:
*
* 1. If two different users purchase an item, the payload is different between them,
* so that one user's purchase can't be replayed to another user.
*
* 2. The payload must be such that you can verify it even when the app wasn't the
* one who initiated the purchase flow (so that items purchased by the user on
* one device work on other devices owned by the user).
*
* Using your own server to store and verify developer payloads across app
* installations is recommended.
*/
return true;
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
@Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
failPurchase(result.getMessage());
return;
}
else {
Log.d(TAG,"Success!");
succeedPurchase(result.getMessage());
//Auto consume the purchase
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);
if (result.isSuccess()) {
Log.d(TAG, "Consumption successful. Provisioning.");
}
else {
Log.e(TAG,"Error while consuming: " + result);
}
setWaitScreen(false);
Log.d(TAG, "End consumption flow.");
}
};
void succeedPurchase(String msg) {
IAPWrapper.onPayResult(mAdapter, IAPWrapper.PAYRESULT_SUCCESS, msg);
}
void failPurchase(String msg) {
IAPWrapper.onPayResult(mAdapter, IAPWrapper.PAYRESULT_FAIL, msg);
}
// Enables or disables the "please wait" screen.
void setWaitScreen(boolean set) {
//Log.e(TAG, "ERROR: need to implement setWaitScreen");
//findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);
//findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(getContext());
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(TAG, "Showing alert dialog: " + message);
bld.create().show();
}
//@Override
/**
* Handle activity result. Call this method from your Activity's
* onActivityResult callback.
* @return
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
LogD("onActivityResult("+requestCode+", "+resultCode+", data)");
return mHelper.handleActivityResult(requestCode, resultCode, data);
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onDestroy() {
PluginWrapper.removeListener(this);
}
}
也许是一些与 activityonResult 相关的问题导致我认为的 glthread 阻塞,或者 IABHelper 类新活动即将出现,所以这个 cocos2dx 活动线程被阻塞,我不确定
我该如何解决这个问题。