Huawei inapps/subs introduced
This commit is contained in:
parent
9a5e3c438a
commit
b7bbe55dad
15 changed files with 1987 additions and 796 deletions
|
@ -13,6 +13,8 @@ import com.android.billingclient.api.SkuDetailsResponseListener;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
|
||||||
|
import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription;
|
||||||
import net.osmand.plus.inapp.util.BillingManager;
|
import net.osmand.plus.inapp.util.BillingManager;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -49,13 +51,21 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
|
|
||||||
public InAppPurchaseHelperImpl(OsmandApplication ctx) {
|
public InAppPurchaseHelperImpl(OsmandApplication ctx) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
|
purchases = new InAppPurchasesImpl(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onSuccess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BillingManager getBillingManager() {
|
private BillingManager getBillingManager() {
|
||||||
return billingManager;
|
return billingManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppRunnable runnable) {
|
protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand runnable) {
|
||||||
billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() {
|
billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,7 +87,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processingTask = !runnable.run(InAppPurchaseHelperImpl.this);
|
runnable.run(InAppPurchaseHelperImpl.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,7 +124,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> skuSubscriptions = new ArrayList<>();
|
List<String> skuSubscriptions = new ArrayList<>();
|
||||||
for (InAppPurchases.InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) {
|
for (InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) {
|
||||||
skuSubscriptions.add(subscription.getSku());
|
skuSubscriptions.add(subscription.getSku());
|
||||||
}
|
}
|
||||||
for (Purchase p : purchases) {
|
for (Purchase p : purchases) {
|
||||||
|
@ -165,9 +175,9 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
|
|
||||||
public void purchaseFullVersion(final Activity activity) {
|
public void purchaseFullVersion(final Activity activity) {
|
||||||
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
|
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
|
||||||
exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, new InAppRunnable() {
|
exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, new InAppCommand() {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
public void run(InAppPurchaseHelper helper) {
|
||||||
try {
|
try {
|
||||||
SkuDetails skuDetails = getSkuDetails(getFullVersion().getSku());
|
SkuDetails skuDetails = getSkuDetails(getFullVersion().getSku());
|
||||||
if (skuDetails == null) {
|
if (skuDetails == null) {
|
||||||
|
@ -179,22 +189,21 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
throw new IllegalStateException("BillingManager disposed");
|
||||||
}
|
}
|
||||||
return false;
|
commandDone();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
complain("Cannot launch full version purchase!");
|
complain("Cannot launch full version purchase!");
|
||||||
logError("purchaseFullVersion Error", e);
|
logError("purchaseFullVersion Error", e);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void purchaseDepthContours(final Activity activity) {
|
public void purchaseDepthContours(final Activity activity) {
|
||||||
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
|
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
|
||||||
exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, new InAppRunnable() {
|
exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, new InAppCommand() {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
public void run(InAppPurchaseHelper helper) {
|
||||||
try {
|
try {
|
||||||
SkuDetails skuDetails = getSkuDetails(getDepthContours().getSku());
|
SkuDetails skuDetails = getSkuDetails(getDepthContours().getSku());
|
||||||
if (skuDetails == null) {
|
if (skuDetails == null) {
|
||||||
|
@ -206,13 +215,12 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
throw new IllegalStateException("BillingManager disposed");
|
||||||
}
|
}
|
||||||
return false;
|
commandDone();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
complain("Cannot launch depth contours purchase!");
|
complain("Cannot launch depth contours purchase!");
|
||||||
logError("purchaseDepthContours Error", e);
|
logError("purchaseDepthContours Error", e);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -295,7 +303,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
List<String> allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus();
|
List<String> allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus();
|
||||||
for (InAppPurchases.InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
|
for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
|
||||||
if (hasDetails(s.getSku())) {
|
if (hasDetails(s.getSku())) {
|
||||||
Purchase purchase = getPurchase(s.getSku());
|
Purchase purchase = getPurchase(s.getSku());
|
||||||
SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku());
|
SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku());
|
||||||
|
@ -309,9 +317,9 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
Purchase purchase = getPurchase(sku);
|
Purchase purchase = getPurchase(sku);
|
||||||
SkuDetails liveUpdatesDetails = getSkuDetails(sku);
|
SkuDetails liveUpdatesDetails = getSkuDetails(sku);
|
||||||
if (liveUpdatesDetails != null) {
|
if (liveUpdatesDetails != null) {
|
||||||
InAppPurchases.InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
|
InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
s = new InAppPurchases.InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails);
|
s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails);
|
||||||
}
|
}
|
||||||
fetchInAppPurchase(s, liveUpdatesDetails, purchase);
|
fetchInAppPurchase(s, liveUpdatesDetails, purchase);
|
||||||
}
|
}
|
||||||
|
@ -441,21 +449,21 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
|
String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
|
||||||
if (!Algorithms.isEmpty(subscriptionPeriod)) {
|
if (!Algorithms.isEmpty(subscriptionPeriod)) {
|
||||||
if (inAppPurchase instanceof InAppPurchases.InAppSubscription) {
|
if (inAppPurchase instanceof InAppSubscription) {
|
||||||
try {
|
try {
|
||||||
((InAppPurchases.InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
|
((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
LOG.error(e);
|
LOG.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (inAppPurchase instanceof InAppPurchases.InAppSubscription) {
|
if (inAppPurchase instanceof InAppSubscription) {
|
||||||
String introductoryPrice = skuDetails.getIntroductoryPrice();
|
String introductoryPrice = skuDetails.getIntroductoryPrice();
|
||||||
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
|
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
|
||||||
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
|
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
|
||||||
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
|
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
|
||||||
if (!Algorithms.isEmpty(introductoryPrice)) {
|
if (!Algorithms.isEmpty(introductoryPrice)) {
|
||||||
InAppPurchases.InAppSubscription s = (InAppPurchases.InAppSubscription) inAppPurchase;
|
InAppSubscription s = (InAppSubscription) inAppPurchase;
|
||||||
try {
|
try {
|
||||||
s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
|
s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
|
||||||
introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
|
introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
|
||||||
|
@ -466,10 +474,10 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InAppRunnable getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
|
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
|
||||||
return new InAppRunnable() {
|
return new InAppCommand() {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
public void run(InAppPurchaseHelper helper) {
|
||||||
try {
|
try {
|
||||||
Activity a = activity.get();
|
Activity a = activity.get();
|
||||||
SkuDetails skuDetails = getSkuDetails(sku);
|
SkuDetails skuDetails = getSkuDetails(sku);
|
||||||
|
@ -481,7 +489,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
throw new IllegalStateException("BillingManager disposed");
|
||||||
}
|
}
|
||||||
return false;
|
commandDone();
|
||||||
} else {
|
} else {
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
|
@ -489,15 +497,14 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
logError("launchPurchaseFlow Error", e);
|
logError("launchPurchaseFlow Error", e);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InAppRunnable getRequestInventoryCommand() {
|
protected InAppCommand getRequestInventoryCommand() {
|
||||||
return new InAppRunnable() {
|
return new InAppCommand() {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
public void run(InAppPurchaseHelper helper) {
|
||||||
logDebug("Setup successful. Querying inventory.");
|
logDebug("Setup successful. Querying inventory.");
|
||||||
try {
|
try {
|
||||||
BillingManager billingManager = getBillingManager();
|
BillingManager billingManager = getBillingManager();
|
||||||
|
@ -506,13 +513,12 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
throw new IllegalStateException("BillingManager disposed");
|
||||||
}
|
}
|
||||||
return false;
|
commandDone();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logError("queryInventoryAsync Error", e);
|
logError("queryInventoryAsync Error", e);
|
||||||
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
323
OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java
Normal file
323
OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
package net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.billingclient.api.SkuDetails;
|
||||||
|
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.Version;
|
||||||
|
|
||||||
|
public class InAppPurchasesImpl extends InAppPurchases {
|
||||||
|
|
||||||
|
private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
|
||||||
|
private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull();
|
||||||
|
private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
|
||||||
|
private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull();
|
||||||
|
private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
|
||||||
|
|
||||||
|
private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{
|
||||||
|
new InAppPurchaseLiveUpdatesOldMonthlyFull(),
|
||||||
|
new InAppPurchaseLiveUpdatesMonthlyFull(),
|
||||||
|
new InAppPurchaseLiveUpdates3MonthsFull(),
|
||||||
|
new InAppPurchaseLiveUpdatesAnnualFull()
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
|
||||||
|
new InAppPurchaseLiveUpdatesOldMonthlyFree(),
|
||||||
|
new InAppPurchaseLiveUpdatesMonthlyFree(),
|
||||||
|
new InAppPurchaseLiveUpdates3MonthsFree(),
|
||||||
|
new InAppPurchaseLiveUpdatesAnnualFree()
|
||||||
|
};
|
||||||
|
|
||||||
|
public InAppPurchasesImpl(OsmandApplication ctx) {
|
||||||
|
super(ctx);
|
||||||
|
fullVersion = FULL_VERSION;
|
||||||
|
if (Version.isFreeVersion(ctx)) {
|
||||||
|
liveUpdates = new LiveUpdatesInAppPurchasesFree();
|
||||||
|
} else {
|
||||||
|
liveUpdates = new LiveUpdatesInAppPurchasesFull();
|
||||||
|
}
|
||||||
|
for (InAppSubscription s : liveUpdates.getAllSubscriptions()) {
|
||||||
|
if (s instanceof InAppPurchaseLiveUpdatesMonthly) {
|
||||||
|
if (s.isDiscounted()) {
|
||||||
|
discountedMonthlyLiveUpdates = s;
|
||||||
|
} else {
|
||||||
|
monthlyLiveUpdates = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Version.isFreeVersion(ctx)) {
|
||||||
|
depthContours = DEPTH_CONTOURS_FREE;
|
||||||
|
} else {
|
||||||
|
depthContours = DEPTH_CONTOURS_FULL;
|
||||||
|
}
|
||||||
|
if (Version.isFreeVersion(ctx)) {
|
||||||
|
contourLines = CONTOUR_LINES_FREE;
|
||||||
|
} else {
|
||||||
|
contourLines = CONTOUR_LINES_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFullVersion(String sku) {
|
||||||
|
return FULL_VERSION.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDepthContours(String sku) {
|
||||||
|
return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContourLines(String sku) {
|
||||||
|
return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLiveUpdates(String sku) {
|
||||||
|
for (InAppPurchase p : LIVE_UPDATES_FULL) {
|
||||||
|
if (p.getSku().equals(sku)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (InAppPurchase p : LIVE_UPDATES_FREE) {
|
||||||
|
if (p.getSku().equals(sku)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseFullVersion extends InAppPurchase {
|
||||||
|
|
||||||
|
private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
|
||||||
|
|
||||||
|
InAppPurchaseFullVersion() {
|
||||||
|
super(SKU_FULL_VERSION_PRICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultPrice(Context ctx) {
|
||||||
|
return ctx.getString(R.string.full_version_price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours {
|
||||||
|
|
||||||
|
private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus";
|
||||||
|
|
||||||
|
InAppPurchaseDepthContoursFull() {
|
||||||
|
super(SKU_DEPTH_CONTOURS_FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
|
||||||
|
|
||||||
|
private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
|
||||||
|
|
||||||
|
InAppPurchaseDepthContoursFree() {
|
||||||
|
super(SKU_DEPTH_CONTOURS_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines {
|
||||||
|
|
||||||
|
private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus";
|
||||||
|
|
||||||
|
InAppPurchaseContourLinesFull() {
|
||||||
|
super(SKU_CONTOUR_LINES_FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
|
||||||
|
|
||||||
|
private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
|
||||||
|
|
||||||
|
InAppPurchaseContourLinesFree() {
|
||||||
|
super(SKU_CONTOUR_LINES_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesMonthlyFull() {
|
||||||
|
super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesMonthlyFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdates3MonthsFull() {
|
||||||
|
super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdates3MonthsFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesAnnualFull() {
|
||||||
|
super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesAnnualFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesOldMonthlyFull() {
|
||||||
|
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesOldMonthlyFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
|
||||||
|
|
||||||
|
private SkuDetails details;
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) {
|
||||||
|
super(details.getSku(), true);
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultPrice(Context ctx) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getTitle(Context ctx) {
|
||||||
|
return details.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getDescription(@NonNull Context ctx) {
|
||||||
|
return details.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
|
||||||
|
|
||||||
|
public LiveUpdatesInAppPurchasesFree() {
|
||||||
|
super(LIVE_UPDATES_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList {
|
||||||
|
|
||||||
|
public LiveUpdatesInAppPurchasesFull() {
|
||||||
|
super(LIVE_UPDATES_FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java
Executable file
96
OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java
Executable file
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature related tools.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public class CipherUtil {
|
||||||
|
private static final String TAG = "CipherUtil";
|
||||||
|
private static final String SIGN_ALGORITHMS = "SHA256WithRSA";
|
||||||
|
private static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAooen3X9jSWarxugznzzMSvp4zir1Pg6uPOm7fqlLOL0Ix52e5FpeotMx871pQ9hrCkiyFg2e6UxD8IXXjvK6QJQbjNJ2jIfKkCusm90yloSEfvyLeiq5y7zg4+DoPglHi8RxZ9y308YIqnRDoslfGm5DnWa8RKUvFRVRiu1p3FN4SYIa/FWLtS5yygemtqMJi8I14V7xqQ5wExCGeSA6j1/AAWXEwZncJwKn0BTXQSvwVBPBRM5ksgt4q+Sc484ZIbntATyxsUipnEBFxq1OXn5Zw5/vVxUC8RSyDMQ/kC2RaEcFtA1tlIIjIdurbpNg3tyViPfQUQndvOs4nDrFzwIDAQAB";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the method to check the signature for the data returned from the interface
|
||||||
|
* @param content Unsigned data
|
||||||
|
* @param sign the signature for content
|
||||||
|
* @param publicKey the public of the application
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean doCheck(String content, String sign, String publicKey) {
|
||||||
|
if (TextUtils.isEmpty(publicKey)) {
|
||||||
|
Log.e(TAG, "publicKey is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(content) || TextUtils.isEmpty(sign)) {
|
||||||
|
Log.e(TAG, "data is error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT);
|
||||||
|
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
|
||||||
|
|
||||||
|
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
|
||||||
|
|
||||||
|
signature.initVerify(pubKey);
|
||||||
|
signature.update(content.getBytes("utf-8"));
|
||||||
|
|
||||||
|
boolean bverify = signature.verify(Base64.decode(sign, Base64.DEFAULT));
|
||||||
|
return bverify;
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e(TAG, "doCheck NoSuchAlgorithmException" + e);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
Log.e(TAG, "doCheck InvalidKeySpecException" + e);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
Log.e(TAG, "doCheck InvalidKeyException" + e);
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
Log.e(TAG, "doCheck SignatureException" + e);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Log.e(TAG, "doCheck UnsupportedEncodingException" + e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the publicKey of the application
|
||||||
|
* During the encoding process, avoid storing the public key in clear text.
|
||||||
|
* @return publickey
|
||||||
|
*/
|
||||||
|
public static String getPublicKey(){
|
||||||
|
return PUBLIC_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java
Executable file
33
OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants Class.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public class Constants {
|
||||||
|
|
||||||
|
/** requestCode for pull up the pmsPay page */
|
||||||
|
public static final int REQ_CODE_BUY_SUB = 4002;
|
||||||
|
public static final int REQ_CODE_BUY_INAPP = 4003;
|
||||||
|
|
||||||
|
/** requestCode for pull up the login page for isEnvReady interface */
|
||||||
|
public static final int REQ_CODE_LOGIN = 2001;
|
||||||
|
|
||||||
|
}
|
103
OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java
Executable file
103
OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java
Executable file
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.huawei.hms.iap.IapApiException;
|
||||||
|
import com.huawei.hms.iap.entity.OrderStatusCode;
|
||||||
|
|
||||||
|
import net.osmand.AndroidUtils;
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the exception returned from the iap api.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public class ExceptionHandle {
|
||||||
|
|
||||||
|
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ExceptionHandle.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exception is solved.
|
||||||
|
*/
|
||||||
|
public static final int SOLVED = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the exception returned from the iap api.
|
||||||
|
* @param activity The Activity to call the iap api.
|
||||||
|
* @param e The exception returned from the iap api.
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static int handle(@Nullable Activity activity, Exception e) {
|
||||||
|
|
||||||
|
if (e instanceof IapApiException) {
|
||||||
|
IapApiException iapApiException = (IapApiException) e;
|
||||||
|
LOG.info("returnCode: " + iapApiException.getStatusCode());
|
||||||
|
switch (iapApiException.getStatusCode()) {
|
||||||
|
case OrderStatusCode.ORDER_STATE_CANCEL:
|
||||||
|
showToast(activity, "Order has been canceled!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_STATE_PARAM_ERROR:
|
||||||
|
showToast(activity, "Order state param error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_STATE_NET_ERROR:
|
||||||
|
showToast(activity, "Order state net error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_VR_UNINSTALL_ERROR:
|
||||||
|
showToast(activity, "Order vr uninstall error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_HWID_NOT_LOGIN:
|
||||||
|
IapRequestHelper.startResolutionForResult(activity, iapApiException.getStatus(), Constants.REQ_CODE_LOGIN);
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_PRODUCT_OWNED:
|
||||||
|
showToast(activity, "Product already owned error!");
|
||||||
|
return OrderStatusCode.ORDER_PRODUCT_OWNED;
|
||||||
|
case OrderStatusCode.ORDER_PRODUCT_NOT_OWNED:
|
||||||
|
showToast(activity, "Product not owned error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_PRODUCT_CONSUMED:
|
||||||
|
showToast(activity, "Product consumed error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED:
|
||||||
|
showToast(activity, "Order account area not supported error!");
|
||||||
|
return SOLVED;
|
||||||
|
case OrderStatusCode.ORDER_NOT_ACCEPT_AGREEMENT:
|
||||||
|
showToast(activity, "User does not agree the agreement");
|
||||||
|
return SOLVED;
|
||||||
|
default:
|
||||||
|
// handle other error scenarios
|
||||||
|
showToast(activity, "Order unknown error!");
|
||||||
|
return SOLVED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(activity, "External error");
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
return SOLVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void showToast(@Nullable Activity activity, String s) {
|
||||||
|
if (AndroidUtils.isActivityNotDestroyed(activity)) {
|
||||||
|
Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java
Executable file
37
OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to callback the result from iap api.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public interface IapApiCallback<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request is successful.
|
||||||
|
* @param result The result of a successful response.
|
||||||
|
*/
|
||||||
|
void onSuccess(T result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback fail.
|
||||||
|
* @param e An Exception from IAPSDK.
|
||||||
|
*/
|
||||||
|
void onFail(Exception e);
|
||||||
|
}
|
351
OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java
Executable file
351
OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java
Executable file
|
@ -0,0 +1,351 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.huawei.hmf.tasks.OnFailureListener;
|
||||||
|
import com.huawei.hmf.tasks.OnSuccessListener;
|
||||||
|
import com.huawei.hmf.tasks.Task;
|
||||||
|
import com.huawei.hms.iap.Iap;
|
||||||
|
import com.huawei.hms.iap.IapApiException;
|
||||||
|
import com.huawei.hms.iap.IapClient;
|
||||||
|
import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseReq;
|
||||||
|
import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseResult;
|
||||||
|
import com.huawei.hms.iap.entity.IsEnvReadyResult;
|
||||||
|
import com.huawei.hms.iap.entity.OwnedPurchasesReq;
|
||||||
|
import com.huawei.hms.iap.entity.OwnedPurchasesResult;
|
||||||
|
import com.huawei.hms.iap.entity.ProductInfoReq;
|
||||||
|
import com.huawei.hms.iap.entity.ProductInfoResult;
|
||||||
|
import com.huawei.hms.iap.entity.PurchaseIntentReq;
|
||||||
|
import com.huawei.hms.iap.entity.PurchaseIntentResult;
|
||||||
|
import com.huawei.hms.iap.entity.StartIapActivityReq;
|
||||||
|
import com.huawei.hms.iap.entity.StartIapActivityResult;
|
||||||
|
import com.huawei.hms.support.api.client.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tool class of Iap interface.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public class IapRequestHelper {
|
||||||
|
private final static String TAG = "IapRequestHelper";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PurchaseIntentReq object.
|
||||||
|
* @param type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param productId ID of the in-app product to be paid.
|
||||||
|
* The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect.
|
||||||
|
* @return PurchaseIntentReq
|
||||||
|
*/
|
||||||
|
private static PurchaseIntentReq createPurchaseIntentReq(int type, String productId) {
|
||||||
|
PurchaseIntentReq req = new PurchaseIntentReq();
|
||||||
|
req.setPriceType(type);
|
||||||
|
req.setProductId(productId);
|
||||||
|
req.setDeveloperPayload("testPurchase");
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ConsumeOwnedPurchaseReq object.
|
||||||
|
* @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
|
||||||
|
* The app transfers this parameter for the Huawei payment server to update the order status and then deliver the in-app product.
|
||||||
|
* @return ConsumeOwnedPurchaseReq
|
||||||
|
*/
|
||||||
|
private static ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseToken) {
|
||||||
|
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
|
||||||
|
req.setPurchaseToken(purchaseToken);
|
||||||
|
req.setDeveloperChallenge("testConsume");
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a OwnedPurchasesReq object.
|
||||||
|
* @param type type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param continuationToken A data location flag which returns from obtainOwnedPurchases api or obtainOwnedPurchaseRecord api.
|
||||||
|
* @return OwnedPurchasesReq
|
||||||
|
*/
|
||||||
|
private static OwnedPurchasesReq createOwnedPurchasesReq(int type, String continuationToken) {
|
||||||
|
OwnedPurchasesReq req = new OwnedPurchasesReq();
|
||||||
|
req.setPriceType(type);
|
||||||
|
req.setContinuationToken(continuationToken);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ProductInfoReq object.
|
||||||
|
* @param type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app.
|
||||||
|
* @return ProductInfoReq
|
||||||
|
*/
|
||||||
|
private static ProductInfoReq createProductInfoReq(int type, List<String> productIds) {
|
||||||
|
ProductInfoReq req = new ProductInfoReq();
|
||||||
|
req.setPriceType(type);
|
||||||
|
req.setProductIds(productIds);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To check whether the country or region of the logged in HUAWEI ID is included in the countries or regions supported by HUAWEI IAP.
|
||||||
|
* @param mClient IapClient instance to call the isEnvReady API.
|
||||||
|
* @param callback IapApiCallback.
|
||||||
|
*/
|
||||||
|
public static void isEnvReady(IapClient mClient, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call isEnvReady");
|
||||||
|
Task<IsEnvReadyResult> task = mClient.isEnvReady();
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<IsEnvReadyResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(IsEnvReadyResult result) {
|
||||||
|
Log.i(TAG, "isEnvReady, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "isEnvReady, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain in-app product details configured in AppGallery Connect.
|
||||||
|
* @param iapClient IapClient instance to call the obtainProductInfo API.
|
||||||
|
* @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app.
|
||||||
|
* @param type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param callback IapApiCallback
|
||||||
|
*/
|
||||||
|
public static void obtainProductInfo(IapClient iapClient, final List<String> productIds, int type, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call obtainProductInfo");
|
||||||
|
|
||||||
|
Task<ProductInfoResult> task = iapClient.obtainProductInfo(createProductInfoReq(type, productIds));
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<ProductInfoResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ProductInfoResult result) {
|
||||||
|
Log.i(TAG, "obtainProductInfo, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "obtainProductInfo, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create orders for in-app products in the PMS
|
||||||
|
* @param iapClient IapClient instance to call the createPurchaseIntent API.
|
||||||
|
* @param productId ID of the in-app product to be paid.
|
||||||
|
* The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect.
|
||||||
|
* @param type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param callback IapApiCallback
|
||||||
|
*/
|
||||||
|
public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call createPurchaseIntent");
|
||||||
|
Task<PurchaseIntentResult> task = iapClient.createPurchaseIntent(createPurchaseIntentReq(type, productId));
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(PurchaseIntentResult result) {
|
||||||
|
Log.i(TAG, "createPurchaseIntent, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "createPurchaseIntent, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, String payload, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call createPurchaseIntent");
|
||||||
|
PurchaseIntentReq req = createPurchaseIntentReq(type, productId);
|
||||||
|
req.setDeveloperPayload(payload);
|
||||||
|
Task<PurchaseIntentResult> task = iapClient.createPurchaseIntent(req);
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(PurchaseIntentResult result) {
|
||||||
|
Log.i(TAG, "createPurchaseIntent, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "createPurchaseIntent, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* to start an activity.
|
||||||
|
* @param activity the activity to launch a new page.
|
||||||
|
* @param status This parameter contains the pendingIntent object of the payment page.
|
||||||
|
* @param reqCode Result code.
|
||||||
|
*/
|
||||||
|
public static void startResolutionForResult(Activity activity, Status status, int reqCode) {
|
||||||
|
if (status == null) {
|
||||||
|
Log.e(TAG, "status is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status.hasResolution()) {
|
||||||
|
try {
|
||||||
|
status.startResolutionForResult(activity, reqCode);
|
||||||
|
} catch (IntentSender.SendIntentException exp) {
|
||||||
|
Log.e(TAG, exp.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "intent is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query information about all subscribed in-app products, including consumables, non-consumables, and auto-renewable subscriptions.</br>
|
||||||
|
* If consumables are returned, the system needs to deliver them and calls the consumeOwnedPurchase API to consume the products.
|
||||||
|
* If non-consumables are returned, the in-app products do not need to be consumed.
|
||||||
|
* If subscriptions are returned, all existing subscription relationships of the user under the app are returned.
|
||||||
|
* @param mClient IapClient instance to call the obtainOwnedPurchases API.
|
||||||
|
* @param type In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
|
||||||
|
* @param callback IapApiCallback
|
||||||
|
*/
|
||||||
|
public static void obtainOwnedPurchases(IapClient mClient, final int type, String continuationToken, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call obtainOwnedPurchases");
|
||||||
|
Task<OwnedPurchasesResult> task = mClient.obtainOwnedPurchases(IapRequestHelper.createOwnedPurchasesReq(type, continuationToken));
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(OwnedPurchasesResult result) {
|
||||||
|
Log.i(TAG, "obtainOwnedPurchases, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "obtainOwnedPurchases, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* obtain the historical consumption information about a consumable in-app product or all subscription receipts of a subscription.
|
||||||
|
* @param iapClient IapClient instance to call the obtainOwnedPurchaseRecord API.
|
||||||
|
* @param priceType In-app product type.
|
||||||
|
* The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription.
|
||||||
|
* @param continuationToken Data locating flag for supporting query in pagination mode.
|
||||||
|
* @param callback IapApiCallback
|
||||||
|
*/
|
||||||
|
public static void obtainOwnedPurchaseRecord(IapClient iapClient, int priceType, String continuationToken, final IapApiCallback callback) {
|
||||||
|
Log.i(TAG, "call obtainOwnedPurchaseRecord");
|
||||||
|
Task<OwnedPurchasesResult> task = iapClient.obtainOwnedPurchaseRecord(createOwnedPurchasesReq(priceType, continuationToken));
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(OwnedPurchasesResult result) {
|
||||||
|
Log.i(TAG, "obtainOwnedPurchaseRecord, success");
|
||||||
|
callback.onSuccess(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
Log.e(TAG, "obtainOwnedPurchaseRecord, fail");
|
||||||
|
callback.onFail(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume all the unconsumed purchases with priceType 0.
|
||||||
|
* @param iapClient IapClient instance to call the consumeOwnedPurchase API.
|
||||||
|
* @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
|
||||||
|
*/
|
||||||
|
public static void consumeOwnedPurchase(IapClient iapClient, String purchaseToken) {
|
||||||
|
Log.i(TAG, "call consumeOwnedPurchase");
|
||||||
|
Task<ConsumeOwnedPurchaseResult> task = iapClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(purchaseToken));
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ConsumeOwnedPurchaseResult result) {
|
||||||
|
// Consume success.
|
||||||
|
Log.i(TAG, "consumeOwnedPurchase success");
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
if (e instanceof IapApiException) {
|
||||||
|
IapApiException apiException = (IapApiException)e;
|
||||||
|
int returnCode = apiException.getStatusCode();
|
||||||
|
Log.e(TAG, "consumeOwnedPurchase fail, IapApiException returnCode: " + returnCode);
|
||||||
|
} else {
|
||||||
|
// Other external errors
|
||||||
|
Log.e(TAG, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* link to subscription manager page
|
||||||
|
* @param activity activity
|
||||||
|
* @param productId the productId of the subscription product
|
||||||
|
*/
|
||||||
|
public static void showSubscription(final Activity activity, String productId) {
|
||||||
|
StartIapActivityReq req = new StartIapActivityReq();
|
||||||
|
if (TextUtils.isEmpty(productId)) {
|
||||||
|
req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_MANAGER_ACTIVITY);
|
||||||
|
} else {
|
||||||
|
req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_EDIT_ACTIVITY);
|
||||||
|
req.setSubscribeProductId(productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
IapClient iapClient = Iap.getIapClient(activity);
|
||||||
|
Task<StartIapActivityResult> task = iapClient.startIapActivity(req);
|
||||||
|
|
||||||
|
task.addOnSuccessListener(new OnSuccessListener<StartIapActivityResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(StartIapActivityResult result) {
|
||||||
|
if(result != null) {
|
||||||
|
result.startActivity(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(new OnFailureListener() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
ExceptionHandle.handle(activity, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,285 +1,484 @@
|
||||||
package net.osmand.plus.inapp;
|
package net.osmand.plus.inapp;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.huawei.hms.iap.Iap;
|
||||||
|
import com.huawei.hms.iap.IapClient;
|
||||||
|
import com.huawei.hms.iap.entity.InAppPurchaseData;
|
||||||
|
import com.huawei.hms.iap.entity.IsEnvReadyResult;
|
||||||
|
import com.huawei.hms.iap.entity.OrderStatusCode;
|
||||||
|
import com.huawei.hms.iap.entity.OwnedPurchasesResult;
|
||||||
|
import com.huawei.hms.iap.entity.ProductInfo;
|
||||||
|
import com.huawei.hms.iap.entity.ProductInfoResult;
|
||||||
|
import com.huawei.hms.iap.entity.PurchaseIntentResult;
|
||||||
|
import com.huawei.hms.iap.entity.PurchaseResultInfo;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.inapp.util.BillingManager;
|
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
|
||||||
|
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
|
||||||
|
import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
|
import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
|
|
||||||
// The helper object
|
private boolean envReady = false;
|
||||||
private BillingManager billingManager;
|
private boolean purchaseSupported = false;
|
||||||
private List<SkuDetails> skuDetailsList;
|
|
||||||
|
|
||||||
|
private List<ProductInfo> productInfos;
|
||||||
|
private OwnedPurchasesResult ownedSubscriptions;
|
||||||
|
private List<OwnedPurchasesResult> ownedInApps = new ArrayList<>();
|
||||||
|
|
||||||
public InAppPurchaseHelperImpl(OsmandApplication ctx) {
|
public InAppPurchaseHelperImpl(OsmandApplication ctx) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
|
purchases = new InAppPurchasesImpl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppRunnable runnable) {
|
|
||||||
billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBillingClientSetupFinished() {
|
public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) {
|
||||||
|
if (envReady) {
|
||||||
|
if (callback != null) {
|
||||||
|
if (purchaseSupported) {
|
||||||
|
callback.onSuccess();
|
||||||
|
} else {
|
||||||
|
callback.onFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Initiating an isEnvReady request when entering the app.
|
||||||
|
// Check if the account service country supports IAP.
|
||||||
|
IapClient mClient = Iap.getIapClient(activity);
|
||||||
|
final WeakReference<Activity> activityRef = new WeakReference<>(activity);
|
||||||
|
IapRequestHelper.isEnvReady(mClient, new IapApiCallback<IsEnvReadyResult>() {
|
||||||
|
|
||||||
|
private void onReady(boolean succeed) {
|
||||||
logDebug("Setup finished.");
|
logDebug("Setup finished.");
|
||||||
|
envReady = true;
|
||||||
BillingManager billingManager = getBillingManager();
|
purchaseSupported = succeed;
|
||||||
// Have we been disposed of in the meantime? If so, quit.
|
if (callback != null) {
|
||||||
if (billingManager == null) {
|
if (succeed) {
|
||||||
stop(true);
|
callback.onSuccess();
|
||||||
return;
|
} else {
|
||||||
|
callback.onFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!billingManager.isServiceConnected()) {
|
|
||||||
// Oh noes, there was a problem.
|
|
||||||
//complain("Problem setting up in-app billing: " + result);
|
|
||||||
notifyError(taskType, billingManager.getBillingClientResponseMessage());
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processingTask = !runnable.run(InAppPurchaseHelperImpl.this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConsumeFinished(String token, BillingResult billingResult) {
|
public void onSuccess(IsEnvReadyResult result) {
|
||||||
|
onReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPurchasesUpdated(final List<Purchase> purchases) {
|
public void onFail(Exception e) {
|
||||||
|
onReady(false);
|
||||||
BillingManager billingManager = getBillingManager();
|
LOG.error("isEnvReady fail, " + e.getMessage(), e);
|
||||||
// Have we been disposed of in the meantime? If so, quit.
|
ExceptionHandle.handle(activityRef.get(), e);
|
||||||
if (billingManager == null) {
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTask == InAppPurchaseTaskType.REQUEST_INVENTORY) {
|
|
||||||
List<String> skuInApps = new ArrayList<>();
|
|
||||||
for (InAppPurchases.InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) {
|
|
||||||
skuInApps.add(purchase.getSku());
|
|
||||||
}
|
|
||||||
for (Purchase p : purchases) {
|
|
||||||
skuInApps.add(p.getSku());
|
|
||||||
}
|
|
||||||
billingManager.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuInApps, new SkuDetailsResponseListener() {
|
|
||||||
@Override
|
|
||||||
public void onSkuDetailsResponse(BillingResult billingResult, final List<SkuDetails> skuDetailsListInApps) {
|
|
||||||
// Is it a failure?
|
|
||||||
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
|
|
||||||
logError("Failed to query inapps sku details: " + billingResult.getResponseCode());
|
|
||||||
notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> skuSubscriptions = new ArrayList<>();
|
|
||||||
for (InAppPurchases.InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) {
|
|
||||||
skuSubscriptions.add(subscription.getSku());
|
|
||||||
}
|
|
||||||
for (Purchase p : purchases) {
|
|
||||||
skuSubscriptions.add(p.getSku());
|
|
||||||
}
|
|
||||||
|
|
||||||
BillingManager billingManager = getBillingManager();
|
|
||||||
// Have we been disposed of in the meantime? If so, quit.
|
|
||||||
if (billingManager == null) {
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
billingManager.querySkuDetailsAsync(BillingClient.SkuType.SUBS, skuSubscriptions, new SkuDetailsResponseListener() {
|
|
||||||
@Override
|
|
||||||
public void onSkuDetailsResponse(BillingResult billingResult, final List<SkuDetails> skuDetailsListSubscriptions) {
|
|
||||||
// Is it a failure?
|
|
||||||
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
|
|
||||||
logError("Failed to query subscriptipons sku details: " + billingResult.getResponseCode());
|
|
||||||
notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SkuDetails> skuDetailsList = new ArrayList<>(skuDetailsListInApps);
|
|
||||||
skuDetailsList.addAll(skuDetailsListSubscriptions);
|
|
||||||
InAppPurchaseHelperImpl.this.skuDetailsList = skuDetailsList;
|
|
||||||
|
|
||||||
mSkuDetailsResponseListener.onSkuDetailsResponse(billingResult, skuDetailsList);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
for (Purchase purchase : purchases) {
|
|
||||||
if (!purchase.isAcknowledged()) {
|
|
||||||
onPurchaseFinished(purchase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand command) {
|
||||||
|
if (envReady) {
|
||||||
|
command.run(this);
|
||||||
|
} else {
|
||||||
|
command.commandDone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InAppCommand getPurchaseInAppCommand(@NonNull final Activity activity, @NonNull final String productId) throws UnsupportedOperationException {
|
||||||
|
return new InAppCommand() {
|
||||||
@Override
|
@Override
|
||||||
public void onPurchaseCanceled() {
|
public void run(InAppPurchaseHelper helper) {
|
||||||
|
try {
|
||||||
|
ProductInfo productInfo = getProductInfo(productId);
|
||||||
|
IapRequestHelper.createPurchaseIntent(getIapClient(), productInfo.getProductId(),
|
||||||
|
IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback<PurchaseIntentResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(PurchaseIntentResult result) {
|
||||||
|
if (result == null) {
|
||||||
|
logError("result is null");
|
||||||
|
} else {
|
||||||
|
// you should pull up the page to complete the payment process
|
||||||
|
IapRequestHelper.startResolutionForResult(activity, result.getStatus(), Constants.REQ_CODE_BUY_INAPP);
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail(Exception e) {
|
||||||
|
int errorCode = ExceptionHandle.handle(activity, e);
|
||||||
|
if (errorCode != ExceptionHandle.SOLVED) {
|
||||||
|
logDebug("createPurchaseIntent, returnCode: " + errorCode);
|
||||||
|
if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) {
|
||||||
|
logError("already own this product");
|
||||||
|
} else {
|
||||||
|
logError("unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
complain("Cannot launch full version purchase!");
|
||||||
|
logError("purchaseFullVersion Error", e);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void purchaseFullVersion(Activity activity) throws UnsupportedOperationException {
|
public void purchaseFullVersion(@NonNull final Activity activity) throws UnsupportedOperationException {
|
||||||
throw new UnsupportedOperationException();
|
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
|
||||||
|
exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, getPurchaseInAppCommand(activity, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void purchaseDepthContours(Activity activity) throws UnsupportedOperationException {
|
public void purchaseDepthContours(@NonNull final Activity activity) throws UnsupportedOperationException {
|
||||||
throw new UnsupportedOperationException();
|
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
|
||||||
|
exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, getPurchaseInAppCommand(activity, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private SkuDetails getSkuDetails(@NonNull String sku) {
|
private ProductInfo getProductInfo(@NonNull String productId) {
|
||||||
List<SkuDetails> skuDetailsList = this.skuDetailsList;
|
List<ProductInfo> productInfos = this.productInfos;
|
||||||
if (skuDetailsList != null) {
|
if (productInfos != null) {
|
||||||
for (SkuDetails details : skuDetailsList) {
|
for (ProductInfo info : productInfos) {
|
||||||
if (details.getSku().equals(sku)) {
|
if (info.getProductId().equals(productId)) {
|
||||||
return details;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasDetails(@NonNull String sku) {
|
private boolean hasDetails(@NonNull String productId) {
|
||||||
return getSkuDetails(sku) != null;
|
return getProductInfo(productId) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Purchase getPurchase(@NonNull String sku) {
|
private InAppPurchaseData getPurchaseData(@NonNull String productId) {
|
||||||
BillingManager billingManager = getBillingManager();
|
InAppPurchaseData data = SubscriptionUtils.getPurchaseData(ownedSubscriptions, productId);
|
||||||
if (billingManager != null) {
|
if (data == null) {
|
||||||
List<Purchase> purchases = billingManager.getPurchases();
|
for (OwnedPurchasesResult result : ownedInApps) {
|
||||||
if (purchases != null) {
|
data = InAppUtils.getPurchaseData(result, productId);
|
||||||
for (Purchase p : purchases) {
|
if (data != null) {
|
||||||
if (p.getSku().equals(sku)) {
|
break;
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return data;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listener that's called when we finish querying the items and subscriptions we own
|
private PurchaseInfo getPurchaseInfo(InAppPurchaseData purchase) {
|
||||||
private SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() {
|
return new PurchaseInfo(purchase.getProductId(), purchase.getOrderID(), purchase.getPurchaseToken());
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull ProductInfo productInfo, @Nullable InAppPurchaseData purchaseData) {
|
||||||
private List<String> getAllOwnedSubscriptionSkus() {
|
if (purchaseData != null) {
|
||||||
List<String> result = new ArrayList<>();
|
inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED);
|
||||||
BillingManager billingManager = getBillingManager();
|
inAppPurchase.setPurchaseTime(purchaseData.getPurchaseTime());
|
||||||
if (billingManager != null) {
|
} else {
|
||||||
for (Purchase p : billingManager.getPurchases()) {
|
inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED);
|
||||||
if (getInAppPurchases().getInAppSubscriptionBySku(p.getSku()) != null) {
|
}
|
||||||
result.add(p.getSku());
|
inAppPurchase.setPrice(productInfo.getPrice());
|
||||||
|
inAppPurchase.setPriceCurrencyCode(productInfo.getCurrency());
|
||||||
|
if (productInfo.getMicrosPrice() > 0) {
|
||||||
|
inAppPurchase.setPriceValue(productInfo.getMicrosPrice() / 1000000d);
|
||||||
|
}
|
||||||
|
String subscriptionPeriod = productInfo.getSubPeriod();
|
||||||
|
if (!Algorithms.isEmpty(subscriptionPeriod)) {
|
||||||
|
if (inAppPurchase instanceof InAppSubscription) {
|
||||||
|
try {
|
||||||
|
((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
LOG.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
if (inAppPurchase instanceof InAppSubscription) {
|
||||||
|
String introductoryPrice = productInfo.getSubSpecialPrice();
|
||||||
|
String introductoryPricePeriod = productInfo.getSubSpecialPeriod();
|
||||||
|
int introductoryPriceCycles = productInfo.getSubSpecialPeriodCycles();
|
||||||
|
long introductoryPriceAmountMicros = productInfo.getSubSpecialPriceMicros();
|
||||||
|
if (!Algorithms.isEmpty(introductoryPrice)) {
|
||||||
|
InAppSubscription s = (InAppSubscription) inAppPurchase;
|
||||||
|
try {
|
||||||
|
s.setIntroductoryInfo(new InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
|
||||||
|
introductoryPriceAmountMicros, introductoryPricePeriod, String.valueOf(introductoryPriceCycles)));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
LOG.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
|
||||||
|
return new InAppCommand() {
|
||||||
|
@Override
|
||||||
|
public void run(InAppPurchaseHelper helper) {
|
||||||
|
try {
|
||||||
|
Activity a = activity.get();
|
||||||
|
ProductInfo productInfo = getProductInfo(sku);
|
||||||
|
if (AndroidUtils.isActivityNotDestroyed(a) && productInfo != null) {
|
||||||
|
IapRequestHelper.createPurchaseIntent(getIapClient(), sku,
|
||||||
|
IapClient.PriceType.IN_APP_SUBSCRIPTION, payload, new IapApiCallback<PurchaseIntentResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(PurchaseIntentResult result) {
|
||||||
|
if (result == null) {
|
||||||
|
logError("GetBuyIntentResult is null");
|
||||||
|
} else {
|
||||||
|
Activity a = activity.get();
|
||||||
|
if (AndroidUtils.isActivityNotDestroyed(a)) {
|
||||||
|
IapRequestHelper.startResolutionForResult(a, result.getStatus(), Constants.REQ_CODE_BUY_SUB);
|
||||||
|
} else {
|
||||||
|
logError("startResolutionForResult on destroyed activity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
|
public void onFail(Exception e) {
|
||||||
|
int errorCode = ExceptionHandle.handle(activity.get(), e);
|
||||||
logDebug("Query sku details finished.");
|
if (ExceptionHandle.SOLVED != errorCode) {
|
||||||
|
logError("createPurchaseIntent, returnCode: " + errorCode);
|
||||||
// Have we been disposed of in the meantime? If so, quit.
|
if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) {
|
||||||
if (getBillingManager() == null) {
|
logError("already own this product");
|
||||||
|
} else {
|
||||||
|
logError("unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
stop(true);
|
stop(true);
|
||||||
return;
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logError("launchPurchaseFlow Error", e);
|
||||||
|
stop(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it a failure?
|
@Override
|
||||||
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
|
protected InAppCommand getRequestInventoryCommand() {
|
||||||
logError("Failed to query inventory: " + billingResult.getResponseCode());
|
return new InAppCommand() {
|
||||||
notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
|
|
||||||
|
@Override
|
||||||
|
public void run(InAppPurchaseHelper helper) {
|
||||||
|
logDebug("Setup successful. Querying inventory.");
|
||||||
|
try {
|
||||||
|
productInfos = new ArrayList<>();
|
||||||
|
if (uiActivity != null) {
|
||||||
|
obtainOwnedSubscriptions();
|
||||||
|
} else {
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logError("queryInventoryAsync Error", e);
|
||||||
|
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
||||||
stop(true);
|
stop(true);
|
||||||
return;
|
commandDone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void obtainOwnedSubscriptions() {
|
||||||
|
if (uiActivity != null) {
|
||||||
|
IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_SUBSCRIPTION,
|
||||||
|
null, new IapApiCallback<OwnedPurchasesResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(OwnedPurchasesResult result) {
|
||||||
|
ownedSubscriptions = result;
|
||||||
|
obtainOwnedInApps(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail(Exception e) {
|
||||||
|
logError("obtainOwnedSubscriptions exception", e);
|
||||||
|
ExceptionHandle.handle((Activity) uiActivity, e);
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void obtainOwnedInApps(final String continuationToken) {
|
||||||
|
// Query users' purchased non-consumable products.
|
||||||
|
IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_NONCONSUMABLE,
|
||||||
|
continuationToken, new IapApiCallback<OwnedPurchasesResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(OwnedPurchasesResult result) {
|
||||||
|
ownedInApps.add(result);
|
||||||
|
if (result != null && !TextUtils.isEmpty(result.getContinuationToken())) {
|
||||||
|
obtainOwnedInApps(result.getContinuationToken());
|
||||||
|
} else {
|
||||||
|
obtainSubscriptionsInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail(Exception e) {
|
||||||
|
logError("obtainOwnedInApps exception", e);
|
||||||
|
ExceptionHandle.handle((Activity) uiActivity, e);
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void obtainSubscriptionsInfo() {
|
||||||
|
Set<String> productIds = new HashSet<>();
|
||||||
|
List<InAppSubscription> subscriptions = purchases.getLiveUpdates().getAllSubscriptions();
|
||||||
|
for (InAppSubscription s : subscriptions) {
|
||||||
|
productIds.add(s.getSku());
|
||||||
|
}
|
||||||
|
productIds.addAll(ownedSubscriptions.getItemList());
|
||||||
|
IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds),
|
||||||
|
IapClient.PriceType.IN_APP_SUBSCRIPTION, new IapApiCallback<ProductInfoResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ProductInfoResult result) {
|
||||||
|
if (result == null) {
|
||||||
|
logError("obtainSubscriptionsInfo: ProductInfoResult is null");
|
||||||
|
commandDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
productInfos.addAll(result.getProductInfoList());
|
||||||
|
obtainInAppsInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail(Exception e) {
|
||||||
|
int errorCode = ExceptionHandle.handle((Activity) uiActivity, e);
|
||||||
|
if (ExceptionHandle.SOLVED != errorCode) {
|
||||||
|
LOG.error("Unknown error");
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void obtainInAppsInfo() {
|
||||||
|
Set<String> productIds = new HashSet<>();
|
||||||
|
for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) {
|
||||||
|
productIds.add(purchase.getSku());
|
||||||
|
}
|
||||||
|
for (OwnedPurchasesResult result : ownedInApps) {
|
||||||
|
productIds.addAll(result.getItemList());
|
||||||
|
}
|
||||||
|
IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds),
|
||||||
|
IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback<ProductInfoResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ProductInfoResult result) {
|
||||||
|
if (result == null || result.getProductInfoList() == null) {
|
||||||
|
logError("obtainInAppsInfo: ProductInfoResult is null");
|
||||||
|
commandDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
productInfos.addAll(result.getProductInfoList());
|
||||||
|
|
||||||
|
processInventory();
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail(Exception e) {
|
||||||
|
int errorCode = ExceptionHandle.handle((Activity) uiActivity, e);
|
||||||
|
if (ExceptionHandle.SOLVED != errorCode) {
|
||||||
|
LOG.error("Unknown error");
|
||||||
|
}
|
||||||
|
commandDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processInventory() {
|
||||||
logDebug("Query sku details was successful.");
|
logDebug("Query sku details was successful.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for items we own. Notice that for each purchase, we check
|
* Check for items we own. Notice that for each purchase, we check
|
||||||
* the developer payload to see if it's correct! See
|
* the developer payload to see if it's correct!
|
||||||
* verifyDeveloperPayload().
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
List<String> allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus();
|
List<String> allOwnedSubscriptionSkus = ownedSubscriptions.getItemList();
|
||||||
for (InAppPurchases.InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
|
for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
|
||||||
if (hasDetails(s.getSku())) {
|
if (hasDetails(s.getSku())) {
|
||||||
Purchase purchase = getPurchase(s.getSku());
|
InAppPurchaseData purchaseData = getPurchaseData(s.getSku());
|
||||||
SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku());
|
ProductInfo liveUpdatesInfo = getProductInfo(s.getSku());
|
||||||
if (liveUpdatesDetails != null) {
|
if (liveUpdatesInfo != null) {
|
||||||
fetchInAppPurchase(s, liveUpdatesDetails, purchase);
|
fetchInAppPurchase(s, liveUpdatesInfo, purchaseData);
|
||||||
}
|
}
|
||||||
allOwnedSubscriptionSkus.remove(s.getSku());
|
allOwnedSubscriptionSkus.remove(s.getSku());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (String sku : allOwnedSubscriptionSkus) {
|
for (String sku : allOwnedSubscriptionSkus) {
|
||||||
Purchase purchase = getPurchase(sku);
|
InAppPurchaseData purchaseData = getPurchaseData(sku);
|
||||||
SkuDetails liveUpdatesDetails = getSkuDetails(sku);
|
ProductInfo liveUpdatesInfo = getProductInfo(sku);
|
||||||
if (liveUpdatesDetails != null) {
|
if (liveUpdatesInfo != null) {
|
||||||
InAppPurchases.InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
|
InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
s = new InAppPurchases.InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails);
|
s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesInfo);
|
||||||
}
|
}
|
||||||
fetchInAppPurchase(s, liveUpdatesDetails, purchase);
|
fetchInAppPurchase(s, liveUpdatesInfo, purchaseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InAppPurchases.InAppPurchase fullVersion = getFullVersion();
|
InAppPurchase fullVersion = getFullVersion();
|
||||||
if (hasDetails(fullVersion.getSku())) {
|
if (hasDetails(fullVersion.getSku())) {
|
||||||
Purchase purchase = getPurchase(fullVersion.getSku());
|
InAppPurchaseData purchaseData = getPurchaseData(fullVersion.getSku());
|
||||||
SkuDetails fullPriceDetails = getSkuDetails(fullVersion.getSku());
|
ProductInfo fullPriceDetails = getProductInfo(fullVersion.getSku());
|
||||||
if (fullPriceDetails != null) {
|
if (fullPriceDetails != null) {
|
||||||
fetchInAppPurchase(fullVersion, fullPriceDetails, purchase);
|
fetchInAppPurchase(fullVersion, fullPriceDetails, purchaseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InAppPurchases.InAppPurchase depthContours = getDepthContours();
|
InAppPurchase depthContours = getDepthContours();
|
||||||
if (hasDetails(depthContours.getSku())) {
|
if (hasDetails(depthContours.getSku())) {
|
||||||
Purchase purchase = getPurchase(depthContours.getSku());
|
InAppPurchaseData purchaseData = getPurchaseData(depthContours.getSku());
|
||||||
SkuDetails depthContoursDetails = getSkuDetails(depthContours.getSku());
|
ProductInfo depthContoursDetails = getProductInfo(depthContours.getSku());
|
||||||
if (depthContoursDetails != null) {
|
if (depthContoursDetails != null) {
|
||||||
fetchInAppPurchase(depthContours, depthContoursDetails, purchase);
|
fetchInAppPurchase(depthContours, depthContoursDetails, purchaseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InAppPurchases.InAppPurchase contourLines = getContourLines();
|
InAppPurchase contourLines = getContourLines();
|
||||||
if (hasDetails(contourLines.getSku())) {
|
if (hasDetails(contourLines.getSku())) {
|
||||||
Purchase purchase = getPurchase(contourLines.getSku());
|
InAppPurchaseData purchaseData = getPurchaseData(contourLines.getSku());
|
||||||
SkuDetails contourLinesDetails = getSkuDetails(contourLines.getSku());
|
ProductInfo contourLinesDetails = getProductInfo(contourLines.getSku());
|
||||||
if (contourLinesDetails != null) {
|
if (contourLinesDetails != null) {
|
||||||
fetchInAppPurchase(contourLines, contourLinesDetails, purchase);
|
fetchInAppPurchase(contourLines, contourLinesDetails, purchaseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Purchase fullVersionPurchase = getPurchase(fullVersion.getSku());
|
InAppPurchaseData fullVersionPurchase = getPurchaseData(fullVersion.getSku());
|
||||||
boolean fullVersionPurchased = fullVersionPurchase != null;
|
boolean fullVersionPurchased = fullVersionPurchase != null;
|
||||||
if (fullVersionPurchased) {
|
if (fullVersionPurchased) {
|
||||||
ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
|
ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Purchase depthContoursPurchase = getPurchase(depthContours.getSku());
|
InAppPurchaseData depthContoursPurchase = getPurchaseData(depthContours.getSku());
|
||||||
boolean depthContoursPurchased = depthContoursPurchase != null;
|
boolean depthContoursPurchased = depthContoursPurchase != null;
|
||||||
if (depthContoursPurchased) {
|
if (depthContoursPurchased) {
|
||||||
ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true);
|
ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true);
|
||||||
|
@ -287,17 +486,17 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
|
|
||||||
// Do we have the live updates?
|
// Do we have the live updates?
|
||||||
boolean subscribedToLiveUpdates = false;
|
boolean subscribedToLiveUpdates = false;
|
||||||
List<Purchase> liveUpdatesPurchases = new ArrayList<>();
|
List<InAppPurchaseData> liveUpdatesPurchases = new ArrayList<>();
|
||||||
for (InAppPurchases.InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
|
for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
|
||||||
Purchase purchase = getPurchase(p.getSku());
|
InAppPurchaseData purchaseData = getPurchaseData(p.getSku());
|
||||||
if (purchase != null) {
|
if (purchaseData != null) {
|
||||||
liveUpdatesPurchases.add(purchase);
|
liveUpdatesPurchases.add(purchaseData);
|
||||||
if (!subscribedToLiveUpdates) {
|
if (!subscribedToLiveUpdates) {
|
||||||
subscribedToLiveUpdates = true;
|
subscribedToLiveUpdates = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OsmandSettings.OsmandPreference<Long> subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
|
OsmandPreference<Long> subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
|
||||||
if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
|
if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
|
||||||
if (subscriptionCancelledTime.get() == 0) {
|
if (subscriptionCancelledTime.get() == 0) {
|
||||||
subscriptionCancelledTime.set(System.currentTimeMillis());
|
subscriptionCancelledTime.set(System.currentTimeMillis());
|
||||||
|
@ -321,10 +520,10 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
OsmandSettings settings = ctx.getSettings();
|
OsmandSettings settings = ctx.getSettings();
|
||||||
settings.INAPPS_READ.set(true);
|
settings.INAPPS_READ.set(true);
|
||||||
|
|
||||||
List<Purchase> tokensToSend = new ArrayList<>();
|
List<InAppPurchaseData> tokensToSend = new ArrayList<>();
|
||||||
if (liveUpdatesPurchases.size() > 0) {
|
if (liveUpdatesPurchases.size() > 0) {
|
||||||
List<String> tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
|
List<String> tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
|
||||||
for (Purchase purchase : liveUpdatesPurchases) {
|
for (InAppPurchaseData purchase : liveUpdatesPurchases) {
|
||||||
if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
|
if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
|
||||||
&& !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
|
&& !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
|
||||||
String payload = purchase.getDeveloperPayload();
|
String payload = purchase.getDeveloperPayload();
|
||||||
|
@ -339,137 +538,101 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tokensSent.contains(purchase.getSku())) {
|
if (!tokensSent.contains(purchase.getProductId())) {
|
||||||
tokensToSend.add(purchase);
|
tokensToSend.add(purchase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<PurchaseInfo> purchaseInfoList = new ArrayList<>();
|
List<PurchaseInfo> purchaseInfoList = new ArrayList<>();
|
||||||
for (Purchase purchase : tokensToSend) {
|
for (InAppPurchaseData purchase : tokensToSend) {
|
||||||
purchaseInfoList.add(getPurchaseInfo(purchase));
|
purchaseInfoList.add(getPurchaseInfo(purchase));
|
||||||
}
|
}
|
||||||
onSkuDetailsResponseDone(purchaseInfoList);
|
onSkuDetailsResponseDone(purchaseInfoList);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private PurchaseInfo getPurchaseInfo(Purchase purchase) {
|
|
||||||
return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchInAppPurchase(@NonNull InAppPurchases.InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) {
|
private IapClient getIapClient() {
|
||||||
if (purchase != null) {
|
return Iap.getIapClient((Activity) uiActivity);
|
||||||
inAppPurchase.setPurchaseState(InAppPurchases.InAppPurchase.PurchaseState.PURCHASED);
|
|
||||||
inAppPurchase.setPurchaseTime(purchase.getPurchaseTime());
|
|
||||||
} else {
|
|
||||||
inAppPurchase.setPurchaseState(InAppPurchases.InAppPurchase.PurchaseState.NOT_PURCHASED);
|
|
||||||
}
|
|
||||||
inAppPurchase.setPrice(skuDetails.getPrice());
|
|
||||||
inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode());
|
|
||||||
if (skuDetails.getPriceAmountMicros() > 0) {
|
|
||||||
inAppPurchase.setPriceValue(skuDetails.getPriceAmountMicros() / 1000000d);
|
|
||||||
}
|
|
||||||
String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
|
|
||||||
if (!Algorithms.isEmpty(subscriptionPeriod)) {
|
|
||||||
if (inAppPurchase instanceof InAppPurchases.InAppSubscription) {
|
|
||||||
try {
|
|
||||||
((InAppPurchases.InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOG.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inAppPurchase instanceof InAppPurchases.InAppSubscription) {
|
|
||||||
String introductoryPrice = skuDetails.getIntroductoryPrice();
|
|
||||||
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
|
|
||||||
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
|
|
||||||
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
|
|
||||||
if (!Algorithms.isEmpty(introductoryPrice)) {
|
|
||||||
InAppPurchases.InAppSubscription s = (InAppPurchases.InAppSubscription) inAppPurchase;
|
|
||||||
try {
|
|
||||||
s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
|
|
||||||
introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOG.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InAppRunnable getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
|
|
||||||
return new InAppRunnable() {
|
|
||||||
@Override
|
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
|
||||||
try {
|
|
||||||
Activity a = activity.get();
|
|
||||||
SkuDetails skuDetails = getSkuDetails(sku);
|
|
||||||
if (AndroidUtils.isActivityNotDestroyed(a) && skuDetails != null) {
|
|
||||||
BillingManager billingManager = getBillingManager();
|
|
||||||
if (billingManager != null) {
|
|
||||||
billingManager.setPayload(payload);
|
|
||||||
billingManager.initiatePurchaseFlow(a, skuDetails);
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
stop(true);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logError("launchPurchaseFlow Error", e);
|
|
||||||
stop(true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InAppRunnable getRequestInventoryCommand() {
|
|
||||||
return new InAppRunnable() {
|
|
||||||
@Override
|
|
||||||
public boolean run(InAppPurchaseHelper helper) {
|
|
||||||
logDebug("Setup successful. Querying inventory.");
|
|
||||||
try {
|
|
||||||
BillingManager billingManager = getBillingManager();
|
|
||||||
if (billingManager != null) {
|
|
||||||
billingManager.queryPurchases();
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("BillingManager disposed");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logError("queryInventoryAsync Error", e);
|
|
||||||
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
|
|
||||||
stop(true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call when a purchase is finished
|
// Call when a purchase is finished
|
||||||
private void onPurchaseFinished(Purchase purchase) {
|
private void onPurchaseFinished(InAppPurchaseData purchase) {
|
||||||
logDebug("Purchase finished: " + purchase);
|
logDebug("Purchase finished: " + purchase.getProductId());
|
||||||
|
|
||||||
// if we were disposed of in the meantime, quit.
|
|
||||||
if (getBillingManager() == null) {
|
|
||||||
stop(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPurchaseDone(getPurchaseInfo(purchase));
|
onPurchaseDone(getPurchaseInfo(purchase));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isBillingManagerExists() {
|
protected boolean isBillingManagerExists() {
|
||||||
return getBillingManager() != null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void destroyBillingManager() {
|
protected void destroyBillingManager() {
|
||||||
BillingManager billingManager = getBillingManager();
|
// non implemented
|
||||||
if (billingManager != null) {
|
|
||||||
billingManager.destroy();
|
|
||||||
this.billingManager = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActivityResult(@NonNull Activity activity, int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == Constants.REQ_CODE_BUY_SUB) {
|
||||||
|
boolean succeed = false;
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
PurchaseResultInfo result = SubscriptionUtils.getPurchaseResult(activity, data);
|
||||||
|
if (result != null) {
|
||||||
|
if (OrderStatusCode.ORDER_STATE_SUCCESS == result.getReturnCode()) {
|
||||||
|
InAppPurchaseData purchaseData = SubscriptionUtils.getInAppPurchaseData(null,
|
||||||
|
result.getInAppPurchaseData(), result.getInAppDataSignature());
|
||||||
|
if (purchaseData != null) {
|
||||||
|
onPurchaseFinished(purchaseData);
|
||||||
|
succeed = true;
|
||||||
|
} else {
|
||||||
|
logDebug("Purchase failed");
|
||||||
|
}
|
||||||
|
} else if (OrderStatusCode.ORDER_STATE_CANCEL == result.getReturnCode()) {
|
||||||
|
logDebug("Purchase cancelled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logDebug("Purchase failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logDebug("Purchase cancelled");
|
||||||
|
}
|
||||||
|
if (!succeed) {
|
||||||
|
stop(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (requestCode == Constants.REQ_CODE_BUY_INAPP) {
|
||||||
|
boolean succeed = false;
|
||||||
|
if (data == null) {
|
||||||
|
logDebug("data is null");
|
||||||
|
} else {
|
||||||
|
PurchaseResultInfo buyResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data);
|
||||||
|
switch (buyResultInfo.getReturnCode()) {
|
||||||
|
case OrderStatusCode.ORDER_STATE_CANCEL:
|
||||||
|
logDebug("Order has been canceled");
|
||||||
|
break;
|
||||||
|
case OrderStatusCode.ORDER_PRODUCT_OWNED:
|
||||||
|
logDebug("Product already owned");
|
||||||
|
break;
|
||||||
|
case OrderStatusCode.ORDER_STATE_SUCCESS:
|
||||||
|
InAppPurchaseData purchaseData = InAppUtils.getInAppPurchaseData(null,
|
||||||
|
buyResultInfo.getInAppPurchaseData(), buyResultInfo.getInAppDataSignature());
|
||||||
|
if (purchaseData != null) {
|
||||||
|
onPurchaseFinished(purchaseData);
|
||||||
|
succeed = true;
|
||||||
|
} else {
|
||||||
|
logDebug("Purchase failed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!succeed) {
|
||||||
|
stop(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
184
OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java
Normal file
184
OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
|
||||||
|
public class InAppPurchasesImpl extends InAppPurchases {
|
||||||
|
|
||||||
|
private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
|
||||||
|
private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
|
||||||
|
private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
|
||||||
|
|
||||||
|
|
||||||
|
private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
|
||||||
|
new InAppPurchaseLiveUpdatesMonthlyFree(),
|
||||||
|
new InAppPurchaseLiveUpdates3MonthsFree(),
|
||||||
|
new InAppPurchaseLiveUpdatesAnnualFree()
|
||||||
|
};
|
||||||
|
|
||||||
|
public InAppPurchasesImpl(OsmandApplication ctx) {
|
||||||
|
super(ctx);
|
||||||
|
fullVersion = FULL_VERSION;
|
||||||
|
depthContours = DEPTH_CONTOURS_FREE;
|
||||||
|
contourLines = CONTOUR_LINES_FREE;
|
||||||
|
liveUpdates = new LiveUpdatesInAppPurchasesFree();
|
||||||
|
inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFullVersion(String sku) {
|
||||||
|
return FULL_VERSION.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDepthContours(String sku) {
|
||||||
|
return DEPTH_CONTOURS_FREE.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContourLines(String sku) {
|
||||||
|
return CONTOUR_LINES_FREE.getSku().equals(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLiveUpdates(String sku) {
|
||||||
|
for (InAppPurchase p : LIVE_UPDATES_FREE) {
|
||||||
|
if (p.getSku().equals(sku)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseFullVersion extends InAppPurchase {
|
||||||
|
|
||||||
|
private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
|
||||||
|
|
||||||
|
InAppPurchaseFullVersion() {
|
||||||
|
super(SKU_FULL_VERSION_PRICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultPrice(Context ctx) {
|
||||||
|
return ctx.getString(R.string.full_version_price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
|
||||||
|
|
||||||
|
private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
|
||||||
|
|
||||||
|
InAppPurchaseDepthContoursFree() {
|
||||||
|
super(SKU_DEPTH_CONTOURS_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
|
||||||
|
|
||||||
|
private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
|
||||||
|
|
||||||
|
InAppPurchaseContourLinesFree() {
|
||||||
|
super(SKU_CONTOUR_LINES_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_MONTHLY_HW_FREE = "net.osmand.test.monthly";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesMonthlyFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_MONTHLY_HW_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_3_MONTHS_HW_FREE = "net.osmand.test.3months";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdates3MonthsFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_3_MONTHS_HW_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
|
||||||
|
|
||||||
|
private static final String SKU_LIVE_UPDATES_ANNUAL_HW_FREE = "net.osmand.test.annual";
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesAnnualFree() {
|
||||||
|
super(SKU_LIVE_UPDATES_ANNUAL_HW_FREE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
|
||||||
|
super(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
|
||||||
|
|
||||||
|
private ProductInfo info;
|
||||||
|
|
||||||
|
InAppPurchaseLiveUpdatesOldSubscription(@NonNull ProductInfo info) {
|
||||||
|
super(info.getProductId(), true);
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultPrice(Context ctx) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getTitle(Context ctx) {
|
||||||
|
return info.getProductName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getDescription(@NonNull Context ctx) {
|
||||||
|
return info.getProductDesc();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected InAppSubscription newInstance(@NonNull String sku) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
|
||||||
|
|
||||||
|
public LiveUpdatesInAppPurchasesFree() {
|
||||||
|
super(LIVE_UPDATES_FREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java
Normal file
49
OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.huawei.hms.iap.entity.InAppPurchaseData;
|
||||||
|
import com.huawei.hms.iap.entity.OwnedPurchasesResult;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
public class InAppUtils {
|
||||||
|
private static final String TAG = "InAppUtils";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static InAppPurchaseData getPurchaseData(OwnedPurchasesResult result, String productId) {
|
||||||
|
if (result == null || result.getInAppPurchaseDataList() == null) {
|
||||||
|
Log.i(TAG, "result is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int index = result.getItemList().indexOf(productId);
|
||||||
|
if (index != -1) {
|
||||||
|
String data = result.getInAppPurchaseDataList().get(index);
|
||||||
|
String signature = result.getInAppSignature().get(index);
|
||||||
|
return getInAppPurchaseData(productId, data, signature);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static InAppPurchaseData getInAppPurchaseData(@Nullable String productId, @NonNull String data, @NonNull String signature) {
|
||||||
|
if (CipherUtil.doCheck(data, signature, CipherUtil.getPublicKey())) {
|
||||||
|
try {
|
||||||
|
InAppPurchaseData purchaseData = new InAppPurchaseData(data);
|
||||||
|
if (purchaseData.getPurchaseState() == InAppPurchaseData.PurchaseState.PURCHASED) {
|
||||||
|
if (productId == null || productId.equals(purchaseData.getProductId())) {
|
||||||
|
return purchaseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "delivery: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "delivery: verify signature error");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
139
OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java
Executable file
139
OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java
Executable file
|
@ -0,0 +1,139 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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 net.osmand.plus.inapp;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.huawei.hms.iap.Iap;
|
||||||
|
import com.huawei.hms.iap.entity.InAppPurchaseData;
|
||||||
|
import com.huawei.hms.iap.entity.OrderStatusCode;
|
||||||
|
import com.huawei.hms.iap.entity.OwnedPurchasesResult;
|
||||||
|
import com.huawei.hms.iap.entity.PurchaseResultInfo;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util for Subscription function.
|
||||||
|
*
|
||||||
|
* @since 2019/12/9
|
||||||
|
*/
|
||||||
|
public class SubscriptionUtils {
|
||||||
|
private static final String TAG = "SubscriptionUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide whether to offer subscription service
|
||||||
|
*
|
||||||
|
* @param result the OwnedPurchasesResult from IapClient.obtainOwnedPurchases
|
||||||
|
* @param productId subscription product id
|
||||||
|
* @return decision result
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static InAppPurchaseData getPurchaseData(OwnedPurchasesResult result, String productId) {
|
||||||
|
if (null == result) {
|
||||||
|
Log.e(TAG, "OwnedPurchasesResult is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> dataList = result.getInAppPurchaseDataList();
|
||||||
|
List<String> signatureList = result.getInAppSignature();
|
||||||
|
for (int i = 0; i < dataList.size(); i++) {
|
||||||
|
String data = dataList.get(i);
|
||||||
|
String signature = signatureList.get(i);
|
||||||
|
InAppPurchaseData purchaseData = getInAppPurchaseData(productId, data, signature);
|
||||||
|
if (purchaseData != null) {
|
||||||
|
return purchaseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static InAppPurchaseData getInAppPurchaseData(@Nullable String productId, @NonNull String data, @NonNull String signature) {
|
||||||
|
try {
|
||||||
|
InAppPurchaseData purchaseData = new InAppPurchaseData(data);
|
||||||
|
if (productId == null || productId.equals(purchaseData.getProductId())) {
|
||||||
|
boolean credible = CipherUtil.doCheck(data, signature, CipherUtil.getPublicKey());
|
||||||
|
if (credible) {
|
||||||
|
return purchaseData.isSubValid() ? purchaseData : null;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "check the data signature fail");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "parse InAppPurchaseData JSONException", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse PurchaseResult data from intent
|
||||||
|
*
|
||||||
|
* @param activity Activity
|
||||||
|
* @param data the intent from onActivityResult
|
||||||
|
* @return PurchaseResultInfo
|
||||||
|
*/
|
||||||
|
public static PurchaseResultInfo getPurchaseResult(Activity activity, Intent data) {
|
||||||
|
PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data);
|
||||||
|
if (null == purchaseResultInfo) {
|
||||||
|
Log.e(TAG, "PurchaseResultInfo is null");
|
||||||
|
} else {
|
||||||
|
int returnCode = purchaseResultInfo.getReturnCode();
|
||||||
|
String errMsg = purchaseResultInfo.getErrMsg();
|
||||||
|
switch (returnCode) {
|
||||||
|
case OrderStatusCode.ORDER_PRODUCT_OWNED:
|
||||||
|
Log.w(TAG, "you have owned this product");
|
||||||
|
break;
|
||||||
|
case OrderStatusCode.ORDER_STATE_SUCCESS:
|
||||||
|
boolean credible = CipherUtil.doCheck(purchaseResultInfo.getInAppPurchaseData(), purchaseResultInfo.getInAppDataSignature(), CipherUtil
|
||||||
|
.getPublicKey());
|
||||||
|
if (credible) {
|
||||||
|
try {
|
||||||
|
InAppPurchaseData inAppPurchaseData = new InAppPurchaseData(purchaseResultInfo.getInAppPurchaseData());
|
||||||
|
if (!inAppPurchaseData.isSubValid()) {
|
||||||
|
return getFailedPurchaseResultInfo();
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "parse InAppPurchaseData JSONException", e);
|
||||||
|
return getFailedPurchaseResultInfo();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "check the data signature fail");
|
||||||
|
}
|
||||||
|
return getFailedPurchaseResultInfo();
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "returnCode: " + returnCode + " , errMsg: " + errMsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return purchaseResultInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PurchaseResultInfo getFailedPurchaseResultInfo() {
|
||||||
|
PurchaseResultInfo info = new PurchaseResultInfo();
|
||||||
|
info.setReturnCode(OrderStatusCode.ORDER_STATE_FAILED);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import android.app.Activity;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -14,12 +13,14 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.PlatformUtil;
|
import net.osmand.PlatformUtil;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.OsmandPlugin;
|
import net.osmand.plus.OsmandPlugin;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.Version;
|
import net.osmand.plus.Version;
|
||||||
import net.osmand.plus.inapp.InAppPurchaseHelper;
|
import net.osmand.plus.inapp.InAppPurchaseHelper;
|
||||||
|
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseInitCallback;
|
||||||
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
|
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
|
||||||
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
|
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
|
||||||
import net.osmand.plus.liveupdates.OsmLiveRestartBottomSheetDialogFragment;
|
import net.osmand.plus.liveupdates.OsmLiveRestartBottomSheetDialogFragment;
|
||||||
|
@ -27,6 +28,7 @@ import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressLint("Registered")
|
@SuppressLint("Registered")
|
||||||
|
@ -34,14 +36,7 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
private static final Log LOG = PlatformUtil.getLog(OsmandInAppPurchaseActivity.class);
|
private static final Log LOG = PlatformUtil.getLog(OsmandInAppPurchaseActivity.class);
|
||||||
|
|
||||||
private InAppPurchaseHelper purchaseHelper;
|
private InAppPurchaseHelper purchaseHelper;
|
||||||
|
private boolean activityDestroyed;
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (isInAppPurchaseAllowed() && isInAppPurchaseSupported()) {
|
|
||||||
purchaseHelper = getMyApplication().getInAppPurchaseHelper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
|
@ -53,19 +48,38 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
deinitInAppPurchaseHelper();
|
deinitInAppPurchaseHelper();
|
||||||
|
activityDestroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initInAppPurchaseHelper() {
|
private void initInAppPurchaseHelper() {
|
||||||
deinitInAppPurchaseHelper();
|
deinitInAppPurchaseHelper();
|
||||||
|
if (purchaseHelper == null) {
|
||||||
|
InAppPurchaseHelper purchaseHelper = getMyApplication().getInAppPurchaseHelper();
|
||||||
|
if (isInAppPurchaseAllowed() && isInAppPurchaseSupported(purchaseHelper)) {
|
||||||
|
this.purchaseHelper = purchaseHelper;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (purchaseHelper != null) {
|
if (purchaseHelper != null) {
|
||||||
purchaseHelper.setUiActivity(this);
|
final WeakReference<OsmandInAppPurchaseActivity> activityRef = new WeakReference<>(this);
|
||||||
|
purchaseHelper.isInAppPurchaseSupported(this, new InAppPurchaseInitCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
OsmandInAppPurchaseActivity activity = activityRef.get();
|
||||||
|
if (!activityDestroyed && AndroidUtils.isActivityNotDestroyed(activity)) {
|
||||||
|
purchaseHelper.setUiActivity(activity);
|
||||||
if (purchaseHelper.needRequestInventory()) {
|
if (purchaseHelper.needRequestInventory()) {
|
||||||
purchaseHelper.requestInventory();
|
purchaseHelper.requestInventory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFail() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void deinitInAppPurchaseHelper() {
|
private void deinitInAppPurchaseHelper() {
|
||||||
if (purchaseHelper != null) {
|
if (purchaseHelper != null) {
|
||||||
purchaseHelper.resetUiActivity(this);
|
purchaseHelper.resetUiActivity(this);
|
||||||
|
@ -80,7 +94,11 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
|
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
|
||||||
if (purchaseHelper != null) {
|
if (purchaseHelper != null) {
|
||||||
app.logEvent("in_app_purchase_redirect");
|
app.logEvent("in_app_purchase_redirect");
|
||||||
|
try {
|
||||||
purchaseHelper.purchaseFullVersion(activity);
|
purchaseHelper.purchaseFullVersion(activity);
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
LOG.error("purchaseFullVersion is not supported", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
app.logEvent("paid_version_redirect");
|
app.logEvent("paid_version_redirect");
|
||||||
|
@ -101,7 +119,11 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
|
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
|
||||||
if (purchaseHelper != null) {
|
if (purchaseHelper != null) {
|
||||||
app.logEvent("depth_contours_purchase_redirect");
|
app.logEvent("depth_contours_purchase_redirect");
|
||||||
|
try {
|
||||||
purchaseHelper.purchaseDepthContours(activity);
|
purchaseHelper.purchaseDepthContours(activity);
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
LOG.error("purchaseDepthContours is not supported", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,8 +151,9 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInAppPurchaseSupported() {
|
public boolean isInAppPurchaseSupported(InAppPurchaseHelper purchaseHelper) {
|
||||||
return Version.isGooglePlayEnabled(getMyApplication());
|
OsmandApplication app = getMyApplication();
|
||||||
|
return Version.isGooglePlayEnabled(app) || Version.isHuawei(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -222,6 +245,17 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
|
boolean handled = false;
|
||||||
|
if (purchaseHelper != null) {
|
||||||
|
handled = purchaseHelper.onActivityResult(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
if (!handled) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onInAppPurchaseError(InAppPurchaseTaskType taskType, String error) {
|
public void onInAppPurchaseError(InAppPurchaseTaskType taskType, String error) {
|
||||||
// not implemented
|
// not implemented
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,14 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
|
||||||
import net.osmand.AndroidNetworkUtils;
|
import net.osmand.AndroidNetworkUtils;
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
import net.osmand.osm.AbstractPoiType;
|
import net.osmand.osm.AbstractPoiType;
|
||||||
import net.osmand.osm.MapPoiTypes;
|
import net.osmand.osm.MapPoiTypes;
|
||||||
import net.osmand.osm.PoiCategory;
|
import net.osmand.osm.PoiCategory;
|
||||||
import net.osmand.osm.PoiType;
|
import net.osmand.osm.PoiType;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.OsmandPlugin;
|
import net.osmand.plus.OsmandPlugin;
|
||||||
|
import net.osmand.plus.activities.OsmandInAppPurchaseActivity;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.Version;
|
import net.osmand.plus.Version;
|
||||||
|
@ -56,7 +58,7 @@ import java.util.Map;
|
||||||
public class DiscountHelper {
|
public class DiscountHelper {
|
||||||
|
|
||||||
private static final String TAG = "DiscountHelper";
|
private static final String TAG = "DiscountHelper";
|
||||||
//private static final String DISCOUNT_JSON = "discount.json";
|
private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(DiscountHelper.class);
|
||||||
|
|
||||||
private static long mLastCheckTime;
|
private static long mLastCheckTime;
|
||||||
private static ControllerData mData;
|
private static ControllerData mData;
|
||||||
|
@ -312,7 +314,11 @@ public class DiscountHelper {
|
||||||
if (purchaseHelper != null) {
|
if (purchaseHelper != null) {
|
||||||
if (url.contains(purchaseHelper.getFullVersion().getSku())) {
|
if (url.contains(purchaseHelper.getFullVersion().getSku())) {
|
||||||
app.logEvent("in_app_purchase_redirect");
|
app.logEvent("in_app_purchase_redirect");
|
||||||
|
try {
|
||||||
purchaseHelper.purchaseFullVersion(mapActivity);
|
purchaseHelper.purchaseFullVersion(mapActivity);
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
LOG.error("purchaseFullVersion is not supported", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (InAppPurchase p : purchaseHelper.getLiveUpdates().getAllSubscriptions()) {
|
for (InAppPurchase p : purchaseHelper.getLiveUpdates().getAllSubscriptions()) {
|
||||||
if (url.contains(p.getSku())) {
|
if (url.contains(p.getSku())) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.osmand.plus.inapp;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -64,6 +65,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
protected InAppPurchaseListener uiActivity = null;
|
protected InAppPurchaseListener uiActivity = null;
|
||||||
|
|
||||||
public interface InAppPurchaseListener {
|
public interface InAppPurchaseListener {
|
||||||
|
|
||||||
void onError(InAppPurchaseTaskType taskType, String error);
|
void onError(InAppPurchaseTaskType taskType, String error);
|
||||||
|
|
||||||
void onGetItems();
|
void onGetItems();
|
||||||
|
@ -75,6 +77,13 @@ public abstract class InAppPurchaseHelper {
|
||||||
void dismissProgress(InAppPurchaseTaskType taskType);
|
void dismissProgress(InAppPurchaseTaskType taskType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface InAppPurchaseInitCallback {
|
||||||
|
|
||||||
|
void onSuccess();
|
||||||
|
|
||||||
|
void onFail();
|
||||||
|
}
|
||||||
|
|
||||||
public enum InAppPurchaseTaskType {
|
public enum InAppPurchaseTaskType {
|
||||||
REQUEST_INVENTORY,
|
REQUEST_INVENTORY,
|
||||||
PURCHASE_FULL_VERSION,
|
PURCHASE_FULL_VERSION,
|
||||||
|
@ -82,9 +91,23 @@ public abstract class InAppPurchaseHelper {
|
||||||
PURCHASE_DEPTH_CONTOURS
|
PURCHASE_DEPTH_CONTOURS
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface InAppRunnable {
|
public abstract class InAppCommand {
|
||||||
|
|
||||||
|
InAppCommandResultHandler resultHandler;
|
||||||
|
|
||||||
// return true if done and false if async task started
|
// return true if done and false if async task started
|
||||||
boolean run(InAppPurchaseHelper helper);
|
abstract void run(InAppPurchaseHelper helper);
|
||||||
|
|
||||||
|
protected void commandDone() {
|
||||||
|
InAppCommandResultHandler resultHandler = this.resultHandler;
|
||||||
|
if (resultHandler != null) {
|
||||||
|
resultHandler.onCommandDone(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface InAppCommandResultHandler {
|
||||||
|
void onCommandDone(@NonNull InAppCommand command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PurchaseInfo {
|
public static class PurchaseInfo {
|
||||||
|
@ -163,9 +186,10 @@ public abstract class InAppPurchaseHelper {
|
||||||
public InAppPurchaseHelper(OsmandApplication ctx) {
|
public InAppPurchaseHelper(OsmandApplication ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
isDeveloperVersion = Version.isDeveloperVersion(ctx);
|
isDeveloperVersion = Version.isDeveloperVersion(ctx);
|
||||||
purchases = new InAppPurchases(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback);
|
||||||
|
|
||||||
public boolean hasInventory() {
|
public boolean hasInventory() {
|
||||||
return lastValidationCheckTime != 0;
|
return lastValidationCheckTime != 0;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +205,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void exec(final @NonNull InAppPurchaseTaskType taskType, final @NonNull InAppRunnable runnable) {
|
protected void exec(final @NonNull InAppPurchaseTaskType taskType, final @NonNull InAppCommand command) {
|
||||||
if (isDeveloperVersion || !Version.isGooglePlayEnabled(ctx)) {
|
if (isDeveloperVersion || !Version.isGooglePlayEnabled(ctx)) {
|
||||||
notifyDismissProgress(taskType);
|
notifyDismissProgress(taskType);
|
||||||
stop(true);
|
stop(true);
|
||||||
|
@ -205,14 +229,20 @@ public abstract class InAppPurchaseHelper {
|
||||||
try {
|
try {
|
||||||
processingTask = true;
|
processingTask = true;
|
||||||
activeTask = taskType;
|
activeTask = taskType;
|
||||||
execImpl(taskType, runnable);
|
command.resultHandler = new InAppCommandResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void onCommandDone(@NonNull InAppCommand command) {
|
||||||
|
processingTask = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
execImpl(taskType, command);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logError("exec Error", e);
|
logError("exec Error", e);
|
||||||
stop(true);
|
stop(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppRunnable runnable);
|
protected abstract void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand command);
|
||||||
|
|
||||||
public boolean needRequestInventory() {
|
public boolean needRequestInventory() {
|
||||||
return !inventoryRequested && ((isSubscribedToLiveUpdates(ctx) && Algorithms.isEmpty(ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get()))
|
return !inventoryRequested && ((isSubscribedToLiveUpdates(ctx) && Algorithms.isEmpty(ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get()))
|
||||||
|
@ -224,16 +254,16 @@ public abstract class InAppPurchaseHelper {
|
||||||
new RequestInventoryTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
new RequestInventoryTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void purchaseFullVersion(final Activity activity) throws UnsupportedOperationException;
|
public abstract void purchaseFullVersion(@NonNull final Activity activity) throws UnsupportedOperationException;
|
||||||
|
|
||||||
public void purchaseLiveUpdates(Activity activity, String sku, String email, String userName,
|
public void purchaseLiveUpdates(@NonNull Activity activity, String sku, String email, String userName,
|
||||||
String countryDownloadName, boolean hideUserName) {
|
String countryDownloadName, boolean hideUserName) {
|
||||||
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
|
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
|
||||||
new LiveUpdatesPurchaseTask(activity, sku, email, userName, countryDownloadName, hideUserName)
|
new LiveUpdatesPurchaseTask(activity, sku, email, userName, countryDownloadName, hideUserName)
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void purchaseDepthContours(final Activity activity) throws UnsupportedOperationException;
|
public abstract void purchaseDepthContours(@NonNull final Activity activity) throws UnsupportedOperationException;
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
private class LiveUpdatesPurchaseTask extends AsyncTask<Void, Void, String> {
|
private class LiveUpdatesPurchaseTask extends AsyncTask<Void, Void, String> {
|
||||||
|
@ -329,7 +359,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract InAppRunnable getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity,
|
protected abstract InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity,
|
||||||
final String sku, final String payload) throws UnsupportedOperationException;
|
final String sku, final String payload) throws UnsupportedOperationException;
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@ -380,7 +410,7 @@ public abstract class InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract InAppRunnable getRequestInventoryCommand() throws UnsupportedOperationException;
|
protected abstract InAppCommand getRequestInventoryCommand() throws UnsupportedOperationException;
|
||||||
|
|
||||||
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
|
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
|
||||||
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
|
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
|
||||||
|
@ -599,6 +629,10 @@ public abstract class InAppPurchaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean onActivityResult(@NonNull Activity activity, int requestCode, int resultCode, Intent data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected void notifyError(InAppPurchaseTaskType taskType, String message) {
|
protected void notifyError(InAppPurchaseTaskType taskType, String message) {
|
||||||
if (uiActivity != null) {
|
if (uiActivity != null) {
|
||||||
uiActivity.onError(taskType, message);
|
uiActivity.onError(taskType, message);
|
||||||
|
|
|
@ -11,14 +11,11 @@ import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.billingclient.api.SkuDetails;
|
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.Period;
|
import net.osmand.Period;
|
||||||
import net.osmand.Period.PeriodUnit;
|
import net.osmand.Period.PeriodUnit;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.Version;
|
|
||||||
import net.osmand.plus.helpers.FontCache;
|
import net.osmand.plus.helpers.FontCache;
|
||||||
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
|
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -33,72 +30,17 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class InAppPurchases {
|
public abstract class InAppPurchases {
|
||||||
|
|
||||||
private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
|
protected InAppPurchase fullVersion;
|
||||||
private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull();
|
protected InAppPurchase depthContours;
|
||||||
private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
|
protected InAppPurchase contourLines;
|
||||||
private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull();
|
protected InAppSubscription monthlyLiveUpdates;
|
||||||
private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
|
protected InAppSubscription discountedMonthlyLiveUpdates;
|
||||||
|
protected InAppSubscriptionList liveUpdates;
|
||||||
|
protected InAppPurchase[] inAppPurchases;
|
||||||
|
|
||||||
private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{
|
protected InAppPurchases(OsmandApplication ctx) {
|
||||||
new InAppPurchaseLiveUpdatesOldMonthlyFull(),
|
|
||||||
new InAppPurchaseLiveUpdatesMonthlyFull(),
|
|
||||||
new InAppPurchaseLiveUpdates3MonthsFull(),
|
|
||||||
new InAppPurchaseLiveUpdatesAnnualFull()
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
|
|
||||||
new InAppPurchaseLiveUpdatesOldMonthlyFree(),
|
|
||||||
new InAppPurchaseLiveUpdatesMonthlyFree(),
|
|
||||||
new InAppPurchaseLiveUpdates3MonthsFree(),
|
|
||||||
new InAppPurchaseLiveUpdatesAnnualFree()
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final InAppSubscription[] LIVE_UPDATES_HW_FREE = new InAppSubscription[]{
|
|
||||||
new InAppPurchaseLiveUpdatesMonthlyHWFree(),
|
|
||||||
new InAppPurchaseLiveUpdates3MonthsHWFree(),
|
|
||||||
new InAppPurchaseLiveUpdatesAnnualHWFree()
|
|
||||||
};
|
|
||||||
|
|
||||||
private InAppPurchase fullVersion;
|
|
||||||
private InAppPurchase depthContours;
|
|
||||||
private InAppPurchase contourLines;
|
|
||||||
private InAppSubscription monthlyLiveUpdates;
|
|
||||||
private InAppSubscription discountedMonthlyLiveUpdates;
|
|
||||||
private InAppSubscriptionList liveUpdates;
|
|
||||||
private InAppPurchase[] inAppPurchases;
|
|
||||||
|
|
||||||
InAppPurchases(OsmandApplication ctx) {
|
|
||||||
fullVersion = FULL_VERSION;
|
|
||||||
if (Version.isHuawei(ctx)) {
|
|
||||||
liveUpdates = new LiveUpdatesInAppPurchasesHWFree();
|
|
||||||
} else if (Version.isFreeVersion(ctx)) {
|
|
||||||
liveUpdates = new LiveUpdatesInAppPurchasesFree();
|
|
||||||
} else {
|
|
||||||
liveUpdates = new LiveUpdatesInAppPurchasesFull();
|
|
||||||
}
|
|
||||||
for (InAppSubscription s : liveUpdates.getAllSubscriptions()) {
|
|
||||||
if (s instanceof InAppPurchaseLiveUpdatesMonthly) {
|
|
||||||
if (s.isDiscounted()) {
|
|
||||||
discountedMonthlyLiveUpdates = s;
|
|
||||||
} else {
|
|
||||||
monthlyLiveUpdates = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Version.isFreeVersion(ctx)) {
|
|
||||||
depthContours = DEPTH_CONTOURS_FREE;
|
|
||||||
} else {
|
|
||||||
depthContours = DEPTH_CONTOURS_FULL;
|
|
||||||
}
|
|
||||||
if (Version.isFreeVersion(ctx)) {
|
|
||||||
contourLines = CONTOUR_LINES_FREE;
|
|
||||||
} else {
|
|
||||||
contourLines = CONTOUR_LINES_FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InAppPurchase getFullVersion() {
|
public InAppPurchase getFullVersion() {
|
||||||
|
@ -166,31 +108,13 @@ public class InAppPurchases {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFullVersion(String sku) {
|
public abstract boolean isFullVersion(String sku);
|
||||||
return FULL_VERSION.getSku().equals(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDepthContours(String sku) {
|
public abstract boolean isDepthContours(String sku);
|
||||||
return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isContourLines(String sku) {
|
public abstract boolean isContourLines(String sku);
|
||||||
return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLiveUpdates(String sku) {
|
public abstract boolean isLiveUpdates(String sku);
|
||||||
for (InAppPurchase p : LIVE_UPDATES_FULL) {
|
|
||||||
if (p.getSku().equals(sku)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (InAppPurchase p : LIVE_UPDATES_FREE) {
|
|
||||||
if (p.getSku().equals(sku)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class InAppSubscriptionList {
|
public abstract static class InAppSubscriptionList {
|
||||||
|
|
||||||
|
@ -268,27 +192,6 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
|
|
||||||
|
|
||||||
public LiveUpdatesInAppPurchasesFree() {
|
|
||||||
super(LIVE_UPDATES_FREE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LiveUpdatesInAppPurchasesHWFree extends InAppSubscriptionList {
|
|
||||||
|
|
||||||
public LiveUpdatesInAppPurchasesHWFree() {
|
|
||||||
super(LIVE_UPDATES_HW_FREE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList {
|
|
||||||
|
|
||||||
public LiveUpdatesInAppPurchasesFull() {
|
|
||||||
super(LIVE_UPDATES_FULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class InAppPurchase {
|
public abstract static class InAppPurchase {
|
||||||
|
|
||||||
public enum PurchaseState {
|
public enum PurchaseState {
|
||||||
|
@ -310,11 +213,11 @@ public class InAppPurchases {
|
||||||
|
|
||||||
private NumberFormat currencyFormatter;
|
private NumberFormat currencyFormatter;
|
||||||
|
|
||||||
private InAppPurchase(@NonNull String sku) {
|
protected InAppPurchase(@NonNull String sku) {
|
||||||
this.sku = sku;
|
this.sku = sku;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InAppPurchase(@NonNull String sku, boolean discounted) {
|
protected InAppPurchase(@NonNull String sku, boolean discounted) {
|
||||||
this(sku);
|
this(sku);
|
||||||
this.discounted = discounted;
|
this.discounted = discounted;
|
||||||
}
|
}
|
||||||
|
@ -792,23 +695,9 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseFullVersion extends InAppPurchase {
|
|
||||||
|
|
||||||
private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
|
|
||||||
|
|
||||||
InAppPurchaseFullVersion() {
|
|
||||||
super(SKU_FULL_VERSION_PRICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDefaultPrice(Context ctx) {
|
|
||||||
return ctx.getString(R.string.full_version_price);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseDepthContours extends InAppPurchase {
|
public static class InAppPurchaseDepthContours extends InAppPurchase {
|
||||||
|
|
||||||
private InAppPurchaseDepthContours(String sku) {
|
protected InAppPurchaseDepthContours(String sku) {
|
||||||
super(sku);
|
super(sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,27 +707,9 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours {
|
|
||||||
|
|
||||||
private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus";
|
|
||||||
|
|
||||||
InAppPurchaseDepthContoursFull() {
|
|
||||||
super(SKU_DEPTH_CONTOURS_FULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
|
|
||||||
|
|
||||||
private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
|
|
||||||
|
|
||||||
InAppPurchaseDepthContoursFree() {
|
|
||||||
super(SKU_DEPTH_CONTOURS_FREE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseContourLines extends InAppPurchase {
|
public static class InAppPurchaseContourLines extends InAppPurchase {
|
||||||
|
|
||||||
private InAppPurchaseContourLines(String sku) {
|
protected InAppPurchaseContourLines(String sku) {
|
||||||
super(sku);
|
super(sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,25 +719,7 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines {
|
protected static abstract class InAppPurchaseLiveUpdatesMonthly extends InAppSubscription {
|
||||||
|
|
||||||
private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus";
|
|
||||||
|
|
||||||
InAppPurchaseContourLinesFull() {
|
|
||||||
super(SKU_CONTOUR_LINES_FULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
|
|
||||||
|
|
||||||
private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
|
|
||||||
|
|
||||||
InAppPurchaseContourLinesFree() {
|
|
||||||
super(SKU_CONTOUR_LINES_FREE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class InAppPurchaseLiveUpdatesMonthly extends InAppSubscription {
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesMonthly(String skuNoVersion, int version) {
|
InAppPurchaseLiveUpdatesMonthly(String skuNoVersion, int version) {
|
||||||
super(skuNoVersion, version);
|
super(skuNoVersion, version);
|
||||||
|
@ -920,64 +773,7 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly {
|
protected static abstract class InAppPurchaseLiveUpdates3Months extends InAppSubscription {
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesMonthlyFull() {
|
|
||||||
super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesMonthlyFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesMonthlyHWFree extends InAppPurchaseLiveUpdatesMonthly {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_MONTHLY_HW_FREE = "net.osmand.test.monthly";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesMonthlyHWFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_MONTHLY_HW_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesMonthlyHWFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyHWFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class InAppPurchaseLiveUpdates3Months extends InAppSubscription {
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdates3Months(String skuNoVersion, int version) {
|
InAppPurchaseLiveUpdates3Months(String skuNoVersion, int version) {
|
||||||
super(skuNoVersion, version);
|
super(skuNoVersion, version);
|
||||||
|
@ -1020,64 +816,7 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months {
|
protected static abstract class InAppPurchaseLiveUpdatesAnnual extends InAppSubscription {
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdates3MonthsFull() {
|
|
||||||
super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdates3MonthsFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdates3MonthsHWFree extends InAppPurchaseLiveUpdates3Months {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_3_MONTHS_HW_FREE = "net.osmand.test.3months";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdates3MonthsHWFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_3_MONTHS_HW_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdates3MonthsHWFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsHWFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class InAppPurchaseLiveUpdatesAnnual extends InAppSubscription {
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesAnnual(String skuNoVersion, int version) {
|
InAppPurchaseLiveUpdatesAnnual(String skuNoVersion, int version) {
|
||||||
super(skuNoVersion, version);
|
super(skuNoVersion, version);
|
||||||
|
@ -1120,63 +859,6 @@ public class InAppPurchases {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesAnnualFull() {
|
|
||||||
super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesAnnualFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesAnnualHWFree extends InAppPurchaseLiveUpdatesAnnual {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_ANNUAL_HW_FREE = "net.osmand.test.annual";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesAnnualHWFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_ANNUAL_HW_FREE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InAppPurchaseLiveUpdatesAnnualHWFree(@NonNull String sku) {
|
|
||||||
super(sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualHWFree(sku) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesOldMonthly extends InAppPurchaseLiveUpdatesMonthly {
|
public static class InAppPurchaseLiveUpdatesOldMonthly extends InAppPurchaseLiveUpdatesMonthly {
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesOldMonthly(String sku) {
|
InAppPurchaseLiveUpdatesOldMonthly(String sku) {
|
||||||
|
@ -1199,54 +881,5 @@ public class InAppPurchases {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesOldMonthlyFull() {
|
|
||||||
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly {
|
|
||||||
|
|
||||||
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2";
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesOldMonthlyFree() {
|
|
||||||
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
|
|
||||||
|
|
||||||
private SkuDetails details;
|
|
||||||
|
|
||||||
InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) {
|
|
||||||
super(details.getSku(), true);
|
|
||||||
this.details = details;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDefaultPrice(Context ctx) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getTitle(Context ctx) {
|
|
||||||
return details.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getDescription(@NonNull Context ctx) {
|
|
||||||
return details.getDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected InAppSubscription newInstance(@NonNull String sku) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue