368 lines
13 KiB
C++

// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRCompatibilityRules.h"
#include "CoreMinimal.h"
#include "AndroidRuntimeSettings.h"
#include "AndroidSDKSettings.h"
#include "GeneralProjectSettings.h"
#include "ISettingsModule.h"
#include "ISettingsCategory.h"
#include "ISettingsContainer.h"
#include "ISettingsSection.h"
#include "OculusXRRuleProcessorSubsystem.h"
#include "GameFramework/InputSettings.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRPSTUtils.h"
#include "Misc/EngineVersionComparison.h"
#define LOCTEXT_NAMESPACE "OculusXRCompatibilityRules"
namespace
{
constexpr int32 MinimumAndroidAPILevel = 32; // With Quest 1 support ending in Jan 2025, API level 29 is no longer supported.
constexpr int32 TargetAndroidAPILevel = 32; // Target API 32 or higher is required to submit to the Meta Quest Store. See https://developer.oculus.com/blog/meta-quest-apps-android-12l-june-30/
constexpr char AndroidNDKVersionNumber[] = "25.1.8937393";
// SDK Max API level is determined by reviewing SetupAndroid.bat for each UE version.
// SDK Min API Level is determined by Quest Store requirements as noted above.
#if UE_VERSION_OLDER_THAN(5, 4, 0)
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
constexpr char AndroidSDKAPIMaxLevel[] = "android-32";
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
constexpr int32 AndroidSDKAPIMaxLevelInt = 32;
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
constexpr char AndroidNDKAPIMaxLevel[] = "android-32";
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
constexpr int32 AndroidNDKAPIMaxLevelInt = 32;
#elif UE_VERSION_OLDER_THAN(5, 5, 0)
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
constexpr char AndroidSDKAPIMaxLevel[] = "android-33";
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
constexpr int32 AndroidSDKAPIMaxLevelInt = 33;
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
constexpr char AndroidNDKAPIMaxLevel[] = "android-33";
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
constexpr int32 AndroidNDKAPIMaxLevelInt = 33;
#else // 5.5 and newer
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
constexpr char AndroidSDKAPIMaxLevel[] = "android-34";
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
constexpr int32 AndroidSDKAPIMaxLevelInt = 34;
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
constexpr char AndroidNDKAPIMaxLevel[] = "android-33"; // API level 34 is not supported by NDK 25.1.8937393
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
constexpr int32 AndroidNDKAPIMaxLevelInt = 33;
#endif
} // namespace
namespace OculusXRCompatibilityRules
{
FUseAndroidSDKMinimumRule::FUseAndroidSDKMinimumRule()
: ISetupRule(
"Compatibility_UseAndroidSDKMinimum",
LOCTEXT("UseAndroidSDKMinimum_DisplayName", "Use Android SDK Minimum Version"),
FText::Format(
LOCTEXT("UseAndroidSDKMinimum_Description", "Minimum Android API level must be at least {0}."),
MinimumAndroidAPILevel),
ESetupRuleCategory::Compatibility,
ESetupRuleSeverity::Critical,
MetaQuest_All) {}
bool FUseAndroidSDKMinimumRule::IsApplied() const
{
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
return Settings->MinSDKVersion >= MinimumAndroidAPILevel;
}
void FUseAndroidSDKMinimumRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, MinSDKVersion, MinimumAndroidAPILevel);
OutShouldRestartEditor = false;
}
FUseAndroidSDKTargetRule::FUseAndroidSDKTargetRule()
: ISetupRule(
"Compatibility_UseAndroidSDKTarget",
LOCTEXT("UseAndroidSDKTarget_DisplayName", "Use Android SDK Target Version"),
FText::Format(
LOCTEXT("UseAndroidSDKTarget_Description", "Target Android API level must be at least {0}."),
TargetAndroidAPILevel),
ESetupRuleCategory::Compatibility,
ESetupRuleSeverity::Critical,
MetaQuest_All) {}
bool FUseAndroidSDKTargetRule::IsApplied() const
{
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
return Settings->TargetSDKVersion >= TargetAndroidAPILevel;
}
void FUseAndroidSDKTargetRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, TargetSDKVersion, TargetAndroidAPILevel);
OutShouldRestartEditor = false;
}
FUseAndroidSDKLevelRule::FUseAndroidSDKLevelRule()
: ISetupRule(
"Compatibility_UseAndroidSDKLevel",
LOCTEXT("UseAndroidSDKLevel_DisplayName", "Use Android SDK Level"),
FText::Format(
LOCTEXT("UseAndroidSDKLevel_Description", "Android SDK level should be set between {0} and {1} prior to packaging apks."),
FText::AsCultureInvariant(AndroidSDKAPIMinLevel), FText::AsCultureInvariant(AndroidSDKAPIMaxLevel)),
TEXT("https://developer.oculus.com/blog/meta-quest-apps-android-12l-june-30/"),
ESetupRuleCategory::Compatibility,
ESetupRuleSeverity::Critical,
MetaQuest_All,
true) {}
bool FUseAndroidSDKLevelRule::IsApplied() const
{
const UAndroidSDKSettings* Settings = GetMutableDefault<UAndroidSDKSettings>();
FString SDKAPILevel = Settings->SDKAPILevel;
if (SDKAPILevel.IsEmpty())
{
return false;
}
if (SDKAPILevel.Equals(TEXT("latest")) || SDKAPILevel.Equals(TEXT("matchndk")))
{
return true;
}
if (!SDKAPILevel.Left(8).Equals(TEXT("android-")))
{
return false;
}
if (FCString::Atoi(*SDKAPILevel.Right(2)) < AndroidSDKAPIMinLevelInt)
{
return false;
}
if (FCString::Atoi(*SDKAPILevel.Right(2)) > AndroidSDKAPIMaxLevelInt)
{
return false;
}
return true;
}
void SaveSDKSettings()
{
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (!SettingsModule)
{
return;
}
ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project");
if (!SettingsContainer.IsValid())
{
return;
}
ISettingsCategoryPtr SettingsCategory = SettingsContainer->GetCategory("Platforms");
if (!SettingsCategory.IsValid())
{
return;
}
ISettingsSectionPtr SettingsSection = SettingsCategory->GetSection("AndroidSDK");
if (!SettingsSection.IsValid())
{
return;
}
TWeakObjectPtr<UObject> SettingsObject = SettingsSection->GetSettingsObject();
if (!SettingsObject.IsValid())
{
return;
}
SettingsObject->UpdateGlobalUserConfigFile();
}
void FUseAndroidSDKLevelRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OutShouldRestartEditor = false;
OCULUSXR_UPDATE_SETTINGS(UAndroidSDKSettings, SDKAPILevel, FText::AsCultureInvariant(AndroidSDKAPIMinLevel).ToString());
SaveSDKSettings();
}
FUseAndroidNDKLevelRule::FUseAndroidNDKLevelRule()
: ISetupRule(
"Compatibility_UseAndroidNDKLevel",
LOCTEXT("UseAndroidNDKLevel_DisplayName", "Use Android NDK Level"),
FText::Format(
LOCTEXT("UseAndroidNDKLevel_Description", "Android NDK level should be set between {0} and {1} prior to packaging apks."),
FText::AsCultureInvariant(AndroidNDKAPIMinLevel), FText::AsCultureInvariant(AndroidNDKAPIMaxLevel)),
TEXT("https://developer.oculus.com/blog/meta-quest-apps-must-target-android-10-starting-september-29/"),
ESetupRuleCategory::Compatibility,
ESetupRuleSeverity::Critical,
MetaQuest_All,
true) {}
bool FUseAndroidNDKLevelRule::IsApplied() const
{
const UAndroidSDKSettings* Settings = GetMutableDefault<UAndroidSDKSettings>();
FString NDKAPILevel = Settings->NDKAPILevel;
if (NDKAPILevel.IsEmpty())
{
return false;
}
if (NDKAPILevel.Equals(TEXT("latest")))
{
return true;
}
if (!NDKAPILevel.Left(8).Equals(TEXT("android-")))
{
return false;
}
if (FCString::Atoi(*NDKAPILevel.Right(2)) < AndroidNDKAPIMinLevelInt)
{
return false;
}
if (FCString::Atoi(*NDKAPILevel.Right(2)) > AndroidNDKAPIMaxLevelInt)
{
return false;
}
return true;
}
void FUseAndroidNDKLevelRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OutShouldRestartEditor = false;
OCULUSXR_UPDATE_SETTINGS(UAndroidSDKSettings, NDKAPILevel, FText::AsCultureInvariant(AndroidNDKAPIMinLevel).ToString());
SaveSDKSettings();
}
bool FUseArm64CPURule::IsApplied() const
{
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
return Settings->bBuildForArm64 && !Settings->bBuildForX8664;
}
void FUseArm64CPURule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForArm64, true);
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForX8664, false);
OutShouldRestartEditor = false;
}
bool FEnablePackageForMetaQuestRule::IsApplied() const
{
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
return Settings->bPackageForMetaQuest && !Settings->bSupportsVulkanSM5 && !Settings->bBuildForES31 && Settings->ExtraApplicationSettings.Find("com.oculus.supportedDevices") != INDEX_NONE;
}
void FEnablePackageForMetaQuestRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bPackageForMetaQuest, true);
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bSupportsVulkanSM5, false);
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForES31, false);
UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
if (Settings->ExtraApplicationSettings.Find("com.oculus.supportedDevices") == INDEX_NONE)
{
const FString SupportedDevicesValue("quest|quest2|questpro");
Settings->ExtraApplicationSettings.Append("<meta-data android:name=\"com.oculus.supportedDevices\" android:value=\"" + SupportedDevicesValue + "\" />");
Settings->UpdateSinglePropertyInConfigFile(Settings->GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UAndroidRuntimeSettings, ExtraApplicationSettings)), Settings->GetDefaultConfigFilename());
}
OutShouldRestartEditor = false;
}
bool FQuest2SupportedDeviceRule::IsApplied() const
{
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::Quest2);
}
void FQuest2SupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
{
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::Quest2);
// UpdateSinglePropertyInConfigFile does not support arrays
Settings->TryUpdateDefaultConfigFile();
OutShouldRestartEditor = false;
}
bool FQuestProSupportedDeviceRule::IsApplied() const
{
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::QuestPro);
}
void FQuestProSupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
{
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::QuestPro);
// UpdateSinglePropertyInConfigFile does not support arrays
Settings->TryUpdateDefaultConfigFile();
OutShouldRestartEditor = false;
}
bool FQuest3SupportedDeviceRule::IsApplied() const
{
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::Quest3);
}
void FQuest3SupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
{
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::Quest3);
// UpdateSinglePropertyInConfigFile does not support arrays
Settings->TryUpdateDefaultConfigFile();
OutShouldRestartEditor = false;
}
bool FEnableFullscreenRule::IsApplied() const
{
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
return Settings->bFullScreen;
}
void FEnableFullscreenRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bFullScreen, true);
OutShouldRestartEditor = false;
}
bool FEnableStartInVRRule::IsApplied() const
{
const UGeneralProjectSettings* Settings = GetDefault<UGeneralProjectSettings>();
return Settings->bStartInVR != 0;
}
void FEnableStartInVRRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UGeneralProjectSettings, bStartInVR, true);
OutShouldRestartEditor = false;
}
bool FDisableTouchInterfaceRule::IsApplied() const
{
const UInputSettings* Settings = GetDefault<UInputSettings>();
return Settings->DefaultTouchInterface.IsNull();
}
void FDisableTouchInterfaceRule::ApplyImpl(bool& OutShouldRestartEditor)
{
OCULUSXR_UPDATE_SETTINGS(UInputSettings, DefaultTouchInterface, nullptr);
OutShouldRestartEditor = false;
}
} // namespace OculusXRCompatibilityRules
#undef LOCTEXT_NAMESPACE