Merge branch 'master' into fix-alignment

This commit is contained in:
Vitaliy 2021-03-06 20:46:12 +02:00
commit 0cd3428437
10 changed files with 103 additions and 82 deletions

View file

@ -186,32 +186,22 @@ public class RouteResultPreparation {
public RouteSegmentResult filterMinorStops(RouteSegmentResult seg) {
List<Integer> stops = null;
int startPoint = seg.getStartPointIndex();
int endPoint = seg.getEndPointIndex();
int start;
int end;
boolean plus = seg.getStartPointIndex() < seg.getEndPointIndex();
int next;
if (startPoint < endPoint) {
start = startPoint;
end = endPoint;
} else {
start = endPoint;
end = startPoint;
}
while (start <= end) {
int[] pointTypes = seg.getObject().getPointTypes(start);
for (int i = seg.getStartPointIndex(); i != seg.getEndPointIndex(); i = next) {
next = plus ? i + 1 : i - 1;
int[] pointTypes = seg.getObject().getPointTypes(i);
if (pointTypes != null) {
for (int j = 0; j < pointTypes.length; j++) {
if (pointTypes[j] == seg.getObject().region.stopMinor) {
if (stops == null) {
stops = new ArrayList<>();
}
stops.add(start);
stops.add(i);
}
}
}
start++;
}
if (stops != null) {

View file

@ -16,7 +16,6 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

View file

@ -348,7 +348,7 @@ dependencies {
implementation 'org.immutables:gson:2.5.0'
implementation 'com.vividsolutions:jts-core:1.14.0'
implementation 'com.google.openlocationcode:openlocationcode:1.0.4'
implementation 'com.android.billingclient:billing:2.0.3'
implementation 'com.android.billingclient:billing:3.0.2'
// turn off for now
//implementation 'com.atilika.kuromoji:kuromoji-ipadic:0.9.0'
implementation 'com.squareup.picasso:picasso:2.71828'

View file

@ -6,9 +6,7 @@ import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.billingclient.api.AccountIdentifiers;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
@ -25,7 +23,6 @@ import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState;
import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription;
import net.osmand.plus.inapp.util.BillingManager;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandPreference;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.srtmplugin.SRTMPlugin;
import net.osmand.util.Algorithms;
@ -37,6 +34,9 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
// The helper object
@ -77,6 +77,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
return billingManager;
}
@Override
protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand runnable) {
billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() {
@ -126,7 +127,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
}
billingManager.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuInApps, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, final List<SkuDetails> skuDetailsListInApps) {
public void onSkuDetailsResponse(@NonNull 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());
@ -153,7 +154,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
billingManager.querySkuDetailsAsync(BillingClient.SkuType.SUBS, skuSubscriptions, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, final List<SkuDetails> skuDetailsListSubscriptions) {
public void onSkuDetailsResponse(@NonNull 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());
@ -197,6 +198,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
if (skuDetails == null) {
throw new IllegalArgumentException("Cannot find sku details");
}
BillingManager billingManager = getBillingManager();
if (billingManager != null) {
billingManager.initiatePurchaseFlow(activity, skuDetails);
@ -321,7 +323,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
}
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, List<SkuDetails> skuDetailsList) {
logDebug("Query sku details finished.");
@ -442,19 +444,8 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
if (liveUpdatesPurchases.size() > 0) {
List<String> tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
for (Purchase purchase : liveUpdatesPurchases) {
if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
&& !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
String payload = purchase.getDeveloperPayload();
if (!Algorithms.isEmpty(payload)) {
String[] arr = payload.split(" ");
if (arr.length > 0) {
settings.BILLING_USER_ID.set(arr[0]);
}
if (arr.length > 1) {
token = arr[1];
settings.BILLING_USER_TOKEN.set(token);
}
}
if (needRestoreUserInfo()) {
restoreUserInfo(purchase);
}
if (!tokensSent.contains(purchase.getSku())) {
tokensToSend.add(purchase);
@ -469,6 +460,37 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
}
};
private void restoreUserInfo(Purchase purchase) {
boolean restored = restoreUserInfoFromString(purchase.getDeveloperPayload());
if (!restored) {
AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers();
if (accountIdentifiers != null) {
restoreUserInfoFromString(accountIdentifiers.getObfuscatedAccountId());
}
}
}
private boolean restoreUserInfoFromString(String userInfo) {
if (Algorithms.isEmpty(userInfo)) {
return false;
}
OsmandSettings settings = ctx.getSettings();
String[] arr = userInfo.split(" ");
if (arr.length > 0) {
settings.BILLING_USER_ID.set(arr[0]);
}
if (arr.length > 1) {
token = arr[1];
settings.BILLING_USER_TOKEN.set(token);
}
return needRestoreUserInfo();
}
private boolean needRestoreUserInfo() {
OsmandSettings settings = ctx.getSettings();
return Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get());
}
private PurchaseInfo getPurchaseInfo(Purchase purchase) {
return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken());
}
@ -511,7 +533,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
String introductoryPrice = skuDetails.getIntroductoryPrice();
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
int introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
if (!Algorithms.isEmpty(introductoryPrice)) {
try {
@ -524,7 +546,8 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
}
}
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
@Override
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String userInfo) {
return new InAppCommand() {
@Override
public void run(InAppPurchaseHelper helper) {
@ -534,7 +557,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
if (AndroidUtils.isActivityNotDestroyed(a) && skuDetails != null) {
BillingManager billingManager = getBillingManager();
if (billingManager != null) {
billingManager.setPayload(payload);
billingManager.setObfuscatedAccountId(userInfo);
billingManager.initiatePurchaseFlow(a, skuDetails);
} else {
throw new IllegalStateException("BillingManager disposed");
@ -551,6 +574,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
};
}
@Override
protected InAppCommand getRequestInventoryCommand() {
return new InAppCommand() {
@Override

View file

@ -276,7 +276,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
}
}
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String payload) {
protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity, final String sku, final String userInfo) {
return new InAppCommand() {
@Override
public void run(InAppPurchaseHelper helper) {
@ -285,7 +285,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
ProductInfo productInfo = getProductInfo(sku);
if (AndroidUtils.isActivityNotDestroyed(a) && productInfo != null) {
IapRequestHelper.createPurchaseIntent(getIapClient(), sku,
IapClient.PriceType.IN_APP_SUBSCRIPTION, payload, new IapApiCallback<PurchaseIntentResult>() {
IapClient.PriceType.IN_APP_SUBSCRIPTION, userInfo, new IapApiCallback<PurchaseIntentResult>() {
@Override
public void onSuccess(PurchaseIntentResult result) {
if (result == null) {

View file

@ -364,8 +364,8 @@ public abstract class InAppPurchaseHelper {
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
if (!Algorithms.isEmpty(userId) && !Algorithms.isEmpty(token)) {
logDebug("Launching purchase flow for live updates subscription for userId=" + userId);
final String payload = userId + " " + token;
exec(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, getPurchaseLiveUpdatesCommand(activity, sku, payload));
final String userInfo = userId + " " + token;
exec(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, getPurchaseLiveUpdatesCommand(activity, sku, userInfo));
} else {
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "Empty userId");
stop(true);
@ -374,7 +374,7 @@ public abstract class InAppPurchaseHelper {
}
protected abstract InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference<Activity> activity,
final String sku, final String payload) throws UnsupportedOperationException;
final String sku, final String userInfo) throws UnsupportedOperationException;
@SuppressLint("StaticFieldLeak")
private class RequestInventoryTask extends AsyncTask<Void, Void, String[]> {

View file

@ -405,16 +405,12 @@ public abstract class InAppPurchases {
String introductoryPrice,
long introductoryPriceAmountMicros,
String introductoryPeriodString,
String introductoryCycles) throws ParseException {
int introductoryCycles) throws ParseException {
this.subscription = subscription;
this.introductoryPrice = introductoryPrice;
this.introductoryPriceAmountMicros = introductoryPriceAmountMicros;
this.introductoryPeriodString = introductoryPeriodString;
try {
this.introductoryCycles = Integer.parseInt(introductoryCycles);
} catch (NumberFormatException e) {
throw new ParseException("Cannot parse introductoryCycles = " + introductoryCycles, 0);
}
this.introductoryCycles = introductoryCycles;
introductoryPriceValue = introductoryPriceAmountMicros / 1000000d;
introductoryPeriod = Period.parse(introductoryPeriodString);
}

View file

@ -2,9 +2,7 @@ package net.osmand.plus.inapp.util;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
@ -25,7 +23,6 @@ import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import net.osmand.PlatformUtil;
import net.osmand.util.Algorithms;
import java.util.ArrayList;
import java.util.Collections;
@ -33,6 +30,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Handles all the interactions with Play Store (via Billing library), maintains connection to
* it through BillingClient and caches temporary states/data if needed
@ -58,7 +58,9 @@ public class BillingManager implements PurchasesUpdatedListener {
// Public key for verifying signature, in base64 encoding
private String mSignatureBase64;
private String mPayload;
private String mObfuscatedAccountId;
private String mObfuscatedProfileId;
private final BillingUpdatesListener mBillingUpdatesListener;
private final List<Purchase> mPurchases = new ArrayList<>();
@ -135,18 +137,29 @@ public class BillingManager implements PurchasesUpdatedListener {
* Start a purchase flow
*/
public void initiatePurchaseFlow(final Activity activity, final SkuDetails skuDetails) {
initiatePurchaseFlow(activity, skuDetails, null);
initiatePurchaseFlow(activity, skuDetails, null, null);
}
/**
* Start a purchase or subscription replace flow
*/
public void initiatePurchaseFlow(final Activity activity, final SkuDetails skuDetails, final String oldSku) {
public void initiatePurchaseFlow(final Activity activity, final SkuDetails skuDetails, final String oldSku, final String purchaseToken) {
Runnable purchaseFlowRequest = new Runnable() {
@Override
public void run() {
LOG.debug("Launching in-app purchase flow. Replace old SKU? " + (oldSku != null));
BillingFlowParams purchaseParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).setOldSku(oldSku).build();
LOG.debug("Launching in-app purchase flow. Replace old SKU? " + (oldSku != null && purchaseToken != null));
BillingFlowParams.Builder paramsBuilder = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails);
if (!TextUtils.isEmpty(mObfuscatedAccountId)) {
paramsBuilder.setObfuscatedAccountId(mObfuscatedAccountId);
}
if (!TextUtils.isEmpty(mObfuscatedProfileId)) {
paramsBuilder.setObfuscatedProfileId(mObfuscatedProfileId);
}
if (oldSku != null && purchaseToken != null) {
paramsBuilder.setOldSku(oldSku, purchaseToken);
}
BillingFlowParams purchaseParams = paramsBuilder.build();
mBillingClient.launchBillingFlow(activity, purchaseParams);
}
};
@ -177,9 +190,11 @@ public class BillingManager implements PurchasesUpdatedListener {
@Override
public void run() {
// Query the purchase async
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(itemType);
mBillingClient.querySkuDetailsAsync(params.build(),
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(itemType)
.build();
mBillingClient.querySkuDetailsAsync(params,
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
@ -247,15 +262,6 @@ public class BillingManager implements PurchasesUpdatedListener {
return Collections.unmodifiableList(mPurchases);
}
public String getPayload() {
return mPayload;
}
public void setPayload(String payload) {
this.mPayload = payload;
}
/**
* Handles the purchase
* <p>Note: Notice that for each purchase, we check if signature is valid on the client.
@ -274,13 +280,9 @@ public class BillingManager implements PurchasesUpdatedListener {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
// Acknowledge the purchase if it hasn't already been acknowledged.
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams.Builder builder =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken());
if (!Algorithms.isEmpty(mPayload)) {
builder.setDeveloperPayload(mPayload);
}
AcknowledgePurchaseParams acknowledgePurchaseParams = builder.build();
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
@ -404,6 +406,14 @@ public class BillingManager implements PurchasesUpdatedListener {
});
}
public void setObfuscatedAccountId(String obfuscatedAccountId) {
mObfuscatedAccountId = obfuscatedAccountId;
}
public void setObfuscatedProfileId(String obfuscatedProfileId) {
mObfuscatedProfileId = obfuscatedProfileId;
}
private void executeServiceRequest(Runnable runnable) {
if (mIsServiceConnected) {
runnable.run();

View file

@ -41,6 +41,8 @@ public abstract class OnlineRoutingEngine implements Cloneable {
private final Set<EngineParameter> allowedParameters = new HashSet<>();
public OnlineRoutingEngine(@Nullable Map<String, String> params) {
// Params represents the entire state of an engine object.
// An engine object with null params used only to provide information about the engine type
if (!isEmpty(params)) {
this.params.putAll(params);
}

View file

@ -310,7 +310,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
outState.putString(PROFILE_STRINGKEY_KEY, changedProfile.stringKey);
outState.putInt(PROFILE_ICON_RES_KEY, changedProfile.iconRes);
outState.putSerializable(PROFILE_COLOR_KEY, changedProfile.color);
outState.putInt(PROFILE_CUSTOM_COLOR_KEY, changedProfile.customColor);
outState.putSerializable(PROFILE_CUSTOM_COLOR_KEY, changedProfile.customColor);
if (changedProfile.parent != null) {
outState.putString(PROFILE_PARENT_KEY, changedProfile.parent.getStringKey());
}
@ -325,7 +325,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
changedProfile.stringKey = savedInstanceState.getString(PROFILE_STRINGKEY_KEY);
changedProfile.iconRes = savedInstanceState.getInt(PROFILE_ICON_RES_KEY);
changedProfile.color = (ProfileIconColors) savedInstanceState.getSerializable(PROFILE_COLOR_KEY);
changedProfile.customColor = savedInstanceState.getInt(PROFILE_CUSTOM_COLOR_KEY);
changedProfile.customColor = (Integer) savedInstanceState.getSerializable(PROFILE_CUSTOM_COLOR_KEY);
String parentStringKey = savedInstanceState.getString(PROFILE_PARENT_KEY);
changedProfile.parent = ApplicationMode.valueOfStringKey(parentStringKey, null);
isBaseProfileImported = savedInstanceState.getBoolean(IS_BASE_PROFILE_IMPORTED);