<commit_before>/**
 * IAPSystemNativeInterface.java
 * Chilli Source
 * Created by <NAME> on 12/06/2013.
 * 
 * The MIT License (MIT)
 * 
 * Copyright (c) 2013 Tag Games Limited
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.chilliworks.chillisource.googleplay;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Intent;

import com.chilliworks.chillisource.core.CSApplication;
import com.chilliworks.chillisource.core.INativeInterface;
import com.chilliworks.chillisource.core.InterfaceIDType;
import com.chilliworks.chillisource.core.Logging;
import com.chilliworks.chillisource.networking.IAPProductDescription;
import com.chilliworks.chillisource.networking.IAPTransactionDescription;

public class GooglePlayIAPNativeInterface  extends INativeInterface
{
	//--------------------------------------------------------------
	/// Member data
	//--------------------------------------------------------------
	public static InterfaceIDType InterfaceID = new InterfaceIDType("CGooglePlayIAPNativeInterface");
	
	private IabHelper mIABHelper = null;
	private boolean mbCancelProductDescRequest = false;
	private boolean mbIsPurchasingEnabled = false;
	private boolean mbRequestDescriptionsInProgress = false;
	private List<Purchase> mCurrentPendingTransactions = new ArrayList<Purchase>();
	private Inventory mInventory = null;
	
	//---------------------------------------------------------------------
	/// Native Methods
	//---------------------------------------------------------------------
	public native void NativeOnProductsDescriptionsRequestComplete(String[] inIDs, String[] inNames, String[] inDescriptions, String[] inFormattedPrices);
	public native void NativeOnTransactionStatusUpdated(int inResult, String inProductID, String inTransactionID, String inReceipt);
	public native void NativeOnTransactionClosed(String inProductID, String inTransactionID, boolean in_success);
	
	//---------------------------------------------------------------------
	/// Constructor
	//---------------------------------------------------------------------
	public GooglePlayIAPNativeInterface()
	{
	}
	//---------------------------------------------------------------------
	/// Is A
	///
	/// @param Interface ID
	/// @return Whether the system implements the given interface
	//---------------------------------------------------------------------
	@Override public boolean IsA(InterfaceIDType inInterfaceType) 
	{
		return (inInterfaceType == InterfaceID);
	}
	//---------------------------------------------------------------------
	/// Init
	///
	/// Prepares and starts the IAB system. This should be the first method
	/// called and it should only be called once. 
	///
	/// @param The public key.
	//---------------------------------------------------------------------
	public void Init(final String instrPublicKey)
	{
		mIABHelper = new IabHelper(CSApplication.get().getActivityContext(), instrPublicKey);
		mIABHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
		{
			@Override public void onIabSetupFinished(IabResult result) 
			{
				if(result.isSuccess())
				{
					mbIsPurchasingEnabled = true;
				}
				else
				{
					mbIsPurchasingEnabled = false;
					Logging.logError("Cannot setup Google Play IAB: " + result.getMessage());
				}
			}
		});
	}
    //---------------------------------------------------------------
	/// Request Product Descriptions
	///
	/// Request the descriptions of all the products on the store
	///
	/// @param Array of product IDs
	//---------------------------------------------------------------
	public void RequestProductDescriptions(final String[] inProductIDs)
	{
		if(mbRequestDescriptionsInProgress && mbCancelProductDescRequest) // previously requested cancel but new request incoming
		{
			//This this will cause the system to received the previously requested product descriptions. This
			//means it will only work if the new request is identical to the previous one. 
			mbCancelProductDescRequest = false;
			return;
		}
		else if(mbRequestDescriptionsInProgress) // just called twice, bail
			return;
		
		
		if(mInventory != null)
		{
			OnProductsRequestComplete(mInventory, inProductIDs);
			return;
		}
		
		// default flags for new request
		mbCancelProductDescRequest = false;
		mbRequestDescriptionsInProgress = true;
			
		CSApplication.get().scheduleUIThreadTask(new Runnable()
		{
			@Override public void run() 
			{
				final List<String> productIDs = new ArrayList<String>(Arrays.asList(inProductIDs));

				mIABHelper.queryInventoryAsync(true, productIDs, new IabHelper.QueryInventoryFinishedListener() 
				{
					@Override public void onQueryInventoryFinished(IabResult result, Inventory inventory) 
					{
						mbRequestDescriptionsInProgress = false;
						
						if(mbCancelProductDescRequest)
							return;
						
						mInventory = inventory;
						
						if(result.isFailure())
						{
							Logging.logError("Cannot query Google IAB inventory: " + result.getMessage());
							OnProductsRequestComplete(null, inProductIDs);
							return;
						}

						OnProductsRequestComplete(mInventory, inProductIDs);
					}
				});
			}
		});
    }
    //---------------------------------------------------------------
    /// On Products Request Complete
    ///
    /// Triggered when the products description request completes<commit_msg>Fixed issue with android IAPs, product description requests are now batched in groups of 20<commit_after>/**
 * IAPSystemNativeInterface.java
 * Chilli Source
 * Created by <NAME> on 12/06/2013.
 * 
 * The MIT License (MIT)
 * 
 * Copyright (c) 2013 Tag Games Limited
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.chilliworks.chillisource.googleplay;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Intent;

import com.chilliworks.chillisource.core.CSApplication;
import com.chilliworks.chillisource.core.INativeInterface;
import com.chilliworks.chillisource.core.InterfaceIDType;
import com.chilliworks.chillisource.core.Logging;
import com.chilliworks.chillisource.networking.IAPProductDescription;
import com.chilliworks.chillisource.networking.IAPTransactionDescription;

public class GooglePlayIAPNativeInterface  extends INativeInterface
{
	//--------------------------------------------------------------
	/// Member data
	//--------------------------------------------------------------
	public static InterfaceIDType InterfaceID = new InterfaceIDType("CGooglePlayIAPNativeInterface");
	
	private IabHelper mIABHelper = null;
	private boolean mbCancelProductDescRequest = false;
	private boolean mbIsPurchasingEnabled = false;
	private boolean mbRequestDescriptionsInProgress = false;
	private List<Purchase> mCurrentPendingTransactions = new ArrayList<Purchase>();
	private Inventory mInventory = null;
	
	private final int k_maxProductIDsPerRequest = 20;
	
	//---------------------------------------------------------------------
	/// Native Methods
	//---------------------------------------------------------------------
	public native void NativeOnProductsDescriptionsRequestComplete(String[] inIDs, String[] inNames, String[] inDescriptions, String[] inFormattedPrices);
	public native void NativeOnTransactionStatusUpdated(int inResult, String inProductID, String inTransactionID, String inReceipt);
	public native void NativeOnTransactionClosed(String inProductID, String inTransactionID, boolean in_success);
	
	//---------------------------------------------------------------------
	/// Constructor
	//---------------------------------------------------------------------
	public GooglePlayIAPNativeInterface()
	{
	}
	//---------------------------------------------------------------------
	/// Is A
	///
	/// @param Interface ID
	/// @return Whether the system implements the given interface
	//---------------------------------------------------------------------
	@Override public boolean IsA(InterfaceIDType inInterfaceType) 
	{
		return (inInterfaceType == InterfaceID);
	}
	//---------------------------------------------------------------------
	/// Init
	///
	/// Prepares and starts the IAB system. This should be the first method
	/// called and it should only be called once. 
	///
	/// @param The public key.
	//---------------------------------------------------------------------
	public void Init(final String instrPublicKey)
	{
		mIABHelper = new IabHelper(CSApplication.get().getActivityContext(), instrPublicKey);
		mIABHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
		{
			@Override public void onIabSetupFinished(IabResult result) 
			{
				if(result.isSuccess())
				{
					mbIsPurchasingEnabled = true;
				}
				else
				{
					mbIsPurchasingEnabled = false;
					Logging.logError("Cannot setup Google Play IAB: " + result.getMessage());
				}
			}
		});
	}
    //---------------------------------------------------------------
	/// Request Product Descriptions
	///
	/// Request the descriptions of all the products on the store
	///
	/// @param Array of product IDs
	//---------------------------------------------------------------
	public void RequestProductDescriptions(final String[] inProductIDs)
	{
		if(mbRequestDescriptionsInProgress && mbCancelProductDescRequest) // previously requested cancel but new request incoming
		{
			//This this will cause the system to received the previously requested product descriptions. This
			//means it will only work if the new request is identical to the previous one. 
			mbCancelProductDescRequest = false;
			return;
		}
		else if(mbRequestDescriptionsInProgress) // just called twice, bail
			return;
		
		
		if(mInventory != null)
		{
			OnProductsRequestComplete(mInventory, inProductIDs);
			return;
		}
		
		// default flags for new request
		mbCancelProductDescRequest = false;
		mbRequestDescriptionsInProgress = true;
			
		CSApplication.get().scheduleUIThreadTask(new Runnable()
		{
			@Override public void run() 
			{
				List<String> productIDs = new ArrayList<String>(Arrays.asList(inProductIDs));
				
				try 
				{
					Inventory itemsInventory = null;
					
					while(productIDs.size() > 0)
					{
						//Gets a sublist of max size|listsize
						ArrayList<String> idBatch = new ArrayList<String>(productIDs.subList(0, Math.min(k_maxProductIDsPerRequest, productIDs.size())));
						
						//Remove the sublist from the master list
						productIDs.removeAll(idBatch);
						
						//Make request with the sublist, this is a blocking call
						Inventory batchItems = mIABHelper.queryInventory(true, idBatch);
						
						if(batchItems != null)
						{
							if(itemsInventory != null)
							{
								//Merge the inventory contents
								itemsInventory = MergeInventories(itemsInventory, batchItems);
							}
							else 
							{
								itemsInventory = batchItems;
							}
						}
						else 
						{
							Logging.logFatal("Could not retrieve batch items - " + idBatch);
						}
					}
					
					mbRequestDescriptionsInProgress = false;
					
					if(mbCancelProductDescRequest)
						return;
					
					mInventory = itemsInventory;
					
					OnProductsRequestComplete(mInventory, inProductIDs);
				} 
				catch (IabException in_exception) 
				{
					Logging.logError("Cannot query Google IAB inventory: " + in_exception.getMessage());
					OnProductsRequestComplete(null, inProductIDs);
					return;
				}
			}
		});
    }
	//---------------------------------------------------------------
    /// Merges the contents of two Inventories
    ///
	/// @author HMcLaughlin
    /// @param Inventory to be merged
	/// @param Inventory to be merged
	/// @return Merged Inventory
    //---------------------------------------------------------------
	private Inventory MergeInventories(final Inventory in_lhs, final Inventory in_rhs)
	{
		Inventory mergedInventory = new Inventory();
		mergedInventory.mPurchaseMap.putAll(in_lhs.mPurchaseMap);
		mergedInventory.mPurchaseMap.putAll(in_rhs.mPurchaseMap);
		mergedInventory.mSkuMap.putAll(in_lhs.mSkuMap);
		mergedInventory.mSkuMap.putAll(in_rhs.mSkuMap);
		
		return mergedInventory;
	}
    //---------------------------------------------------------------
    /// On Products Request Complete
    ///
    /// Triggered when the products description request completes
{'fraction_non_alphanumeric': 0.2007447762223295, 'fraction_numerical': 0.0020688228398041512, 'mean_word_length': 2.1830553116769096, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3116543', 'n_tokens_mistral': 4180, 'n_tokens_neox': 3520, 'n_words': 1090}