VRTowerDef/Plugins/MetaXR/Source/OculusXREditor/Private/OculusXRPlatformToolWidget.cpp

1651 lines
60 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRPlatformToolWidget.h"
#include "Widgets/Text/SRichTextBlock.h"
#include "DesktopPlatformModule.h"
#include "Editor.h"
#include "Styling/AppStyle.h"
#include "Misc/FileHelper.h"
#include "Internationalization/Regex.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Layout/SExpandableArea.h"
#include "Widgets/Images/SImage.h"
#include "HAL/FileManagerGeneric.h"
#include "DOM/JsonObject.h"
#include "Serialization/JsonSerializer.h"
#include "OculusXRHMDModule.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "Interfaces/IPluginManager.h"
#include "SHyperlinkLaunchURL.h"
#include "Misc/EngineVersionComparison.h"
#define LOCTEXT_NAMESPACE "OculusPlatformToolWidget"
#define TEXT_INDENT_OFFSET 20.0f
const FString UrlPlatformUtil = "https://www.oculus.com/download_app/?id=1076686279105243";
const FString ProjectPlatformUtilPath = "Oculus/Tools/ovr-platform-util.exe";
FText OculusPlatformDialogTitle = LOCTEXT("DownloadOculusPlatformUtility", "Download Oculus Platform Utility");
FText OculusPlatformDialogMessage = LOCTEXT("DownloadOculusPlatformUtilityMessage",
"Oculus Platform Window would like to download the latest version of the Oculus Platform Utility."
" Oculus Platform Utility is a command-line tool that enables the uploading of builds to your release channels on the Oculus Developer Dashboard."
"\n\nYou can learn more about the Oculus Platform Utility at https://developer.oculus.com/distribute/publish-reference-platform-command-line-utility/"
"\n\nCanceling will prevent the download and the UPLOAD button will be unfunctional. Would you like the tool to download the Oculus Platform Utility to your project?");
static bool bShowUploadDebugSymbols = false;
FString SOculusPlatformToolWidget::LogText;
SOculusPlatformToolWidget::SOculusPlatformToolWidget()
{
LogTextUpdated = false;
ActiveUploadButton = true;
Options2DCollapsed = true;
RequestUploadButtonActive = true;
OptionsRedistPackagesCollapsed = true;
EnableUploadButtonDel.BindRaw(this, &SOculusPlatformToolWidget::EnableUploadButton);
UpdateLogTextDel.BindRaw(this, &SOculusPlatformToolWidget::UpdateLogText);
SetProcessDel.BindRaw(this, &SOculusPlatformToolWidget::SetPlatformProcess);
LoadConfigSettings();
FOculusXRHMDModule::GetPluginWrapper().SendEvent2("oculus_platform_tool", "show_window", "integration");
}
void SOculusPlatformToolWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
// Update log text if it changes, otherwise constant updating will yeild the field unselectable.
if (LogTextUpdated)
{
ToolConsoleLog->SetText(FText::FromString(LogText));
LogTextUpdated = false;
}
if (RequestUploadButtonActive != ActiveUploadButton)
{
ActiveUploadButton = RequestUploadButtonActive;
BuildButtonToolbar(ButtonToolbar);
}
}
void SOculusPlatformToolWidget::Construct(const FArguments& InArgs)
{
auto logTextBox = SNew(SMultiLineEditableTextBox).IsReadOnly(true);
ToolConsoleLog = logTextBox;
auto mainVerticalBox = SNew(SVerticalBox);
GeneralSettingsBox = mainVerticalBox;
auto buttonToolbarBox = SNew(SHorizontalBox);
ButtonToolbar = buttonToolbarBox;
auto optionalSettings = SNew(SVerticalBox);
OptionalSettings = optionalSettings;
auto expansionFilesSettings = SNew(SVerticalBox);
ExpansionFilesSettings = expansionFilesSettings;
BuildGeneralSettingsBox(GeneralSettingsBox);
BuildButtonToolbar(ButtonToolbar);
BuildExpansionFileBox(ExpansionFilesSettings);
if (PlatformSettings != nullptr)
{
if (PlatformSettings->GetTargetPlatform() == (uint8)EOculusXRPlatformTarget::Rift)
{
BuildRiftOptionalFields(OptionalSettings);
}
else
{
OptionalSettings.Get()->ClearChildren();
}
}
FString ODHIconPath = IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir() / TEXT("Resources/Icon128.png");
const FName BrushName(*ODHIconPath);
FSlateApplication::Get().GetRenderer()->GenerateDynamicImageResource(BrushName);
ODHIconDynamicImageBrush = MakeShareable(new FSlateDynamicImageBrush(BrushName, FVector2D(60.0f, 60.0f)));
#if PLATFORM_MAC
FString odhLink = "https://developer.oculus.com/downloads/package/oculus-developer-hub-mac/?source=unreal";
#else
FString odhLink = "https://developer.oculus.com/downloads/package/oculus-developer-hub-win/?source=unreal";
#endif
ChildSlot
[SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.LightGroupBorder"))
.Padding(2)
[SNew(SVerticalBox)
+ SVerticalBox::Slot().Padding(0, 0).FillHeight(1.f)
[SNew(SScrollBox)
+ SScrollBox::Slot()
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.InitiallyCollapsed(false)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(LOCTEXT("GeneralSettings", "<RichTextBlock.Bold>General Settings</>"))]
.BodyContent()
[mainVerticalBox]]
+ SScrollBox::Slot()
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.InitiallyCollapsed(true)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(LOCTEXT("OptionalSettings", "<RichTextBlock.Bold>Optional Settings</>"))]
.BodyContent()
[SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight()
[optionalSettings]]]
+ SScrollBox::Slot()
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.InitiallyCollapsed(true)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(LOCTEXT("ExpansionFileSettings", "<RichTextBlock.Bold>Expansion Files</>"))]
.BodyContent()
[SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight()
[expansionFilesSettings]]]]
+ SVerticalBox::Slot().AutoHeight()
[buttonToolbarBox]
+ SVerticalBox::Slot().FillHeight(1.f)
[SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[logTextBox]]
+ SVerticalBox::Slot().AutoHeight().Padding(2.0f)
[SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().AutoWidth()
[SNew(SBox)
.WidthOverride(60.0f)
.HeightOverride(60.0f)
[SNew(SImage)
.Image(ODHIconDynamicImageBrush.IsValid() ? ODHIconDynamicImageBrush.Get() : nullptr)]]
+ SHorizontalBox::Slot().FillWidth(1.0f)
[SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight().Padding(2.0f)
[SNew(SRichTextBlock)
.Text(LOCTEXT("ODHCallout",
"<RichTextBlock.Bold>Oculus Developer Hub</> is a desktop companion tool that can upload builds, manage apps and reduce friction in daily Quest development."))
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)]
+ SVerticalBox::Slot().AutoHeight()
[SNew(SBox)
.HAlign(HAlign_Left)
[SNew(SHyperlinkLaunchURL, odhLink)
.Text(LOCTEXT("ODHDownloadPage", "Download Oculus Developer Hub"))
.ToolTipText(LOCTEXT("ODHDownloadPageTooltip", "Opens a page that provides the download link for Oculus Developer Hub"))]]]]]]];
}
void SOculusPlatformToolWidget::BuildGeneralSettingsBox(TSharedPtr<SVerticalBox> box)
{
if (PlatformSettings == nullptr)
{
return;
}
box.Get()->ClearChildren();
BuildTextComboBoxField(GeneralSettingsBox, LOCTEXT("TargetPlatform", "Target Platform"),
&OculusPlatforms, OculusPlatforms[PlatformSettings->GetTargetPlatform()],
&SOculusPlatformToolWidget::OnPlatformSettingChanged);
// Build field for Oculus Application ID.
BuildTextField(box, LOCTEXT("AppID", "Oculus Application ID"), FText::FromString(PlatformSettings->GetApplicationID()),
LOCTEXT("AppIDTT", "Specifies the ID of your app. Obtained from the API tab of your app in the Oculus Dashboard."),
&SOculusPlatformToolWidget::OnApplicationIDChanged);
// Build field for Oculus Application Token.
BuildTextField(box, LOCTEXT("AppToken", "Oculus Application Token"), FText::FromString(PlatformSettings->GetApplicationToken()),
LOCTEXT("AppTokenTT", "Specifies the app secret token. Obtained from the API tab of your app in the Oculus Dashboard."),
&SOculusPlatformToolWidget::OnApplicationTokenChanged, true);
// Build field for Release Channel.
BuildTextField(box, LOCTEXT("ReleaseChannel", "Release Channel"), FText::FromString(PlatformSettings->GetReleaseChannel()),
LOCTEXT("ReleaseChannelTT", "Specifies the release channel for uploading the build. Release channel names are not case-sensitive."),
&SOculusPlatformToolWidget::OnReleaseChannelChanged);
// Build field for Release Notes.
BuildTextField(box, LOCTEXT("ReleaseNote", "Release Note"), FText::FromString(PlatformSettings->GetReleaseNote()),
LOCTEXT("ReleaseNoteTT", "Specifies the release note text shown to users."),
&SOculusPlatformToolWidget::OnReleaseNoteChanged);
// Platform specific fields.
if (PlatformSettings->GetTargetPlatform() == (uint8)EOculusXRPlatformTarget::Rift)
{
// Build field for Rift Build Directory.
BuildFileDirectoryField(box, LOCTEXT("BuildPath", "Rift Build Directory"), FText::FromString(PlatformSettings->OculusRiftBuildDirectory),
LOCTEXT("BuildPathTT", "Specifies the full path to the directory containing your build files."),
&SOculusPlatformToolWidget::OnSelectRiftBuildDirectory, &SOculusPlatformToolWidget::OnClearRiftBuildDirectory);
// Build field for Build Version.
BuildTextField(box, LOCTEXT("BuildVersion", "Build Version"), FText::FromString(PlatformSettings->OculusRiftBuildVersion),
LOCTEXT("BuildVersionTT", "Specifies the version number shown to users."),
&SOculusPlatformToolWidget::OnRiftBuildVersionChanged);
// Build field for Launch File Path.
BuildFileDirectoryField(box, LOCTEXT("LaunchPath", "Launch File Path"), FText::FromString(PlatformSettings->GetLaunchFilePath()),
LOCTEXT("LaunchPathTT", " Specifies the path to the executable that launches your app."),
&SOculusPlatformToolWidget::OnSelectLaunchFilePath, &SOculusPlatformToolWidget::OnClearLaunchFilePath);
}
else
{
// Build field for APK File Path.
BuildFileDirectoryField(box, LOCTEXT("APKLaunchPath", "APK File Path"), FText::FromString(PlatformSettings->GetLaunchFilePath()),
LOCTEXT("APKLaunchPathTT", " Specifies the path to the APK that launches your app."),
&SOculusPlatformToolWidget::OnSelectLaunchFilePath, &SOculusPlatformToolWidget::OnClearLaunchFilePath);
BuildCheckBoxField(box, LOCTEXT("UploadDebugSymbols", "Upload Debug Symbols"), PlatformSettings->UploadDebugSymbols,
LOCTEXT("UploadDebugSymbolsTT", "If checked, debug symbols will be uploaded along with the application."),
&SOculusPlatformToolWidget::OnUploadDebugSymbolsChanged);
if (PlatformSettings->UploadDebugSymbols)
{
if (bShowUploadDebugSymbols != PlatformSettings->UploadDebugSymbols)
{
if (PlatformSettings->GetSymbolDirPath().IsEmpty())
{
FString defaultPath = GenerateSymbolPath();
PlatformSettings->SetSymbolDirPath(FPaths::ConvertRelativePathToFull(defaultPath));
PlatformSettings->SaveConfig();
}
}
// Build field for Debug symbol directory path.
BuildFileDirectoryField(box, LOCTEXT("SymbolPath", "Symbol Directory Path"), FText::FromString(PlatformSettings->GetSymbolDirPath()),
LOCTEXT("SymbolPathTT", "Specifies the path to the directory containing the app symbols (libUE4.so)."),
&SOculusPlatformToolWidget::OnSelectSymbolDirPath, &SOculusPlatformToolWidget::OnClearSymbolDirPath, 1);
BuildCheckBoxField(box, LOCTEXT("DebugSymbolsOnly", "Upload Debug Symbols Only"), PlatformSettings->DebugSymbolsOnly,
LOCTEXT("DebugSymbolsOnlyTT", "If checked, the tool will upload onyl debug symbols to an existing build. Requires Build ID, App ID, App Token, and Debug Symbols Directory."),
&SOculusPlatformToolWidget::OnDebugSymbolsOnlyChanged, 1);
if (PlatformSettings->DebugSymbolsOnly)
{
BuildTextField(box, LOCTEXT("BuildID", "Build ID"), FText::FromString(PlatformSettings->BuildID),
LOCTEXT("BuildIDTT", "Specifies the Build ID to upload debug symbols to."),
&SOculusPlatformToolWidget::OnBuildIDChanged, false, 1);
}
}
bShowUploadDebugSymbols = PlatformSettings->UploadDebugSymbols;
}
}
void SOculusPlatformToolWidget::BuildTextField(TSharedPtr<SVerticalBox> box, FText name, FText text, FText tooltip,
PTextComittedDel deleg, bool isPassword, int32 indentAmount)
{
FMargin textMargin = FMargin(TEXT_INDENT_OFFSET * indentAmount, 1.0f, 1.0f, 1.0f);
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
.Padding(textMargin)
[SNew(STextBlock)
.Text(name)
.ToolTipText(tooltip)]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SEditableTextBox)
.Text(text)
.IsPassword(isPassword)
.OnTextCommitted(this, deleg)]];
}
void SOculusPlatformToolWidget::BuildTextComboBoxField(TSharedPtr<SVerticalBox> box, FText name,
TArray<TSharedPtr<FString>>* options, TSharedPtr<FString> current, PTextComboBoxDel deleg, int32 indentAmount)
{
FMargin textMargin = FMargin(TEXT_INDENT_OFFSET * indentAmount, 1.0f, 1.0f, 1.0f);
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
.Padding(textMargin)
[SNew(SRichTextBlock)
.DecoratorStyleSet(&FAppStyle::Get())
.Text(name)]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(STextComboBox)
.OptionsSource(options)
.InitiallySelectedItem(current)
.OnSelectionChanged(this, deleg)]];
}
void SOculusPlatformToolWidget::BuildCheckBoxField(TSharedPtr<SVerticalBox> box, FText name, bool check,
FText tooltip, PCheckBoxChangedDel deleg, int32 indentAmount)
{
FMargin textMargin = FMargin(TEXT_INDENT_OFFSET * indentAmount, 1.0f, 1.0f, 1.0f);
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
.Padding(textMargin)
[SNew(SRichTextBlock)
.DecoratorStyleSet(&FAppStyle::Get())
.Text(name)]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SCheckBox)
.OnCheckStateChanged(this, deleg)
.IsChecked(check ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)]];
}
void SOculusPlatformToolWidget::BuildFileDirectoryField(TSharedPtr<SVerticalBox> box, FText name, FText path, FText tooltip,
PButtonClickedDel deleg, PButtonClickedDel clearDeleg, int32 indentAmount)
{
EVisibility cancelButtonVisibility = path.IsEmpty() ? EVisibility::Hidden : EVisibility::Visible;
FMargin textMargin = FMargin(TEXT_INDENT_OFFSET * indentAmount, 1.0f, 1.0f, 1.0f);
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
.Padding(textMargin)
[SNew(STextBlock)
.Text(name)
.ToolTipText(tooltip)]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SEditableText)
.Text(path)
.IsReadOnly(true)
.Justification(ETextJustify::Left)]
+ SHorizontalBox::Slot().Padding(1).AutoWidth().HAlign(EHorizontalAlignment::HAlign_Right)
[SNew(SButton)
.Text(FText::FromString("X"))
.Visibility(cancelButtonVisibility)
.OnClicked(this, clearDeleg)
.ButtonColorAndOpacity(FLinearColor(0.36f, 0.1f, 0.05f))]
+ SHorizontalBox::Slot().Padding(1).AutoWidth().HAlign(EHorizontalAlignment::HAlign_Right)
[SNew(SButton)
.Text((LOCTEXT("Choose", "Choose...")))
.OnClicked(this, deleg)]];
}
void SOculusPlatformToolWidget::BuildButtonToolbar(TSharedPtr<SHorizontalBox> box)
{
box.Get()->ClearChildren();
box.Get()->AddSlot().FillWidth(1.f);
box.Get()->AddSlot().AutoWidth().Padding(2.f)
[SNew(SButton)
.Text((LOCTEXT("Upload", "Upload")))
.OnClicked(this, &SOculusPlatformToolWidget::OnStartPlatformUpload)
.IsEnabled(ActiveUploadButton)];
box.Get()->AddSlot().AutoWidth().Padding(2.f)
[SNew(SButton)
.Text((LOCTEXT("Cancel", "Cancel")))
.OnClicked(this, &SOculusPlatformToolWidget::OnCancelUpload)
.IsEnabled(!ActiveUploadButton)];
box.Get()->AddSlot().FillWidth(1.f);
}
void SOculusPlatformToolWidget::BuildRiftOptionalFields(TSharedPtr<SVerticalBox> box)
{
if (PlatformSettings == nullptr)
{
return;
}
box.Get()->ClearChildren();
// Add Launch Parameter Field
BuildTextField(box, LOCTEXT("LaunchParams", "Launch Parameters"), FText::FromString(PlatformSettings->OculusRiftLaunchParams),
LOCTEXT("LaunchParamsTT", ""),
&SOculusPlatformToolWidget::OnRiftLaunchParamsChanged);
// Add Firewall Exception Toggle
BuildCheckBoxField(box, LOCTEXT("Firewall", "Firewall Exception"), PlatformSettings->OculusRiftFireWallException,
LOCTEXT("FirewallTT", ""),
&SOculusPlatformToolWidget::OnRiftFirewallChanged);
// Add Gamepad Emulation Dropdown
BuildTextComboBoxField(box, LOCTEXT("GamepadEmu", "Gamepad Emulation"),
&RiftGamepadEmulation, RiftGamepadEmulation[(uint8)PlatformSettings->GetRiftGamepadEmulation()],
&SOculusPlatformToolWidget::OnRiftGamepadEmulationChanged);
// Generate 2D Settings Expandable Area
TSharedRef<SVerticalBox> settings2DBox = SNew(SVerticalBox);
// Add 2D Launch File Field
BuildFileDirectoryField(settings2DBox, LOCTEXT("2DLaunch", "2D Launch File"), FText::FromString(PlatformSettings->OculusRift2DLaunchPath),
LOCTEXT("2DLaunchPathTT", ""),
&SOculusPlatformToolWidget::OnSelect2DLaunchPath, &SOculusPlatformToolWidget::OnClear2DLaunchPath);
// Add 2D Launch Parameter Field
BuildTextField(settings2DBox, LOCTEXT("2DLaunchParams", "2D Launch Parameters"), FText::FromString(PlatformSettings->OculusRift2DLaunchParams),
LOCTEXT("2DLaunchParamsTT", ""),
&SOculusPlatformToolWidget::On2DLaunchParamsChanged);
box.Get()->AddSlot().AutoHeight().Padding(1)
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.InitiallyCollapsed(Options2DCollapsed)
.OnAreaExpansionChanged(this, &SOculusPlatformToolWidget::On2DOptionsExpanded)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(LOCTEXT("2DSettings", "<RichTextBlock.Bold>2D Settings</>"))]
.BodyContent()
[settings2DBox]];
BuildRedistPackagesBox(box);
}
void SOculusPlatformToolWidget::BuildRedistPackagesBox(TSharedPtr<SVerticalBox> box)
{
// Create check box toggle for each redistributable package we loaded
TSharedRef<SVerticalBox> redistBox = SNew(SVerticalBox);
for (int i = 0; i < PlatformSettings->OculusRedistPackages.Num(); i++)
{
FOculusXRRedistPackage* Package = &PlatformSettings->OculusRedistPackages[i];
redistBox->AddSlot()
.Padding(1)
.AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
[SNew(SRichTextBlock)
.DecoratorStyleSet(&FAppStyle::Get())
.Text(FText::FromString(Package->Name))]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SCheckBox)
.OnCheckStateChanged(this, &SOculusPlatformToolWidget::OnRedistPackageStateChanged, Package)
.IsChecked(Package->Included ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)]];
}
box.Get()->AddSlot().AutoHeight().Padding(1)
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.InitiallyCollapsed(OptionsRedistPackagesCollapsed)
.OnAreaExpansionChanged(this, &SOculusPlatformToolWidget::OnRedistPackagesExpanded)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(LOCTEXT("RedistPack", "<RichTextBlock.Bold>Redistributable Packages</>"))]
.BodyContent()
[redistBox]];
}
void SOculusPlatformToolWidget::BuildExpansionFileBox(TSharedPtr<SVerticalBox> box)
{
if (PlatformSettings == nullptr)
{
return;
}
ExpansionFilesSettings.Get()->ClearChildren();
if (PlatformSettings->GetTargetPlatform() == (uint8)EOculusXRPlatformTarget::Rift)
{
BuildFileDirectoryField(box, LOCTEXT("LanguagePacks", "Language Packs Directory"), FText::FromString(PlatformSettings->GetLanguagePacksPath()),
LOCTEXT("LanguagePacksTT", ""), &SOculusPlatformToolWidget::OnSelectLanguagePacksPath, &SOculusPlatformToolWidget::OnClearLanguagePacksPath);
}
BuildFileDirectoryField(box, LOCTEXT("ExpansionFilesDirectory", "Expansion Files Directory"), FText::FromString(PlatformSettings->GetExpansionFilesPath()),
LOCTEXT("ExpansionFilesTT", ""), &SOculusPlatformToolWidget::OnSelectExpansionFilesPath, &SOculusPlatformToolWidget::OnClearExpansionFilesPath);
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
if (AssetConfigs)
{
for (int i = 0; i < AssetConfigs->Num(); i++)
{
auto AssetConfigBox = SNew(SVerticalBox);
BuildAssetConfigBox(AssetConfigBox, (*AssetConfigs)[i], i);
box.Get()->AddSlot().AutoHeight().Padding(1)
[SNew(SExpandableArea)
.HeaderPadding(5)
.Padding(5)
.BorderBackgroundColor(FLinearColor(0.4f, 0.4f, 0.4f, 1.0f))
.BodyBorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BodyBorderBackgroundColor(FLinearColor::White)
.HeaderContent()
[SNew(SRichTextBlock)
.TextStyle(FAppStyle::Get(), "ToolBar.Heading")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
.Text(FText::FromString((*AssetConfigs)[i].Name))]
.BodyContent()
[AssetConfigBox]];
}
}
}
void SOculusPlatformToolWidget::BuildAssetConfigBox(TSharedPtr<SVerticalBox> box, FOculusXRAssetConfig config, int index)
{
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
[SNew(SRichTextBlock)
.DecoratorStyleSet(&FAppStyle::Get())
.Text(LOCTEXT("AssetType", "Asset Type"))]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(STextComboBox)
.OptionsSource(&AssetType)
.InitiallySelectedItem(AssetType[(uint8)config.AssetType])
.OnSelectionChanged(this, &SOculusPlatformToolWidget::OnAssetConfigTypeChanged, index)]];
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
[SNew(SRichTextBlock)
.DecoratorStyleSet(&FAppStyle::Get())
.Text(LOCTEXT("AssetRequired", "Required"))]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SCheckBox)
.OnCheckStateChanged(this, &SOculusPlatformToolWidget::OnAssetConfigRequiredChanged, index)
.IsChecked(config.Required ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)]];
box.Get()->AddSlot().Padding(1).AutoHeight()
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(1).AutoWidth()
[SNew(SBox)
.WidthOverride(250.f)
[SNew(STextBlock)
.Text(LOCTEXT("SKU", "SKU"))]]
+ SHorizontalBox::Slot().Padding(1).FillWidth(1.f)
[SNew(SEditableTextBox)
.Text(FText::FromString(config.Sku))
.OnTextCommitted(this, &SOculusPlatformToolWidget::OnAssetConfigSKUChanged, index)]];
}
bool SOculusPlatformToolWidget::ConstructArguments(FString& args)
{
if (PlatformSettings == nullptr)
{
return false;
}
if (PlatformSettings->UploadDebugSymbols && PlatformSettings->DebugSymbolsOnly)
{
return ConstructDebugSymbolArguments(args);
}
// Build the args string that will be passed to the CLI. Print all errors that occur to the log.
bool success = true;
switch (PlatformSettings->GetTargetPlatform())
{
case (uint8)EOculusXRPlatformTarget::Rift:
args = "upload-rift-build";
break;
case (uint8)EOculusXRPlatformTarget::Quest:
args = "upload-quest-build";
break;
default:
UpdateLogText(LogText + "ERROR: Invalid target platform selected");
success = false;
break;
}
// Oculus Application ID check and command.
ValidateTextField(&SOculusPlatformToolWidget::IDFieldValidator, PlatformSettings->GetApplicationID(),
LOCTEXT("ApplicationID", "Application ID").ToString(), success);
args += " --app_id \"" + PlatformSettings->GetApplicationID() + "\"";
// Oculus Application Token check and command.
ValidateTextField(&SOculusPlatformToolWidget::GenericFieldValidator, PlatformSettings->GetApplicationToken(),
LOCTEXT("ApplicationToken", "Application Token").ToString(), success);
args += " --app_secret \"" + PlatformSettings->GetApplicationToken() + "\"";
// Release Channel check and command.
ValidateTextField(&SOculusPlatformToolWidget::GenericFieldValidator, PlatformSettings->GetReleaseChannel(),
LOCTEXT("ReleaseChannel", "Release Channel").ToString(), success);
args += " --channel \"" + PlatformSettings->GetReleaseChannel() + "\"";
// Release Note check and command. Not a required command.
if (!PlatformSettings->GetReleaseNote().IsEmpty())
{
FString SanatizedReleaseNote = PlatformSettings->GetReleaseNote();
SanatizedReleaseNote = SanatizedReleaseNote.Replace(TEXT("\""), TEXT("\"\""));
args += " --notes \"" + SanatizedReleaseNote + "\"";
}
// Platform specific commands
if (PlatformSettings->GetTargetPlatform() == (uint8)EOculusXRPlatformTarget::Rift)
{
// Launch File Path check and command.
ValidateTextField(&SOculusPlatformToolWidget::FileFieldValidator, PlatformSettings->GetLaunchFilePath(),
LOCTEXT("LaunchFile", "Launch File Path").ToString(), success);
args += " --launch-file \"" + PlatformSettings->GetLaunchFilePath() + "\"";
// Rift Build Directory check and command.
ValidateTextField(&SOculusPlatformToolWidget::DirectoryFieldValidator, PlatformSettings->OculusRiftBuildDirectory,
LOCTEXT("RiftBuildDir", "Rift Build Directory").ToString(), success);
args += " --build_dir \"" + PlatformSettings->OculusRiftBuildDirectory + "\"";
// Rift Build Version check and command.
ValidateTextField(&SOculusPlatformToolWidget::GenericFieldValidator, PlatformSettings->OculusRiftBuildVersion,
LOCTEXT("BuildVersion", "Build Version").ToString(), success);
args += " --version \"" + PlatformSettings->OculusRiftBuildVersion + "\"";
// Rift Launch Parameters check and command
if (!PlatformSettings->OculusRiftLaunchParams.IsEmpty())
{
ValidateTextField(&SOculusPlatformToolWidget::LaunchParamValidator, PlatformSettings->OculusRiftLaunchParams,
LOCTEXT("LaunchParam", "Launch Parameters").ToString(), success);
args += " --launch_params \"" + PlatformSettings->OculusRiftLaunchParams + "\"";
}
// Rift 2D Options checks and commands
if (!PlatformSettings->OculusRift2DLaunchPath.IsEmpty())
{
ValidateTextField(&SOculusPlatformToolWidget::FileFieldValidator, PlatformSettings->OculusRift2DLaunchPath,
LOCTEXT("2DLaunchFile", "2D Launch File Path").ToString(), success);
args += " --launch_file_2d \"" + PlatformSettings->OculusRift2DLaunchPath + "\"";
if (!PlatformSettings->OculusRift2DLaunchParams.IsEmpty())
{
ValidateTextField(&SOculusPlatformToolWidget::LaunchParamValidator, PlatformSettings->OculusRift2DLaunchParams,
LOCTEXT("2DLaunchParams", "2D Launch Parameters").ToString(), success);
args += " --launch_params_2d \"" + PlatformSettings->OculusRift2DLaunchParams + "\"";
}
}
// Rift Firewall Exception command
if (PlatformSettings->OculusRiftFireWallException)
{
args += " --firewall_exceptions true";
}
// Rift Gamepad Emulation command
if (PlatformSettings->GetRiftGamepadEmulation() > EOculusXRGamepadEmulation::Off && PlatformSettings->GetRiftGamepadEmulation() < EOculusXRGamepadEmulation::Length)
{
args += " --gamepad-emulation ";
switch (PlatformSettings->GetRiftGamepadEmulation())
{
case EOculusXRGamepadEmulation::Twinstick:
args += "TWINSTICK";
break;
case EOculusXRGamepadEmulation::RightDPad:
args += "RIGHT_D_PAD";
break;
case EOculusXRGamepadEmulation::LeftDPad:
args += "LEFT_D_PAD";
break;
default:
args += "OFF";
break;
}
}
// Rift Redistributable Packages commands
TArray<FString> IncludedPackages;
for (int i = 0; i < PlatformSettings->OculusRedistPackages.Num(); i++)
{
FOculusXRRedistPackage Package = PlatformSettings->OculusRedistPackages[i];
if (Package.Included)
{
IncludedPackages.Add(Package.Id);
}
}
if (IncludedPackages.Num() > 0)
{
args += " --redistributables \"" + FString::Join(IncludedPackages, TEXT(",")) + "\"";
}
}
else
{
// APK File Path check and command.
ValidateTextField(&SOculusPlatformToolWidget::FileFieldValidator, PlatformSettings->GetLaunchFilePath(),
LOCTEXT("APKLaunchFile", "APK File Path").ToString(), success);
args += " --apk \"" + PlatformSettings->GetLaunchFilePath() + "\"";
if (PlatformSettings->UploadDebugSymbols)
{
ValidateTextField(&SOculusPlatformToolWidget::DirectoryFieldValidator, PlatformSettings->GetSymbolDirPath(),
LOCTEXT("SymbolDirPath", "Symbol Directory Path").ToString(), success);
if (success)
{
args += " --debug-symbols-dir \"" + PlatformSettings->GetSymbolDirPath() + "\"";
}
}
}
if (!PlatformSettings->GetExpansionFilesPath().IsEmpty())
{
ValidateTextField(&SOculusPlatformToolWidget::DirectoryFieldValidator, PlatformSettings->GetExpansionFilesPath(),
LOCTEXT("ExpansionFilesPath", "Expansion Files Path").ToString(), success);
args += " --assets-dir \"" + PlatformSettings->GetExpansionFilesPath() + "\"";
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
if (AssetConfigs->Num() > 0)
{
TArray<FString> AssetConfig;
for (int i = 0; i < AssetConfigs->Num(); i++)
{
TArray<FString> ConfigParams;
FOculusXRAssetConfig Config = (*AssetConfigs)[i];
if (Config.Required)
{
ConfigParams.Add("\\\"required\\\":true");
}
if (Config.AssetType > EOculusXRAssetType::Default && Config.AssetType < EOculusXRAssetType::Length)
{
FString command = "\\\"type\\\":";
switch (Config.AssetType)
{
case EOculusXRAssetType::Store:
ConfigParams.Add(command + "\\\"STORE\\\"");
break;
case EOculusXRAssetType::Language_Pack:
ConfigParams.Add(command + "\\\"LANGUAGE_PACK\\\"");
break;
default:
ConfigParams.Add(command + "\\\"DEFAULT\\\"");
break;
}
}
if (!Config.Sku.IsEmpty())
{
ConfigParams.Add("\\\"sku\\\":\\\"" + Config.Sku + "\\\"");
}
if (ConfigParams.Num() > 0)
{
FString ConfigCommand = "\\\"" + Config.Name + "\\\":{" + FString::Join(ConfigParams, TEXT(",")) + "}";
AssetConfig.Add(ConfigCommand);
}
}
if (AssetConfig.Num())
{
args += " --asset_files_config {" + FString::Join(AssetConfig, TEXT(",")) + "}";
}
}
}
args += " --upload-from-engine UNREAL";
UE_LOG(LogTemp, Warning, TEXT("%s"), *args);
return success;
}
bool SOculusPlatformToolWidget::ConstructDebugSymbolArguments(FString& args)
{
bool success = true;
args = "upload-debug-symbols";
ValidateTextField(&SOculusPlatformToolWidget::IDFieldValidator, PlatformSettings->BuildID,
LOCTEXT("BuildID", "Build ID").ToString(), success);
args += " --parent \"" + PlatformSettings->BuildID + "\"";
// Oculus Application ID check and command.
ValidateTextField(&SOculusPlatformToolWidget::IDFieldValidator, PlatformSettings->GetApplicationID(),
LOCTEXT("ApplicationID", "Application ID").ToString(), success);
args += " --app_id \"" + PlatformSettings->GetApplicationID() + "\"";
// Oculus Application Token check and command.
ValidateTextField(&SOculusPlatformToolWidget::GenericFieldValidator, PlatformSettings->GetApplicationToken(),
LOCTEXT("ApplicationToken", "Application Token").ToString(), success);
args += " --app_secret \"" + PlatformSettings->GetApplicationToken() + "\"";
ValidateTextField(&SOculusPlatformToolWidget::DirectoryFieldValidator, PlatformSettings->GetSymbolDirPath(),
LOCTEXT("SymbolDirPath", "Symbol Directory Path").ToString(), success);
args += " --debug-symbols-dir \"" + PlatformSettings->GetSymbolDirPath() + "\"";
args += " --debug-symbols-pattern \"*.so\"";
return success;
}
void SOculusPlatformToolWidget::EnableUploadButton(bool enabled)
{
RequestUploadButtonActive = enabled;
}
void SOculusPlatformToolWidget::LoadConfigSettings()
{
PlatformSettings = GetMutableDefault<UOculusXRPlatformToolSettings>();
PlatformEnum = StaticEnum<EOculusXRPlatformTarget>();
GamepadEmulationEnum = StaticEnum<EOculusXRGamepadEmulation>();
AssetTypeEnum = StaticEnum<EOculusXRAssetType>();
RiftGamepadEmulation.Empty();
OculusPlatforms.Empty();
for (uint8 i = 0; i < (uint8)EOculusXRPlatformTarget::Length; i++)
{
OculusPlatforms.Add(MakeShareable(new FString(PlatformEnum->GetDisplayNameTextByIndex((int64)i).ToString())));
}
for (uint8 i = 0; i < (uint8)EOculusXRGamepadEmulation::Length; i++)
{
RiftGamepadEmulation.Add(MakeShareable(new FString(GamepadEmulationEnum->GetDisplayNameTextByIndex((int64)i).ToString())));
}
for (uint8 i = 0; i < (uint8)EOculusXRAssetType::Length; i++)
{
AssetType.Add(MakeShareable(new FString(AssetTypeEnum->GetDisplayNameTextByIndex((int64)i).ToString())));
}
LoadRedistPackages();
}
void SOculusPlatformToolWidget::LoadRedistPackages()
{
(new FAsyncTask<FPlatformLoadRedistPackagesTask>(UpdateLogTextDel))->StartBackgroundTask();
}
FReply SOculusPlatformToolWidget::OnStartPlatformUpload()
{
FString launchArgs;
UpdateLogText("");
FOculusXRHMDModule::GetPluginWrapper().SendEvent2("oculus_platform_tool", "upload", "integration");
if (ConstructArguments(launchArgs))
{
UpdateLogText(LogText + LOCTEXT("StartUpload", "Starting Platform Tool Upload Process . . .\n").ToString());
(new FAsyncTask<FPlatformUploadTask>(launchArgs, EnableUploadButtonDel, UpdateLogTextDel, SetProcessDel))->StartBackgroundTask();
}
return FReply::Handled();
}
void SOculusPlatformToolWidget::OnPlatformSettingChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo)
{
if (!ItemSelected.IsValid())
{
return;
}
for (uint8 i = 0; i < (uint8)EOculusXRPlatformTarget::Length; i++)
{
if (PlatformEnum->GetDisplayNameTextByIndex(i).EqualTo(FText::FromString(*ItemSelected)))
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetTargetPlatform(i);
PlatformSettings->SaveConfig();
LoadConfigSettings();
BuildGeneralSettingsBox(GeneralSettingsBox);
BuildExpansionFileBox(ExpansionFilesSettings);
OptionalSettings.Get()->ClearChildren();
if (i == (uint8)EOculusXRPlatformTarget::Rift)
{
BuildRiftOptionalFields(OptionalSettings);
}
break;
}
}
}
}
void SOculusPlatformToolWidget::OnApplicationIDChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetApplicationID(InText.ToString());
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnApplicationTokenChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetApplicationToken(InText.ToString());
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnReleaseChannelChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetReleaseChannel(InText.ToString());
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnReleaseNoteChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetReleaseNote(InText.ToString());
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnRiftBuildVersionChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRiftBuildVersion = InText.ToString();
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnRiftLaunchParamsChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRiftLaunchParams = InText.ToString();
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::On2DLaunchParamsChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRift2DLaunchParams = InText.ToString();
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnRiftFirewallChanged(ECheckBoxState CheckState)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRiftFireWallException = CheckState == ECheckBoxState::Checked ? true : false;
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnRedistPackageStateChanged(ECheckBoxState CheckState, FOculusXRRedistPackage* Package)
{
if (PlatformSettings != nullptr)
{
Package->Included = CheckState == ECheckBoxState::Checked;
PlatformSettings->SaveConfig();
BuildRiftOptionalFields(OptionalSettings);
}
}
void SOculusPlatformToolWidget::OnAssetConfigTypeChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo, int i)
{
if (PlatformSettings != nullptr)
{
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
for (int e = 0; e < (uint8)EOculusXRAssetType::Length; e++)
{
if (AssetTypeEnum->GetDisplayNameTextByIndex(e).ToString().Equals(*ItemSelected.Get()))
{
(*AssetConfigs)[i].AssetType = (EOculusXRAssetType)e;
break;
}
}
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
}
void SOculusPlatformToolWidget::OnAssetConfigRequiredChanged(ECheckBoxState CheckState, int i)
{
if (PlatformSettings != nullptr)
{
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
(*AssetConfigs)[i].Required = CheckState == ECheckBoxState::Checked;
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
}
void SOculusPlatformToolWidget::OnAssetConfigSKUChanged(const FText& InText, ETextCommit::Type InCommitType, int i)
{
if (PlatformSettings != nullptr)
{
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
(*AssetConfigs)[i].Sku = InText.ToString();
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
}
void SOculusPlatformToolWidget::OnUploadDebugSymbolsChanged(ECheckBoxState CheckState)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->UploadDebugSymbols = CheckState == ECheckBoxState::Checked ? true : false;
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
}
void SOculusPlatformToolWidget::OnDebugSymbolsOnlyChanged(ECheckBoxState CheckState)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->DebugSymbolsOnly = CheckState == ECheckBoxState::Checked ? true : false;
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
}
void SOculusPlatformToolWidget::OnBuildIDChanged(const FText& InText, ETextCommit::Type InCommitType)
{
if (PlatformSettings != nullptr)
{
PlatformSettings->BuildID = InText.ToString();
PlatformSettings->SaveConfig();
}
}
void SOculusPlatformToolWidget::OnRiftGamepadEmulationChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo)
{
if (!ItemSelected.IsValid())
{
return;
}
for (uint8 i = 0; i < (uint8)EOculusXRGamepadEmulation::Length; i++)
{
if (GamepadEmulationEnum->GetDisplayNameTextByIndex(i).EqualTo(FText::FromString(*ItemSelected)))
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetRiftGamepadEmulation(i);
PlatformSettings->SaveConfig();
break;
}
}
}
}
FReply SOculusPlatformToolWidget::OnSelectRiftBuildDirectory()
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
if (PlatformSettings != nullptr)
{
FString path;
FString defaultPath = PlatformSettings->OculusRiftBuildDirectory.IsEmpty() ? FPaths::ProjectContentDir() : PlatformSettings->OculusRiftBuildDirectory;
if (FDesktopPlatformModule::Get()->OpenDirectoryDialog(parentWindowHandle, "Choose Rift Build Directory", defaultPath, path))
{
PlatformSettings->OculusRiftBuildDirectory = path;
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClearRiftBuildDirectory()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRiftBuildDirectory.Empty();
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnSelectLaunchFilePath()
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
if (PlatformSettings != nullptr)
{
TArray<FString> path;
FString defaultPath = PlatformSettings->GetLaunchFilePath().IsEmpty() ? FPaths::ProjectContentDir() : PlatformSettings->GetLaunchFilePath();
FString fileType = PlatformSettings->GetTargetPlatform() == (uint8)EOculusXRPlatformTarget::Rift ? "Executables (*.exe)|*.exe" : "APKs (*.apk)|*.apk";
if (FDesktopPlatformModule::Get()->OpenFileDialog(parentWindowHandle, "Choose Launch File", defaultPath, defaultPath, fileType, EFileDialogFlags::None, path))
{
if (path.Num() > 0)
{
PlatformSettings->SetLaunchFilePath(FPaths::ConvertRelativePathToFull(path[0]));
}
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClearLaunchFilePath()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetLaunchFilePath("");
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
return FReply::Handled();
}
FString SOculusPlatformToolWidget::GenerateSymbolPath()
{
return FPaths::ProjectDir() + TEXT("Binaries/Android/") + FApp::GetProjectName() + TEXT("_Symbols_v1/") + FApp::GetProjectName() + TEXT("-arm64");
}
FReply SOculusPlatformToolWidget::OnSelectSymbolDirPath()
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
if (PlatformSettings != nullptr)
{
FString dirPath;
FString defaultPath = PlatformSettings->GetSymbolDirPath().IsEmpty() ? GenerateSymbolPath() : PlatformSettings->GetSymbolDirPath();
if (FDesktopPlatformModule::Get()->OpenDirectoryDialog(parentWindowHandle, "Choose Launch File", defaultPath, dirPath))
{
PlatformSettings->SetSymbolDirPath(FPaths::ConvertRelativePathToFull(dirPath));
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClearSymbolDirPath()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetSymbolDirPath("");
PlatformSettings->SaveConfig();
BuildGeneralSettingsBox(GeneralSettingsBox);
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnSelect2DLaunchPath()
{
if (PlatformSettings != nullptr)
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
TArray<FString> path;
FString defaultPath = PlatformSettings->OculusRift2DLaunchPath.IsEmpty() ? FPaths::ProjectContentDir() : PlatformSettings->OculusRift2DLaunchPath;
if (FDesktopPlatformModule::Get()->OpenFileDialog(parentWindowHandle, "Choose 2D Launch File", defaultPath, defaultPath, "Executables (*.exe)|*.exe", EFileDialogFlags::None, path))
{
if (path.Num() > 0)
{
PlatformSettings->OculusRift2DLaunchPath = FPaths::ConvertRelativePathToFull(path[0]);
}
PlatformSettings->SaveConfig();
BuildRiftOptionalFields(OptionalSettings);
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClear2DLaunchPath()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->OculusRift2DLaunchPath.Empty();
PlatformSettings->SaveConfig();
BuildRiftOptionalFields(OptionalSettings);
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnCancelUpload()
{
if (FMessageDialog::Open(EAppMsgType::OkCancel, LOCTEXT("CancelUploadWarning", "Are you sure you want to cancel the upload process?")) == EAppReturnType::Ok)
{
if (PlatformProcess.IsValid())
{
FPlatformProcess::TerminateProc(PlatformProcess);
UpdateLogText(LogText + LOCTEXT("UploadCancel", "Upload process was canceled.").ToString());
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnSelectLanguagePacksPath()
{
if (PlatformSettings != nullptr)
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
FString path;
FString defaultPath = PlatformSettings->GetLanguagePacksPath().IsEmpty() ? FPaths::ProjectContentDir() : PlatformSettings->GetLanguagePacksPath();
if (FDesktopPlatformModule::Get()->OpenDirectoryDialog(parentWindowHandle, "Choose Language Packs Directory", defaultPath, path))
{
PlatformSettings->SetLanguagePacksPath(path);
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClearLanguagePacksPath()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetLanguagePacksPath("");
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnSelectExpansionFilesPath()
{
if (PlatformSettings != nullptr)
{
TSharedPtr<SWindow> parentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
const void* parentWindowHandle = (parentWindow.IsValid() && parentWindow->GetNativeWindow().IsValid()) ? parentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
FString path;
FString defaultPath = PlatformSettings->GetExpansionFilesPath().IsEmpty() ? FPaths::ProjectContentDir() : PlatformSettings->GetExpansionFilesPath();
if (FDesktopPlatformModule::Get()->OpenDirectoryDialog(parentWindowHandle, "Choose Expansion Files Directory", defaultPath, path))
{
if (!path.Equals(PlatformSettings->GetExpansionFilesPath()))
{
if (!path.IsEmpty() && FPaths::DirectoryExists(path))
{
TArray<FString> Files;
//FFileManagerGeneric::Get().FindFilesRecursive(Files, *path, TEXT("*.*"), true, false, false);
IFileManager::Get().FindFiles(Files, *path);
TArray<FOculusXRAssetConfig>* AssetConfigs = PlatformSettings->GetAssetConfigs();
for (int i = 0; i < Files.Num(); i++)
{
FOculusXRAssetConfig AssetConfig;
AssetConfig.Name = Files[i];
AssetConfigs->Push(AssetConfig);
}
PlatformSettings->SetExpansionFilesPath(path);
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
}
}
}
return FReply::Handled();
}
FReply SOculusPlatformToolWidget::OnClearExpansionFilesPath()
{
if (PlatformSettings != nullptr)
{
PlatformSettings->SetExpansionFilesPath("");
PlatformSettings->GetAssetConfigs()->Empty();
PlatformSettings->SaveConfig();
BuildExpansionFileBox(ExpansionFilesSettings);
}
return FReply::Handled();
}
void SOculusPlatformToolWidget::ValidateTextField(PFieldValidatorDel del, FString text, FString name, bool& success)
{
FString error = "";
FFieldValidatorDel fieldValidator;
// Check the given field with the given field validator and print the error if it fails.
fieldValidator.BindSP(this, del);
if (!fieldValidator.Execute(text, error))
{
FString errorMessage = LOCTEXT("Error", "ERROR: Please verify that the {0} is correct. ").ToString();
errorMessage = FString::Format(*errorMessage, { name });
UpdateLogText(LogText + errorMessage + (error.IsEmpty() ? "\n" : error + "\n"));
success = false;
}
}
bool SOculusPlatformToolWidget::GenericFieldValidator(FString text, FString& error)
{
if (text.IsEmpty())
{
error = LOCTEXT("FieldEmpty", "The field is empty.").ToString();
return false;
}
return true;
}
bool SOculusPlatformToolWidget::IDFieldValidator(FString text, FString& error)
{
const FRegexPattern RegExPat(TEXT("^[0-9]+$"));
FRegexMatcher RegMatcher(RegExPat, text);
if (!GenericFieldValidator(text, error))
{
return false;
}
else if (!RegMatcher.FindNext())
{
error = LOCTEXT("InvalidChar", "The field contains invalid characters.").ToString();
return false;
}
return true;
}
bool SOculusPlatformToolWidget::DirectoryFieldValidator(FString text, FString& error)
{
if (!GenericFieldValidator(text, error))
{
return false;
}
if (!FPaths::DirectoryExists(text))
{
error = LOCTEXT("DirectoryNull", "The directory does not exist.").ToString();
return false;
}
return true;
}
bool SOculusPlatformToolWidget::FileFieldValidator(FString text, FString& error)
{
if (!GenericFieldValidator(text, error))
{
return false;
}
if (!FPaths::FileExists(text))
{
error = LOCTEXT("FileNull", "The file does not exist.").ToString();
return false;
}
return true;
}
bool SOculusPlatformToolWidget::LaunchParamValidator(FString text, FString& error)
{
if (text.Contains("\""))
{
error = LOCTEXT("LaunchParamError", "The field contains illegal characters.").ToString();
return false;
}
return true;
}
void SOculusPlatformToolWidget::On2DOptionsExpanded(bool bExpanded)
{
Options2DCollapsed = !bExpanded;
}
void SOculusPlatformToolWidget::OnRedistPackagesExpanded(bool bExpanded)
{
OptionsRedistPackagesCollapsed = !bExpanded;
}
void SOculusPlatformToolWidget::UpdateLogText(FString text)
{
// Make sure that log text updating happens on the right thread.
LogText = text;
LogTextUpdated = true;
}
void SOculusPlatformToolWidget::SetPlatformProcess(FProcHandle proc)
{
PlatformProcess = proc;
}
//=======================================================================================
//FPlatformDownloadTask
FPlatformDownloadTask::FPlatformDownloadTask(FUpdateLogTextDel textDel, FEvent* saveEvent)
{
UpdateLogText = textDel;
SaveCompleteEvent = saveEvent;
FOculusXRHMDModule::GetPluginWrapper().SendEvent2("oculus_platform_tool", "provision_util", "integration");
}
void FPlatformDownloadTask::DoWork()
{
// Create HTTP request for downloading oculus platform tool
downloadCompleteEvent = FGenericPlatformProcess::GetSynchEventFromPool(false);
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> httpRequest = FHttpModule::Get().CreateRequest();
httpRequest->OnProcessRequestComplete().BindRaw(this, &FPlatformDownloadTask::OnDownloadRequestComplete);
httpRequest->OnRequestProgress().BindRaw(this, &FPlatformDownloadTask::OnRequestDownloadProgress);
httpRequest->SetURL(UrlPlatformUtil);
httpRequest->SetVerb("GET");
httpRequest->ProcessRequest();
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("DownloadProgress", "Downloading Platform Tool: {0}%\n").ToString());
ToolConsoleLog = SOculusPlatformToolWidget::LogText;
UpdateProgressLog(0);
// Wait for download to complete
downloadCompleteEvent->Wait();
// Save HTTP data
FString fullPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()) + ProjectPlatformUtilPath;
if (FFileHelper::SaveArrayToFile(httpData, *fullPath))
{
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("DownloadSuccess", "Platform tool successfully downloaded.\n").ToString());
}
else
{
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("DownloadError", "An error has occured with downloading the platform tool.\n").ToString());
}
if (SaveCompleteEvent != nullptr)
{
SaveCompleteEvent->Trigger();
}
}
void FPlatformDownloadTask::UpdateProgressLog(int progress)
{
UpdateLogText.Execute(FString::Format(*ToolConsoleLog, { progress }));
}
void FPlatformDownloadTask::OnRequestDownloadProgress(FHttpRequestPtr HttpRequest, int32 BytesSend, int32 InBytesReceived)
{
// Update progress on download in tool console log
FHttpResponsePtr httpResponse = HttpRequest->GetResponse();
if (httpResponse.IsValid())
{
int progress = ((float)InBytesReceived / (float)httpResponse->GetContentLength()) * 100;
UpdateProgressLog(progress);
}
}
void FPlatformDownloadTask::OnDownloadRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
{
// Extract data from HTTP response and trigger download complete event
if (bSucceeded && HttpResponse.IsValid())
{
httpData = HttpResponse->GetContent();
downloadCompleteEvent->Trigger();
}
}
//=======================================================================================
//FPlatformUploadTask
FPlatformUploadTask::FPlatformUploadTask(FString args, FEnableUploadButtonDel del, FUpdateLogTextDel textDel, FSetProcessDel procDel)
{
LaunchArgs = args;
EnableUploadButton = del;
UpdateLogText = textDel;
SetProcess = procDel;
EnableUploadButton.Execute(false);
}
void FPlatformUploadTask::DoWork()
{
// Check if the platform tool exists in the project directory. If not, start process to download it.
if (!FPaths::FileExists(FPaths::ProjectContentDir() + ProjectPlatformUtilPath))
{
FEvent* PlatformToolCreatedEvent = FGenericPlatformProcess::GetSynchEventFromPool(false);
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("NoCLI", "Unable to find Oculus Platform Utility.\n").ToString());
#if UE_VERSION_OLDER_THAN(5, 3, 0)
EAppReturnType::Type dialogChoice = FMessageDialog::Open(EAppMsgType::OkCancel, OculusPlatformDialogMessage, &OculusPlatformDialogTitle);
#else
EAppReturnType::Type dialogChoice = FMessageDialog::Open(EAppMsgType::OkCancel, OculusPlatformDialogMessage, OculusPlatformDialogTitle);
#endif
if (dialogChoice == EAppReturnType::Ok)
{
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("DownloadCLI", "Downloading Oculus Platform Utility . . .\n").ToString());
(new FAsyncTask<FPlatformDownloadTask>(UpdateLogText, PlatformToolCreatedEvent))->StartBackgroundTask();
PlatformToolCreatedEvent->Wait();
}
else
{
return;
}
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("StartUploadAfterDownload", "Starting upload . . .\n").ToString());
}
// Start up the CLI and pass in arguments.
FPlatformProcess::CreatePipe(ReadPipe, WritePipe);
FProcHandle PlatformProcess = FPlatformProcess::CreateProc(*(FPaths::ProjectContentDir() + ProjectPlatformUtilPath), *LaunchArgs, false, true, true, nullptr, 0, nullptr, WritePipe, ReadPipe);
SetProcess.Execute(PlatformProcess);
// Redirect CLI output to the tool's log.
while (FPlatformProcess::IsProcRunning(PlatformProcess))
{
FString log = FPlatformProcess::ReadPipe(ReadPipe);
if (!log.IsEmpty())
{
// Remove parts of the log that contain escape character codes
int32 escapeIndex = log.Find("\u001b");
while (escapeIndex >= 0)
{
int32 lineEndIndex = log.Find("\n", ESearchCase::IgnoreCase, ESearchDir::FromStart, escapeIndex);
if (lineEndIndex < 0) // If an escape character code exists without a new line end, just remove the escape character
{
lineEndIndex = escapeIndex + 1;
}
log.RemoveAt(escapeIndex, lineEndIndex - escapeIndex);
escapeIndex = log.Find("\u001b");
}
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + log);
}
}
EnableUploadButton.Execute(true);
}
//=======================================================================================
//FPlatformLoadRedistPackagesTask
FPlatformLoadRedistPackagesTask::FPlatformLoadRedistPackagesTask(FUpdateLogTextDel textDel)
{
UpdateLogText = textDel;
}
void FPlatformLoadRedistPackagesTask::DoWork()
{
UOculusXRPlatformToolSettings* PlatformSettings = GetMutableDefault<UOculusXRPlatformToolSettings>();
// Check to see if the CLI exists, we need this to load avalible redist packages
if (!FPaths::FileExists(FPaths::ProjectContentDir() + ProjectPlatformUtilPath))
{
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("LoadRedist", "Loading redistributable packages . . .\n").ToString());
FEvent* PlatformToolCreatedEvent = FGenericPlatformProcess::GetSynchEventFromPool(false);
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("NoCLI", "Unable to find Oculus Platform Utility.\n").ToString());
#if UE_VERSION_OLDER_THAN(5, 3, 0)
EAppReturnType::Type dialogChoice = FMessageDialog::Open(EAppMsgType::OkCancel, OculusPlatformDialogMessage, &OculusPlatformDialogTitle);
#else
EAppReturnType::Type dialogChoice = FMessageDialog::Open(EAppMsgType::OkCancel, OculusPlatformDialogMessage, OculusPlatformDialogTitle);
#endif
if (dialogChoice == EAppReturnType::Ok)
{
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("DownloadCLI", "Downloading Oculus Platform Utility . . .\n").ToString());
(new FAsyncTask<FPlatformDownloadTask>(UpdateLogText, PlatformToolCreatedEvent))->StartBackgroundTask();
PlatformToolCreatedEvent->Wait();
}
else
{
return;
}
}
// Launch CLI and pass command to list out redist packages currently avalible
TArray<FOculusXRRedistPackage> LoadedPackages;
FString Args = "list-redists";
FPlatformProcess::CreatePipe(ReadPipe, WritePipe);
FProcHandle PlatformProcess = FPlatformProcess::CreateProc(*(FPaths::ProjectContentDir() + ProjectPlatformUtilPath), *Args, false, true, true, nullptr, 0, nullptr, WritePipe, ReadPipe);
// Load redist packages
while (FPlatformProcess::IsProcRunning(PlatformProcess))
{
FString log = FPlatformProcess::ReadPipe(ReadPipe);
if (!log.IsEmpty() && !log.Contains("\u001b") && !log.Contains("ID"))
{
TArray<FString> Packages;
log.ParseIntoArrayLines(Packages);
if (Packages.Num() > 0)
{
for (int i = 0; i < Packages.Num(); i++)
{
FString id, name;
Packages[i].Split("|", &id, &name);
if (!id.IsEmpty() && !name.IsEmpty())
{
FOculusXRRedistPackage newPackage;
newPackage.Name = name;
newPackage.Id = id;
LoadedPackages.Add(newPackage);
}
}
}
}
}
// Check to see if our stored copy of redist packages is outdated
if (PlatformSettings != nullptr)
{
if (LoadedPackages.Num() > PlatformSettings->OculusRedistPackages.Num())
{
PlatformSettings->OculusRedistPackages = LoadedPackages;
PlatformSettings->SaveConfig();
UpdateLogText.Execute(SOculusPlatformToolWidget::LogText + LOCTEXT("FinishRedistLoad", "Finished updating redistributable packages.\n").ToString());
}
}
}
#undef LOCTEXT_NAMESPACE