Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
8ecb5a66e9
4 changed files with 144 additions and 35 deletions
|
@ -43,6 +43,7 @@ import net.osmand.plus.dialogs.RateUsBottomSheetDialog;
|
||||||
import net.osmand.plus.download.DownloadIndexesThread;
|
import net.osmand.plus.download.DownloadIndexesThread;
|
||||||
import net.osmand.plus.helpers.AvoidSpecificRoads;
|
import net.osmand.plus.helpers.AvoidSpecificRoads;
|
||||||
import net.osmand.plus.helpers.WaypointHelper;
|
import net.osmand.plus.helpers.WaypointHelper;
|
||||||
|
import net.osmand.plus.inapp.InAppHelper;
|
||||||
import net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu;
|
import net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu;
|
||||||
import net.osmand.plus.monitoring.LiveMonitoringHelper;
|
import net.osmand.plus.monitoring.LiveMonitoringHelper;
|
||||||
import net.osmand.plus.poi.PoiFiltersHelper;
|
import net.osmand.plus.poi.PoiFiltersHelper;
|
||||||
|
@ -157,6 +158,7 @@ public class OsmandApplication extends MultiDexApplication {
|
||||||
// targetPointsHelper.clearPointToNavigate(false);
|
// targetPointsHelper.clearPointToNavigate(false);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
InAppHelper.initialize(this);
|
||||||
initRemoteConfig();
|
initRemoteConfig();
|
||||||
startApplication();
|
startApplication();
|
||||||
System.out.println("Time to start application " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms");
|
System.out.println("Time to start application " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms");
|
||||||
|
|
|
@ -84,6 +84,7 @@ import net.osmand.plus.helpers.DiscountHelper;
|
||||||
import net.osmand.plus.helpers.ExternalApiHelper;
|
import net.osmand.plus.helpers.ExternalApiHelper;
|
||||||
import net.osmand.plus.helpers.GpxImportHelper;
|
import net.osmand.plus.helpers.GpxImportHelper;
|
||||||
import net.osmand.plus.helpers.WakeLockHelper;
|
import net.osmand.plus.helpers.WakeLockHelper;
|
||||||
|
import net.osmand.plus.inapp.InAppHelper;
|
||||||
import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
||||||
import net.osmand.plus.mapcontextmenu.MapContextMenuFragment;
|
import net.osmand.plus.mapcontextmenu.MapContextMenuFragment;
|
||||||
import net.osmand.plus.mapcontextmenu.other.DestinationReachedMenu;
|
import net.osmand.plus.mapcontextmenu.other.DestinationReachedMenu;
|
||||||
|
@ -180,6 +181,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
||||||
private boolean permissionGranted;
|
private boolean permissionGranted;
|
||||||
|
|
||||||
private boolean mIsDestroyed = false;
|
private boolean mIsDestroyed = false;
|
||||||
|
private InAppHelper inAppHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -929,6 +931,9 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
||||||
if (atlasMapRendererView != null) {
|
if (atlasMapRendererView != null) {
|
||||||
atlasMapRendererView.handleOnDestroy();
|
atlasMapRendererView.handleOnDestroy();
|
||||||
}
|
}
|
||||||
|
if (inAppHelper != null) {
|
||||||
|
inAppHelper.stop();
|
||||||
|
}
|
||||||
mIsDestroyed = true;
|
mIsDestroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,6 +1217,9 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (inAppHelper != null && inAppHelper.onActivityResultHandled(requestCode, resultCode, data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (ActivityResultListener listener : activityResultListeners) {
|
for (ActivityResultListener listener : activityResultListeners) {
|
||||||
if (listener.processResult(requestCode, resultCode, data)) {
|
if (listener.processResult(requestCode, resultCode, data)) {
|
||||||
removeActivityResultListener(listener);
|
removeActivityResultListener(listener);
|
||||||
|
@ -1538,4 +1546,39 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
||||||
NEW_IF_EXPIRED,
|
NEW_IF_EXPIRED,
|
||||||
CURRENT,
|
CURRENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InAppHelper execInAppTask(@NonNull InAppHelper.InAppRunnable runnable) {
|
||||||
|
if (inAppHelper != null) {
|
||||||
|
inAppHelper.stop();
|
||||||
|
}
|
||||||
|
inAppHelper = new InAppHelper(getMyApplication(), false);
|
||||||
|
inAppHelper.addListener(new InAppHelper.InAppListener() {
|
||||||
|
@Override
|
||||||
|
public void onError(String error) {
|
||||||
|
inAppHelper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGetItems() {
|
||||||
|
inAppHelper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemPurchased(String sku) {
|
||||||
|
inAppHelper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showProgress() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dismissProgress() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inAppHelper.exec(runnable);
|
||||||
|
return inAppHelper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import net.osmand.plus.OsmandSettings;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.Version;
|
import net.osmand.plus.Version;
|
||||||
import net.osmand.plus.activities.MapActivity;
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
import net.osmand.plus.inapp.InAppHelper;
|
||||||
|
import net.osmand.plus.liveupdates.OsmLiveActivity;
|
||||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -124,10 +126,8 @@ public class DiscountHelper {
|
||||||
settings.DISCOUNT_SHOW_DATETIME_MS.set(System.currentTimeMillis());
|
settings.DISCOUNT_SHOW_DATETIME_MS.set(System.currentTimeMillis());
|
||||||
showDiscountBanner(mapActivity, message, description, icon, url);
|
showDiscountBanner(mapActivity, message, description, icon, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logError("JSON parsing error: ", e);
|
logError("JSON parsing error: ", e);
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,8 @@ public class DiscountHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void showDiscountBanner(final MapActivity mapActivity, final String title, final String description, final String icon, final String url) {
|
private static void showDiscountBanner(final MapActivity mapActivity, final String title,
|
||||||
|
final String description, final String icon, final String url) {
|
||||||
final DiscountBarController toolbarController = new DiscountBarController();
|
final DiscountBarController toolbarController = new DiscountBarController();
|
||||||
toolbarController.setTitle(title);
|
toolbarController.setTitle(title);
|
||||||
toolbarController.setDescription(description);
|
toolbarController.setDescription(description);
|
||||||
|
@ -166,6 +167,7 @@ public class DiscountHelper {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
mapActivity.getMyApplication().logEvent(mapActivity, "motd_click");
|
mapActivity.getMyApplication().logEvent(mapActivity, "motd_click");
|
||||||
mBannerVisible = false;
|
mBannerVisible = false;
|
||||||
|
mapActivity.hideTopToolbar(toolbarController);
|
||||||
openUrl(mapActivity, url);
|
openUrl(mapActivity, url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -174,6 +176,7 @@ public class DiscountHelper {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
mapActivity.getMyApplication().logEvent(mapActivity, "motd_click");
|
mapActivity.getMyApplication().logEvent(mapActivity, "motd_click");
|
||||||
mBannerVisible = false;
|
mBannerVisible = false;
|
||||||
|
mapActivity.hideTopToolbar(toolbarController);
|
||||||
openUrl(mapActivity, url);
|
openUrl(mapActivity, url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -196,10 +199,26 @@ public class DiscountHelper {
|
||||||
mapActivity.showTopToolbar(toolbarController);
|
mapActivity.showTopToolbar(toolbarController);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void openUrl(MapActivity mapActivity, String url) {
|
private static void openUrl(final MapActivity mapActivity, String url) {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
if (url.startsWith("osmand-in-app:")) {
|
||||||
intent.setData(Uri.parse(url));
|
if (url.contains(InAppHelper.SKU_FULL_VERSION_PRICE)) {
|
||||||
mapActivity.startActivity(intent);
|
mapActivity.execInAppTask(new InAppHelper.InAppRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run(InAppHelper helper) {
|
||||||
|
helper.purchaseFullVersion(mapActivity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (url.contains(InAppHelper.SKU_LIVE_UPDATES)){
|
||||||
|
Intent intent = new Intent(mapActivity, OsmLiveActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||||
|
intent.putExtra(OsmLiveActivity.OPEN_SUBSCRIPTION_INTENT_PARAM, true);
|
||||||
|
mapActivity.startActivity(intent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
mapActivity.startActivity(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DiscountBarController extends TopToolbarController {
|
private static class DiscountBarController extends TopToolbarController {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package net.osmand.plus.inapp;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import net.osmand.AndroidNetworkUtils;
|
import net.osmand.AndroidNetworkUtils;
|
||||||
|
@ -41,10 +42,10 @@ public class InAppHelper {
|
||||||
private static long lastValidationCheckTime;
|
private static long lastValidationCheckTime;
|
||||||
private static String mFullVersionPrice;
|
private static String mFullVersionPrice;
|
||||||
|
|
||||||
private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
|
public static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
|
||||||
private static final String SKU_LIVE_UPDATES_FULL = "osm_live_subscription_2";
|
private static final String SKU_LIVE_UPDATES_FULL = "osm_live_subscription_2";
|
||||||
private static final String SKU_LIVE_UPDATES_FREE = "osm_free_live_subscription_2";
|
private static final String SKU_LIVE_UPDATES_FREE = "osm_free_live_subscription_2";
|
||||||
private static String SKU_LIVE_UPDATES;
|
public static String SKU_LIVE_UPDATES;
|
||||||
|
|
||||||
private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily
|
private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily
|
||||||
// (arbitrary) request code for the purchase flow
|
// (arbitrary) request code for the purchase flow
|
||||||
|
@ -60,6 +61,24 @@ public class InAppHelper {
|
||||||
private OsmandApplication ctx;
|
private OsmandApplication ctx;
|
||||||
private List<InAppListener> listeners = new ArrayList<>();
|
private List<InAppListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
|
||||||
|
* (that you got from the Google Play developer console). This is not your
|
||||||
|
* developer public key, it's the *app-specific* public key.
|
||||||
|
*
|
||||||
|
* Instead of just storing the entire literal string here embedded in the
|
||||||
|
* program, construct the key at runtime from pieces or
|
||||||
|
* use bit manipulation (for example, XOR with some other string) to hide
|
||||||
|
* the actual key. The key itself is not secret information, but we don't
|
||||||
|
* want to make it easy for an attacker to replace the public key with one
|
||||||
|
* of their own and then fake messages from the server.
|
||||||
|
*/
|
||||||
|
private static final String BASE64_ENCODED_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgk8cEx" +
|
||||||
|
"UO4mfEwWFLkQnX1Tkzehr4SnXLXcm2Osxs5FTJPEgyTckTh0POKVMrxeGLn0KoTY2NTgp1U/inp" +
|
||||||
|
"wccWisPhVPEmw9bAVvWsOkzlyg1kv03fJdnAXRBSqDDPV6X8Z3MtkPVqZkupBsxyIllEILKHK06" +
|
||||||
|
"OCw49JLTsMR3oTRifGzma79I71X0spw0fM+cIRlkS2tsXN8GPbdkJwHofZKPOXS51pgC1zU8uWX" +
|
||||||
|
"I+ftJO46a1XkNh1dO2anUiQ8P/H4yOTqnMsXF7biyYuiwjXPOcy0OMhEHi54Dq6Mr3u5ZALOAkc" +
|
||||||
|
"YTjh1H/ZgqIHy5ZluahINuDE76qdLYMXrDMQIDAQAB";
|
||||||
|
|
||||||
public interface InAppListener {
|
public interface InAppListener {
|
||||||
void onError(String error);
|
void onError(String error);
|
||||||
|
|
||||||
|
@ -72,6 +91,10 @@ public class InAppHelper {
|
||||||
void dismissProgress();
|
void dismissProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface InAppRunnable {
|
||||||
|
void run(InAppHelper helper);
|
||||||
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
@ -100,9 +123,7 @@ public class InAppHelper {
|
||||||
return !Algorithms.isEmpty(mLiveUpdatesPrice) && !Algorithms.isEmpty(mFullVersionPrice);
|
return !Algorithms.isEmpty(mLiveUpdatesPrice) && !Algorithms.isEmpty(mFullVersionPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InAppHelper(OsmandApplication ctx, boolean forceRequestInventory) {
|
public static void initialize(OsmandApplication ctx) {
|
||||||
this.ctx = ctx;
|
|
||||||
this.forceRequestInventory = forceRequestInventory;
|
|
||||||
if (SKU_LIVE_UPDATES == null) {
|
if (SKU_LIVE_UPDATES == null) {
|
||||||
if (Version.isFreeVersion(ctx)) {
|
if (Version.isFreeVersion(ctx)) {
|
||||||
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FREE;
|
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FREE;
|
||||||
|
@ -110,6 +131,12 @@ public class InAppHelper {
|
||||||
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FULL;
|
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InAppHelper(OsmandApplication ctx, boolean forceRequestInventory) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.forceRequestInventory = forceRequestInventory;
|
||||||
|
|
||||||
isDeveloperVersion = Version.isDeveloperVersion(ctx);
|
isDeveloperVersion = Version.isDeveloperVersion(ctx);
|
||||||
if (isDeveloperVersion) {
|
if (isDeveloperVersion) {
|
||||||
mSubscribedToLiveUpdates = true;
|
mSubscribedToLiveUpdates = true;
|
||||||
|
@ -119,29 +146,47 @@ public class InAppHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(final boolean stopAfterResult) {
|
public void exec(final @NonNull InAppRunnable runnable) {
|
||||||
this.stopAfterResult = stopAfterResult;
|
this.stopAfterResult = true;
|
||||||
/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
|
|
||||||
* (that you got from the Google Play developer console). This is not your
|
|
||||||
* developer public key, it's the *app-specific* public key.
|
|
||||||
*
|
|
||||||
* Instead of just storing the entire literal string here embedded in the
|
|
||||||
* program, construct the key at runtime from pieces or
|
|
||||||
* use bit manipulation (for example, XOR with some other string) to hide
|
|
||||||
* the actual key. The key itself is not secret information, but we don't
|
|
||||||
* want to make it easy for an attacker to replace the public key with one
|
|
||||||
* of their own and then fake messages from the server.
|
|
||||||
*/
|
|
||||||
String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgk8cEx" +
|
|
||||||
"UO4mfEwWFLkQnX1Tkzehr4SnXLXcm2Osxs5FTJPEgyTckTh0POKVMrxeGLn0KoTY2NTgp1U/inp" +
|
|
||||||
"wccWisPhVPEmw9bAVvWsOkzlyg1kv03fJdnAXRBSqDDPV6X8Z3MtkPVqZkupBsxyIllEILKHK06" +
|
|
||||||
"OCw49JLTsMR3oTRifGzma79I71X0spw0fM+cIRlkS2tsXN8GPbdkJwHofZKPOXS51pgC1zU8uWX" +
|
|
||||||
"I+ftJO46a1XkNh1dO2anUiQ8P/H4yOTqnMsXF7biyYuiwjXPOcy0OMhEHi54Dq6Mr3u5ZALOAkc" +
|
|
||||||
"YTjh1H/ZgqIHy5ZluahINuDE76qdLYMXrDMQIDAQAB";
|
|
||||||
|
|
||||||
// Create the helper, passing it our context and the public key to verify signatures with
|
// Create the helper, passing it our context and the public key to verify signatures with
|
||||||
logDebug("Creating InAppHelper.");
|
logDebug("Creating InAppHelper.");
|
||||||
mHelper = new IabHelper(ctx, base64EncodedPublicKey);
|
mHelper = new IabHelper(ctx, BASE64_ENCODED_PUBLIC_KEY);
|
||||||
|
|
||||||
|
// enable debug logging (for a production application, you should set this to false).
|
||||||
|
mHelper.enableDebugLogging(false);
|
||||||
|
|
||||||
|
// Start setup. This is asynchronous and the specified listener
|
||||||
|
// will be called once setup completes.
|
||||||
|
logDebug("Starting setup.");
|
||||||
|
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
|
||||||
|
public void onIabSetupFinished(IabResult result) {
|
||||||
|
logDebug("Setup finished.");
|
||||||
|
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
// Oh noes, there was a problem.
|
||||||
|
complain("Problem setting up in-app billing: " + result);
|
||||||
|
notifyError(result.getMessage());
|
||||||
|
if (stopAfterResult) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have we been disposed of in the meantime? If so, quit.
|
||||||
|
if (mHelper == null) return;
|
||||||
|
|
||||||
|
runnable.run(InAppHelper.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(final boolean stopAfterResult) {
|
||||||
|
this.stopAfterResult = stopAfterResult;
|
||||||
|
|
||||||
|
// Create the helper, passing it our context and the public key to verify signatures with
|
||||||
|
logDebug("Creating InAppHelper.");
|
||||||
|
mHelper = new IabHelper(ctx, BASE64_ENCODED_PUBLIC_KEY);
|
||||||
|
|
||||||
// enable debug logging (for a production application, you should set this to false).
|
// enable debug logging (for a production application, you should set this to false).
|
||||||
mHelper.enableDebugLogging(false);
|
mHelper.enableDebugLogging(false);
|
||||||
|
@ -601,15 +646,15 @@ public class InAppHelper {
|
||||||
ctx.showToastMessage(message);
|
ctx.showToastMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logDebug(String msg) {
|
private void logDebug(String msg) {
|
||||||
if (mDebugLog) Log.d(TAG, msg);
|
if (mDebugLog) Log.d(TAG, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logError(String msg) {
|
private void logError(String msg) {
|
||||||
Log.e(TAG, msg);
|
Log.e(TAG, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logError(String msg, Throwable e) {
|
private void logError(String msg, Throwable e) {
|
||||||
Log.e(TAG, "Error: " + msg, e);
|
Log.e(TAG, "Error: " + msg, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue