package org.mtransit.android.util;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.WeakHashMap;
import org.mtransit.android.R;
import org.mtransit.android.commons.ArrayUtils;
import org.mtransit.android.commons.MTLog;
import org.mtransit.android.commons.PreferenceUtils;
import org.mtransit.android.commons.ToastUtils;
import org.mtransit.android.ui.fragment.PurchaseDialogFragment;
import org.mtransit.android.util.iab.IabHelper;
import org.mtransit.android.util.iab.IabResult;
import org.mtransit.android.util.iab.Inventory;
import org.mtransit.android.util.iab.Purchase;
import org.mtransit.android.util.iab.SkuDetails;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.support.v4.util.ArrayMap;
import android.widget.Toast;
public final class VendingUtils implements MTLog.Loggable {
private static final String TAG = VendingUtils.class.getSimpleName();
@Override
public String getLogTag() {
return TAG;
}
private static final boolean FORCE_HAS_SUBSCRIPTION = false;
private static final boolean FORCE_DO_NOT_HAVE_SUBSCRIPTION = false;
public static final String SKU_SUBSCRIPTION = "_subscription_";
public static final String SKU_STARTS_WITH_F = "f_";
private static final String WEEKLY = "weekly";
private static final String MONTHLY = "monthly";
private static final String YEARLY = "yearly";
public static final ArrayList<String> SORTED_PERIOD_CAT = ArrayUtils.asArrayList(WEEKLY, MONTHLY, YEARLY);
public static final ArrayMap<String, Integer> PERIOD_RES_ID;
static {
ArrayMap<String, Integer> map = new ArrayMap<String, Integer>();
map.put(WEEKLY, R.string.support_every_week);
map.put(MONTHLY, R.string.support_every_month);
map.put(YEARLY, R.string.support_every_year);
PERIOD_RES_ID = map;
}
public static final String DEFAULT_PRICE_CAT = "1";
public static final String DEFAULT_PERIOD_CAT = MONTHLY;
public static final ArrayList<String> AVAILABLE_SUBSCRIPTIONS;
static {
ArrayList<String> list = new ArrayList<String>();
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "1");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "2");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "3");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "4");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "5");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "7");
list.add(SKU_STARTS_WITH_F + WEEKLY + SKU_SUBSCRIPTION + "10");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "1");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "2");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "3");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "4");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "5");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "7");
list.add(SKU_STARTS_WITH_F + MONTHLY + SKU_SUBSCRIPTION + "10");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "1");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "2");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "3");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "4");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "5");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "7");
list.add(SKU_STARTS_WITH_F + YEARLY + SKU_SUBSCRIPTION + "10");
AVAILABLE_SUBSCRIPTIONS = list;
}
public static final ArrayList<String> ALL_VALID_SUBSCRIPTIONS;
static {
ArrayList<String> set = new ArrayList<String>();
set.add("weekly_subscription"); // Inactive
set.add("monthly_subscription"); // Active - offered by default for months
set.add("yearly_subscription"); // Active - never offered
set.addAll(AVAILABLE_SUBSCRIPTIONS);
ALL_VALID_SUBSCRIPTIONS = set;
}
private static IabHelper mHelper;
private static WeakReference<Context> contextWR;
private static WeakHashMap<OnVendingResultListener, Object> listenersWR = new WeakHashMap<VendingUtils.OnVendingResultListener, Object>();
private static void checkBilling(Context context, OnVendingResultListener listener) {
addListener(listener);
setContext(context);
if (mHelper == null) {
initBilling(context);
}
broadcastNewVendingResult(isHasSubscription(context));
}
private static void addListener(OnVendingResultListener listener) {
listenersWR.put(listener, null);
}
private static void removeListener(OnVendingResultListener listener) {
listenersWR.remove(listener);
}
private static void setContext(Context context) {
contextWR = new WeakReference<Context>(context);
}
public static void onPause() {
if (contextWR != null) {
contextWR.clear();
contextWR = null;
}
}
public static void onResume(Context context, OnVendingResultListener listener) {
checkBilling(context, listener);
}
private static void initBilling(Context context) {
mHelper = new IabHelper(context, context.getString(R.string.google_play_license_key));
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
@Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
MTLog.w(TAG, "Problem setting up in-app billing: " + result);
return;
}
if (mHelper == null) return;
mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (mHelper == null) return;
if (result.isFailure()) {
MTLog.w(TAG, "Failed to query inventory: %s", result);
return;
}
List<String> allOwnedSkus = inventory.getAllOwnedSkus(IabHelper.ITEM_TYPE_SUBS);
Boolean mHasSubscription = false;
if (allOwnedSkus != null) {
for (String ownedSku : allOwnedSkus) {
if (ALL_VALID_SUBSCRIPTIONS.contains(ownedSku)) {
Purchase subscriptionPurchase = inventory.getPurchase(ownedSku);
if (subscriptionPurchase != null && verifyDeveloperPayload(subscriptionPurchase)) {
mHasSubscription = true;
break;
}
}
}
}
setHasSubscription(mHasSubscription);
}
});
}
});
}
private static boolean verifyDeveloperPayload(Purchase purchase) {
return true;
}
private static Boolean hasSubscription = null;
private static void setHasSubscription(Boolean newHasSubscription) {
if (FORCE_HAS_SUBSCRIPTION) {
newHasSubscription = true;
} else if (FORCE_DO_NOT_HAVE_SUBSCRIPTION) {
newHasSubscription = false;
}
hasSubscription = newHasSubscription;
broadcastNewVendingResult(hasSubscription);
if (hasSubscription != null) {
Context context = contextWR == null ? null : contextWR.get();
if (context != null) {
PreferenceUtils.savePrefLcl(context, PREF_KEY_HAS_SUBSCRIPTION, hasSubscription, false); // async
}
}
}
public static Boolean isHasSubscription(Context context) {
if (hasSubscription == null) {
if (PreferenceUtils.hasPrefLcl(context, PREF_KEY_HAS_SUBSCRIPTION)) {
boolean newHasSubscription = PreferenceUtils.getPrefLcl(context, PREF_KEY_HAS_SUBSCRIPTION, PREF_KEY_HAS_SUBSCRIPTION_DEFAULT);
if (FORCE_HAS_SUBSCRIPTION) {
newHasSubscription = true;
} else if (FORCE_DO_NOT_HAVE_SUBSCRIPTION) {
newHasSubscription = false;
}
hasSubscription = newHasSubscription;
}
}
return hasSubscription;
}
private static void broadcastNewVendingResult(Boolean newHasSubscription) {
Iterator<OnVendingResultListener> it = listenersWR.keySet().iterator();
while (it.hasNext()) {
OnVendingResultListener listener = it.next();
if (listener != null) {
listener.onVendingResult(newHasSubscription);
}
}
}
public static void purchase(Activity activity) {
showNewDialog(activity.getFragmentManager(), PurchaseDialogFragment.newInstance());
}
private static final String DIALOG_TAG = "dialog";
private static void showNewDialog(FragmentManager fm, DialogFragment newDialog) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(DIALOG_TAG);
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
if (newDialog != null) {
newDialog.show(ft, DIALOG_TAG);
}
}
public static void getInventory(IabHelper.QueryInventoryFinishedListener listener) {
try {
if (mHelper != null && mHelper.subscriptionsSupported()) {
mHelper.queryInventoryAsync(true, null, AVAILABLE_SUBSCRIPTIONS, listener);
}
} catch (Exception e) {
MTLog.w(TAG, e, "Error while gettting inventory!");
}
}
@Deprecated
public static void logInventory(Activity activity) {
MTLog.i(TAG, "logInventory(%s)", activity);
try {
MTLog.i(TAG, "logInventory() > mHelper: %s", mHelper);
if (mHelper != null && mHelper.subscriptionsSupported()) {
MTLog.i(TAG, "logInventory() > Query inventory...");
mHelper.queryInventoryAsync(true, null, AVAILABLE_SUBSCRIPTIONS, new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
MTLog.i(TAG, "onQueryInventoryFinished(%s,%s)", result, inventory);
MTLog.i(TAG, "Query inventory finished.");
if (result == null || result.isFailure() || inventory == null) {
MTLog.w(TAG, "Failed to query inventory: %s (%s)", result, inventory);
return;
}
MTLog.i(TAG, "Query inventory was successful.");
MTLog.i(TAG, "all sku...");
for (String sku : inventory.getAllSkus()) {
if (!inventory.hasDetails(sku)) {
MTLog.i(TAG, "Skip sku %s (no details)", sku);
continue;
}
SkuDetails skuDetails = inventory.getSkuDetails(sku);
MTLog.i(TAG, "sku: %s: %s", sku, skuDetails);
}
MTLog.i(TAG, "all sku... DONE");
MTLog.i(TAG, "all owned sku...");
for (String sku : inventory.getAllOwnedSkus()) {
if (!inventory.hasDetails(sku)) {
MTLog.i(TAG, "Skip sku %s (no details)", sku);
continue;
}
SkuDetails skuDetails = inventory.getSkuDetails(sku);
MTLog.i(TAG, "sku: %s: %s", sku, skuDetails);
}
MTLog.i(TAG, "all owned sku... DONE");
MTLog.i(TAG, "all purchase...");
for (Purchase purchase : inventory.getAllPurchases()) {
MTLog.i(TAG, "purchase: %s", purchase);
}
MTLog.i(TAG, "all purchase... DONE");
}
});
}
} catch (Exception e) {
MTLog.w(TAG, e, "Error while logging inventory!");
}
}
private static final int RC_REQUEST = 10001;
public static void purchase(Activity activity, String sku) {
if (mHelper != null && mHelper.subscriptionsSupported()) {
String payload = "";
mHelper.launchPurchaseFlow(activity, sku, IabHelper.ITEM_TYPE_SUBS, RC_REQUEST, new IabHelper.OnIabPurchaseFinishedListener() {
@Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Context context = contextWR == null ? null : contextWR.get();
if (mHelper == null) return;
if (result.isFailure()) {
MTLog.w(TAG, "onIabPurchaseFinished() > Error purchasing: %s", result);
if (context != null) {
int resId = R.string.support_subs_default_failure_message;
if (result.getResponse() == IabHelper.IABHELPER_USER_CANCELLED) {
resId = R.string.support_subs_user_canceled_message;
}
ToastUtils.makeTextAndShowCentered(context, resId);
}
return;
}
if (!verifyDeveloperPayload(purchase)) {
MTLog.w(TAG, "onIabPurchaseFinished() > Error purchasing. Authenticity verification failed.");
if (context != null) {
int resId = R.string.support_subs_authenticity_check_fail_message;
ToastUtils.makeTextAndShowCentered(context, resId, Toast.LENGTH_LONG);
}
return;
}
String purchasedSku = purchase.getSku();
if (ALL_VALID_SUBSCRIPTIONS.contains(purchasedSku)) {
if (context != null) {
int resId = R.string.support_subs_purchase_successful_message;
ToastUtils.makeTextAndShowCentered(context, resId, Toast.LENGTH_LONG);
}
setHasSubscription(true);
}
}
}, payload);
}
}
public static boolean onActivityResult(Context context, int requestCode, int resultCode, Intent data) {
setContext(context);
if (mHelper != null && mHelper.handleActivityResult(requestCode, resultCode, data)) {
return true; // handled
}
return false; // not handled
}
private static final String PREF_KEY_HAS_SUBSCRIPTION = "pHasSubscription";
private static final boolean PREF_KEY_HAS_SUBSCRIPTION_DEFAULT = false;
public static void destroyBilling(OnVendingResultListener listener) {
removeListener(listener);
if (listenersWR.isEmpty()) {
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
}
public interface OnVendingResultListener {
void onVendingResult(Boolean hasSubscription);
}
}