227 lines
8.9 KiB
C++
227 lines
8.9 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "OnlineStoreGooglePlay.h"
|
|
#include "OnlineJniGooglePlayStoreHelper.h"
|
|
#include "OnlineSubsystemGooglePlay.h"
|
|
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Culture.h"
|
|
#include "Internationalization/FastDecimalFormat.h"
|
|
|
|
FOnlineStoreGooglePlayV2::FOnlineStoreGooglePlayV2(FOnlineSubsystemGooglePlay* InSubsystem)
|
|
: bIsQueryInFlight(false)
|
|
, Subsystem(InSubsystem)
|
|
{
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT( "FOnlineStoreGooglePlayV2::FOnlineStoreGooglePlayV2" ));
|
|
}
|
|
|
|
FOnlineStoreGooglePlayV2::FOnlineStoreGooglePlayV2()
|
|
: bIsQueryInFlight(false)
|
|
, Subsystem(nullptr)
|
|
{
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT( "FOnlineStoreGooglePlayV2::FOnlineStoreGooglePlayV2 empty" ));
|
|
}
|
|
|
|
FOnlineStoreGooglePlayV2::~FOnlineStoreGooglePlayV2()
|
|
{
|
|
}
|
|
|
|
TSharedRef<FOnlineStoreOffer> ConvertProductToStoreOffer(const FOnlineStoreOffer& Product)
|
|
{
|
|
return MakeShared<FOnlineStoreOffer>(Product);
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::OnGooglePlayAvailableIAPQueryComplete(EGooglePlayBillingResponseCode InResponseCode, const TArray<FProvidedProductInformation>& InProvidedProductInformation)
|
|
{
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT("OnGooglePlayAvailableIAPQueryComplete Response: %s NumProucts: %d"), LexToString(InResponseCode), InProvidedProductInformation.Num());
|
|
|
|
bool bSuccess = (InResponseCode == EGooglePlayBillingResponseCode::Ok);
|
|
TArray<FUniqueOfferId> OfferIds;
|
|
FString ErrorStr;
|
|
|
|
if(!bIsQueryInFlight)
|
|
{
|
|
UE_LOG_ONLINE_STOREV2(Log, TEXT("OnGooglePlayAvailableIAPQueryComplete: No IAP query in flight"));
|
|
}
|
|
|
|
if (bSuccess)
|
|
{
|
|
for (const FProvidedProductInformation& Product : InProvidedProductInformation)
|
|
{
|
|
TSharedRef<FOnlineStoreOffer> NewProductOffer = ConvertProductToStoreOffer(Product);
|
|
|
|
AddOffer(NewProductOffer);
|
|
OfferIds.Add(NewProductOffer->OfferId);
|
|
|
|
UE_LOG_ONLINE_STOREV2(Log, TEXT("Product Identifier: %s, Name: %s, Desc: %s, Long Desc: %s, Price: %s IntPrice: %lld"),
|
|
*NewProductOffer->OfferId,
|
|
*NewProductOffer->Title.ToString(),
|
|
*NewProductOffer->Description.ToString(),
|
|
*NewProductOffer->LongDescription.ToString(),
|
|
*NewProductOffer->PriceText.ToString(),
|
|
NewProductOffer->NumericPrice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorStr = LexToString(InResponseCode);
|
|
}
|
|
|
|
QueryOnlineStoreOffersCompleteDelegate.ExecuteIfBound(bSuccess, OfferIds, ErrorStr);
|
|
QueryOnlineStoreOffersCompleteDelegate.Unbind();
|
|
bIsQueryInFlight = false;
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::QueryCategories(const FUniqueNetId& UserId, const FOnQueryOnlineStoreCategoriesComplete& Delegate)
|
|
{
|
|
Delegate.ExecuteIfBound(false, TEXT("No CatalogService"));
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::GetCategories(TArray<FOnlineStoreCategory>& OutCategories) const
|
|
{
|
|
OutCategories.Empty();
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::QueryOffersByFilter(const FUniqueNetId& UserId, const FOnlineStoreFilter& Filter, const FOnQueryOnlineStoreOffersComplete& Delegate)
|
|
{
|
|
Delegate.ExecuteIfBound(false, TArray<FUniqueOfferId>(), TEXT("No CatalogService"));
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::QueryOffersById(const FUniqueNetId& UserId, const TArray<FUniqueOfferId>& OfferIds, const FOnQueryOnlineStoreOffersComplete& Delegate)
|
|
{
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT("FOnlineStoreGooglePlayV2::QueryOffersById"));
|
|
|
|
if (bIsQueryInFlight)
|
|
{
|
|
Delegate.ExecuteIfBound(false, OfferIds, TEXT("Request already in flight"));
|
|
}
|
|
else if (OfferIds.Num() == 0)
|
|
{
|
|
Delegate.ExecuteIfBound(false, OfferIds, TEXT("No offers to query for"));
|
|
}
|
|
else
|
|
{
|
|
bIsQueryInFlight = true;
|
|
QueryOnlineStoreOffersCompleteDelegate = Delegate;
|
|
extern bool AndroidThunkCpp_Iap_QueryInAppPurchases(const TArray<FString>&);
|
|
if (!AndroidThunkCpp_Iap_QueryInAppPurchases(OfferIds))
|
|
{
|
|
Delegate.ExecuteIfBound(false, OfferIds, TEXT("Query did not start"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::AddOffer(const TSharedRef<FOnlineStoreOffer>& NewOffer)
|
|
{
|
|
TSharedRef<FOnlineStoreOffer>* Existing = CachedOffers.Find(NewOffer->OfferId);
|
|
if (Existing != nullptr)
|
|
{
|
|
// Replace existing offer
|
|
*Existing = NewOffer;
|
|
}
|
|
else
|
|
{
|
|
CachedOffers.Add(NewOffer->OfferId, NewOffer);
|
|
}
|
|
}
|
|
|
|
void FOnlineStoreGooglePlayV2::GetOffers(TArray<FOnlineStoreOfferRef>& OutOffers) const
|
|
{
|
|
for (const auto& CachedEntry : CachedOffers)
|
|
{
|
|
const TSharedRef<FOnlineStoreOffer>& CachedOffer = CachedEntry.Value;
|
|
OutOffers.Add(CachedOffer);
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FOnlineStoreOffer> FOnlineStoreGooglePlayV2::GetOffer(const FUniqueOfferId& OfferId) const
|
|
{
|
|
TSharedPtr<FOnlineStoreOffer> Result;
|
|
|
|
const TSharedRef<FOnlineStoreOffer>* Existing = CachedOffers.Find(OfferId);
|
|
if (Existing != nullptr)
|
|
{
|
|
Result = (*Existing);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void JNICALL UE::Jni::FGooglePlayStoreHelper::NativeQueryComplete(JNIEnv* jenv, jobject /*Thiz*/, jint ResponseCode, Java::Lang::TArray<jstring>* ProductIDs, Java::Lang::TArray<jstring>* Titles, Java::Lang::TArray<jstring>* Descriptions, Java::Lang::TArray<jstring>* Prices, Java::Lang::TArray<jlong>* PriceValuesRaw, Java::Lang::TArray<jstring>* CurrencyCodes)
|
|
{
|
|
TArray<FOnlineStoreOffer> ProvidedProductInformation;
|
|
EGooglePlayBillingResponseCode EGPResponse = (EGooglePlayBillingResponseCode)ResponseCode;
|
|
bool bWasSuccessful = (EGPResponse == EGooglePlayBillingResponseCode::Ok);
|
|
|
|
if (jenv && bWasSuccessful)
|
|
{
|
|
jsize NumProducts = jenv->GetArrayLength(ProductIDs);
|
|
jsize NumTitles = jenv->GetArrayLength(Titles);
|
|
jsize NumDescriptions = jenv->GetArrayLength(Descriptions);
|
|
jsize NumPrices = jenv->GetArrayLength(Prices);
|
|
jsize NumPricesRaw = jenv->GetArrayLength(PriceValuesRaw);
|
|
jsize NumCurrencyCodes = jenv->GetArrayLength(CurrencyCodes);
|
|
|
|
ensure((NumProducts == NumTitles) && (NumProducts == NumDescriptions) && (NumProducts == NumPrices) && (NumProducts == NumPricesRaw) && (NumProducts == NumCurrencyCodes));
|
|
|
|
jlong* PriceValues = jenv->GetLongArrayElements(PriceValuesRaw, 0);
|
|
|
|
for (jsize Idx = 0; Idx < NumProducts; Idx++)
|
|
{
|
|
FOnlineStoreOffer NewProductInfo;
|
|
|
|
NewProductInfo.OfferId = FJavaHelper::FStringFromLocalRef(jenv, (jstring)jenv->GetObjectArrayElement(ProductIDs, Idx));
|
|
|
|
int32 OpenParenIdx = -1;
|
|
int32 CloseParenIdx = -1;
|
|
FString Title = FJavaHelper::FStringFromLocalRef(jenv, (jstring)jenv->GetObjectArrayElement(Titles, Idx));
|
|
if (Title.FindLastChar(TEXT(')'), CloseParenIdx) && Title.FindLastChar(TEXT('('), OpenParenIdx) && (OpenParenIdx < CloseParenIdx))
|
|
{
|
|
Title = Title.Left(OpenParenIdx).TrimEnd();
|
|
}
|
|
NewProductInfo.Title = FText::FromString(Title);
|
|
|
|
NewProductInfo.Description = FText::FromString(FJavaHelper::FStringFromLocalRef(jenv, (jstring)jenv->GetObjectArrayElement(Descriptions, Idx)));
|
|
NewProductInfo.PriceText = FText::FromString(FJavaHelper::FStringFromLocalRef(jenv, (jstring)jenv->GetObjectArrayElement(Prices, Idx)));
|
|
NewProductInfo.CurrencyCode = FJavaHelper::FStringFromLocalRef(jenv, (jstring)jenv->GetObjectArrayElement(CurrencyCodes, Idx));
|
|
|
|
// Convert the backend stated price into its base units
|
|
FInternationalization& I18N = FInternationalization::Get();
|
|
const FCulture& Culture = *I18N.GetCurrentCulture();
|
|
|
|
const FDecimalNumberFormattingRules& FormattingRules = Culture.GetCurrencyFormattingRules(NewProductInfo.CurrencyCode);
|
|
const FNumberFormattingOptions& FormattingOptions = FormattingRules.CultureDefaultFormattingOptions;
|
|
|
|
// Value provided in PriceValues[idx] by Google Billing Library is multiplied by 1'000'000 (it is a value in micro units). We need to divide it by 10^6 and multiply by
|
|
// 10^FormattingOptions.MaximumFractionalDigits to properly adjust the value to the number of fractional digits expected
|
|
double Val = static_cast<double>(PriceValues[Idx]) * static_cast<double>(FMath::Pow(10.0f, FormattingOptions.MaximumFractionalDigits - 6));
|
|
|
|
NewProductInfo.NumericPrice = FMath::TruncToInt(Val + 0.5);
|
|
NewProductInfo.ReleaseDate = FDateTime::MinValue();
|
|
NewProductInfo.ExpirationDate = FDateTime::MaxValue();
|
|
|
|
ProvidedProductInformation.Add(NewProductInfo);
|
|
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT("\nProduct Identifier: %s, Name: %s, Description: %s, Price: %s, Price Raw: %lld, Currency Code: %s\n"),
|
|
*NewProductInfo.OfferId,
|
|
*NewProductInfo.Title.ToString(),
|
|
*NewProductInfo.Description.ToString(),
|
|
*NewProductInfo.GetDisplayPrice().ToString(),
|
|
NewProductInfo.NumericPrice,
|
|
*NewProductInfo.CurrencyCode);
|
|
}
|
|
jenv->ReleaseLongArrayElements(PriceValuesRaw, PriceValues, JNI_ABORT);
|
|
}
|
|
|
|
UE_LOG_ONLINE_STOREV2(Verbose, TEXT("QueryOffersById result Success: %d Response: %s"), bWasSuccessful, LexToString(EGPResponse));
|
|
|
|
if (auto OnlineSubGP = static_cast<FOnlineSubsystemGooglePlay* const>(IOnlineSubsystem::Get(GOOGLEPLAY_SUBSYSTEM)))
|
|
{
|
|
OnlineSubGP->ExecuteNextTick([OnlineSubGP, EGPResponse, Response = MoveTemp(ProvidedProductInformation)]()
|
|
{
|
|
TSharedPtr<FOnlineStoreGooglePlayV2> StoreInt = StaticCastSharedPtr<FOnlineStoreGooglePlayV2>(OnlineSubGP->GetStoreV2Interface());
|
|
StoreInt->OnGooglePlayAvailableIAPQueryComplete(EGPResponse, Response);
|
|
});
|
|
}
|
|
}
|