/*
* Copyright 2013 nishino.keiichiro@gmail.com
*
* 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.redoceanred.unity.android;
import java.util.Arrays;
import java.util.List;
import com.redoceanred.android.billing.util.IabHelper;
import com.redoceanred.android.billing.util.IabResult;
import com.redoceanred.android.billing.util.Inventory;
import com.redoceanred.android.billing.util.Purchase;
import com.redoceanred.android.billing.util.SkuDetails;
import com.redoceanred.unity.android.activity.BillingNativeActivity;
import com.unity3d.player.UnityPlayer;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
/**
* アプリ内課金Plugin.
*
* @author keiichiro
*/
public class BillingPlugin {
private static final String TAG = BillingPlugin.class.getSimpleName();
/**
* Unity層へのCallbackクラス.
*/
private BillingPluginCallback mCallBack;
/**
* 課金アイテムのID.
*/
private String mProductId;
/**
* プラグインの内部状態.
*/
private enum PurchaseState {
/**
* 初期化中.
*/
Initialize,
/**
* アイドル.
*/
Idle,
/**
* 消費アイテム購入処理中.
*/
ConsumablePurchase,
/**
* 非消費アイテム購入処理中.
*/
NoConSumablePurchase,
/**
* サブスクリプション(期間)アイテム購入処理中.
*/
SubscriptionPurchase,
/**
* {@link BillingPlugin}getPurchaseData()にて消費アイテムの課金情報を復帰中.(消費処理中)
*/
ConsumeRestorePurchaseData
}
/**
* プラグインの内部状態.
*/
private PurchaseState mPurchaseState = PurchaseState.Initialize;
/**
* プラグインの初期化を実行.
*
* @param publicKey
* アプリのイセンスキー.
* @param inapp
* In-app ProductsのID文字列.
* ex gas,premium
* @param inapp
* Subscription ProductsのID文字列.
* ex infinite_gas
* @param gameObject
* Pluginを使用しているGame Object名.Callback対象となる.
*/
public void initPlugin(String publicKey, String inapp, String subs, String gameObject) {
Log.d(TAG, "Called initPlugin " + inapp + " " + subs + gameObject);
mCallBack = new BillingPluginCallback(gameObject);
if (publicKey == null || publicKey.equals("")) {
mCallBack.initMessage(false);
} else {
initBilling(publicKey, inapp, subs);
}
}
/**
* 通常アイテムの購入処理を実行する.
*
* @param productId
* 課金アイテムのID.
* @param consume
* true 消費アイテム, false 非消費アイテム.
* @param payload
* verify用文字列.
* @return true 成功, false 失敗.
*/
public boolean purchaseInApp(String productId, boolean consume, String payload) {
Log.d(TAG, "Called purchase : " + productId + " consume : " + consume);
if (!mPurchaseState.equals(PurchaseState.Idle)) {
// Processing Purchase or initialize. 何か処理中の場合は購入処理不可.
return false;
}
if (consume) {
mPurchaseState = PurchaseState.ConsumablePurchase;
} else {
mPurchaseState = PurchaseState.NoConSumablePurchase;
}
mProductId = productId;
mHelper.launchPurchaseFlow(UnityPlayer.currentActivity, productId, RC_REQUEST, mPurchaseFinishedListener, payload);
return true;
}
/**
* Subscriptionアイテム(定期購入)のサポート.
* @return true サポート,false 非サポート.
*/
public boolean getSubscriptionsSupported() {
if (mPurchaseState.equals(PurchaseState.Initialize)) {
return false;
}
return mHelper.subscriptionsSupported();
}
/**
* Subscriptionアイテムの購入処理を実行する.
*
* @param productId
* 課金アイテムのID.
* @param payload
* verify用文字列.
* @return true 成功, false 失敗.
*/
public boolean purchaseSubscription(String productId, String payload) {
if (!mPurchaseState.equals(PurchaseState.Idle)) {
// Processing Purchase.
return false;
}
if (!mHelper.subscriptionsSupported()) {
// Subscription Not Support.
return false;
}
mPurchaseState = PurchaseState.SubscriptionPurchase;
mProductId = productId;
mHelper.launchPurchaseFlow(UnityPlayer.currentActivity, productId, IabHelper.ITEM_TYPE_SUBS, RC_REQUEST, mPurchaseFinishedListener, payload);
return true;
}
/**
* 初期化処理後にアプリの購入済みアイテム情報を取得する.
*
* @param productId
* 課金アイテムのID.
* @param consume
* true 消費アイテム, false 非消費アイテム.
* @return true 成功, false 失敗.
*/
public boolean getPurchaseData(String productId, boolean consume) {
if (!mPurchaseState.equals(PurchaseState.Idle)) {
return false;
}
final Purchase purchase = mInventory.getPurchase(productId);
if (purchase != null) {
if (consume) {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
});
mPurchaseState = PurchaseState.ConsumeRestorePurchaseData;
}
return true;
} else {
return false;
}
}
/**
* InAppBillingServiceのアイテム情報を取得する.
* @param productId 課金アイテムのID.
* @return アイテム情報(Json).
* ref. http://developer.android.com/google/play/billing/billing_reference.html#getSkuDetails
*/
public String getProductDetail(String productId) {
SkuDetails d = mInventory.getSkuDetails(productId);
if (d != null) {
return d.getJson();
} else {
return "";
}
}
/**
* アイテムのPriceを取得する.
* @param productId 課金アイテムのID.
* @return Price.
*/
public String getProductPrice(String productId) {
SkuDetails d = mInventory.getSkuDetails(productId);
if (d != null) {
return d.getPrice();
} else {
return "";
}
}
/**
* アイテムのTitleを取得する.
* @param productId 課金アイテムのID.
* @return Title.
*/
public String getProductTitle(String productId) {
SkuDetails d = mInventory.getSkuDetails(productId);
if (d != null) {
return d.getTitle();
} else {
return "";
}
}
/**
* アイテムのDescriptionを取得する.
* @param productId 課金アイテムのID.
* @return Description.
*/
public String getProductDescription(String productId) {
SkuDetails d = mInventory.getSkuDetails(productId);
if (d != null) {
return d.getDescription();
} else {
return "";
}
}
/**
* Playストアアプリを起動する.Subscription解約時に起動する.
*/
public void startPlayStore() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + UnityPlayer.currentActivity.getPackageName()));
try {
UnityPlayer.currentActivity.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, e.toString());
}
}
/**
* Acvitityからの実行結果をhandleする.
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
return mHelper.handleActivityResult(requestCode, resultCode, data);
}
/**
* リリース処理を実行する.
*/
public void dispose() {
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
// 以下Billingサンプルからの転載.
private IabHelper mHelper;
private Inventory mInventory;
static final int RC_REQUEST = 10001;
public void initBilling(String publicKey, final String inappSkus, final String subsSkus) {
mHelper = new IabHelper(UnityPlayer.currentActivity, publicKey);
mHelper.enableDebugLogging(true);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
mCallBack.initMessage(false);
return;
}
// In App アイテムIDをListに変換.
List<String> inappArray = null;
if (inappSkus != null) {
inappArray = Arrays.asList(inappSkus.split(","));
}
// Subscription アイテムIDをListに変換.
List<String> subsArray = null;
if (subsSkus != null) {
subsArray = Arrays.asList(subsSkus.split(","));
}
// アイテム情報を取得.
mHelper.queryInventoryAsync(true, inappArray, subsArray, mGotInventoryListener);
}
});
}
// 初期化完了通知.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
mCallBack.initMessage(false);
return;
}
// 情報を内部に保持する.
mInventory = inventory;
initState();
if (UnityPlayer.currentActivity instanceof BillingNativeActivity) {
// ActivityにPluginのインスタンスを登録.
BillingNativeActivity nativeActivity = (BillingNativeActivity) UnityPlayer.currentActivity;
nativeActivity.setBillingPlugin(BillingPlugin.this);
}
mCallBack.initMessage(true);
}
};
// 購入完了イベントの通知.
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
String sku = null;
if (purchase == null) {
sku = mProductId;
} else {
sku = purchase.getSku();
}
mCallBack.purchaseMessage(sku, false);
initState();
return;
}
if (mPurchaseState.equals(PurchaseState.ConsumablePurchase)) {
// 消費アイテムの場合はGoogle側の消費を行う.
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
} else {
// purcase success event.
mCallBack.purchaseMessage(purchase.getSku(), true, purchase.getDeveloperPayload());
initState();
}
}
};
// 消費完了イベントの通知.
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (mPurchaseState.equals(PurchaseState.ConsumeRestorePurchaseData)) {
initState();
return;
}
if (result.isSuccess()) {
// purcase success event.
mCallBack.purchaseMessage(purchase.getSku(), true, purchase.getDeveloperPayload());
} else {
// purcase failed event.
mCallBack.purchaseMessage(purchase.getSku(), false);
}
initState();
}
};
// 状態を初期化.
private void initState() {
mPurchaseState = PurchaseState.Idle;
mProductId = null;
}
}