package eu.faircode.netguard; /* This file is part of NetGuard. NetGuard is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. NetGuard is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with NetGuard. If not, see . Copyright 2015-2017 by Marcel Bokhorst (M66B) */ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; import com.android.vending.billing.IInAppBillingService; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; public class IAB implements ServiceConnection { private static final String TAG = "NetGuard.IAB"; private Context context; private Delegate delegate; private IInAppBillingService service = null; private static final int IAB_VERSION = 3; public interface Delegate { void onReady(IAB iab); } public IAB(Delegate delegate, Context context) { this.context = context.getApplicationContext(); this.delegate = delegate; } public void bind() { Log.i(TAG, "Bind"); Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); serviceIntent.setPackage("com.android.vending"); context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE); } public void unbind() { if (service != null) { Log.i(TAG, "Unbind"); context.unbindService(this); service = null; } } @Override public void onServiceConnected(ComponentName name, IBinder binder) { Log.i(TAG, "Connected"); service = IInAppBillingService.Stub.asInterface(binder); delegate.onReady(this); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG, "Disconnected"); service = null; } public boolean isAvailable(String sku) throws RemoteException, JSONException { // Get available SKUs ArrayList skuList = new ArrayList<>(); skuList.add(sku); Bundle query = new Bundle(); query.putStringArrayList("ITEM_ID_LIST", skuList); Bundle bundle = service.getSkuDetails(IAB_VERSION, context.getPackageName(), "inapp", query); Log.i(TAG, "getSkuDetails"); Util.logBundle(bundle); int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); Log.i(TAG, "Response=" + getResult(response)); if (response != 0) throw new IllegalArgumentException(getResult(response)); // Check available SKUs boolean found = false; ArrayList details = bundle.getStringArrayList("DETAILS_LIST"); if (details != null) for (String item : details) { JSONObject object = new JSONObject(item); if (sku.equals(object.getString("productId"))) { found = true; break; } } Log.i(TAG, sku + "=" + found); return found; } public void updatePurchases() throws RemoteException { // Get purchases List skus = getPurchases(); SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); for (String product : prefs.getAll().keySet()) if (!ActivityPro.SKU_DONATION.equals(product)) { Log.i(TAG, "removing SKU=" + product); editor.remove(product); } for (String sku : skus) { Log.i(TAG, "adding SKU=" + sku); editor.putBoolean(sku, true); } editor.apply(); } public boolean isPurchased(String sku) throws RemoteException { return getPurchases().contains(sku); } public List getPurchases() throws RemoteException { // Get purchases Bundle bundle = service.getPurchases(IAB_VERSION, context.getPackageName(), "inapp", null); Log.i(TAG, "getPurchases"); Util.logBundle(bundle); int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); Log.i(TAG, "Response=" + getResult(response)); if (response != 0) throw new IllegalArgumentException(getResult(response)); ArrayList details = bundle.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); return (details == null ? new ArrayList() : details); } public PendingIntent getBuyIntent(String sku, boolean subscription) throws RemoteException { if (service == null) return null; Bundle bundle = service.getBuyIntent(IAB_VERSION, context.getPackageName(), sku, subscription ? "subs" : "inapp", "netguard"); Log.i(TAG, "getBuyIntent sku=" + sku + " subscription=" + subscription); Util.logBundle(bundle); int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); Log.i(TAG, "Response=" + getResult(response)); if (response != 0) throw new IllegalArgumentException(getResult(response)); if (!bundle.containsKey("BUY_INTENT")) throw new IllegalArgumentException("BUY_INTENT missing"); return bundle.getParcelable("BUY_INTENT"); } public static void setBought(String sku, Context context) { Log.i(TAG, "Bought " + sku); SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); prefs.edit().putBoolean(sku, true).apply(); } public static boolean isPurchased(String sku, Context context) { if (Util.isDebuggable(context)) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return !prefs.getBoolean("debug_iab", false); } SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); if (ActivityPro.SKU_SUPPORT1.equals(sku) || ActivityPro.SKU_SUPPORT2.equals(sku)) return prefs.getBoolean(sku, false); return (prefs.getBoolean(sku, false) || prefs.getBoolean(ActivityPro.SKU_PRO1, false) || prefs.getBoolean(ActivityPro.SKU_DONATION, false)); } public static boolean isPurchasedAny(Context context) { if (Util.isDebuggable(context)) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return !(prefs.getBoolean("debug_iab", false) || prefs.getBoolean("debug_ads", false)); } SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); for (String key : prefs.getAll().keySet()) if (prefs.getBoolean(key, false)) return true; return false; } public static String getResult(int responseCode) { switch (responseCode) { case 0: return "OK"; case 1: return "USER_CANCELED"; case 2: return "SERVICE_UNAVAILABLE"; case 3: return "BILLING_UNAVAILABLE"; case 4: return "ITEM_UNAVAILABLE"; case 5: return "DEVELOPER_ERROR"; case 6: return "ERROR"; case 7: return "ITEM_ALREADY_OWNED"; case 8: return "ITEM_NOT_OWNED"; default: return Integer.toString(responseCode); } } }