[android] Added purchase controller abstraction

This commit is contained in:
Alexander Zatsepin 2018-09-05 16:04:10 +03:00 committed by yoksnod
parent 2a0aa3f97b
commit 36da010799
12 changed files with 256 additions and 65 deletions

View file

@ -72,8 +72,9 @@ import com.mapswithme.maps.maplayer.subway.SubwayManager;
import com.mapswithme.maps.maplayer.traffic.OnTrafficLayerToggleListener;
import com.mapswithme.maps.maplayer.traffic.TrafficManager;
import com.mapswithme.maps.maplayer.traffic.widget.TrafficButton;
import com.mapswithme.maps.purchase.BillingFactory;
import com.mapswithme.maps.purchase.BillingManager;
import com.mapswithme.maps.purchase.PurchaseFactory;
import com.mapswithme.maps.purchase.PurchaseController;
import com.mapswithme.maps.purchase.PurchaseControllerProvider;
import com.mapswithme.maps.routing.NavigationController;
import com.mapswithme.maps.routing.RoutePointInfo;
import com.mapswithme.maps.routing.RoutingBottomMenuListener;
@ -146,7 +147,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
FloatingSearchToolbarController.SearchToolbarListener,
OnTrafficLayerToggleListener,
OnSubwayLayerToggleListener,
BookmarkManager.BookmarksCatalogListener
BookmarkManager.BookmarksCatalogListener,
PurchaseControllerProvider
{
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
private static final String TAG = MwmActivity.class.getSimpleName();
@ -232,7 +234,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
private PlacePageTracker mPlacePageTracker;
@SuppressWarnings("NullableProblems")
@NonNull
private BillingManager mBillingManager;
private PurchaseController mPurchaseController;
@NonNull
private final OnClickListener mOnMyPositionClickListener = new CurrentPositionClickListener();
@ -551,7 +553,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
SharingHelper.INSTANCE.initialize();
mBillingManager = BillingFactory.ADS_REMOVAL.createBillingManager(this, "android.test.purchased");
mPurchaseController = PurchaseFactory.ADS_REMOVAL.createPurchaseController(this, "ads.removal.monthly.test");
//TODO: uncomment after correct visible rect calculation.
//mVisibleRectMeasurer = new VisibleRectMeasurer(new VisibleRectListener() {
@ -1346,7 +1348,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
protected void onStart()
{
super.onStart();
mBillingManager.initialize();
SearchEngine.INSTANCE.addListener(this);
Framework.nativeSetMapObjectListener(this);
BookmarkManager.INSTANCE.addLoadingListener(this);
@ -1358,13 +1359,13 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (mNavigationController != null)
TrafficManager.INSTANCE.attach(mNavigationController);
mPlacePage.onActivityStarted();
mPurchaseController.start(this);
}
@Override
protected void onStop()
{
super.onStop();
mBillingManager.destroy();
SearchEngine.INSTANCE.removeListener(this);
Framework.nativeRemoveMapObjectListener();
BookmarkManager.INSTANCE.removeLoadingListener(this);
@ -1374,6 +1375,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
TrafficManager.INSTANCE.detachAll();
mToggleMapLayerController.detachCore();
mPlacePage.onActivityStopped();
mPurchaseController.stop();
}
@Override
@ -1877,6 +1879,13 @@ public class MwmActivity extends BaseMwmFragmentActivity
});
}
@NonNull
@Override
public PurchaseController getPurchaseController()
{
return mPurchaseController;
}
private void adjustMenuLineFrameVisibility(@Nullable final Runnable completion)
{
final RoutingController controller = RoutingController.get();

View file

@ -27,8 +27,6 @@ import com.mapswithme.maps.location.LocationHelper;
import com.mapswithme.maps.location.TrackRecorder;
import com.mapswithme.maps.maplayer.subway.SubwayManager;
import com.mapswithme.maps.maplayer.traffic.TrafficManager;
import com.mapswithme.maps.purchase.BillingFactory;
import com.mapswithme.maps.purchase.PurchaseValidator;
import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.maps.scheduling.ConnectivityJobScheduler;
import com.mapswithme.maps.scheduling.ConnectivityListener;
@ -76,8 +74,6 @@ public class MwmApplication extends Application
@SuppressWarnings("NullableProblems")
@NonNull
private PushwooshHelper mPushwooshHelper;
@NonNull
private final PurchaseValidator mPurchaseValidator = BillingFactory.ADS_REMOVAL.createPurchaseManager();
private boolean mFrameworkInitialized;
private boolean mPlatformInitialized;
@ -285,7 +281,6 @@ public class MwmApplication extends Application
RoutingController.get().initialize();
TrafficManager.INSTANCE.initialize();
SubwayManager.from(this).initialize();
mPurchaseValidator.initialize();
mFrameworkInitialized = true;
}
@ -425,12 +420,6 @@ public class MwmApplication extends Application
MyTracker.initTracker();
}
@NonNull
public PurchaseValidator getPurchaseValidator()
{
return mPurchaseValidator;
}
public static void onUpgrade()
{
Counters.resetAppSessionCounters();

View file

@ -0,0 +1,69 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
abstract class AbstractPurchaseController<V, B> implements PurchaseController
{
@NonNull
private final PurchaseValidator<V> mValidator;
@NonNull
private final BillingManager<B> mBillingManager;
AbstractPurchaseController(@NonNull PurchaseValidator<V> validator,
@NonNull BillingManager<B> billingManager)
{
mValidator = validator;
mBillingManager = billingManager;
}
@Override
public final void start(@NonNull Activity activity)
{
mValidator.initialize();
mBillingManager.initialize(activity);
onStart(activity);
}
@Override
public final void stop()
{
mValidator.destroy();
mBillingManager.destroy();
onStop();
}
@Override
public boolean isPurchaseDone()
{
return mValidator.hasActivePurchase();
}
@Override
public boolean isPurchaseSupported()
{
return mBillingManager.isBillingSupported();
}
@Override
public void launchPurchaseFlow(@NonNull String productId)
{
mBillingManager.launchBillingFlowForProduct(productId);
}
@NonNull
PurchaseValidator<V> getValidator()
{
return mValidator;
}
@NonNull
BillingManager<B> getBillingManager()
{
return mBillingManager;
}
abstract void onStart(@NonNull Activity activity);
abstract void onStop();
}

View file

@ -0,0 +1,39 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
class AdPurchaseController extends AbstractPurchaseController<AdValidationCallback, PlayStoreBillingCallback>
{
@NonNull
private final AdValidationCallback mValidationCallback;
AdPurchaseController(@NonNull PurchaseValidator<AdValidationCallback> validator,
@NonNull BillingManager<PlayStoreBillingCallback> billingManager)
{
super(validator, billingManager);
mValidationCallback = new StubAdValidationCallback();
}
@Override
void onStart(@NonNull Activity activity)
{
getValidator().addCallback(mValidationCallback);
}
@Override
void onStop()
{
getValidator().removeCallback(mValidationCallback);
}
private static class StubAdValidationCallback implements AdValidationCallback
{
@Override
public void onValidate(@NonNull AdValidationStatus status)
{
// Do nothing by default.
}
}
}

View file

@ -13,6 +13,12 @@ class AdSubscriptionValidator implements PurchaseValidator<AdValidationCallback>
Framework.nativeSetSubscriptionValidationListener(this);
}
@Override
public void destroy()
{
Framework.nativeSetSubscriptionValidationListener(null);
}
@Override
public void validate(@NonNull String purchaseToken)
{
@ -20,9 +26,9 @@ class AdSubscriptionValidator implements PurchaseValidator<AdValidationCallback>
}
@Override
public void hasActivePurchase()
public boolean hasActivePurchase()
{
Framework.nativeHasActiveSubscription();
return Framework.nativeHasActiveSubscription();
}
@Override

View file

@ -1,34 +0,0 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
import com.android.billingclient.api.BillingClient;
public enum BillingFactory
{
ADS_REMOVAL
{
@NonNull
@Override
public PurchaseValidator createPurchaseManager()
{
return new AdSubscriptionValidator();
}
@NonNull
@Override
public BillingManager createBillingManager(@NonNull Activity activity,
@NonNull String... productIds)
{
return new PlayStoreBillingManager(activity, BillingClient.SkuType.SUBS, productIds);
}
};
@NonNull
public abstract PurchaseValidator createPurchaseManager();
@NonNull
public abstract BillingManager createBillingManager(@NonNull Activity activity,
@NonNull String... productIds);
}

View file

@ -1,5 +1,6 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
@ -12,7 +13,7 @@ public interface BillingManager<T>
/**
* Initializes the current billing manager.
*/
void initialize();
void initialize(@NonNull Activity context);
/**
* Destroys the billing manager.

View file

@ -25,8 +25,8 @@ public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingC
{
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 Activity mActivity;
@Nullable
private BillingClient mBillingClient;
@Nullable
@ -42,19 +42,18 @@ public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingC
@NonNull
private final List<Runnable> mPendingRequests = new ArrayList<>();
PlayStoreBillingManager(@NonNull Activity activity,
@NonNull @BillingClient.SkuType String productType,
PlayStoreBillingManager(@NonNull @BillingClient.SkuType String productType,
@NonNull String... productIds)
{
mActivity = activity;
mProductIds = Collections.unmodifiableList(Arrays.asList(productIds));
mProductType = productType;
}
@Override
public void initialize()
public void initialize(@NonNull Activity context)
{
LOGGER.i(TAG, "Creating play store billing client...");
mActivity = context;
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
mConnection = new PlayStoreBillingConnection(mBillingClient, this);
mConnection.open();
@ -63,6 +62,7 @@ public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingC
@Override
public void destroy()
{
mActivity = null;
mConnection.close();
mPendingRequests.clear();
}
@ -153,6 +153,15 @@ public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingC
return mBillingClient;
}
@NonNull
private Activity getActivityOrThrow()
{
if (mActivity == null)
throw new IllegalStateException("Manager must be initialized! Call 'initialize' method first.");
return mActivity;
}
@Override
public void onConnected()
{
@ -214,7 +223,7 @@ public class PlayStoreBillingManager implements BillingManager<PlayStoreBillingC
.setType(mProductType)
.build();
int responseCode = getClientOrThrow().launchBillingFlow(mActivity, params);
int responseCode = getClientOrThrow().launchBillingFlow(getActivityOrThrow(), params);
LOGGER.i(TAG, "Launch billing flow response: " + responseCode);
}
}

View file

@ -0,0 +1,42 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
/**
* Provides necessary purchase functionality to the UI. Controls whole platform-specific billing
* process. This controller has to be used only within {@link #start(Activity)} and {@link #stop()}
* interval.
*/
public interface PurchaseController
{
/**
* Starts the controller.
* @param activity the activity which controller serves.
*/
void start(@NonNull Activity activity);
/**
* Stops the controller.
*/
void stop();
/**
* Indicates whether the purchase already done or not.
*/
boolean isPurchaseDone();
/**
* Indicates whether the purchase flow is supported by this device or not.
*/
boolean isPurchaseSupported();
/**
* Launches the purchase flow for the specified product.
*
* @param id of the product which is going to be purchased.
*/
void launchPurchaseFlow(@NonNull String productId);
}

View file

@ -0,0 +1,9 @@
package com.mapswithme.maps.purchase;
import android.support.annotation.NonNull;
public interface PurchaseControllerProvider
{
@NonNull
public PurchaseController getPurchaseController();
}

View file

@ -0,0 +1,47 @@
package com.mapswithme.maps.purchase;
import android.app.Activity;
import android.support.annotation.NonNull;
import com.android.billingclient.api.BillingClient;
public enum PurchaseFactory
{
ADS_REMOVAL
{
@NonNull
@Override
public PurchaseValidator<AdValidationCallback> createPurchaseValidator()
{
return new AdSubscriptionValidator();
}
@NonNull
@Override
public BillingManager<PlayStoreBillingCallback> createBillingManager
(@NonNull Activity activity, @NonNull String... productIds)
{
return new PlayStoreBillingManager(BillingClient.SkuType.SUBS, productIds);
}
@Override
public PurchaseController createPurchaseController(@NonNull Activity activity,
@NonNull String... productIds)
{
BillingManager<PlayStoreBillingCallback> billingManager
= createBillingManager(activity, productIds);
PurchaseValidator<AdValidationCallback> validator = createPurchaseValidator();
return new AdPurchaseController(validator, billingManager);
}
};
@NonNull
public abstract PurchaseValidator createPurchaseValidator();
@NonNull
public abstract BillingManager createBillingManager(@NonNull Activity activity,
@NonNull String... productIds);
public abstract PurchaseController createPurchaseController(@NonNull Activity activity,
@NonNull String... productIds);
}

View file

@ -9,13 +9,18 @@ import android.support.annotation.NonNull;
* <b>one-to-one</b>. If you need to validate different purchases you have to create different
* implementations of this interface.
*/
public interface PurchaseValidator<T>
interface PurchaseValidator<T>
{
/**
* Initializes validator for further work.
* Initializes the validator for further work.
*/
void initialize();
/**
* Destroys this validator.
*/
void destroy();
/**
* Validates the purchase with specified token.
*
@ -26,7 +31,7 @@ public interface PurchaseValidator<T>
/**
* Indicates whether the app has active purchase or not.
*/
void hasActivePurchase();
boolean hasActivePurchase();
/**
* Ads observer of validation.