/*
* Copyright 2015. Appsi Mobile
*
* 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.appsimobile.appsii.iab;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Singleton;
/**
* The normal implementation of the feature manager.
* <p/>
* Created by nick on 04/02/15.
*/
@Singleton
public class FeatureManagerImpl
implements FeatureManager, BaseIabHelper.OnIabSetupFinishedListener {
/**
* The list of all skus present in the play store
*/
static final List<String> sAllSkus;
static {
ArrayList<String> result = new ArrayList<>();
result.add(AGENDA_FEATURE);
result.add(PEOPLE_FEATURE);
result.add(SETTINGS_FEATURE);
result.add(CALLS_FEATURE);
result.add(SMS_FEATURE);
result.add(SETTINGS_AGENDA_FEATURE);
result.add(SMS_CALLS_PEOPLE_FEATURE);
result.add(ALL_FEATURE);
sAllSkus = Collections.unmodifiableList(result);
}
/**
* The context we can use to connect to the iab-helper
*/
final Context mContext;
final ArrayList<FeatureManagerListener> mFeatureManagerListeners = new ArrayList<>(4);
/**
* The inventory that was loaded
*/
Inventory mInventory;
/**
* The result from connecting to the iab-helper
*/
IabResult mIabResult;
/**
* The inventory helper that is in use.
* When non-null, it is initializing in case mIabResult is null.
* If mIabResult is not noll, initialization finished. Check the
* status with mIabResult.isSuccessful()
*/
IabInventoryHelper mInventoryHelper;
/**
* The loader used to load the sku-details and the purchased status
*/
AsyncTask<Void, Void, Inventory> mLoadFeaturesTask;
FeatureManagerImpl(Context context) {
mContext = context;
}
@Nullable
public Inventory getInventory() {
return mInventory;
}
@Override
public void onIabSetupFinished(IabResult result) {
mIabResult = result;
if (result.isSuccess()) {
loadInventory();
} else {
mInventoryHelper.dispose();
mInventoryHelper = null;
notifyIabSetupFailed();
}
}
public boolean areFeaturesLoaded() {
return mInventory != null;
}
/**
* Loads the inventory. This depends on a few things. If there is not yet a
* result, but the helper is initialized, this means we are already loading
* the inventory.
* In case there is a result, but it is invalid, we return and do nothing
* <p/>
* In case there is no helper yet, it is created and it's setup is started
*/
private void loadInventory() {
// when the inventory-helper is not yet connected,
// but connecting just wait until that callback is received.
// this will auto-load the purchases anyway
if (mIabResult == null && mInventoryHelper != null) return;
// if the previous attempt failed, it is up to the client to
// restart this process again.
// The inventory-helper is only nullified if the load failed
if (mIabResult != null && !mIabResult.isSuccess()) {
mIabResult = null;
return;
}
// if the inventoryHelper is not yet initialized, start
// connecting to the iab-service. When is is successfully
// connected, it will call this method again. In that
// case, mIabResult is not null, and the task to query
// the purchases will be started.
if (mInventoryHelper == null) {
mInventoryHelper = new IabInventoryHelper(mContext);
mInventoryHelper.startSetup(this);
return;
}
// In an existing loader exists, cancel it.
if (mLoadFeaturesTask != null) {
mLoadFeaturesTask.cancel(true);
}
// Create a new loader and start it.
mLoadFeaturesTask = new AsyncTask<Void, Void, Inventory>() {
@Override
protected Inventory doInBackground(Void... params) {
try {
return getInventoryFromHelper();
} catch (IabException | RuntimeException e) {
Log.wtf("FeatureManager", "error loading inventory", e);
return null;
}
}
@Override
protected void onPostExecute(Inventory inventory) {
onInventoryLoaded(inventory);
}
};
// Do not execute on the main executor because in case of slow
// network connections this may block the other short running
// tasks as well; such as querying the database
mLoadFeaturesTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void notifyIabSetupFailed() {
int size = mFeatureManagerListeners.size();
for (int i = size - 1; i >= 0; i--) {
FeatureManagerListener listener = mFeatureManagerListeners.get(i);
listener.onIabSetupFailed();
}
}
public boolean areFeaturesLoading() {
return mLoadFeaturesTask != null;
}
/**
* Performs the actual query on the inventory-helper. This is executed on
* a background thread.
*/
protected Inventory getInventoryFromHelper() throws IabException {
return mInventoryHelper.queryInventory(true, sAllSkus);
}
void onInventoryLoaded(Inventory inventory) {
mInventory = inventory;
mLoadFeaturesTask = null;
mInventoryHelper.dispose();
mInventoryHelper = null;
notifyInventoryReady();
}
/**
* Triggers a load. Returns true if a load was started
*/
public boolean load(boolean force) {
if (force) {
loadInventory();
return true;
}
if (!areFeaturesLoaded() && !areFeaturesLoading()) {
loadInventory();
return true;
}
return false;
}
private void notifyInventoryReady() {
int size = mFeatureManagerListeners.size();
for (int i = size - 1; i >= 0; i--) {
FeatureManagerListener listener = mFeatureManagerListeners.get(i);
listener.onInventoryReady();
}
}
public SkuDetails getSkuDetailForSku(String sku) {
if (mInventory == null) return null;
return mInventory.getSkuDetails(sku);
}
public Purchase getPurchaseForSku(String sku) {
if (mInventory == null) return null;
return mInventory.getPurchase(sku);
}
@Override
public void registerFeatureManagerListener(FeatureManagerListener listener) {
mFeatureManagerListeners.add(listener);
}
@Override
public void unregisterFeatureManagerListener(FeatureManagerListener listener) {
mFeatureManagerListeners.remove(listener);
}
}