forked from organicmaps/organicmaps
[android] Implement handling of billing connection state and executing asyn request in the play store manager
This commit is contained in:
parent
a78bfca626
commit
9a937e0498
7 changed files with 264 additions and 18 deletions
|
@ -0,0 +1,28 @@
|
|||
package com.mapswithme.maps.purchase;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Represents a billing connection abstraction.
|
||||
*/
|
||||
public interface BillingConnection
|
||||
{
|
||||
/**
|
||||
* Connects to the billing manager.
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* @return the connection state of the billing manager.
|
||||
*/
|
||||
@NonNull
|
||||
State getState();
|
||||
|
||||
enum State
|
||||
{
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
CLOSED
|
||||
}
|
||||
}
|
|
@ -4,20 +4,43 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Manages a billing flow.
|
||||
* Manages a billing flow for the specific product.
|
||||
*/
|
||||
interface BillingManager
|
||||
public interface BillingManager<T>
|
||||
{
|
||||
/**
|
||||
* Launches the billing flow. Controls whole native part of billing.
|
||||
* @param productId id of product which is going to be bought by user.
|
||||
* Initializes the current billing manager.
|
||||
*/
|
||||
void launchBillingFlow(@NonNull String productId);
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Destroys the billing manager.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Launches the billing flow. Controls whole native part of billing.
|
||||
*/
|
||||
void launchBillingFlow();
|
||||
|
||||
/**
|
||||
* Obtains a purchase token if it exists.
|
||||
* @param productId id of product which token is needed for.
|
||||
*/
|
||||
@Nullable
|
||||
String obtainTokenForProduct(@NonNull String productId);
|
||||
String obtainPurchaseToken();
|
||||
|
||||
/**
|
||||
* Queries product details. They will be delivered to the caller through callback {@link T}.
|
||||
*/
|
||||
void queryProductDetails();
|
||||
|
||||
/**
|
||||
* Adds a billing callback.
|
||||
*/
|
||||
void addCallback(@NonNull T callback);
|
||||
|
||||
/**
|
||||
* Removes the billing callback.
|
||||
*/
|
||||
void removeCallback(@NonNull T callback);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.mapswithme.maps.purchase;
|
|||
import android.app.Activity;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
|
||||
public enum Factory
|
||||
{
|
||||
ADS_REMOVAL
|
||||
|
@ -16,9 +18,9 @@ public enum Factory
|
|||
|
||||
@NonNull
|
||||
@Override
|
||||
BillingManager createBillingManager(@NonNull Activity activity)
|
||||
public BillingManager createBillingManager(@NonNull Activity activity, @NonNull String productId)
|
||||
{
|
||||
return new PlayStoreBillingManager(activity);
|
||||
return new PlayStoreBillingManager(activity, productId, BillingClient.SkuType.SUBS);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,5 +28,5 @@ public enum Factory
|
|||
public abstract PurchaseValidator createPurchaseManager();
|
||||
|
||||
@NonNull
|
||||
abstract BillingManager createBillingManager(@NonNull Activity activity);
|
||||
public abstract BillingManager createBillingManager(@NonNull Activity activity, @NonNull String productId);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.mapswithme.maps.purchase;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PlayStoreBillingCallback
|
||||
{
|
||||
void onPurchaseDetailsLoaded(@NonNull List<PurchaseDetails> details);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.mapswithme.maps.purchase;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.android.billingclient.api.BillingClientStateListener;
|
||||
import com.mapswithme.util.log.Logger;
|
||||
import com.mapswithme.util.log.LoggerFactory;
|
||||
|
||||
class PlayStoreBillingConnection implements BillingConnection,
|
||||
BillingClientStateListener
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
|
||||
private static final String TAG = PlayStoreBillingConnection.class.getSimpleName();
|
||||
@NonNull
|
||||
private final BillingClient mBillingClient;
|
||||
@NonNull
|
||||
private State mState = State.DISCONNECTED;
|
||||
@Nullable
|
||||
private final ConnectionListener mListener;
|
||||
|
||||
PlayStoreBillingConnection(@NonNull BillingClient billingClient,
|
||||
@Nullable ConnectionListener listener)
|
||||
{
|
||||
mBillingClient = billingClient;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect()
|
||||
{
|
||||
mState = State.CONNECTING;
|
||||
mBillingClient.startConnection(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBillingSetupFinished(int responseCode)
|
||||
{
|
||||
LOGGER.i(TAG, "Setup finished. Response code: " + responseCode);
|
||||
if (responseCode == BillingClient.BillingResponse.OK)
|
||||
{
|
||||
mState = State.CONNECTED;
|
||||
if (mListener != null)
|
||||
mListener.onConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
mState = State.DISCONNECTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBillingServiceDisconnected()
|
||||
{
|
||||
mState = State.DISCONNECTED;
|
||||
}
|
||||
|
||||
interface ConnectionListener
|
||||
{
|
||||
void onConnected();
|
||||
}
|
||||
}
|
|
@ -8,31 +8,91 @@ import com.android.billingclient.api.BillingClient;
|
|||
import com.android.billingclient.api.BillingClient.BillingResponse;
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||
import com.android.billingclient.api.SkuDetails;
|
||||
import com.android.billingclient.api.SkuDetailsParams;
|
||||
import com.mapswithme.util.log.Logger;
|
||||
import com.mapswithme.util.log.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayStoreBillingManager implements BillingManager, PurchasesUpdatedListener
|
||||
public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingCallback>,
|
||||
PurchasesUpdatedListener,
|
||||
PlayStoreBillingConnection.ConnectionListener
|
||||
{
|
||||
private final static Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
|
||||
private final static String TAG = PlayStoreBillingManager.class.getSimpleName();
|
||||
@NonNull
|
||||
private final Activity mActivity;
|
||||
@Nullable
|
||||
private BillingClient mBillingClient;
|
||||
@Nullable
|
||||
private PlayStoreBillingCallback mCallback;
|
||||
@NonNull
|
||||
private final BillingClient mBillingClient;
|
||||
|
||||
PlayStoreBillingManager(@NonNull Activity activity)
|
||||
private final String mProductId;
|
||||
@NonNull
|
||||
@BillingClient.SkuType
|
||||
private final String mProductType;
|
||||
@SuppressWarnings({ "NullableProblems" })
|
||||
@NonNull
|
||||
private BillingConnection mConnection;
|
||||
@NonNull
|
||||
private final List<Runnable> mPendingRequests = new ArrayList<>();
|
||||
|
||||
PlayStoreBillingManager(@NonNull Activity activity, @NonNull String productId,
|
||||
@NonNull @BillingClient.SkuType String productType)
|
||||
{
|
||||
mActivity = activity;
|
||||
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
|
||||
mProductId = productId;
|
||||
mProductType = productType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchBillingFlow(@NonNull String productId)
|
||||
public void initialize()
|
||||
{
|
||||
LOGGER.i(TAG, "Creating play store billing client...");
|
||||
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
|
||||
mConnection = new PlayStoreBillingConnection(mBillingClient, this);
|
||||
mConnection.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
// Coming soon.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryProductDetails()
|
||||
{
|
||||
executeServiceRequest(new QueryProductDetailsRequest());
|
||||
}
|
||||
|
||||
private void executeServiceRequest(@NonNull Runnable task)
|
||||
{
|
||||
switch (mConnection.getState())
|
||||
{
|
||||
case CONNECTING:
|
||||
mPendingRequests.add(task);
|
||||
break;
|
||||
case CONNECTED:
|
||||
task.run();
|
||||
break;
|
||||
case DISCONNECTED:
|
||||
mConnection.connect();
|
||||
break;
|
||||
case CLOSED:
|
||||
throw new IllegalStateException("Billing service connection already closed, " +
|
||||
"please initialize it again.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchBillingFlow()
|
||||
{
|
||||
@BillingResponse
|
||||
int result = mBillingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
|
||||
int result = getClientOrThrow().isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
|
||||
if (result == BillingResponse.FEATURE_NOT_SUPPORTED)
|
||||
{
|
||||
LOGGER.w(TAG, "Subscription is not supported by this device!");
|
||||
|
@ -44,14 +104,63 @@ public class PlayStoreBillingManager implements BillingManager, PurchasesUpdated
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public String obtainTokenForProduct(@NonNull String productId)
|
||||
public String obtainPurchaseToken()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(@NonNull PlayStoreBillingCallback callback)
|
||||
{
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(@NonNull PlayStoreBillingCallback callback)
|
||||
{
|
||||
mCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases)
|
||||
{
|
||||
// Coming soon.
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private BillingClient getClientOrThrow()
|
||||
{
|
||||
if (mBillingClient == null)
|
||||
throw new IllegalStateException("Manager must be initialized! Call 'initialize' method first.");
|
||||
|
||||
return mBillingClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected()
|
||||
{
|
||||
for(@NonNull Runnable request: mPendingRequests)
|
||||
request.run();
|
||||
|
||||
mPendingRequests.clear();
|
||||
}
|
||||
|
||||
private class QueryProductDetailsRequest implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
SkuDetailsParams.Builder builder = SkuDetailsParams.newBuilder();
|
||||
builder.setSkusList(Collections.singletonList(mProductId))
|
||||
.setType(mProductType);
|
||||
getClientOrThrow().querySkuDetailsAsync(builder.build(), this::onSkuDetailsResponseInternal);
|
||||
}
|
||||
|
||||
private void onSkuDetailsResponseInternal(@BillingResponse int responseCode,
|
||||
@Nullable List<SkuDetails> skuDetails)
|
||||
{
|
||||
LOGGER.i(TAG, "Product details response, code: " + responseCode + ", " +
|
||||
"details: " + skuDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package com.mapswithme.maps.purchase;
|
||||
|
||||
public class PurchaseDetails
|
||||
{
|
||||
}
|
Loading…
Add table
Reference in a new issue