From 66020fff349a921f611d404caa96a259f137e164 Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Mon, 25 Apr 2022 11:15:22 +0300 Subject: [PATCH 1/5] Sandwich integration Android and Unity (#75) * Android sandwich integration. * Integration fixes. * iOS mapping fixes. * Compile fixes. * CR fixes * CR fixes. * Upgrade Qonversion Sandwich dependency version. --- Editor/QonversionDependencies.xml | 4 +- .../unitywrapper/AutomationsDelegate.java | 73 ----- .../unitywrapper/AutomationsWrapper.java | 75 +++++ ...java.meta => AutomationsWrapper.java.meta} | 0 .../com/qonversion/unitywrapper/Mapper.java | 276 ----------------- .../qonversion/unitywrapper/Mapper.java.meta | 32 -- .../unitywrapper/QonversionWrapper.java | 283 +++++------------- Runtime/Android/QonversionWrapperAndroid.cs | 35 +-- Runtime/Scripts/Constants.cs | 3 - Runtime/Scripts/IQonversionWrapper.cs | 15 +- Runtime/Scripts/Mapper.cs | 30 +- Runtime/Scripts/Models/AutomationsEvent.cs | 4 +- Runtime/Scripts/Models/Eligibility.cs | 6 +- Runtime/Scripts/Models/Permission.cs | 10 +- Runtime/Scripts/Models/Product.cs | 53 ++-- Runtime/Scripts/Models/QonversionError.cs | 18 +- Runtime/Scripts/Models/SkProduct/SKProduct.cs | 11 +- .../Models/SkProduct/SKProductDiscount.cs | 11 +- Runtime/Scripts/Qonversion.cs | 78 ++--- Runtime/Scripts/QonversionWrapperNoop.cs | 30 +- Runtime/iOS/Plugins/QonversionBridge.m | 2 +- Runtime/iOS/QonversionWrapperIOS.cs | 76 ++--- 22 files changed, 313 insertions(+), 812 deletions(-) delete mode 100644 Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java create mode 100644 Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java rename Runtime/Android/Plugins/com/qonversion/unitywrapper/{AutomationsDelegate.java.meta => AutomationsWrapper.java.meta} (100%) delete mode 100644 Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java delete mode 100644 Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java.meta diff --git a/Editor/QonversionDependencies.xml b/Editor/QonversionDependencies.xml index 109266c..e7cb674 100644 --- a/Editor/QonversionDependencies.xml +++ b/Editor/QonversionDependencies.xml @@ -1,11 +1,11 @@ - + - + diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java b/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java deleted file mode 100644 index d00648a..0000000 --- a/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.qonversion.unitywrapper; - -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.qonversion.android.sdk.automations.Automations; -import com.qonversion.android.sdk.automations.QActionResult; - -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.Map; - -public class AutomationsDelegate implements com.qonversion.android.sdk.automations.AutomationsDelegate { - private static final String EVENT_SCREEN_SHOWN = "OnAutomationsScreenShown"; - private static final String EVENT_ACTION_STARTED = "OnAutomationsActionStarted"; - private static final String EVENT_ACTION_FAILED = "OnAutomationsActionFailed"; - private static final String EVENT_ACTION_FINISHED = "OnAutomationsActionFinished"; - private static final String EVENT_AUTOMATIONS_FINISHED = "OnAutomationsFinished"; - - public static String TAG = "AutomationsDelegate"; - private final MessageSender messageSender; - - public AutomationsDelegate(MessageSender messageSender) { - this.messageSender = messageSender; - Automations.setDelegate(this); - } - - private void sendMessageToUnity(@NotNull Object objectToConvert, @NotNull String methodName) { - try { - messageSender.sendMessageToUnity(objectToConvert, methodName); - } catch (JsonProcessingException e) { - handleException(e); - } - } - - private void handleException(Exception e) { - Log.e(TAG, "An error occurred while processing automations flow: " + e.getLocalizedMessage()); - } - - @Override - public void automationsDidShowScreen(@NonNull String screenId) { - Map result = new HashMap<>(); - result.put("screenID", screenId); - sendMessageToUnity(result, EVENT_SCREEN_SHOWN); - } - - @Override - public void automationsDidStartExecuting(@NonNull QActionResult actionResult) { - Map result = Mapper.mapActionResult(actionResult); - sendMessageToUnity(result, EVENT_ACTION_STARTED); - } - - @Override - public void automationsDidFailExecuting(@NonNull QActionResult actionResult) { - Map result = Mapper.mapActionResult(actionResult); - sendMessageToUnity(result, EVENT_ACTION_FAILED); - } - - @Override - public void automationsDidFinishExecuting(@NonNull QActionResult actionResult) { - Map result = Mapper.mapActionResult(actionResult); - sendMessageToUnity(result, EVENT_ACTION_FINISHED); - } - - @Override - public void automationsFinished() { - Map result = new HashMap<>(); - sendMessageToUnity(result, EVENT_AUTOMATIONS_FINISHED); - } -} diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java b/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java new file mode 100644 index 0000000..3214d1c --- /dev/null +++ b/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java @@ -0,0 +1,75 @@ +package com.qonversion.unitywrapper; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +import io.qonversion.sandwich.AutomationsEventListener; +import io.qonversion.sandwich.AutomationsSandwich; + +public class AutomationsWrapper implements AutomationsEventListener { + private static final String EVENT_SCREEN_SHOWN = "OnAutomationsScreenShown"; + private static final String EVENT_ACTION_STARTED = "OnAutomationsActionStarted"; + private static final String EVENT_ACTION_FAILED = "OnAutomationsActionFailed"; + private static final String EVENT_ACTION_FINISHED = "OnAutomationsActionFinished"; + private static final String EVENT_AUTOMATIONS_FINISHED = "OnAutomationsFinished"; + + public static String TAG = "AutomationsDelegate"; + private final MessageSender messageSender; + private final AutomationsSandwich automationsSandwich; + + public AutomationsWrapper(MessageSender messageSender) { + this.messageSender = messageSender; + automationsSandwich = new AutomationsSandwich(); + } + + public void subscribe() { + automationsSandwich.subscribe(this); + } + + @Override + public void onAutomationEvent(@NonNull Event event, @Nullable Map data) { + String methodName = ""; + switch (event) { + case ScreenShown: + methodName = EVENT_SCREEN_SHOWN; + break; + case ActionStarted: + methodName = EVENT_ACTION_STARTED; + break; + case ActionFinished: + methodName = EVENT_ACTION_FINISHED; + break; + case ActionFailed: + methodName = EVENT_ACTION_FAILED; + break; + case AutomationsFinished: + methodName = EVENT_AUTOMATIONS_FINISHED; + break; + default: + return; + } + + sendMessageToUnity(data == null ? new HashMap<>() : data, methodName); + } + + private void sendMessageToUnity(@NotNull Object objectToConvert, @NotNull String methodName) { + try { + messageSender.sendMessageToUnity(objectToConvert, methodName); + } catch (JsonProcessingException e) { + handleException(e); + } + } + + private void handleException(Exception e) { + Log.e(TAG, "An error occurred while processing automations flow: " + e.getLocalizedMessage()); + } +} diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java.meta b/Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java.meta similarity index 100% rename from Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsDelegate.java.meta rename to Runtime/Android/Plugins/com/qonversion/unitywrapper/AutomationsWrapper.java.meta diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java b/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java deleted file mode 100644 index 14adfb8..0000000 --- a/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java +++ /dev/null @@ -1,276 +0,0 @@ -package com.qonversion.unitywrapper; - -import androidx.annotation.Nullable; - -import com.android.billingclient.api.SkuDetails; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.qonversion.android.sdk.QonversionError; -import com.qonversion.android.sdk.automations.AutomationsEvent; -import com.qonversion.android.sdk.automations.QActionResult; -import com.qonversion.android.sdk.dto.QPermission; -import com.qonversion.android.sdk.dto.eligibility.QEligibility; -import com.qonversion.android.sdk.dto.offerings.QOffering; -import com.qonversion.android.sdk.dto.offerings.QOfferings; -import com.qonversion.android.sdk.dto.products.QProduct; -import com.qonversion.android.sdk.dto.products.QProductDuration; -import com.qonversion.android.sdk.dto.products.QProductType; -import com.qonversion.android.sdk.dto.products.QTrialDuration; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public final class Mapper { - private static final String ID = "id"; - private static final String STORE_ID = "store_id"; - private static final String TYPE = "type"; - private static final String OFFERING_ID = "offeringId"; - private static final String DURATION = "duration"; - private static final String TRIAL_DURATION = "trialDuration"; - private static final String STORE_PRODUCT = "storeProduct"; - private static final String PRETTY_PRICE = "prettyPrice"; - private static final String SKU_DETAILS_JSON = "originalJson"; - - static List> mapPermissions(Map permissions) { - List> result = new ArrayList<>(); - - for (Map.Entry entry : permissions.entrySet()) { - Map permissionMap = new HashMap<>(); - QPermission permission = entry.getValue(); - - permissionMap.put("id", permission.getPermissionID()); - permissionMap.put("associated_product", permission.getProductID()); - permissionMap.put("renew_state", permission.getRenewState().getType()); - permissionMap.put("active", permission.isActive()); - permissionMap.put("started_timestamp", permission.getStartedDate().getTime()); - - Date expirationDate = permission.getExpirationDate(); - if (expirationDate != null) { - permissionMap.put("expiration_timestamp", expirationDate.getTime()); - } - - result.add(permissionMap); - } - - return result; - } - - static List> mapProducts(Map products) { - List> result = new ArrayList<>(); - - for (Map.Entry entry : products.entrySet()) { - Map mappedProducts = Mapper.mapProduct(entry.getValue()); - result.add(mappedProducts); - } - - return result; - } - - static Map mapProduct(QProduct product) { - Map result = new HashMap<>(); - - result.put(ID, product.getQonversionID()); - result.put(STORE_ID, product.getStoreID()); - result.put(TYPE, product.getType().getType()); - - String offeringId = product.getOfferingID(); - result.put(OFFERING_ID, offeringId); - - QProductDuration duration = product.getDuration(); - if (duration != null) { - result.put(DURATION, duration.getType()); - } - - QTrialDuration trialDuration = product.getTrialDuration(); - if (trialDuration != null) { - result.put(TRIAL_DURATION, trialDuration.getType()); - } - - SkuDetails skuDetails = product.getSkuDetail(); - if (skuDetails != null) { - Map mappedSkuDetails = mapSkuDetails(skuDetails); - result.put(STORE_PRODUCT, mappedSkuDetails); - result.put(PRETTY_PRICE, product.getPrettyPrice()); - } - - return result; - } - - static QProduct mapProductFromJson(String productJson) throws Exception { - ObjectMapper mapper = new ObjectMapper(); - - TypeReference> typeRef = new TypeReference>() {}; - Map map = mapper.readValue(productJson, typeRef); - - String id = String.valueOf(map.get(ID)); - String storeId = String.valueOf(map.get(STORE_ID)); - String offeringId = String.valueOf(map.get(OFFERING_ID)); - String prettyPrice = String.valueOf(map.get(PRETTY_PRICE)); - - Object skuDetailsObj = map.get(STORE_PRODUCT); - SkuDetails skuDetails = mapSkuDetailsFromObject(skuDetailsObj); - - Integer type = (Integer) map.get(TYPE); - if (type == null) return null; - QProductType qProductType = QProductType.Companion.fromType(type); - - QProductDuration qProductDuration = null; - Integer duration = (Integer) map.get(DURATION); - if (duration != null) { - qProductDuration = QProductDuration.Companion.fromType(duration); - } - - QTrialDuration qTrialDuration = null; - Integer trialDuration = (Integer) map.get(TRIAL_DURATION); - if (trialDuration != null) { - qTrialDuration = QTrialDuration.Companion.fromType(trialDuration); - } - - QProduct product = new QProduct(id, storeId, qProductType, qProductDuration); - product.setOfferingID(offeringId); - product.setSkuDetail(skuDetails); - product.setPrettyPrice(prettyPrice); - product.setTrialDuration(qTrialDuration); - - return product; - } - - static private SkuDetails mapSkuDetailsFromObject(Object map) throws Exception { - ObjectMapper mapper = new ObjectMapper(); - TypeReference> typeRef = new TypeReference>() { - }; - - Map skuDetailsMap = mapper.convertValue(map, typeRef); - String originalJson = String.valueOf(skuDetailsMap.get(SKU_DETAILS_JSON)); - - return new SkuDetails(originalJson); - } - - static private Map mapSkuDetails(SkuDetails skuDetails) { - Map result = new HashMap<>(); - - result.put("description", skuDetails.getDescription()); - result.put("freeTrialPeriod", skuDetails.getFreeTrialPeriod()); - result.put("iconUrl", skuDetails.getIconUrl()); - result.put("introductoryPrice", skuDetails.getIntroductoryPrice()); - result.put("introductoryPriceAmountMicros", skuDetails.getIntroductoryPriceAmountMicros()); - result.put("introductoryPriceCycles", skuDetails.getIntroductoryPriceCycles()); - result.put("introductoryPricePeriod", skuDetails.getIntroductoryPricePeriod()); - result.put("originalJson", skuDetails.getOriginalJson()); - result.put("originalPrice", skuDetails.getOriginalPrice()); - result.put("originalPriceAmountMicros", skuDetails.getOriginalPriceAmountMicros()); - result.put("price", skuDetails.getPrice()); - result.put("priceAmountMicros", skuDetails.getPriceAmountMicros()); - result.put("priceCurrencyCode", skuDetails.getPriceCurrencyCode()); - result.put("sku", skuDetails.getSku()); - result.put("subscriptionPeriod", skuDetails.getSubscriptionPeriod()); - result.put("title", skuDetails.getTitle()); - result.put("type", skuDetails.getType()); - result.put("hashCode", skuDetails.hashCode()); - - return result; - } - - static Map mapOfferings(QOfferings offerings) { - Map result = new HashMap<>(); - - if (offerings.getMain() != null) { - Map mainOffering = Mapper.mapOffering(offerings.getMain()); - result.put("main", mainOffering); - } - - List> availableOfferings = new ArrayList<>(); - - for (QOffering offering : offerings.getAvailableOfferings()) { - Map mappedOffering = Mapper.mapOffering(offering); - availableOfferings.add(mappedOffering); - } - - result.put("availableOfferings", availableOfferings); - - return result; - } - - private static Map mapOffering(QOffering offering) { - Map result = new HashMap<>(); - result.put("id", offering.getOfferingID()); - - Integer tagValue = offering.getTag().getTag(); - if (tagValue != null) { - result.put("tag", tagValue); - } - - List> mappedProducts = new ArrayList<>(); - - for (QProduct product : offering.getProducts()) { - Map mappedProduct = Mapper.mapProduct(product); - mappedProducts.add(mappedProduct); - } - - result.put("products", mappedProducts); - - return result; - } - - static List> mapEligibilities(Map map) { - List> result = new ArrayList<>(); - - for (Map.Entry entry : map.entrySet()) { - Map mappedEligibility = new HashMap<>(); - QEligibility eligibility = entry.getValue(); - mappedEligibility.put("productId", entry.getKey()); - mappedEligibility.put("status", eligibility.getStatus().getType()); - - result.add(mappedEligibility); - } - - return result; - } - - static Map mapActionResult(QActionResult actionResult) { - Map result = new HashMap<>(); - result.put("type", actionResult.getType().getType()); - result.put("error", Mapper.mapQonversionError(actionResult.getError())); - result.put("value", mapStringsMap(actionResult.getValue())); - - return result; - } - - static Map mapAutomationsEvent(AutomationsEvent automationsEvent) { - Map result = new HashMap<>(); - - result.put("type", automationsEvent.getType().getType()); - result.put("timestamp", (double)automationsEvent.getDate().getTime()); - return result; - } - - static Map mapQonversionError(@Nullable QonversionError error) { - if (error == null) { - return null; - } - - Map result = new HashMap<>(); - result.put("code", error.getCode().toString()); - result.put("description", error.getDescription()); - result.put("additionalMessage", error.getAdditionalMessage()); - - return result; - } - - static Map mapStringsMap(@Nullable Map map) { - if (map == null) { - return null; - } - - Map result = new HashMap<>(); - for (Map.Entry entry : map.entrySet()) { - result.put(entry.getKey(), entry.getValue()); - } - - return result; - } -} diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java.meta b/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java.meta deleted file mode 100644 index 3f5bdb9..0000000 --- a/Runtime/Android/Plugins/com/qonversion/unitywrapper/Mapper.java.meta +++ /dev/null @@ -1,32 +0,0 @@ -fileFormatVersion: 2 -guid: a7d66b4ac0f984c55983e2f5f0022c58 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 1 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Android: Android - second: - enabled: 1 - settings: {} - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java b/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java index 6e5465a..888f09c 100644 --- a/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java +++ b/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java @@ -1,22 +1,10 @@ package com.qonversion.unitywrapper; -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; +import android.app.Application; import android.util.Log; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.core.JsonProcessingException; -import com.qonversion.android.sdk.QUserProperties; - -import com.qonversion.android.sdk.QonversionEligibilityCallback; -import com.qonversion.android.sdk.QonversionErrorCode; -import com.qonversion.android.sdk.QonversionOfferingsCallback; -import com.qonversion.android.sdk.QonversionProductsCallback; -import com.qonversion.android.sdk.UpdatedPurchasesListener; -import com.qonversion.android.sdk.dto.eligibility.QEligibility; -import com.qonversion.android.sdk.dto.offerings.QOfferings; -import com.qonversion.android.sdk.dto.products.QProduct; import com.unity3d.player.UnityPlayer; import org.jetbrains.annotations.NotNull; @@ -28,268 +16,124 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.qonversion.android.sdk.AttributionSource; -import com.qonversion.android.sdk.Qonversion; -import com.qonversion.android.sdk.QonversionError; -import com.qonversion.android.sdk.QonversionLaunchCallback; -import com.qonversion.android.sdk.QonversionPermissionsCallback; -import com.qonversion.android.sdk.dto.QLaunchResult; -import com.qonversion.android.sdk.dto.QPermission; +import io.qonversion.sandwich.QonversionSandwich; +import io.qonversion.sandwich.ResultListener; +import io.qonversion.sandwich.SandwichError; -import android.preference.PreferenceManager; +import androidx.annotation.NonNull; public class QonversionWrapper { public static String TAG = "QonversionWrapper"; public static String ON_UPDATED_PURCHASES_LISTENER = "OnReceiveUpdatedPurchases"; - private static UpdatedPurchasesListener updatedPurchasesListener = null; private static MessageSender messageSender; - private static AutomationsDelegate automationsDelegate = null; - - public static synchronized void storeSdkInfo(String version, String versionKey, String source, String sourceKey) { - Context context = UnityPlayer.currentActivity.getApplicationContext(); - - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - editor.putString(versionKey, version); - editor.putString(sourceKey, source); - editor.apply(); - } - - public static synchronized void launch(String unityListener, String projectKey, boolean observerMode) { - Log.d(TAG, "Qonversion Launch starting. Project key: " + projectKey); + private static AutomationsWrapper automationsWrapper; + private static QonversionSandwich qonversionSandwich; + public static synchronized void initialize(String unityListener) { messageSender = new MessageSender(unityListener); - Activity unityActivity = UnityPlayer.currentActivity; + final Application application = UnityPlayer.currentActivity.getApplication(); + qonversionSandwich = new QonversionSandwich( + application, + () -> UnityPlayer.currentActivity, + permissions -> sendMessageToUnity(permissions, ON_UPDATED_PURCHASES_LISTENER) + ); + automationsWrapper = new AutomationsWrapper(messageSender); + } - Qonversion.launch(unityActivity.getApplication(), projectKey, observerMode, new QonversionLaunchCallback() { - @Override - public void onSuccess(@NotNull QLaunchResult launchResult) { - Log.d(TAG, "Qonversion initialized. UID: " + launchResult.getUid()); - } + public static synchronized void storeSdkInfo(String version, String source) { + qonversionSandwich.storeSdkInfo(source, version); + } - @Override - public void onError(@NotNull QonversionError qonversionError) { - Log.d(TAG, "Qonversion initializing error: " + qonversionError.getCode() + ", " + qonversionError.getDescription() + ", " + qonversionError.getAdditionalMessage()); - } - }); + public static synchronized void launch(String projectKey, boolean observerMode, String unityCallbackName) { + qonversionSandwich.launch(projectKey, observerMode, getResultListener(unityCallbackName)); } public static synchronized void syncPurchases() { - Qonversion.syncPurchases(); + qonversionSandwich.syncPurchases(); } public static synchronized void setDebugMode() { - Qonversion.setDebugMode(); + qonversionSandwich.setDebugMode(); } public static synchronized void setProperty(String key, String value) { - try { - QUserProperties enumKey = QUserProperties.valueOf(key); - Qonversion.setProperty(enumKey, value); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Failed to map QUserProperties. " + e.getLocalizedMessage()); - } + qonversionSandwich.setDefinedProperty(key, value); } public static synchronized void setUserProperty(String key, String value) { - Qonversion.setUserProperty(key, value); + qonversionSandwich.setCustomProperty(key, value); } public static synchronized void attribution(String conversionData, String attributionSource) { try { - AttributionSource source = AttributionSource.valueOf(attributionSource); - ObjectMapper mapper = new ObjectMapper(); TypeReference> typeRef - = new TypeReference>() { - }; + = new TypeReference>() {}; Map conversionInfo = mapper.readValue(conversionData, typeRef); - Qonversion.attribution(conversionInfo, source); - Log.d(TAG, "Attribution sent"); - } catch (Exception e) { - Log.e(TAG, "pushAttribution error: " + e.getLocalizedMessage()); + qonversionSandwich.addAttributionData(attributionSource, conversionInfo); + } catch (JsonProcessingException e) { + handleSerializationException(e); } } public static synchronized void identify(String value) { - Qonversion.identify(value); + qonversionSandwich.identify(value); } public static synchronized void logout() { - Qonversion.logout(); + qonversionSandwich.logout(); } public static synchronized void checkPermissions(String unityCallbackName) { - Qonversion.checkPermissions(new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.checkPermissions(getResultListener(unityCallbackName)); } public static synchronized void purchase(String productId, String unityCallbackName) { - Qonversion.purchase(UnityPlayer.currentActivity, productId, new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.purchase(productId, getResultListener(unityCallbackName)); } - public static synchronized void purchaseProduct(String productJson, String unityCallbackName) { - try { - QProduct product = Mapper.mapProductFromJson(productJson); - if (product == null) { - QonversionError error = new QonversionError(QonversionErrorCode.PurchaseInvalid, "Qonversion Product is null"); - handleErrorResponse(error, unityCallbackName); - return; - } - Qonversion.purchase(UnityPlayer.currentActivity, product, new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); - } catch (Exception e) { - handleException(e); - } + public static synchronized void purchaseProduct(String productId, String offeringId, String unityCallbackName) { + qonversionSandwich.purchaseProduct(productId, offeringId, getResultListener(unityCallbackName)); } public static synchronized void updatePurchase(String productId, String oldProductId, int prorationMode, String unityCallbackName) { - Qonversion.updatePurchase(UnityPlayer.currentActivity, productId, oldProductId, prorationMode, new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.updatePurchase(productId, oldProductId, prorationMode, getResultListener(unityCallbackName)); } - public static synchronized void updatePurchaseWithProduct(String newProductJson, String oldProductId, int prorationMode, String unityCallbackName) { - try { - QProduct product = Mapper.mapProductFromJson(newProductJson); - if (product == null) { - QonversionError error = new QonversionError(QonversionErrorCode.PurchaseInvalid, "Qonversion Product is null"); - handleErrorResponse(error, unityCallbackName); - return; - } - Qonversion.updatePurchase(UnityPlayer.currentActivity, product, oldProductId, prorationMode, new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); - } catch (Exception e) { - handleException(e); - } + public static synchronized void updatePurchaseWithProduct(String productId, String offeringId, String oldProductId, int prorationMode, String unityCallbackName) { + qonversionSandwich.updatePurchaseWithProduct(productId, offeringId, oldProductId, prorationMode, getResultListener(unityCallbackName)); } public static synchronized void restore(String unityCallbackName) { - Qonversion.restore(new QonversionPermissionsCallback() { - @Override - public void onSuccess(@NotNull Map permissions) { - handlePermissionsResponse(permissions, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.restore(getResultListener(unityCallbackName)); } public static synchronized void products(String unityCallbackName) { - Qonversion.products(new QonversionProductsCallback() { - @Override - public void onSuccess(@NotNull Map products) { - List> mappedProducts = Mapper.mapProducts(products); - sendMessageToUnity(mappedProducts, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.products(getResultListener(unityCallbackName)); } public static synchronized void offerings(String unityCallbackName) { - Qonversion.offerings(new QonversionOfferingsCallback() { - @Override - public void onSuccess(@NotNull QOfferings qOfferings) { - Map mappedOfferings = Mapper.mapOfferings(qOfferings); - sendMessageToUnity(mappedOfferings, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + qonversionSandwich.offerings(getResultListener(unityCallbackName)); } public static synchronized void checkTrialIntroEligibilityForProductIds(String productIds, String unityCallbackName) { try { ObjectMapper mapper = new ObjectMapper(); TypeReference> typeRef = new TypeReference>() {}; - List productIdsList = mapper.readValue(productIds, typeRef); - - Qonversion.checkTrialIntroEligibilityForProductIds(productIdsList, new QonversionEligibilityCallback() { - @Override - public void onSuccess(@NotNull Map eligibilities) { - List> mappedEligibilities = Mapper.mapEligibilities(eligibilities); - sendMessageToUnity(mappedEligibilities, unityCallbackName); - } - - @Override - public void onError(@NotNull QonversionError error) { - handleErrorResponse(error, unityCallbackName); - } - }); + List productIdList = mapper.readValue(productIds, typeRef); + + qonversionSandwich.checkTrialIntroEligibility(productIdList, getResultListener(unityCallbackName)); } catch (JsonProcessingException e) { - handleException(e); + handleSerializationException(e); } } - public static synchronized void addUpdatedPurchasesDelegate() { - updatedPurchasesListener = permissions -> handlePermissionsResponse(permissions, ON_UPDATED_PURCHASES_LISTENER); - Qonversion.setUpdatedPurchasesListener(updatedPurchasesListener); - } - - public static synchronized void removeUpdatedPurchasesDelegate() { - updatedPurchasesListener = null; - } - public static synchronized void setNotificationsToken(String token) { - Qonversion.setNotificationsToken(token); + qonversionSandwich.setNotificationToken(token); } public static synchronized boolean handleNotification(String notification) { @@ -301,7 +145,7 @@ public static synchronized boolean handleNotification(String notification) { }; Map notificationInfo = mapper.readValue(notification, typeRef); - boolean result = Qonversion.handleNotification(notificationInfo); + boolean result = qonversionSandwich.handleNotification(notificationInfo); return result; } catch (Exception e) { @@ -309,24 +153,33 @@ public static synchronized boolean handleNotification(String notification) { } } - public static synchronized void subscribeAutomationsDelegate() { - automationsDelegate = new AutomationsDelegate(messageSender); + public static synchronized void subscribeOnAutomationEvents() { + automationsWrapper.subscribe(); } - private static void handlePermissionsResponse(@NotNull Map permissions, @NotNull String methodName) { - List> mappedPermissions = Mapper.mapPermissions(permissions); - sendMessageToUnity(mappedPermissions, methodName); + private static ResultListener getResultListener(@NotNull String methodName) { + return new ResultListener() { + @Override + public void onSuccess(@NonNull Map data) { + sendMessageToUnity(data, methodName); + } + + @Override + public void onError(@NonNull SandwichError error) { + handleErrorResponse(error, methodName); + } + }; } - private static void handleErrorResponse(@NotNull QonversionError error, @NotNull String methodName) { + private static void handleErrorResponse(@NotNull SandwichError error, @NotNull String methodName) { ObjectMapper mapper = new ObjectMapper(); - ObjectNode rootNode = mapper.createObjectNode(); - String message = String.format("%s. %s", error.getDescription(), error.getAdditionalMessage()); ObjectNode errorNode = mapper.createObjectNode(); - errorNode.put("message", message); - errorNode.put("code", error.getCode().name()); + errorNode.put("code", error.getCode()); + errorNode.put("description", error.getDescription()); + errorNode.put("additionalMessage", error.getAdditionalMessage()); + ObjectNode rootNode = mapper.createObjectNode(); rootNode.set("error", errorNode); sendMessageToUnity(rootNode, methodName); @@ -336,11 +189,11 @@ private static void sendMessageToUnity(@NotNull Object objectToConvert, @NotNull try { messageSender.sendMessageToUnity(objectToConvert, methodName); } catch (JsonProcessingException e) { - handleException(e); + handleSerializationException(e); } } - private static void handleException(Exception e) { + private static void handleSerializationException(JsonProcessingException e) { Log.e(TAG, "An error occurred while serializing data: " + e.getLocalizedMessage()); } } \ No newline at end of file diff --git a/Runtime/Android/QonversionWrapperAndroid.cs b/Runtime/Android/QonversionWrapperAndroid.cs index 672193c..b45b544 100644 --- a/Runtime/Android/QonversionWrapperAndroid.cs +++ b/Runtime/Android/QonversionWrapperAndroid.cs @@ -5,14 +5,19 @@ namespace QonversionUnity { internal class QonversionWrapperAndroid : IQonversionWrapper { - public void StoreSdkInfo(string version, string versionKey, string source, string sourceKey) + public void Initialize(string gameObjectName) { - CallQonversion("storeSdkInfo", version, versionKey, source, sourceKey); + CallQonversion("initialize", gameObjectName); } - public void Launch(string gameObjectName, string projectKey, bool observerMode) + public void StoreSdkInfo(string version, string source) { - CallQonversion("launch", gameObjectName, projectKey, observerMode); + CallQonversion("storeSdkInfo", version, source); + } + + public void Launch(string projectKey, bool observerMode, string callbackName) + { + CallQonversion("launch", projectKey, observerMode, callbackName); } public void SetDebugMode() @@ -104,9 +109,9 @@ public void Purchase(string productId, string callbackName) CallQonversion("purchase", productId, callbackName); } - public void PurchaseProduct(string productJson, string callbackName) + public void PurchaseProduct(string productId, string offeringId, string callbackName) { - CallQonversion("purchaseProduct", productJson, callbackName); + CallQonversion("purchaseProduct", productId, offeringId, callbackName); } public void Restore(string callbackName) @@ -119,9 +124,9 @@ public void UpdatePurchase(string productId, string oldProductId, ProrationMode CallQonversion("updatePurchase", productId, oldProductId, (int)prorationMode, callbackName); } - public void UpdatePurchaseWithProduct(string productJson, string oldProductId, ProrationMode prorationMode, string callbackName) + public void UpdatePurchaseWithProduct(string productId, string offeringId, string oldProductId, ProrationMode prorationMode, string callbackName) { - CallQonversion("updatePurchaseWithProduct", productJson, oldProductId, (int)prorationMode, callbackName); + CallQonversion("updatePurchaseWithProduct", productId, offeringId, oldProductId, (int)prorationMode, callbackName); } public void Products(string callbackName) @@ -139,16 +144,6 @@ public void CheckTrialIntroEligibilityForProductIds(string productIdsJson, strin CallQonversion("checkTrialIntroEligibilityForProductIds", productIdsJson, callbackName); } - public void AddUpdatedPurchasesDelegate() - { - CallQonversion("addUpdatedPurchasesDelegate"); - } - - public void RemoveUpdatedPurchasesDelegate() - { - CallQonversion("removeUpdatedPurchasesDelegate"); - } - public void SetNotificationsToken(string token) { CallQonversion("setNotificationsToken", token); @@ -159,9 +154,9 @@ public bool HandleNotification(string notification) return CallQonversion("handleNotification", notification); } - public void AddAutomationsDelegate() + public void SubscribeOnAutomationEvents() { - CallQonversion("subscribeAutomationsDelegate"); + CallQonversion("subscribeOnAutomationEvents"); } private const string QonversionWrapper = "com.qonversion.unitywrapper.QonversionWrapper"; diff --git a/Runtime/Scripts/Constants.cs b/Runtime/Scripts/Constants.cs index a24ba20..67b0cfc 100644 --- a/Runtime/Scripts/Constants.cs +++ b/Runtime/Scripts/Constants.cs @@ -2,9 +2,6 @@ { static class Constants { - public const string KeyPrefix = "com.qonversion.keys"; - public const string SourceKey = KeyPrefix + ".source"; - public const string VersionKey = KeyPrefix + ".sourceVersion"; public const int SkuDetailsPriceRatio = 1000000; } } \ No newline at end of file diff --git a/Runtime/Scripts/IQonversionWrapper.cs b/Runtime/Scripts/IQonversionWrapper.cs index b58a20c..fd6d237 100644 --- a/Runtime/Scripts/IQonversionWrapper.cs +++ b/Runtime/Scripts/IQonversionWrapper.cs @@ -2,20 +2,21 @@ { internal interface IQonversionWrapper { - void StoreSdkInfo(string version, string versionKey, string source, string sourceKey); + void Initialize(string gameObjectName); + void StoreSdkInfo(string version, string source); void SetDebugMode(); void SetAdvertisingID(); - void Launch(string gameObjectName, string projectKey, bool observerMode); + void Launch(string projectKey, bool observerMode, string callbackName); void SetUserProperty(string key, string value); void SetProperty(UserProperty key, string value); void SyncPurchases(); void AddAttributionData(string conversionData, AttributionSource source); void CheckPermissions(string callbackName); void Purchase(string productId, string callbackName); - void PurchaseProduct(string productJson, string callbackName); + void PurchaseProduct(string productId, string offeringId, string callbackName); void Restore(string callbackName); void UpdatePurchase(string productId, string oldProductId, ProrationMode prorationMode, string callbackName); - void UpdatePurchaseWithProduct(string productJson, string oldProductId, ProrationMode prorationMode, string callbackName); + void UpdatePurchaseWithProduct(string productId, string offeringId, string oldProductId, ProrationMode prorationMode, string callbackName); void Products(string callbackName); void Offerings(string callbackName); void CheckTrialIntroEligibilityForProductIds(string productIdsJson, string callbackName); @@ -23,13 +24,9 @@ internal interface IQonversionWrapper void Identify(string userID); void Logout(); void PromoPurchase(string storeProductId, string callbackName); - void AddPromoPurchasesDelegate(); - void RemovePromoPurchasesDelegate(); - void AddUpdatedPurchasesDelegate(); - void RemoveUpdatedPurchasesDelegate(); void SetNotificationsToken(string token); bool HandleNotification(string notification); - void AddAutomationsDelegate(); + void SubscribeOnAutomationEvents(); void PresentCodeRedemptionSheet(); } } \ No newline at end of file diff --git a/Runtime/Scripts/Mapper.cs b/Runtime/Scripts/Mapper.cs index 75fbcd2..9268485 100644 --- a/Runtime/Scripts/Mapper.cs +++ b/Runtime/Scripts/Mapper.cs @@ -11,16 +11,19 @@ internal static Dictionary PermissionsFromJson(string jsonSt { var result = new Dictionary(); - if (!(Json.Deserialize(jsonStr) is List permissions)) + if (!(Json.Deserialize(jsonStr) is Dictionary permissions)) { Debug.LogError("Could not parse QPermissions"); return result; } - foreach (Dictionary permissionDict in permissions) + foreach (KeyValuePair permissionPair in permissions) { - Permission permission = new Permission(permissionDict); - result.Add(permission.PermissionID, permission); + if (permissionPair.Value is Dictionary permissionDict) + { + Permission permission = new Permission(permissionDict); + result.Add(permissionPair.Key, permission); + } } return result; @@ -30,16 +33,19 @@ internal static Dictionary ProductsFromJson(string jsonStr) { var result = new Dictionary(); - if (!(Json.Deserialize(jsonStr) is List products)) + if (!(Json.Deserialize(jsonStr) is Dictionary products)) { Debug.LogError("Could not parse QProducts"); return result; } - foreach (Dictionary productDict in products) + foreach (KeyValuePair productPair in products) { - Product product = new Product(productDict); - result.Add(product.QonversionId, product); + if (productPair.Value is Dictionary productDict) + { + Product product = new Product(productDict); + result.Add(productPair.Key, product); + } } return result; @@ -82,18 +88,18 @@ internal static Dictionary EligibilitiesFromJson(string jso { var result = new Dictionary(); - if (!(Json.Deserialize(jsonStr) is List elibilities)) + if (!(Json.Deserialize(jsonStr) is Dictionary elibilities)) { Debug.LogError("Could not parse Eligibilities"); return result; } - foreach (Dictionary eligibilityDict in elibilities) + foreach (KeyValuePair eligibilityPair in elibilities) { - if (eligibilityDict.TryGetValue("productId", out object value)) + if (eligibilityPair.Value is Dictionary eligibilityDict) { Eligibility eligibility = new Eligibility(eligibilityDict); - result.Add(value as string, eligibility); + result.Add(eligibilityPair.Key, eligibility); } } diff --git a/Runtime/Scripts/Models/AutomationsEvent.cs b/Runtime/Scripts/Models/AutomationsEvent.cs index 10079b9..969cea8 100644 --- a/Runtime/Scripts/Models/AutomationsEvent.cs +++ b/Runtime/Scripts/Models/AutomationsEvent.cs @@ -10,10 +10,8 @@ public class AutomationsEvent public AutomationsEvent(Dictionary dict) { - - if (dict.TryGetValue("timestamp", out object time)) Date = Utils.FormatDate((long)time); + if (dict.TryGetValue("timestamp", out object time)) Date = Utils.FormatDate(Convert.ToInt64((double)time)); if (dict.TryGetValue("type", out object eventType)) Type = FormatAutomationsEventType(eventType); - } public override string ToString() diff --git a/Runtime/Scripts/Models/Eligibility.cs b/Runtime/Scripts/Models/Eligibility.cs index c830239..ff227a5 100644 --- a/Runtime/Scripts/Models/Eligibility.cs +++ b/Runtime/Scripts/Models/Eligibility.cs @@ -24,12 +24,12 @@ private EligibilityStatus FormatEligibilityStatus(object status) { case "non_intro_or_trial_product": result = EligibilityStatus.NonIntroProduct; break; - case "intro_or_trial_eligible": - result = EligibilityStatus.Eligible; - break; case "intro_or_trial_ineligible": result = EligibilityStatus.Ineligible; break; + case "intro_or_trial_eligible": + result = EligibilityStatus.Eligible; + break; default: result = EligibilityStatus.Unknown; break; diff --git a/Runtime/Scripts/Models/Permission.cs b/Runtime/Scripts/Models/Permission.cs index 6edfcef..0efa2dc 100644 --- a/Runtime/Scripts/Models/Permission.cs +++ b/Runtime/Scripts/Models/Permission.cs @@ -31,11 +31,11 @@ public class Permission public Permission(Dictionary dict) { if (dict.TryGetValue("id", out object value)) PermissionID = value as string; - if (dict.TryGetValue("associated_product", out value)) ProductID = value as string; - if (dict.TryGetValue("renew_state", out value)) RenewState = FormatRenewState(value); + if (dict.TryGetValue("associatedProduct", out value)) ProductID = value as string; + if (dict.TryGetValue("renewState", out value)) RenewState = FormatRenewState(value); if (dict.TryGetValue("active", out value)) IsActive = (bool)value; - if (dict.TryGetValue("started_timestamp", out value)) StartedDate = FormatDate(value); - if (dict.TryGetValue("expiration_timestamp", out value)) ExpirationDate = FormatDate(value); + if (dict.TryGetValue("startedTimestamp", out value)) StartedDate = FormatDate(value); + if (dict.TryGetValue("expirationTimestamp", out value)) ExpirationDate = FormatDate(value); } public override string ToString() @@ -48,7 +48,7 @@ public override string ToString() $"{nameof(IsActive)}: {IsActive}"; } - private DateTime FormatDate(object time) => Utils.FormatDate((long)time); + private DateTime FormatDate(object time) => Utils.FormatDate(Convert.ToInt64((double)time)); private QProductRenewState FormatRenewState(object renewState) => (QProductRenewState)Convert.ToInt32(renewState); diff --git a/Runtime/Scripts/Models/Product.cs b/Runtime/Scripts/Models/Product.cs index 8851246..cb63178 100644 --- a/Runtime/Scripts/Models/Product.cs +++ b/Runtime/Scripts/Models/Product.cs @@ -56,49 +56,42 @@ public class Product /// Formatted introductory price of a subscription, including its currency sign, such as €2.99 [CanBeNull] public readonly string PrettyIntroductoryPrice; - internal readonly string OriginalJson; - public Product(Dictionary dict) { - OriginalJson = Json.Serialize(dict); - if (dict.TryGetValue("id", out object value)) QonversionId = value as string; - if (dict.TryGetValue("store_id", out value)) StoreId = value as string; + if (dict.TryGetValue("storeId", out value)) StoreId = value as string; if (dict.TryGetValue("type", out value)) Type = FormatType(value); if (dict.TryGetValue("duration", out value)) Duration = FormatDuration(value); if (dict.TryGetValue("trialDuration", out value)) TrialDuration = FormatTrialDuration(value); if (dict.TryGetValue("prettyPrice", out value)) PrettyPrice = value as string; if (dict.TryGetValue("offeringId", out value)) OfferingId = value as string; - if (dict.TryGetValue("storeProduct", out value)) + if (Application.platform == RuntimePlatform.Android && dict.TryGetValue("skuDetails", out value)) { - if (Application.platform == RuntimePlatform.Android) + if (value is Dictionary skuDetails) { - if (value is Dictionary skuDetails) - { - SkuDetails = new SkuDetails(skuDetails); - - Price = (double)SkuDetails.PriceAmountMicros / Constants.SkuDetailsPriceRatio; - CurrencyCode = SkuDetails.PriceCurrencyCode; - StoreTitle = SkuDetails.Title; - StoreDescription = SkuDetails.Description; - - string introPrice = SkuDetails.IntroductoryPrice; - PrettyIntroductoryPrice = (introPrice.Length != 0) ? introPrice : null; - } + SkuDetails = new SkuDetails(skuDetails); + + Price = (double) SkuDetails.PriceAmountMicros / Constants.SkuDetailsPriceRatio; + CurrencyCode = SkuDetails.PriceCurrencyCode; + StoreTitle = SkuDetails.Title; + StoreDescription = SkuDetails.Description; + + string introPrice = SkuDetails.IntroductoryPrice; + PrettyIntroductoryPrice = (introPrice.Length != 0) ? introPrice : null; } - else + } + else if (Application.platform == RuntimePlatform.IPhonePlayer && dict.TryGetValue("skProduct", out value)) + { + if (value is Dictionary skProduct) { - if (value is Dictionary skProduct) - { - SkProduct = new SKProduct(skProduct); - - Price = double.Parse(SkProduct.Price); - CurrencyCode = SkProduct.CurrencyCode; - StoreTitle = SkProduct.LocalizedTitle; - StoreDescription = SkProduct.LocalizedDescription; - PrettyIntroductoryPrice = SkProduct.IntroductoryPrice.CurrencySymbol + SkProduct.IntroductoryPrice.Price; - } + SkProduct = new SKProduct(skProduct); + + Price = double.Parse(SkProduct.Price); + CurrencyCode = SkProduct.CurrencyCode; + StoreTitle = SkProduct.LocalizedTitle; + StoreDescription = SkProduct.LocalizedDescription; + PrettyIntroductoryPrice = SkProduct.IntroductoryPrice.CurrencySymbol + SkProduct.IntroductoryPrice.Price; } } } diff --git a/Runtime/Scripts/Models/QonversionError.cs b/Runtime/Scripts/Models/QonversionError.cs index 8287257..1e4548b 100644 --- a/Runtime/Scripts/Models/QonversionError.cs +++ b/Runtime/Scripts/Models/QonversionError.cs @@ -9,8 +9,22 @@ public class QonversionError public QonversionError(Dictionary dict) { - if (dict.TryGetValue("message", out object value)) Message = value as string; - if (dict.TryGetValue("code", out value)) Code = value as string; + if (dict.TryGetValue("code", out object value)) Code = value as string; + string message = ""; + if (dict.TryGetValue("description", out object description)) + { + message += description; + if (dict.TryGetValue("domain", out object domain)) + { + message += ". Domain: " + domain.ToString(); + } + if (dict.TryGetValue("additionalMessage", out object additionalMessage)) + { + message += "\nDebugInfo: " + additionalMessage; + } + } + + Message = message; } internal QonversionError(string code, string message) diff --git a/Runtime/Scripts/Models/SkProduct/SKProduct.cs b/Runtime/Scripts/Models/SkProduct/SKProduct.cs index 8f07ae4..c6d6cb4 100644 --- a/Runtime/Scripts/Models/SkProduct/SKProduct.cs +++ b/Runtime/Scripts/Models/SkProduct/SKProduct.cs @@ -56,7 +56,16 @@ public SKProduct(Dictionary dict) if (dict.TryGetValue("downloadContentLengths", out value)) DownloadContentLengths = value as List; if (dict.TryGetValue("downloadContentVersion", out value)) DownloadContentVersion = value as string; - if (dict.TryGetValue("currencyCode", out value)) CurrencyCode = value as string; + if (dict.TryGetValue("priceLocale", out object priceLocale)) + { + if (priceLocale is Dictionary priceLocaleDict) + { + if (priceLocaleDict.TryGetValue("currencyCode", out value)) + { + CurrencyCode = value as string; + } + } + } } public override string ToString() diff --git a/Runtime/Scripts/Models/SkProduct/SKProductDiscount.cs b/Runtime/Scripts/Models/SkProduct/SKProductDiscount.cs index 3a1e58e..a0984f0 100644 --- a/Runtime/Scripts/Models/SkProduct/SKProductDiscount.cs +++ b/Runtime/Scripts/Models/SkProduct/SKProductDiscount.cs @@ -20,10 +20,17 @@ public SKProductDiscount(Dictionary dict) if (dict.TryGetValue("identifier", out object value)) Identifier = value as string; if (dict.TryGetValue("type", out value)) Type = FormatDiscountType(value); if (dict.TryGetValue("price", out value)) Price = value as string; - if (dict.TryGetValue("localeIdentifier", out value)) LocaleIdentifier = value as string; if (dict.TryGetValue("paymentMode", out value)) PaymentMode = FormatPaymentMode(value); if (dict.TryGetValue("numberOfPeriods", out value)) NumberOfPeriods = Convert.ToInt32(value); - if (dict.TryGetValue("currencySymbol", out value)) CurrencySymbol = value as string; + + if (dict.TryGetValue("priceLocale", out object priceLocale)) + { + if (priceLocale is Dictionary priceLocaleDict) + { + if (priceLocaleDict.TryGetValue("localeIdentifier", out value)) LocaleIdentifier = value as string; + if (priceLocaleDict.TryGetValue("currencySymbol", out value)) CurrencySymbol = value as string; + } + } if (dict.TryGetValue("subscriptionPeriod", out value)) { diff --git a/Runtime/Scripts/Qonversion.cs b/Runtime/Scripts/Qonversion.cs index 8653fed..d9f381f 100644 --- a/Runtime/Scripts/Qonversion.cs +++ b/Runtime/Scripts/Qonversion.cs @@ -40,6 +40,7 @@ public class Qonversion : MonoBehaviour public delegate void OnUpdatedPurchasesReceived(Dictionary permissions); private const string GameObjectName = "QonvesrionRuntimeGameObject"; + private const string OnLaunchMethodName = "OnLaunch"; private const string OnCheckPermissionsMethodName = "OnCheckPermissions"; private const string OnPurchaseMethodName = "OnPurchase"; private const string OnPromoPurchaseMethodName = "OnPromoPurchase"; @@ -77,6 +78,7 @@ private static IQonversionWrapper getFinalInstance() _Instance = new QonversionWrapperNoop(); break; } + _Instance.Initialize(GameObjectName); } GameObject go = new GameObject(GameObjectName); @@ -96,22 +98,10 @@ public static event OnPromoPurchasesReceived PromoPurchasesReceived add { _onPromoPurchasesReceived += value; - - if (_onPromoPurchasesReceived.GetInvocationList().Length == 1) - { - IQonversionWrapper instance = getFinalInstance(); - instance.AddPromoPurchasesDelegate(); - } } remove { _onPromoPurchasesReceived -= value; - - if (_onPromoPurchasesReceived == null) - { - IQonversionWrapper instance = getFinalInstance(); - instance.RemovePromoPurchasesDelegate(); - } } } @@ -123,22 +113,10 @@ public static event OnUpdatedPurchasesReceived UpdatedPurchasesReceived add { _onUpdatedPurchasesReceived += value; - - if (_onUpdatedPurchasesReceived.GetInvocationList().Length == 1) - { - IQonversionWrapper instance = getFinalInstance(); - instance.AddUpdatedPurchasesDelegate(); - } } remove { _onUpdatedPurchasesReceived -= value; - - if (_onUpdatedPurchasesReceived == null) - { - IQonversionWrapper instance = getFinalInstance(); - instance.RemoveUpdatedPurchasesDelegate(); - } } } @@ -147,7 +125,7 @@ internal static void SetAutomationsDelegate(AutomationsDelegate automationsDeleg _automationsDelegate = automationsDelegate; IQonversionWrapper instance = getFinalInstance(); - instance.AddAutomationsDelegate(); + instance.SubscribeOnAutomationEvents(); } /// @@ -161,8 +139,8 @@ internal static void SetAutomationsDelegate(AutomationsDelegate automationsDeleg public static void Launch(string apiKey, bool observerMode) { IQonversionWrapper instance = getFinalInstance(); - instance.StoreSdkInfo(SdkVersion, Constants.VersionKey, SdkSource, Constants.SourceKey); - instance.Launch(GameObjectName, apiKey, observerMode); + instance.StoreSdkInfo(SdkVersion, SdkSource); + instance.Launch(apiKey, observerMode, OnLaunchMethodName); } /// @@ -355,17 +333,15 @@ public static void Purchase(string productId, OnPermissionsReceived callback) /// public static void PurchaseProduct([NotNull] Product product, OnPermissionsReceived callback) { - if(product == null) + if (product == null) { callback(null, new QonversionError("PurchaseInvalid", "Product is null")); return; } - var productJson = product.OriginalJson; - PurchaseProductCallback = callback; IQonversionWrapper instance = getFinalInstance(); - instance.PurchaseProduct(productJson, OnPurchaseProductMethodName); + instance.PurchaseProduct(product.QonversionId, product.OfferingId, OnPurchaseProductMethodName); } private static OnPermissionsReceived RestoreCallback { get; set; } @@ -419,11 +395,9 @@ public static void UpdatePurchaseWithProduct([NotNull] Product product, string o return; } - var productJson = product.OriginalJson; - UpdatePurchaseWithProductCallback = callback; IQonversionWrapper instance = getFinalInstance(); - instance.UpdatePurchaseWithProduct(productJson, oldProductId, prorationMode, OnUpdatePurchaseWithProductMethodName); + instance.UpdatePurchaseWithProduct(product.QonversionId, product.OfferingId, oldProductId, prorationMode, OnUpdatePurchaseWithProductMethodName); } private static OnProductsReceived ProductsCallback { get; set; } @@ -492,6 +466,12 @@ public static bool HandleNotification(Dictionary notification) return instance.HandleNotification(notification.toJson()); } + // Called from the native SDK - Called when launch completed + private void OnLaunch(string jsonString) + { + Debug.Log("OnLaunch " + jsonString); + } + // Called from the native SDK - Called when permissions received from the checkPermissions() method private void OnCheckPermissions(string jsonString) { @@ -615,26 +595,28 @@ private void OnEligibilities(string jsonString) // Called from the native SDK - Called when deferred or pending purchase occured private void OnReceiveUpdatedPurchases(string jsonString) { - if (_onUpdatedPurchasesReceived == null) - { - return; - } + Debug.Log("OnReceiveUpdatedPurchases " + jsonString); + + if (_onUpdatedPurchasesReceived == null) + { + return; + } - Debug.Log("OnReceiveUpdatedPurchases " + jsonString); - Dictionary permissions = Mapper.PermissionsFromJson(jsonString); - _onUpdatedPurchasesReceived(permissions); + Dictionary permissions = Mapper.PermissionsFromJson(jsonString); + _onUpdatedPurchasesReceived(permissions); } private void OnReceivePromoPurchase(string storeProductId) { - if (_onPromoPurchasesReceived == null) - { - return; - } + Debug.Log("OnReceivePromoPurchase " + storeProductId); + + if (_onPromoPurchasesReceived == null) + { + return; + } - Debug.Log("OnReceivePromoPurchase " + storeProductId); - _storedPromoProductId = storeProductId; - _onPromoPurchasesReceived(storeProductId, PromoPurchase); + _storedPromoProductId = storeProductId; + _onPromoPurchasesReceived(storeProductId, PromoPurchase); } private static OnPermissionsReceived PromoPurchaseCallback { get; set; } diff --git a/Runtime/Scripts/QonversionWrapperNoop.cs b/Runtime/Scripts/QonversionWrapperNoop.cs index baddb40..f5be18b 100644 --- a/Runtime/Scripts/QonversionWrapperNoop.cs +++ b/Runtime/Scripts/QonversionWrapperNoop.cs @@ -2,7 +2,10 @@ namespace QonversionUnity { internal class QonversionWrapperNoop : IQonversionWrapper { - public void Launch(string gameObjectName, string projectKey, bool observerMode) + public void Initialize(string gameObjectName) + { + } + public void Launch(string projectKey, bool observerMode, string callbackName) { } @@ -38,7 +41,7 @@ public void Purchase(string productId, string callbackName) { } - public void PurchaseProduct(string productJson, string callbackName) + public void PurchaseProduct(string productId, string offeringId, string callbackName) { } @@ -50,7 +53,7 @@ public void UpdatePurchase(string productId, string oldProductId, ProrationMode { } - public void UpdatePurchaseWithProduct(string productJson, string oldProductId, ProrationMode prorationMode, string callbackName) + public void UpdatePurchaseWithProduct(string productId, string offeringId, string oldProductId, ProrationMode prorationMode, string callbackName) { } @@ -62,7 +65,7 @@ public void Offerings(string callbackName) { } - public void StoreSdkInfo(string version, string versionKey, string source, string sourceKey) + public void StoreSdkInfo(string version, string source) { } @@ -86,21 +89,6 @@ public void PromoPurchase(string storeProductId, string callbackName) { } - public void AddPromoPurchasesDelegate() - { - } - - public void RemovePromoPurchasesDelegate() - { - } - public void AddUpdatedPurchasesDelegate() - { - } - - public void RemoveUpdatedPurchasesDelegate() - { - } - public void SetNotificationsToken(string token) { } @@ -110,14 +98,12 @@ public bool HandleNotification(string notification) return false; } - public void AddAutomationsDelegate() + public void SubscribeOnAutomationEvents() { - } public void PresentCodeRedemptionSheet() { - } } } \ No newline at end of file diff --git a/Runtime/iOS/Plugins/QonversionBridge.m b/Runtime/iOS/Plugins/QonversionBridge.m index 928a3b2..ef47c0f 100644 --- a/Runtime/iOS/Plugins/QonversionBridge.m +++ b/Runtime/iOS/Plugins/QonversionBridge.m @@ -247,6 +247,6 @@ bool _handleNotification(const char* notification) { return result; } -void _subscribeAutomationsDelegate() { +void _subscribeOnAutomationEvents() { automationsDelegate = [[QNUAutomationsDelegate alloc] initWithListenerName:unityListenerName]; } diff --git a/Runtime/iOS/QonversionWrapperIOS.cs b/Runtime/iOS/QonversionWrapperIOS.cs index 2823f31..91eceb0 100644 --- a/Runtime/iOS/QonversionWrapperIOS.cs +++ b/Runtime/iOS/QonversionWrapperIOS.cs @@ -12,7 +12,10 @@ internal class QonversionWrapperIOS : IQonversionWrapper { #if UNITY_IOS [DllImport("__Internal")] - private static extern void _storeSdkInfo(string version, string versionKey, string source, string sourceKey); + private static extern void _initialize(string gameObjectName); + + [DllImport("__Internal")] + private static extern void _storeSdkInfo(string version, string source); [DllImport("__Internal")] private static extern void _setDebugMode(); @@ -36,7 +39,7 @@ internal class QonversionWrapperIOS : IQonversionWrapper private static extern void _setUserProperty(string key, string value); [DllImport("__Internal")] - private static extern void _launchWithKey(string gameObjectName, string key); + private static extern void _launchWithKey(string key, string callbackName); [DllImport("__Internal")] private static extern void _addAttributionData(string conversionData, int provider); @@ -51,7 +54,7 @@ internal class QonversionWrapperIOS : IQonversionWrapper private static extern void _purchase(string productID, string callbackName); [DllImport("__Internal")] - private static extern void _purchaseProduct(string productJson, string callbackName); + private static extern void _purchaseProduct(string productId, string offeringId, string callbackName); [DllImport("__Internal")] private static extern void _products(string callbackName); @@ -65,18 +68,6 @@ internal class QonversionWrapperIOS : IQonversionWrapper [DllImport("__Internal")] private static extern void _promoPurchase(string storeProductId, string callbackName); - [DllImport("__Internal")] - private static extern void _addPromoPurchasesDelegate(); - - [DllImport("__Internal")] - private static extern void _removePromoPurchasesDelegate(); - - [DllImport("__Internal")] - private static extern void _addUpdatedPurchasesDelegate(); - - [DllImport("__Internal")] - private static extern void _removeUpdatedPurchasesDelegate(); - [DllImport("__Internal")] private static extern void _setNotificationsToken(string token); @@ -84,23 +75,30 @@ internal class QonversionWrapperIOS : IQonversionWrapper private static extern bool _handleNotification(string notification); [DllImport("__Internal")] - private static extern void _subscribeAutomationsDelegate(); + private static extern void _subscribeOnAutomationEvents(); [DllImport("__Internal")] private static extern void _presentCodeRedemptionSheet(); #endif - public void StoreSdkInfo(string version, string versionKey, string source, string sourceKey) + public void Initialize(string gameObjectName) { #if UNITY_IOS - _storeSdkInfo(version, versionKey, source, sourceKey); + _initialize(gameObjectName); #endif } - public void Launch(string gameObjectName, string projectKey, bool observerMode) + public void StoreSdkInfo(string version, string source) { #if UNITY_IOS - _launchWithKey(gameObjectName, projectKey); + _storeSdkInfo(version, source); +#endif + } + + public void Launch(string projectKey, bool observerMode, string callbackName) + { +#if UNITY_IOS + _launchWithKey(projectKey, callbackName); #endif } @@ -186,10 +184,10 @@ public void Purchase(string productId, string callbackName) #endif } - public void PurchaseProduct(string productJson, string callbackName) + public void PurchaseProduct(string productId, string offeringId, string callbackName) { #if UNITY_IOS - _purchaseProduct(productJson, callbackName); + _purchaseProduct(productId, offeringId, callbackName); #endif } @@ -204,7 +202,7 @@ public void UpdatePurchase(string productId, string oldProductId, ProrationMode { } - public void UpdatePurchaseWithProduct(string productJson, string oldProductId, ProrationMode prorationMode, string callbackName) + public void UpdatePurchaseWithProduct(string productId, string offeringId, string oldProductId, ProrationMode prorationMode, string callbackName) { } @@ -236,34 +234,6 @@ public void PromoPurchase(string storeProductId, string callbackName) #endif } - public void AddPromoPurchasesDelegate() - { -#if UNITY_IOS - _addPromoPurchasesDelegate(); -#endif - } - - public void RemovePromoPurchasesDelegate() - { -#if UNITY_IOS - _removePromoPurchasesDelegate(); -#endif - } - - public void AddUpdatedPurchasesDelegate() - { -#if UNITY_IOS - _addUpdatedPurchasesDelegate(); -#endif - } - - public void RemoveUpdatedPurchasesDelegate() - { -#if UNITY_IOS - _removeUpdatedPurchasesDelegate(); -#endif - } - public void SetNotificationsToken(string token) { #if UNITY_IOS @@ -280,10 +250,10 @@ public bool HandleNotification(string notification) #endif } - public void AddAutomationsDelegate() + public void SubscribeOnAutomationEvents() { #if UNITY_IOS - _subscribeAutomationsDelegate(); + _subscribeOnAutomationEvents(); #endif } } From aa8acf61c3fc631a5f96dc87c312602044d0c5e1 Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Thu, 1 Sep 2022 12:27:45 +0300 Subject: [PATCH 2/5] Fixes while testing and upgrading sandwich. (#99) --- Runtime/Android/QonversionWrapperAndroid.cs | 5 ++- Runtime/Scripts/IQonversionWrapper.cs | 2 +- Runtime/Scripts/Mapper.cs | 39 ++++++++------------- Runtime/Scripts/Models/ActionResult.cs | 6 ++-- Runtime/Scripts/Models/Permission.cs | 2 +- Runtime/Scripts/Models/QonversionError.cs | 2 +- Runtime/Scripts/Qonversion.cs | 9 +++-- Runtime/Scripts/QonversionWrapperNoop.cs | 2 +- Runtime/iOS/QonversionWrapperIOS.cs | 5 ++- 9 files changed, 29 insertions(+), 43 deletions(-) diff --git a/Runtime/Android/QonversionWrapperAndroid.cs b/Runtime/Android/QonversionWrapperAndroid.cs index 3863011..56f6714 100644 --- a/Runtime/Android/QonversionWrapperAndroid.cs +++ b/Runtime/Android/QonversionWrapperAndroid.cs @@ -189,10 +189,9 @@ public void RemovePromoPurchasesDelegate() { } - public void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime) + public void SetPermissionsCacheLifetime(string lifetime) { - string lifetimeName = Enum.GetName(typeof(PermissionsCacheLifetime), lifetime); - CallQonversion("setPermissionsCacheLifetime", lifetimeName); + CallQonversion("setPermissionsCacheLifetime", lifetime); } } } \ No newline at end of file diff --git a/Runtime/Scripts/IQonversionWrapper.cs b/Runtime/Scripts/IQonversionWrapper.cs index 8b09fc9..e0edb55 100644 --- a/Runtime/Scripts/IQonversionWrapper.cs +++ b/Runtime/Scripts/IQonversionWrapper.cs @@ -28,6 +28,6 @@ internal interface IQonversionWrapper bool HandleNotification(string notification); void SubscribeOnAutomationEvents(); void PresentCodeRedemptionSheet(); - void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime); + void SetPermissionsCacheLifetime(string lifetime); } } \ No newline at end of file diff --git a/Runtime/Scripts/Mapper.cs b/Runtime/Scripts/Mapper.cs index ebffcdb..71a6fbb 100644 --- a/Runtime/Scripts/Mapper.cs +++ b/Runtime/Scripts/Mapper.cs @@ -7,31 +7,20 @@ namespace QonversionUnity { internal class Mapper { - internal static Dictionary PermissionsFromPurchaseJson(string jsonStr) - { - if (Json.Deserialize(jsonStr) is not Dictionary result) - { - Debug.LogError("Could not parse purchase result"); - return null; - } - - var resultPermissions = new Dictionary(); - - if (result["permissions"] is not List permissions) - { - Debug.LogError("Could not parse QPermissions"); - return resultPermissions; - } - - foreach (Dictionary permissionDict in permissions) - { - var permission = new Permission(permissionDict); - resultPermissions.Add(permission.PermissionID, permission); - } - - return resultPermissions; + internal static string GetLifetimeKey(PermissionsCacheLifetime lifetime) { + var keys = new Dictionary() { + {PermissionsCacheLifetime.WEEK, "Week"}, + {PermissionsCacheLifetime.TWO_WEEKS, "TwoWeeks"}, + {PermissionsCacheLifetime.MONTH, "Month"}, + {PermissionsCacheLifetime.TWO_MONTHS, "TwoMonths"}, + {PermissionsCacheLifetime.THREE_MONTHS, "ThreeMonths"}, + {PermissionsCacheLifetime.SIX_MONTHS, "SixMonths"}, + {PermissionsCacheLifetime.YEAR, "Year"}, + {PermissionsCacheLifetime.UNLIMITED, "Unlimited"} + }; + return keys[lifetime]; } - + internal static bool GetIsCancelledFromJson(string jsonStr) { if (Json.Deserialize(jsonStr) is not Dictionary result) @@ -119,7 +108,7 @@ internal static string ScreenIdFromJson(string jsonStr) return null; } - return screenResult.GetString("screenID", ""); + return screenResult.GetString("screenId", ""); } internal static Dictionary EligibilitiesFromJson(string jsonStr) diff --git a/Runtime/Scripts/Models/ActionResult.cs b/Runtime/Scripts/Models/ActionResult.cs index f791a61..372fc83 100644 --- a/Runtime/Scripts/Models/ActionResult.cs +++ b/Runtime/Scripts/Models/ActionResult.cs @@ -11,9 +11,9 @@ public class ActionResult public ActionResult(Dictionary dict) { - if (dict.TryGetValue("error", out object rawError ) && rawError != null) Error = new QonversionError((Dictionary)rawError); - if (dict.TryGetValue("value", out object rawParams) && rawParams != null) Parameters = (Dictionary)rawParams; - if (dict.TryGetValue("type", out object actionType)) Type = FormatActionResultType(actionType); + if (dict.TryGetValue("error", out var rawError ) && rawError != null) Error = new QonversionError((Dictionary)rawError); + if (dict.TryGetValue("value", out var rawParams) && rawParams != null) Parameters = (Dictionary)rawParams; + if (dict.TryGetValue("type", out var actionType)) Type = FormatActionResultType(actionType); } public override string ToString() diff --git a/Runtime/Scripts/Models/Permission.cs b/Runtime/Scripts/Models/Permission.cs index 0efa2dc..7240d8c 100644 --- a/Runtime/Scripts/Models/Permission.cs +++ b/Runtime/Scripts/Models/Permission.cs @@ -35,7 +35,7 @@ public Permission(Dictionary dict) if (dict.TryGetValue("renewState", out value)) RenewState = FormatRenewState(value); if (dict.TryGetValue("active", out value)) IsActive = (bool)value; if (dict.TryGetValue("startedTimestamp", out value)) StartedDate = FormatDate(value); - if (dict.TryGetValue("expirationTimestamp", out value)) ExpirationDate = FormatDate(value); + if (dict.TryGetValue("expirationTimestamp", out value) && value != null) ExpirationDate = FormatDate(value); } public override string ToString() diff --git a/Runtime/Scripts/Models/QonversionError.cs b/Runtime/Scripts/Models/QonversionError.cs index 1e4548b..98b6e99 100644 --- a/Runtime/Scripts/Models/QonversionError.cs +++ b/Runtime/Scripts/Models/QonversionError.cs @@ -16,7 +16,7 @@ public QonversionError(Dictionary dict) message += description; if (dict.TryGetValue("domain", out object domain)) { - message += ". Domain: " + domain.ToString(); + message += ". Domain: " + domain; } if (dict.TryGetValue("additionalMessage", out object additionalMessage)) { diff --git a/Runtime/Scripts/Qonversion.cs b/Runtime/Scripts/Qonversion.cs index d6d5d11..27ee7b0 100644 --- a/Runtime/Scripts/Qonversion.cs +++ b/Runtime/Scripts/Qonversion.cs @@ -509,10 +509,10 @@ public static void CheckTrialIntroEligibilityForProductIds(IList product /// The default value is Desired permissions cache lifetime duration. - public static void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime) - { + public static void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime) { + var lifetimeKey = Mapper.GetLifetimeKey(lifetime); IQonversionWrapper instance = getFinalInstance(); - instance.SetPermissionsCacheLifetime(lifetime); + instance.SetPermissionsCacheLifetime(lifetimeKey); } /// @@ -723,7 +723,7 @@ private void HandlePurchaseResult(OnPurchaseResultReceived callback, string json } else { - var permissions = Mapper.PermissionsFromPurchaseJson(jsonString); + var permissions = Mapper.PermissionsFromJson(jsonString); callback(permissions, null, false); } } @@ -737,7 +737,6 @@ private void OnAutomationsScreenShown(string jsonString) string screenId = Mapper.ScreenIdFromJson(jsonString); - Debug.Log(screenId); _automationsDelegate.OnAutomationsScreenShown(screenId); } diff --git a/Runtime/Scripts/QonversionWrapperNoop.cs b/Runtime/Scripts/QonversionWrapperNoop.cs index 0609618..4ba1450 100644 --- a/Runtime/Scripts/QonversionWrapperNoop.cs +++ b/Runtime/Scripts/QonversionWrapperNoop.cs @@ -106,7 +106,7 @@ public void PresentCodeRedemptionSheet() { } - public void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime) + public void SetPermissionsCacheLifetime(string lifetime) { } diff --git a/Runtime/iOS/QonversionWrapperIOS.cs b/Runtime/iOS/QonversionWrapperIOS.cs index a4f9de0..6486915 100644 --- a/Runtime/iOS/QonversionWrapperIOS.cs +++ b/Runtime/iOS/QonversionWrapperIOS.cs @@ -260,11 +260,10 @@ public void SubscribeOnAutomationEvents() #endif } - public void SetPermissionsCacheLifetime(PermissionsCacheLifetime lifetime) + public void SetPermissionsCacheLifetime(string lifetime) { #if UNITY_IOS - string lifetimeName = Enum.GetName(typeof(PermissionsCacheLifetime), lifetime); - _setPermissionsCacheLifetime(lifetimeName); + _setPermissionsCacheLifetime(lifetime); #endif } } From 2fca735a8f66bad388954b4011474af3c14e397c Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Fri, 9 Sep 2022 10:27:10 +0300 Subject: [PATCH 3/5] iOS integration along with lost Android native fixes. (#103) * Returned broken C# 8 support (#100) * [create-pull-request] automated change * iOS integration along with lost Android native fixes. * CR fixes Co-authored-by: SpertsyanKM --- Editor/QonversionDependencies.xml | 4 +- .../unitywrapper/QonversionWrapper.java | 48 ++- Runtime/Android/QonversionWrapperAndroid.cs | 8 - Runtime/Scripts/Models/Permission.cs | 8 +- Runtime/Scripts/Models/Product.cs | 7 +- .../Plugins/Common/QNUAutomationsDelegate.h | 5 +- .../Plugins/Common/QNUAutomationsDelegate.m | 39 +- Runtime/iOS/Plugins/Common/QNUConstants.h | 10 - .../iOS/Plugins/Common/QNUConstants.h.meta | 27 -- Runtime/iOS/Plugins/Common/QNUConstants.m | 10 - .../iOS/Plugins/Common/QNUConstants.m.meta | 37 -- Runtime/iOS/Plugins/Common/UtilityBridge.h | 36 +- Runtime/iOS/Plugins/Common/UtilityBridge.m | 377 +----------------- Runtime/iOS/Plugins/QonversionBridge.m | 207 ++++------ Runtime/iOS/QonversionWrapperIOS.cs | 5 +- 15 files changed, 163 insertions(+), 665 deletions(-) delete mode 100644 Runtime/iOS/Plugins/Common/QNUConstants.h delete mode 100644 Runtime/iOS/Plugins/Common/QNUConstants.h.meta delete mode 100644 Runtime/iOS/Plugins/Common/QNUConstants.m delete mode 100644 Runtime/iOS/Plugins/Common/QNUConstants.m.meta diff --git a/Editor/QonversionDependencies.xml b/Editor/QonversionDependencies.xml index 7ccbc9f..2736c7a 100644 --- a/Editor/QonversionDependencies.xml +++ b/Editor/QonversionDependencies.xml @@ -1,11 +1,11 @@ - + - + diff --git a/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java b/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java index 9b17bb3..10ac8e0 100644 --- a/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java +++ b/Runtime/Android/Plugins/com/qonversion/unitywrapper/QonversionWrapper.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.qonversion.sandwich.PurchaseResultListener; import io.qonversion.sandwich.QonversionSandwich; import io.qonversion.sandwich.ResultListener; import io.qonversion.sandwich.SandwichError; @@ -94,19 +95,19 @@ public static synchronized void checkPermissions(String unityCallbackName) { } public static synchronized void purchase(String productId, String unityCallbackName) { - qonversionSandwich.purchase(productId, getResultListener(unityCallbackName)); + qonversionSandwich.purchase(productId, getPurchaseResultListener(unityCallbackName)); } public static synchronized void purchaseProduct(String productId, String offeringId, String unityCallbackName) { - qonversionSandwich.purchaseProduct(productId, offeringId, getResultListener(unityCallbackName)); + qonversionSandwich.purchaseProduct(productId, offeringId, getPurchaseResultListener(unityCallbackName)); } public static synchronized void updatePurchase(String productId, String oldProductId, int prorationMode, String unityCallbackName) { - qonversionSandwich.updatePurchase(productId, oldProductId, prorationMode, getResultListener(unityCallbackName)); + qonversionSandwich.updatePurchase(productId, oldProductId, prorationMode, getPurchaseResultListener(unityCallbackName)); } public static synchronized void updatePurchaseWithProduct(String productId, String offeringId, String oldProductId, int prorationMode, String unityCallbackName) { - qonversionSandwich.updatePurchaseWithProduct(productId, offeringId, oldProductId, prorationMode, getResultListener(unityCallbackName)); + qonversionSandwich.updatePurchaseWithProduct(productId, offeringId, oldProductId, prorationMode, getPurchaseResultListener(unityCallbackName)); } public static synchronized void restore(String unityCallbackName) { @@ -134,12 +135,7 @@ public static synchronized void checkTrialIntroEligibilityForProductIds(String p } public static synchronized void setPermissionsCacheLifetime(String lifetimeKey) { - try { - QPermissionsCacheLifetime lifetime = QPermissionsCacheLifetime.valueOf(lifetimeKey); - Qonversion.setPermissionsCacheLifetime(lifetime); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Failed to map QPermissionsCacheLifetime. " + e.getLocalizedMessage()); - } + qonversionSandwich.setPermissionsCacheLifetime(lifetimeKey); } public static synchronized void setNotificationsToken(String token) { @@ -181,20 +177,22 @@ public void onError(@NonNull SandwichError error) { }; } - private static void handlePurchaseResponse(@NotNull Map permissions, @NotNull String methodName) { - List> mappedPermissions = Mapper.mapPermissions(permissions); - Map result = new HashMap<>(); - result.put("permissions", mappedPermissions); - sendMessageToUnity(result, methodName); - } + private static PurchaseResultListener getPurchaseResultListener(@NotNull String methodName) { + return new PurchaseResultListener() { + @Override + public void onSuccess(@NonNull Map data) { + sendMessageToUnity(data, methodName); + } - private static void handlePurchaseErrorResponse(@NotNull QonversionError error, @NotNull String methodName) { - final ObjectMapper mapper = new ObjectMapper(); - final ObjectNode rootNode = createErrorNode(error); - final boolean isCancelled = error.getCode() == QonversionErrorCode.CanceledPurchase; - final JsonNode isCancelledNode = mapper.convertValue(isCancelled, JsonNode.class); - rootNode.set("isCancelled", isCancelledNode); - sendMessageToUnity(rootNode, methodName); + @Override + public void onError(@NonNull SandwichError error, boolean isCancelled) { + final ObjectMapper mapper = new ObjectMapper(); + final ObjectNode rootNode = createErrorNode(error); + final JsonNode isCancelledNode = mapper.convertValue(isCancelled, JsonNode.class); + rootNode.set("isCancelled", isCancelledNode); + sendMessageToUnity(rootNode, methodName); + } + }; } private static void handleErrorResponse(@NotNull SandwichError error, @NotNull String methodName) { @@ -203,7 +201,7 @@ private static void handleErrorResponse(@NotNull SandwichError error, @NotNull S sendMessageToUnity(rootNode, methodName); } - private static ObjectNode createErrorNode(@NotNull QonversionError error) { + private static ObjectNode createErrorNode(@NotNull SandwichError error) { ObjectMapper mapper = new ObjectMapper(); ObjectNode errorNode = mapper.createObjectNode(); errorNode.put("code", error.getCode()); @@ -212,7 +210,7 @@ private static ObjectNode createErrorNode(@NotNull QonversionError error) { ObjectNode rootNode = mapper.createObjectNode(); rootNode.set("error", errorNode); - return node; + return rootNode; } private static void sendMessageToUnity(@NotNull Object objectToConvert, @NotNull String methodName) { diff --git a/Runtime/Android/QonversionWrapperAndroid.cs b/Runtime/Android/QonversionWrapperAndroid.cs index 56f6714..30dee82 100644 --- a/Runtime/Android/QonversionWrapperAndroid.cs +++ b/Runtime/Android/QonversionWrapperAndroid.cs @@ -181,14 +181,6 @@ public void PromoPurchase(string storeProductId, string callbackName) { } - public void AddPromoPurchasesDelegate() - { - } - - public void RemovePromoPurchasesDelegate() - { - } - public void SetPermissionsCacheLifetime(string lifetime) { CallQonversion("setPermissionsCacheLifetime", lifetime); diff --git a/Runtime/Scripts/Models/Permission.cs b/Runtime/Scripts/Models/Permission.cs index 7240d8c..90fca69 100644 --- a/Runtime/Scripts/Models/Permission.cs +++ b/Runtime/Scripts/Models/Permission.cs @@ -48,7 +48,13 @@ public override string ToString() $"{nameof(IsActive)}: {IsActive}"; } - private DateTime FormatDate(object time) => Utils.FormatDate(Convert.ToInt64((double)time)); + private DateTime FormatDate(object time) { + if (time is double) { + return Utils.FormatDate(Convert.ToInt64((double)time)); + } + + return Utils.FormatDate((long) time); + } private QProductRenewState FormatRenewState(object renewState) => (QProductRenewState)Convert.ToInt32(renewState); diff --git a/Runtime/Scripts/Models/Product.cs b/Runtime/Scripts/Models/Product.cs index e27fa8e..bc1a3ff 100644 --- a/Runtime/Scripts/Models/Product.cs +++ b/Runtime/Scripts/Models/Product.cs @@ -89,7 +89,7 @@ public Product(Dictionary dict) SkProduct = new SKProduct(skProduct); double parsedPrice; - if (double.TryParse(SkProduct.Price, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedPrice)) + if (double.TryParse(SkProduct.Price, NumberStyles.Any, CultureInfo.InvariantCulture, out parsedPrice)) { Price = parsedPrice; } @@ -101,7 +101,10 @@ public Product(Dictionary dict) CurrencyCode = SkProduct.CurrencyCode; StoreTitle = SkProduct.LocalizedTitle; StoreDescription = SkProduct.LocalizedDescription; - PrettyIntroductoryPrice = SkProduct.IntroductoryPrice.CurrencySymbol + SkProduct.IntroductoryPrice.Price; + if (SkProduct.IntroductoryPrice != null) { + PrettyIntroductoryPrice = SkProduct.IntroductoryPrice.CurrencySymbol + + SkProduct.IntroductoryPrice.Price; + } } } } diff --git a/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.h b/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.h index 256982b..3535d4c 100644 --- a/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.h +++ b/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.h @@ -6,13 +6,14 @@ // #import -#import "Qonversion.h" +@import QonversionSandwich; NS_ASSUME_NONNULL_BEGIN -@interface QNUAutomationsDelegate : NSObject +@interface QNUAutomationsDelegate : NSObject - (instancetype)initWithListenerName:(char *)unityListenerName; +- (void)subscribe; @end diff --git a/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.m b/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.m index cd4e1ed..10e4f5f 100644 --- a/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.m +++ b/Runtime/iOS/Plugins/Common/QNUAutomationsDelegate.m @@ -16,6 +16,13 @@ char* listenerName = nil; +@interface QNUAutomationsDelegate () + +@property (nonatomic, strong) AutomationsSandwich *automationsSandwich; +@property (nonatomic, copy) NSDictionary *automationEvents; + +@end + @implementation QNUAutomationsDelegate - (instancetype)initWithListenerName:(char *)unityListenerName { @@ -23,33 +30,27 @@ - (instancetype)initWithListenerName:(char *)unityListenerName { if (self) { listenerName = unityListenerName; - [QONAutomations setDelegate:self]; + _automationsSandwich = [AutomationsSandwich new]; + _automationEvents = @{ + @"automations_screen_shown": kEventScreenShown, + @"automations_action_started": kEventActionStarted, + @"automations_action_failed": kEventActionFailed, + @"automations_action_finished": kEventActionFinished, + @"automations_finished": kEventAutomationsFinished + }; } return self; } -- (void)automationsDidShowScreen:(NSString * _Nonnull)screenID { - [UtilityBridge sendUnityMessage:@{@"screenID": screenID} toMethod:kEventScreenShown unityListener: listenerName]; +- (void)subscribe { + [self.automationsSandwich subscribe:self]; } -- (void)automationsDidStartExecutingActionResult:(QONActionResult * _Nonnull)actionResult { - NSDictionary *actionResultDict = [UtilityBridge convertActionResult:actionResult]; - [UtilityBridge sendUnityMessage:actionResultDict toMethod:kEventActionStarted unityListener: listenerName]; -} - -- (void)automationsDidFailExecutingActionResult:(QONActionResult * _Nonnull)actionResult { - NSDictionary *actionResultDict = [UtilityBridge convertActionResult:actionResult]; - [UtilityBridge sendUnityMessage:actionResultDict toMethod:kEventActionFailed unityListener: listenerName]; -} - -- (void)automationsDidFinishExecutingActionResult:(QONActionResult * _Nonnull)actionResult { - NSDictionary *actionResultDict = [UtilityBridge convertActionResult:actionResult]; - [UtilityBridge sendUnityMessage:actionResultDict toMethod:kEventActionFinished unityListener: listenerName]; -} +- (void)automationDidTriggerWithEvent:(NSString * _Nonnull)event payload:(NSDictionary * _Nullable)payload { + NSString *methodName = self.automationEvents[event]; -- (void)automationsFinished { - [UtilityBridge sendUnityMessage:@{} toMethod:kEventAutomationsFinished unityListener: listenerName]; + [UtilityBridge sendUnityMessage:payload ?: @{} toMethod:methodName unityListener: listenerName]; } @end diff --git a/Runtime/iOS/Plugins/Common/QNUConstants.h b/Runtime/iOS/Plugins/Common/QNUConstants.h deleted file mode 100644 index 69f1894..0000000 --- a/Runtime/iOS/Plugins/Common/QNUConstants.h +++ /dev/null @@ -1,10 +0,0 @@ -#import - -extern NSString *const qonversionIdKey; -extern NSString *const storeIdKey; -extern NSString *const typeKey; -extern NSString *const durationKey; -extern NSString *const prettyPriceKey; -extern NSString *const trialDurationKey; -extern NSString *const offeringIdKey; -extern NSString *const storeProductKey; diff --git a/Runtime/iOS/Plugins/Common/QNUConstants.h.meta b/Runtime/iOS/Plugins/Common/QNUConstants.h.meta deleted file mode 100644 index 229417c..0000000 --- a/Runtime/iOS/Plugins/Common/QNUConstants.h.meta +++ /dev/null @@ -1,27 +0,0 @@ -fileFormatVersion: 2 -guid: b46f5637706e64724ba032dd4fe3fe18 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 1 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 1 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/iOS/Plugins/Common/QNUConstants.m b/Runtime/iOS/Plugins/Common/QNUConstants.m deleted file mode 100644 index f0ad9ab..0000000 --- a/Runtime/iOS/Plugins/Common/QNUConstants.m +++ /dev/null @@ -1,10 +0,0 @@ -#import "QNUConstants.h" - -NSString *const qonversionIdKey = @"id"; -NSString *const storeIdKey = @"store_id"; -NSString *const typeKey = @"type"; -NSString *const durationKey = @"duration"; -NSString *const prettyPriceKey = @"prettyPrice"; -NSString *const trialDurationKey = @"trialDuration"; -NSString *const offeringIdKey = @"offeringId"; -NSString *const storeProductKey = @"storeProduct"; diff --git a/Runtime/iOS/Plugins/Common/QNUConstants.m.meta b/Runtime/iOS/Plugins/Common/QNUConstants.m.meta deleted file mode 100644 index ae6bdea..0000000 --- a/Runtime/iOS/Plugins/Common/QNUConstants.m.meta +++ /dev/null @@ -1,37 +0,0 @@ -fileFormatVersion: 2 -guid: 62238e067a55f44198333e2993735fd5 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 1 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - iPhone: iOS - second: - enabled: 1 - settings: {} - - first: - tvOS: tvOS - second: - enabled: 1 - settings: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/iOS/Plugins/Common/UtilityBridge.h b/Runtime/iOS/Plugins/Common/UtilityBridge.h index 87cf9ac..f454a2c 100644 --- a/Runtime/iOS/Plugins/Common/UtilityBridge.h +++ b/Runtime/iOS/Plugins/Common/UtilityBridge.h @@ -1,42 +1,22 @@ #import -#import -#import "QNUConstants.h" +@import QonversionSandwich; @interface UtilityBridge : NSObject -+ (const char*)convertNSStringToCString:(NSString*)nsString; + (NSString*)сonvertCStringToNSString:(const char *)string; + (NSDictionary*)dictionaryFromJsonString:(NSString*) jsonString; - -+ (NSNumber *)convertProperty:(NSString *)propertyStr; -+ (NSArray *)convertPermissions:(NSArray *)permissions; -+ (NSArray *)convertProducts:(NSArray *)products; -+ (QNProduct *)convertProductFromJson:(NSString *)productJson; -+ (NSDictionary *)convertOfferings:(QNOfferings *)offerings; -+ (NSDictionary *)convertIntroEligibility:(NSDictionary *)introEligibilityInfo; -+ (NSDictionary *)convertError:(NSError *)error; -+ (NSDictionary *)convertPlainError:(NSError *)error; ++ (NSDictionary *)convertError:(SandwichError *)error; ++ (NSDictionary *)convertSandwichError:(SandwichError *)error; + (void)sendUnityMessage:(NSObject *)objectToConvert toMethod:(NSString *)methodName unityListener:(const char *)unityListenerName; -+ (void)handlePermissionsResponse:(NSDictionary *) result withError:( NSError *)error - toMethod:(NSString *)methodName - unityListener:(const char *)unityListenerName; - -+ (void)handlePurchaseResponse:(NSDictionary *) permissions - isCancelled:(BOOL) cancelled - withError:(NSError *) error - toMethod:(NSString *) methodName - unityListener:(const char *) unityListenerName; - -+ (void)handleErrorResponse:(NSError *)error toMethod:(NSString *) methodName ++ (void)handleErrorResponse:(SandwichError *)error toMethod:(NSString *)methodName unityListener:(const char *)unityListenerName; -+ (NSData *)convertHexToData:(NSString *)hex; -+ (NSDictionary *)convertActionResult:(QONActionResult *)actionResult; -+ (NSDictionary *)convertError:(NSError *)error; -+ (NSDictionary *)convertAutomationsEvent:(QONAutomationsEvent *)event; -+ (NSNumber *)convertPermissionsCacheLifetime:(NSString *)lifetimeKey; ++ (void)handleResult:(NSDictionary *)result + error:(SandwichError *)error + callbackName:(NSString *)callbackName + unityListener:(const char *)unityListenerName; @end diff --git a/Runtime/iOS/Plugins/Common/UtilityBridge.m b/Runtime/iOS/Plugins/Common/UtilityBridge.m index ef3f190..0689adc 100644 --- a/Runtime/iOS/Plugins/Common/UtilityBridge.m +++ b/Runtime/iOS/Plugins/Common/UtilityBridge.m @@ -2,17 +2,6 @@ @implementation UtilityBridge -+ (const char*)convertNSStringToCString:(NSString*) nsString { - if (nsString == NULL) - return NULL; - - const char* nsStringUtf8 = [nsString UTF8String]; - char* cString = (char*)malloc(strlen(nsStringUtf8) + 1); - strcpy(cString, nsStringUtf8); - - return cString; -} - + (NSString*)сonvertCStringToNSString:(const char *)string { if (string == NULL) { return nil; @@ -21,22 +10,6 @@ + (const char*)convertNSStringToCString:(NSString*) nsString { return [NSString stringWithUTF8String:string]; } -+ (NSData *)convertHexToData:(NSString *)hex { - NSString *token = [hex stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSMutableData *data = [[NSMutableData alloc] init]; - unsigned char whole_byte; - char byte_chars[3] = {'\0','\0','\0'}; - int i; - for (i=0; i < [token length] / 2; i++) { - byte_chars[0] = [token characterAtIndex:i * 2]; - byte_chars[1] = [token characterAtIndex:i * 2 + 1]; - whole_byte = strtol(byte_chars, NULL, 16); - [data appendBytes:&whole_byte length:1]; - } - - return [data copy]; -} - + (NSDictionary*)dictionaryFromJsonString:(NSString*) jsonString { if (!jsonString) { return @{}; @@ -48,287 +21,27 @@ + (NSDictionary*)dictionaryFromJsonString:(NSString*) jsonString { return dictionary; } -+ (NSNumber *)convertProperty:(NSString *)propertyStr { - NSDictionary *propertiesDict = @{ - @"Email": @(QNPropertyEmail), - @"Name": @(QNPropertyName), - @"AppsFlyerUserId": @(QNPropertyAppsFlyerUserID), - @"AdjustAdId": @(QNPropertyAdjustUserID), - @"KochavaDeviceId": @(QNPropertyKochavaDeviceID), - @"CustomUserId": @(QNPropertyUserID), - @"FirebaseAppInstanceId": @(QNPropertyFirebaseAppInstanceId), - }; - - NSNumber *propertyIndex = propertiesDict[propertyStr]; - return propertyIndex; -} - -+ (NSDictionary *)convertError:(NSError *)error { - NSDictionary *errorDict = [UtilityBridge convertPlainError:error]; ++ (NSDictionary *)convertError:(SandwichError *)error { + NSDictionary *errorDict = [UtilityBridge convertSandwichError:error]; NSMutableDictionary *result = [NSMutableDictionary new]; result[@"error"] = errorDict; + result[@"isCancelled"] = error.additionalInfo[@"isCancelled"]; return [result copy]; } -+ (NSDictionary *)convertPlainError:(NSError *)error { - NSString *errorMessage = [NSString stringWithFormat:@"%@. Domain: %@", error.localizedDescription, error.domain]; ++ (NSDictionary *)convertSandwichError:(SandwichError *)error { NSMutableDictionary *errorDict = [NSMutableDictionary new]; - errorDict[@"code"] = @(error.code).stringValue; - - NSString *debugMessage = error.userInfo[NSDebugDescriptionErrorKey]; - if (debugMessage.length > 0) { - errorMessage = [NSString stringWithFormat:@"%@\nDebugInfo:%@", errorMessage, debugMessage]; - } - - errorDict[@"message"] = errorMessage; + errorDict[@"code"] = error.code; + errorDict[@"domain"] = error.domain; + errorDict[@"description"] = error.details; + errorDict[@"additionalMessage"] = error.additionalMessage; return [errorDict copy]; } -+ (NSArray *)convertPermissions:(NSArray *)permissions { - NSMutableArray *result = [NSMutableArray new]; - - for (QNPermission *permission in permissions) { - NSMutableDictionary *permissionDict = [@{ - @"id": permission.permissionID, - @"associated_product": permission.productID, - @"renew_state": @(permission.renewState), - @"started_timestamp": @(permission.startedDate.timeIntervalSince1970 * 1000), - @"active": @(permission.isActive) - } mutableCopy]; - - if (permission.expirationDate) { - permissionDict[@"expiration_timestamp"] = @(permission.expirationDate.timeIntervalSince1970 * 1000); - } - - [result addObject:permissionDict]; - } - - return result; -} - -+ (NSArray *)convertProducts:(NSArray *)products { - NSMutableArray *result = [NSMutableArray new]; - - for (QNProduct *product in products) { - NSNumber *trialDuration = product.trialDuration ? @(product.trialDuration) : @(QNTrialDurationNotAvailable); - NSMutableDictionary *productsDict = [@{ - qonversionIdKey: product.qonversionID, - typeKey: @(product.type), - durationKey: @(product.duration), - prettyPriceKey: product.prettyPrice, - trialDurationKey: trialDuration - } mutableCopy]; - - if (product.storeID.length > 0) { - productsDict[storeIdKey] = product.storeID; - } - - if (product.offeringID.length > 0) { - productsDict[offeringIdKey] = product.offeringID; - } - - if (product.skProduct) { - NSDictionary *skProductInfo = [UtilityBridge convertSKProduct:product.skProduct]; - productsDict[storeProductKey] = skProductInfo; - } - [result addObject:productsDict]; - } - - return result; -} - -+ (QNProduct *)convertProductFromJson:(NSString *)productJson { - NSDictionary *productDict = [UtilityBridge dictionaryFromJsonString: productJson]; - - NSNumber *type = productDict[typeKey] != nil ? productDict[typeKey] : @(QNProductTypeUnknown); - NSNumber *duration = productDict[durationKey] != nil ? productDict[durationKey] : @(QNProductDurationUnknown); - NSNumber *trialDuration = productDict[trialDurationKey] != nil ? productDict[trialDurationKey] : @(QNTrialDurationNotAvailable); - - QNProductType productType = type.integerValue; - QNProductDuration productDuration = duration.integerValue; - QNTrialDuration productTrialDuration = trialDuration.integerValue; - - QNProduct *product = [QNProduct new]; - product.qonversionID = productDict[qonversionIdKey]; - product.storeID = productDict[storeIdKey]; - product.offeringID = productDict[offeringIdKey]; - product.prettyPrice = productDict[prettyPriceKey]; - product.type = productType; - product.duration = productDuration; - product.trialDuration = productTrialDuration; - - return product; -} - -+ (NSDictionary *)convertSKProduct:(SKProduct *)product { - NSMutableDictionary *result = [NSMutableDictionary new]; - result[@"localizedDescription"] = product.localizedDescription; - result[@"localizedTitle"] = product.localizedTitle; - result[@"price"] = [product.price stringValue]; - result[@"localeIdentifier"] = product.priceLocale.localeIdentifier; - result[@"productIdentifier"] = product.productIdentifier; - result[@"isDownloadable"] = @(product.isDownloadable); - result[@"downloadContentVersion"] = product.downloadContentVersion; - result[@"downloadContentLengths"] = product.downloadContentLengths; - - if (@available(iOS 10.0, *)) { - result[@"currencyCode"] = product.priceLocale.currencyCode; - } - - if (@available(iOS 11.2, *)) { - NSDictionary *subscriptionPeriod = [UtilityBridge convertSubscriptionPeriod:product.subscriptionPeriod]; - result[@"subscriptionPeriod"] = [subscriptionPeriod copy]; - - NSDictionary *introductoryPrice = [UtilityBridge convertDiscount:product.introductoryPrice]; - result[@"introductoryPrice"] = [introductoryPrice copy]; - - if (@available(iOS 12.2, *)) { - NSArray *discounts = [UtilityBridge convertDiscounts:product.discounts]; - result[@"discounts"] = discounts; - } - } - - if (@available(iOS 12.0, *)) { - result[@"subscriptionGroupIdentifier"] = product.subscriptionGroupIdentifier; - } - - if (@available(iOS 14.0, *)) { - result[@"isFamilyShareable"] = @(product.isFamilyShareable); - } - - return [result copy]; -} - - -+ (NSArray *)convertDiscounts:(NSArray *)discounts API_AVAILABLE(ios(11.2)) { - NSMutableArray *result = [NSMutableArray new]; - for (SKProductDiscount *discount in discounts) { - NSDictionary *introductoryPriceInfo = [UtilityBridge convertDiscount:discount]; - [result addObject:introductoryPriceInfo]; - } - - return [result copy]; -} - -+ (NSDictionary *)convertDiscount:(SKProductDiscount *)discount API_AVAILABLE(ios(11.2)) { - NSMutableDictionary *introductoryPrice = [NSMutableDictionary new]; - introductoryPrice[@"price"] = [discount.price stringValue]; - introductoryPrice[@"localeIdentifier"] = discount.priceLocale.localeIdentifier; - introductoryPrice[@"numberOfPeriods"] = @(discount.numberOfPeriods); - - NSDictionary *subscriptionPeriod = [UtilityBridge convertSubscriptionPeriod:discount.subscriptionPeriod]; - introductoryPrice[@"subscriptionPeriod"] = [subscriptionPeriod copy]; - - introductoryPrice[@"paymentMode"] = @(discount.paymentMode); - introductoryPrice[@"currencySymbol"] = discount.priceLocale.currencySymbol; - - if (@available(iOS 12.2, *)) { - introductoryPrice[@"identifier"] = discount.identifier; - introductoryPrice[@"type"] = @(discount.type); - } - - return [introductoryPrice copy]; -} - -+ (NSDictionary *)convertSubscriptionPeriod:(SKProductSubscriptionPeriod *)subscriptionPeriod API_AVAILABLE(ios(11.2)) { - NSMutableDictionary *introductorySubscriptionPeriod = [NSMutableDictionary new]; - introductorySubscriptionPeriod[@"numberOfUnits"] = @(subscriptionPeriod.numberOfUnits); - introductorySubscriptionPeriod[@"unit"] = @(subscriptionPeriod.unit); - - return [introductorySubscriptionPeriod copy]; -} - -+ (NSDictionary *)convertOfferings:(QNOfferings *)offerings { - NSMutableDictionary *result = [NSMutableDictionary new]; - - if (offerings.main) { - result[@"main"] = [UtilityBridge convertOffering:offerings.main]; - } - - NSMutableArray *availableOfferings = [NSMutableArray new]; - - for (QNOffering *offering in offerings.availableOfferings) { - NSDictionary *convertedOffering = [UtilityBridge convertOffering:offering]; - - [availableOfferings addObject:convertedOffering]; - } - - result[@"availableOfferings"] = [availableOfferings copy]; - - return [result copy]; -} - -+ (NSDictionary *)convertOffering:(QNOffering *)offering { - NSMutableDictionary *result = [NSMutableDictionary new]; - - result[@"id"] = offering.identifier; - result[@"tag"] = @(offering.tag); - - NSArray *convertedProducts = [UtilityBridge convertProducts:offering.products]; - - result[@"products"] = [convertedProducts copy]; - - return [result copy]; -} - -+ (NSDictionary *)convertIntroEligibility:(NSDictionary *)introEligibilityInfo { - NSDictionary *statuses = @{ - @(QNIntroEligibilityStatusNonIntroProduct): @"non_intro_or_trial_product", - @(QNIntroEligibilityStatusEligible): @"intro_or_trial_eligible", - @(QNIntroEligibilityStatusIneligible): @"intro_or_trial_ineligible" - }; - - NSMutableArray *convertedData = [NSMutableArray new]; - - for (NSString *key in introEligibilityInfo.allKeys) { - QNIntroEligibility *eligibility = introEligibilityInfo[key]; - NSString *statusValue = statuses[@(eligibility.status)] ? : @"unknonw"; - - NSDictionary *eligibilityInfo = @{@"productId": key, @"status": statusValue}; - - [convertedData addObject:eligibilityInfo]; - } - - return [convertedData copy]; -} - -+ (void)handlePermissionsResponse:(NSDictionary *) result withError:( NSError *)error - toMethod:(NSString *) methodName - unityListener:(const char *)unityListenerName { - if (error) { - [UtilityBridge handleErrorResponse:error toMethod:methodName unityListener:unityListenerName]; - return; - } - - NSArray *permissions = [UtilityBridge convertPermissions:result.allValues]; - [UtilityBridge sendUnityMessage:permissions toMethod:methodName unityListener: unityListenerName]; -} - -+ (void)handlePurchaseResponse:(NSDictionary *) permissions - isCancelled:(BOOL) cancelled - withError:(NSError *) error - toMethod:(NSString *) methodName - unityListener:(const char *) unityListenerName { - if (error) { - NSMutableDictionary *errorDict = [[UtilityBridge convertError:error] mutableCopy]; - errorDict[@"isCancelled"] = cancelled ? @(1) : @(0); - [UtilityBridge sendUnityMessage:errorDict toMethod:methodName unityListener: unityListenerName]; - return; - } - - NSArray *convertedPermissions = [UtilityBridge convertPermissions:permissions.allValues]; - - NSDictionary *result = @{ - @"permissions": convertedPermissions - }; - - [UtilityBridge sendUnityMessage:result toMethod:methodName unityListener: unityListenerName]; -} - -+ (void)handleErrorResponse:(NSError *)error toMethod:(NSString *) methodName ++ (void)handleErrorResponse:(SandwichError *)error toMethod:(NSString *)methodName unityListener:(const char *)unityListenerName{ NSDictionary *errorDict = [UtilityBridge convertError:error]; [UtilityBridge sendUnityMessage:errorDict toMethod:methodName unityListener: unityListenerName]; @@ -349,69 +62,15 @@ + (void)sendUnityMessage:(NSObject *)objectToConvert toMethod:(NSString *)method } } -+ (NSDictionary *)convertActionResult:(QONActionResult *)actionResult { - NSMutableDictionary *result = [NSMutableDictionary new]; - - NSDictionary *types = @{ - @(QONActionResultTypeURL): @"url", - @(QONActionResultTypeDeeplink): @"deeplink", - @(QONActionResultTypeNavigation): @"navigate", - @(QONActionResultTypePurchase): @"purchase", - @(QONActionResultTypeRestore): @"restore", - @(QONActionResultTypeClose): @"close" - }; - - result[@"type"] = types[@(actionResult.type)] ? : @"unknown"; - result[@"value"] = actionResult.parameters; - if (actionResult.error) { - result[@"error"] = [UtilityBridge convertPlainError:actionResult.error]; - } - - return [result copy]; - } - - + (NSDictionary *)convertAutomationsEvent:(QONAutomationsEvent *)event { - NSMutableDictionary *result = [NSMutableDictionary new]; - - NSDictionary *types = @{ - @(QONAutomationsEventTypeTrialStarted): @"trial_started", - @(QONAutomationsEventTypeTrialConverted): @"trial_converted", - @(QONAutomationsEventTypeTrialCanceled): @"trial_canceled", - @(QONAutomationsEventTypeTrialBillingRetry): @"trial_billing_retry_entered", - @(QONAutomationsEventTypeSubscriptionStarted): @"subscription_started", - @(QONAutomationsEventTypeSubscriptionRenewed): @"subscription_renewed", - @(QONAutomationsEventTypeSubscriptionRefunded): @"subscription_refunded", - @(QONAutomationsEventTypeSubscriptionCanceled): @"subscription_canceled", - @(QONAutomationsEventTypeSubscriptionBillingRetry): @"subscription_billing_retry_entered", - @(QONAutomationsEventTypeInAppPurchase): @"in_app_purchase", - @(QONAutomationsEventTypeSubscriptionUpgraded): @"subscription_upgraded", - @(QONAutomationsEventTypeTrialStillActive): @"trial_still_active", - @(QONAutomationsEventTypeTrialExpired): @"trial_expired", - @(QONAutomationsEventTypeSubscriptionExpired): @"subscription_expired", - @(QONAutomationsEventTypeSubscriptionDowngraded): @"subscription_downgraded", - @(QONAutomationsEventTypeSubscriptionProductChanged): @"subscription_product_changed" - }; - - result[@"type"] = types[@(event.type)] ? : @"unknown"; - result[@"timestamp"] = @(event.date.timeIntervalSince1970 * 1000); - - return [result copy]; - } - -+ (NSNumber *)convertPermissionsCacheLifetime:(NSString *)lifetimeKey { - NSDictionary *lifetimesDict = @{ - @"WEEK": @(QNPermissionsCacheLifetimeWeek), - @"TWO_WEEKS": @(QNPermissionsCacheLifetimeTwoWeeks), - @"MONTH": @(QNPermissionsCacheLifetimeMonth), - @"TWO_MONTHS": @(QNPermissionsCacheLifetimeTwoMonth), - @"THREE_MONTHS": @(QNPermissionsCacheLifetimeThreeMonth), - @"SIX_MONTHS": @(QNPermissionsCacheLifetimeSixMonth), - @"YEAR": @(QNPermissionsCacheLifetimeYear), - @"UNLIMITED": @(QNPermissionsCacheLifetimeUnlimited), - }; - - NSNumber *lifetimeIndex = lifetimesDict[lifetimeKey]; - return lifetimeIndex; ++ (void)handleResult:(NSDictionary *)result + error:(SandwichError *)error + callbackName:(NSString *)callbackName + unityListener:(const char *)unityListenerName { + if (error) { + [UtilityBridge handleErrorResponse:error toMethod:callbackName unityListener:unityListenerName]; + } else { + [UtilityBridge sendUnityMessage:result toMethod:callbackName unityListener:unityListenerName]; + } } @end diff --git a/Runtime/iOS/Plugins/QonversionBridge.m b/Runtime/iOS/Plugins/QonversionBridge.m index fed7cf7..1058018 100644 --- a/Runtime/iOS/Plugins/QonversionBridge.m +++ b/Runtime/iOS/Plugins/QonversionBridge.m @@ -1,170 +1,153 @@ -#import #import "UtilityBridge.h" #import "QNUAutomationsDelegate.h" +@import QonversionSandwich; char* unityListenerName = nil; -@interface PurchasesDelegateWrapper : NSObject +@interface QonversionEventListenerWrapper : NSObject -- (void)qonversionDidReceiveUpdatedPermissions:(NSDictionary *_Nonnull)permissions; -- (void)shouldPurchasePromoProductWithIdentifier:(NSString *)productID executionBlock:(QNPromoPurchaseCompletionHandler)executionBlock; - -@property (nonatomic, strong) NSMutableDictionary *promoPurchasesExecutionBlocks; +- (void)qonversionDidReceiveUpdatedPermissions:(NSDictionary * _Nonnull)permissions; +- (void)shouldPurchasePromoProductWith:(NSString * _Nonnull)productId; @end -@implementation PurchasesDelegateWrapper - -- (void)shouldPurchasePromoProductWithIdentifier:(NSString *)productID executionBlock:(QNPromoPurchaseCompletionHandler)executionBlock { - if (!_promoPurchasesExecutionBlocks) { - _promoPurchasesExecutionBlocks = [[NSMutableDictionary alloc] init]; - } - [_promoPurchasesExecutionBlocks setObject:executionBlock forKey:productID]; +@implementation QonversionEventListenerWrapper - UnitySendMessage(unityListenerName, "OnReceivePromoPurchase", productID.UTF8String); +- (void)qonversionDidReceiveUpdatedPermissions:(NSDictionary * _Nonnull)permissions { + [UtilityBridge sendUnityMessage:permissions toMethod:@"OnReceiveUpdatedPurchases" unityListener: unityListenerName]; } -- (void)qonversionDidReceiveUpdatedPermissions:(NSDictionary *_Nonnull)permissions { - NSArray *permissionsArray = [UtilityBridge convertPermissions:permissions.allValues]; - [UtilityBridge sendUnityMessage:permissionsArray toMethod:@"OnReceiveUpdatedPurchases" unityListener: unityListenerName]; +- (void)shouldPurchasePromoProductWith:(NSString * _Nonnull)productId { + UnitySendMessage(unityListenerName, "OnReceivePromoPurchase", productId.UTF8String); } @end -static PurchasesDelegateWrapper *purchasesDelegate; -static PurchasesDelegateWrapper *promoPurchasesDelegate; static QNUAutomationsDelegate *automationsDelegate; +static QonversionSandwich *qonversionSandwich; + +void _initialize(const char* unityListener) { + unsigned long len = strlen(unityListener); + unityListenerName = malloc(len + 1); + strcpy(unityListenerName, unityListener); + + qonversionSandwich = [[QonversionSandwich alloc] initWithQonversionEventListener:[QonversionEventListenerWrapper new]]; + automationsDelegate = [[QNUAutomationsDelegate alloc] initWithListenerName:unityListenerName]; +} -void _storeSdkInfo(const char* version, const char* versionKey, const char* source, const char* sourceKey) { +void _storeSdkInfo(const char* version, const char* source) { NSString *versionStr = [UtilityBridge сonvertCStringToNSString:version]; - NSString *versionKeyStr = [UtilityBridge сonvertCStringToNSString:versionKey]; NSString *sourceStr = [UtilityBridge сonvertCStringToNSString:source]; - NSString *sourceKeyStr = [UtilityBridge сonvertCStringToNSString:sourceKey]; - [[NSUserDefaults standardUserDefaults] setValue:versionStr forKey:versionKeyStr]; - [[NSUserDefaults standardUserDefaults] setValue:sourceStr forKey:sourceKeyStr]; + [qonversionSandwich storeSdkInfoWithSource:sourceStr version:versionStr]; } void _setDebugMode() { - [Qonversion setDebugMode]; + [qonversionSandwich setDebugMode]; } -void _launchWithKey(const char* unityListener, const char* key) { - unsigned long len = strlen(unityListener); - unityListenerName = malloc(len + 1); - strcpy(unityListenerName, unityListener); +void _launchWithKey(const char* key, const char* unityCallbackName) { + NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; + + NSString *projectKey = [UtilityBridge сonvertCStringToNSString:key]; - [Qonversion launchWithKey:[UtilityBridge сonvertCStringToNSString:key]]; + [qonversionSandwich launchWithProjectKey:projectKey completion:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; + }]; } void _setAdvertisingID() { - [Qonversion setAdvertisingID]; + [qonversionSandwich setAdvertisingId]; } void _presentCodeRedemptionSheet() { if (@available(iOS 14.0, *)) { - [Qonversion presentCodeRedemptionSheet]; + [qonversionSandwich presentCodeRedemptionSheet]; } } void _setAppleSearchAdsAttributionEnabled(const bool enable) { - [Qonversion setAppleSearchAdsAttributionEnabled:enable]; + [qonversionSandwich setAppleSearchAdsAttributionEnabled:enable]; } void _setProperty(const char* propertyName, const char* value) { - NSString *propertyNameStr = [UtilityBridge сonvertCStringToNSString:propertyName]; + NSString *propertyStr = [UtilityBridge сonvertCStringToNSString:propertyName]; NSString *valueStr = [UtilityBridge сonvertCStringToNSString:value]; - NSNumber *propertyIndex = [UtilityBridge convertProperty:propertyNameStr]; - if (propertyIndex) { - [Qonversion setProperty:propertyIndex.integerValue value:valueStr]; - } + [qonversionSandwich setDefinedProperty:propertyStr value:valueStr]; } void _setUserProperty(const char* key, const char* value) { NSString *keyStr = [UtilityBridge сonvertCStringToNSString:key]; NSString *valueStr = [UtilityBridge сonvertCStringToNSString:value]; - - [Qonversion setUserProperty:keyStr value:valueStr]; + + [qonversionSandwich setCustomProperty:keyStr value:valueStr]; } -void _addAttributionData(const char* conversionData, const int provider) { +void _addAttributionData(const char* conversionData, const char* provider) { NSDictionary *conversionInfo = [UtilityBridge dictionaryFromJsonString: [UtilityBridge сonvertCStringToNSString: conversionData]]; - - [Qonversion addAttributionData:conversionInfo - fromProvider:(QNAttributionProvider)provider]; + NSString *providerStr = [UtilityBridge сonvertCStringToNSString:provider]; + + [qonversionSandwich addAttributionDataWithSourceKey:providerStr value:conversionInfo]; } void _identify(const char* userId) { NSString *userIdStr = [UtilityBridge сonvertCStringToNSString:userId]; - [Qonversion identify:userIdStr]; + [qonversionSandwich identify:userIdStr]; } void _logout() { - [Qonversion logout]; + [qonversionSandwich logout]; } void _checkPermissions(const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - [Qonversion checkPermissions:^(NSDictionary *result, NSError *error) { - [UtilityBridge handlePermissionsResponse:result withError:error toMethod:callbackName unityListener:unityListenerName]; + [qonversionSandwich checkPermissions:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } void _restore(const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - [Qonversion restoreWithCompletion:^(NSDictionary *result, NSError *error) { - [UtilityBridge handlePermissionsResponse:result withError:error toMethod:callbackName unityListener:unityListenerName]; + [qonversionSandwich restore:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } void _purchase(const char* productId, const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - - [Qonversion purchase:[UtilityBridge сonvertCStringToNSString:productId] completion:^(NSDictionary *result, NSError *error, BOOL cancelled) { - [UtilityBridge handlePurchaseResponse:result isCancelled:cancelled withError:error toMethod:callbackName unityListener:unityListenerName]; + NSString *productIdStr = [UtilityBridge сonvertCStringToNSString:productId]; + + [qonversionSandwich purchase:productIdStr completion:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } -void _purchaseProduct(const char* productJson, const char* unityCallbackName) { - NSString *productJsonStr = [UtilityBridge сonvertCStringToNSString:productJson]; +void _purchaseProduct(const char* productId, const char* offeringId, const char* unityCallbackName) { + NSString *productIdStr = [UtilityBridge сonvertCStringToNSString:productId]; + NSString *offeringIdStr = [UtilityBridge сonvertCStringToNSString:offeringId]; NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - - QNProduct *product = [UtilityBridge convertProductFromJson: productJsonStr]; - [Qonversion purchaseProduct:product completion:^(NSDictionary * _Nonnull result, - NSError * _Nullable error, - BOOL cancelled) { - [UtilityBridge handlePurchaseResponse:result isCancelled:cancelled withError:error toMethod:callbackName unityListener:unityListenerName]; + + [qonversionSandwich purchaseProduct:productIdStr :offeringIdStr completion:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } void _products(const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - - [Qonversion products:^(NSDictionary *result, NSError *error) { - if (error) { - [UtilityBridge handleErrorResponse:error toMethod:callbackName unityListener:unityListenerName]; - return; - } - - NSArray *products = [UtilityBridge convertProducts:result.allValues]; - [UtilityBridge sendUnityMessage:products toMethod:callbackName unityListener: unityListenerName]; + + [qonversionSandwich products:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } void _offerings(const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; - - [Qonversion offerings:^(QNOfferings * _Nullable result, NSError * _Nullable error) { - if (error) { - [UtilityBridge handleErrorResponse:error toMethod:callbackName unityListener:unityListenerName]; - return; - } - - NSDictionary *offerings = [UtilityBridge convertOfferings:result]; - [UtilityBridge sendUnityMessage:offerings toMethod:callbackName unityListener: unityListenerName]; + + [qonversionSandwich offerings:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } @@ -181,14 +164,8 @@ void _checkTrialIntroEligibilityForProductIds(const char* productIdsJson, const return; } if (products) { - [Qonversion checkTrialIntroEligibilityForProductIds:products completion:^(NSDictionary * _Nonnull result, NSError * _Nullable error) { - if (error) { - [UtilityBridge handleErrorResponse:error toMethod:callbackName unityListener:unityListenerName]; - return; - } - - NSDictionary *eligibilities = [UtilityBridge convertIntroEligibility:result]; - [UtilityBridge sendUnityMessage:eligibilities toMethod:callbackName unityListener: unityListenerName]; + [qonversionSandwich checkTrialIntroEligibility:products completion:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; }]; } } @@ -196,66 +173,30 @@ void _checkTrialIntroEligibilityForProductIds(const char* productIdsJson, const void _promoPurchase(const char* storeProductId, const char* unityCallbackName) { NSString *callbackName = [UtilityBridge сonvertCStringToNSString:unityCallbackName]; NSString *storeProductIdStr = [UtilityBridge сonvertCStringToNSString:storeProductId]; - QNPromoPurchaseCompletionHandler executionBlock = [promoPurchasesDelegate.promoPurchasesExecutionBlocks objectForKey:storeProductIdStr]; - if (executionBlock) { - [promoPurchasesDelegate.promoPurchasesExecutionBlocks removeObjectForKey:storeProductIdStr]; - QNPurchaseCompletionHandler completion = ^(NSDictionary *result, NSError *_Nullable error, BOOL cancelled) { - [UtilityBridge handlePermissionsResponse:result withError:error toMethod:callbackName unityListener:unityListenerName]; - }; - - executionBlock(completion); - } else { - NSError *error = [NSError errorWithDomain:keyQNErrorDomain code:QNErrorProductNotFound userInfo:nil]; - [UtilityBridge handleErrorResponse:error toMethod:callbackName unityListener:unityListenerName]; - } -} - -void _addPromoPurchasesDelegate() { - if (!promoPurchasesDelegate) { - promoPurchasesDelegate = [PurchasesDelegateWrapper alloc]; - } - [Qonversion setPromoPurchasesDelegate:promoPurchasesDelegate]; -} - -void _removePromoPurchasesDelegate() { - promoPurchasesDelegate = nil; - } - -void _addUpdatedPurchasesDelegate() { - if (!purchasesDelegate) { - purchasesDelegate = [PurchasesDelegateWrapper alloc]; - } - [Qonversion setPurchasesDelegate:purchasesDelegate]; -} - -void _removeUpdatedPurchasesDelegate() { - purchasesDelegate = nil; + + [qonversionSandwich promoPurchase:storeProductIdStr completion:^(NSDictionary * _Nullable result, SandwichError * _Nullable error) { + [UtilityBridge handleResult:result error:error callbackName:callbackName unityListener:unityListenerName]; + }]; } void _setPermissionsCacheLifetime(const char* lifetimeName) { NSString *lifetimeNameStr = [UtilityBridge сonvertCStringToNSString:lifetimeName]; - NSNumber *lifetimeIndex = [UtilityBridge convertPermissionsCacheLifetime:lifetimeNameStr]; - - if (lifetimeIndex) { - [Qonversion setPermissionsCacheLifetime:lifetimeIndex.integerValue]; - } + [qonversionSandwich setPermissionsCacheLifetime:lifetimeNameStr]; } void _setNotificationsToken(const char* token) { - NSString *hexString = [UtilityBridge сonvertCStringToNSString:token]; - NSData *tokenData = [UtilityBridge convertHexToData:hexString]; - - [Qonversion setNotificationsToken:tokenData]; + NSString *tokenStr = [UtilityBridge сonvertCStringToNSString:token]; + [qonversionSandwich setNotificationToken:tokenStr]; } bool _handleNotification(const char* notification) { NSDictionary *notificationInfo = [UtilityBridge dictionaryFromJsonString: [UtilityBridge сonvertCStringToNSString: notification]]; - BOOL result = [Qonversion handleNotification:notificationInfo]; + BOOL isQonversionNotification = [qonversionSandwich handleNotification:notificationInfo]; - return result; + return isQonversionNotification; } void _subscribeOnAutomationEvents() { - automationsDelegate = [[QNUAutomationsDelegate alloc] initWithListenerName:unityListenerName]; + [automationsDelegate subscribe]; } diff --git a/Runtime/iOS/QonversionWrapperIOS.cs b/Runtime/iOS/QonversionWrapperIOS.cs index 6486915..1a997ac 100644 --- a/Runtime/iOS/QonversionWrapperIOS.cs +++ b/Runtime/iOS/QonversionWrapperIOS.cs @@ -42,7 +42,7 @@ internal class QonversionWrapperIOS : IQonversionWrapper private static extern void _launchWithKey(string key, string callbackName); [DllImport("__Internal")] - private static extern void _addAttributionData(string conversionData, int provider); + private static extern void _addAttributionData(string conversionData, string providerName); [DllImport("__Internal")] private static extern void _checkPermissions(string callbackName); @@ -141,7 +141,8 @@ public void SetProperty(UserProperty key, string value) public void AddAttributionData(string conversionData, AttributionSource source) { #if UNITY_IOS - _addAttributionData(conversionData, (int)source); + string sourceName = Enum.GetName(typeof(AttributionSource), source); + _addAttributionData(conversionData, sourceName); #endif } From 60a9aa663056e77207c03c2c190c8de11f5a8c3e Mon Sep 17 00:00:00 2001 From: kamospertsyan Date: Mon, 12 Sep 2022 11:19:04 +0300 Subject: [PATCH 4/5] Upgrade to Sandwich 0.1.0. --- Editor/QonversionDependencies.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Editor/QonversionDependencies.xml b/Editor/QonversionDependencies.xml index 2736c7a..dd77060 100644 --- a/Editor/QonversionDependencies.xml +++ b/Editor/QonversionDependencies.xml @@ -1,11 +1,11 @@ - + - + From ebc02bf5316a4e375226d642c8a52e09b8c4e8cf Mon Sep 17 00:00:00 2001 From: SpertsyanKM Date: Mon, 12 Sep 2022 08:25:29 +0000 Subject: [PATCH 5/5] [create-pull-request] automated change --- Runtime/Scripts/Qonversion.cs | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/Qonversion.cs b/Runtime/Scripts/Qonversion.cs index 796b697..adf0a4d 100644 --- a/Runtime/Scripts/Qonversion.cs +++ b/Runtime/Scripts/Qonversion.cs @@ -53,7 +53,7 @@ public class Qonversion : MonoBehaviour private const string OnOfferingsMethodName = "OnOfferings"; private const string OnEligibilitiesMethodName = "OnEligibilities"; - private const string SdkVersion = "3.5.1"; + private const string SdkVersion = "3.6.0"; private const string SdkSource = "unity"; private static IQonversionWrapper _Instance; diff --git a/package.json b/package.json index 2ce1592..f95e5f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.qonversion.unity", "displayName": "Qonversion", - "version": "3.5.1", + "version": "3.6.0", "unity": "2018.3", "description": "Empower your mobile app marketing and product decisions with precise subscription data.", "author": {