CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/UI/MemStickScreen.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"1819#include "android/jni/app-android.h"2021#include "Common/Log.h"22#include "Common/UI/UI.h"23#include "Common/UI/View.h"24#include "Common/UI/ViewGroup.h"2526#include "Common/StringUtils.h"27#include "Common/System/System.h"28#include "Common/System/Request.h"29#include "Common/System/NativeApp.h"30#include "Common/System/Display.h"31#include "Common/System/OSD.h"32#include "Common/Data/Text/I18n.h"33#include "Common/Data/Text/Parsers.h"3435#include "Common/File/AndroidStorage.h"36#include "Common/File/FileUtil.h"37#include "Common/File/Path.h"38#include "Common/File/DiskFree.h"3940#include "Common/Thread/ThreadManager.h"4142#include "Core/Config.h"43#include "Core/Reporting.h"44#include "Core/System.h"45#include "Core/Util/GameManager.h"46#include "Core/Util/MemStick.h"4748#include "UI/MemStickScreen.h"49#include "UI/MainScreen.h"50#include "UI/MiscScreens.h"51#include "UI/OnScreenDisplay.h"5253static std::string FormatSpaceString(int64_t space) {54if (space >= 0) {55char buffer[50];56NiceSizeFormat(space, buffer, sizeof(buffer));57return buffer;58} else {59return "N/A";60}61}6263MemStickScreen::MemStickScreen(bool initialSetup)64: initialSetup_(initialSetup) {65#if PPSSPP_PLATFORM(ANDROID)66// Let's only offer the browse-for-folder choice on Android 10 or later.67// Earlier versions often don't really have working folder browsers.68storageBrowserWorking_ = System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 29;69#else70// For testing UI only71storageBrowserWorking_ = true;72#endif7374if (initialSetup_) {75// Preselect current choice.76if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) {77choice_ = CHOICE_BROWSE_FOLDER;78} else {79WARN_LOG_REPORT(Log::System, "Scoped storage not enabled - shouldn't be in MemStickScreen at initial setup");80choice_ = CHOICE_STORAGE_ROOT;81// Shouldn't really be here in initial setup.82}83} else {84// Detect the current choice, so it's preselected in the UI.85#if PPSSPP_PLATFORM(UWP)86if (g_Config.memStickDirectory == g_Config.internalDataDirectory) {87#else88if (g_Config.memStickDirectory == Path(g_extFilesDir)) {89#endif90choice_ = CHOICE_PRIVATE_DIRECTORY;91} else if (g_Config.memStickDirectory == Path(g_externalDir)) {92choice_ = CHOICE_STORAGE_ROOT;93} else if (storageBrowserWorking_) {94choice_ = CHOICE_BROWSE_FOLDER;95} else {96choice_ = CHOICE_SET_MANUAL;97}98}99}100101static void AddExplanation(UI::ViewGroup *viewGroup, MemStickScreen::Choice choice, UI::View *extraView = nullptr) {102auto iz = GetI18NCategory(I18NCat::MEMSTICK);103using namespace UI;104105int flags = FLAG_WRAP_TEXT;106107UI::ViewGroup *holder = new UI::LinearLayout(ORIENT_VERTICAL);108109UI::ViewGroup *indentHolder = new UI::LinearLayout(ORIENT_HORIZONTAL);110indentHolder->Add(new Spacer(20.0));111indentHolder->Add(holder);112113viewGroup->Add(indentHolder);114115if (extraView) {116holder->Add(extraView);117}118119switch (choice) {120case MemStickScreen::CHOICE_STORAGE_ROOT:121// Old school choice122holder->Add(new TextView(iz->T("DataWillStay", "Data will stay even if you uninstall PPSSPP"), flags, false))->SetBullet(true);123holder->Add(new TextView(iz->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold"), flags, false))->SetBullet(true);124holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);125break;126case MemStickScreen::CHOICE_BROWSE_FOLDER:127holder->Add(new TextView(iz->T("DataWillStay", "Data will stay even if you uninstall PPSSPP"), flags, false))->SetBullet(true);128holder->Add(new TextView(iz->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold"), flags, false))->SetBullet(true);129#if !PPSSPP_PLATFORM(UWP)130holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);131#endif132break;133case MemStickScreen::CHOICE_PRIVATE_DIRECTORY:134// Consider https://www.compart.com/en/unicode/U+26A0 (unicode warning sign?)? or a graphic?135holder->Add(new TextView(iz->T("DataWillBeLostOnUninstall", "Warning! Data will be lost when you uninstall PPSSPP!"), flags, false))->SetBullet(true);136holder->Add(new TextView(iz->T("DataCannotBeShared", "Data CANNOT be shared between PPSSPP regular/Gold!"), flags, false))->SetBullet(true);137#if !PPSSPP_PLATFORM(UWP)138#if GOLD139holder->Add(new TextView(iz->T("USBAccessThroughGold", "USB access through Android/data/org.ppsspp.ppssppgold/files"), flags, false))->SetBullet(true);140#else141holder->Add(new TextView(iz->T("USBAccessThrough", "USB access through Android/data/org.ppsspp.ppsspp/files"), flags, false))->SetBullet(true);142#endif143#endif144break;145case MemStickScreen::CHOICE_SET_MANUAL:146default:147holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);148// What more?149150// Should we have a special text here? It'll popup a text window for editing.151break;152}153}154155void MemStickScreen::CreateViews() {156using namespace UI;157158auto di = GetI18NCategory(I18NCat::DIALOG);159auto ms = GetI18NCategory(I18NCat::MEMSTICK);160161Margins actionMenuMargins(15, 0, 15, 0);162163root_ = new LinearLayout(ORIENT_HORIZONTAL);164165Spacer *spacerColumn = new Spacer(new LinearLayoutParams(20.0, FILL_PARENT, 0.0f));166ScrollView *mainColumnScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));167168ViewGroup *mainColumn = new LinearLayoutList(ORIENT_VERTICAL);169mainColumnScroll->Add(mainColumn);170171root_->Add(spacerColumn);172root_->Add(mainColumnScroll);173174if (initialSetup_) {175mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));176mainColumn->Add(new TextView(ms->T("Welcome to PPSSPP!"), ALIGN_LEFT, false));177}178179mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 18.0f, 0.0f)));180181mainColumn->Add(new TextView(ms->T("MemoryStickDescription", "Choose where to keep PSP data (Memory Stick)"), ALIGN_LEFT, false));182mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 18.0f, 0.0f)));183184ViewGroup *subColumns = new LinearLayoutList(ORIENT_HORIZONTAL);185mainColumn->Add(subColumns);186187ViewGroup *leftColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0));188subColumns->Add(leftColumn);189190ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(220, FILL_PARENT, actionMenuMargins));191subColumns->Add(rightColumnItems);192193// For legacy Android systems, so you can switch back to the old ways if you move to SD or something.194// Trying to avoid needing a scroll view, so only showing the explanation for one option at a time.195#if !PPSSPP_PLATFORM(UWP)196if (!System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) {197leftColumn->Add(new RadioButton(&choice_, CHOICE_STORAGE_ROOT, ms->T("Use PSP folder at root of storage")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);198if (choice_ == CHOICE_STORAGE_ROOT) {199AddExplanation(leftColumn, (MemStickScreen::Choice)choice_);200}201}202#endif203204if (storageBrowserWorking_) {205leftColumn->Add(new RadioButton(&choice_, CHOICE_BROWSE_FOLDER, ms->T("Create or Choose a PSP folder")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);206207// TODO: Show current folder here if we have one set.208} else {209leftColumn->Add(new RadioButton(&choice_, CHOICE_SET_MANUAL, ms->T("Manually specify PSP folder")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);210leftColumn->Add(new TextView(ms->T("DataWillStay", "Data will stay even if you uninstall PPSSPP.")))->SetBullet(true);211leftColumn->Add(new TextView(ms->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold.")))->SetBullet(true);212// TODO: Show current folder here if we have one set.213}214errorNoticeView_ = leftColumn->Add(new NoticeView(NoticeLevel::WARN, ms->T("Cancelled - try again"), ""));215errorNoticeView_->SetVisibility(UI::V_GONE);216217if (choice_ == CHOICE_BROWSE_FOLDER || choice_ == CHOICE_SET_MANUAL) {218UI::View *extraView = nullptr;219if (!g_Config.memStickDirectory.empty()) {220extraView = new TextView(StringFromFormat(" %s: %s", ms->T_cstr("Current"), g_Config.memStickDirectory.ToVisualString().c_str()), ALIGN_LEFT, false);221}222AddExplanation(leftColumn, (MemStickScreen::Choice)choice_, extraView);223}224225std::string privateString(ms->T("Use App Private Data"));226227if (initialSetup_) {228privateString = StringFromFormat("%s (%s)", ms->T_cstr("Skip for now"), privateString.c_str());229}230231leftColumn->Add(new RadioButton(&choice_, CHOICE_PRIVATE_DIRECTORY, privateString))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);232if (choice_ == CHOICE_PRIVATE_DIRECTORY) {233AddExplanation(leftColumn, (MemStickScreen::Choice)choice_);234}235236leftColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));237238std::string_view confirmButtonText = "";239ImageID confirmButtonImage = ImageID::invalid();240switch (choice_) {241case CHOICE_BROWSE_FOLDER:242confirmButtonText = di->T("OK");243confirmButtonImage = ImageID("I_FOLDER_OPEN");244break;245case CHOICE_PRIVATE_DIRECTORY:246if (initialSetup_) {247confirmButtonText = di->T("Skip");248confirmButtonImage = ImageID("I_WARNING");249} else {250confirmButtonText = di->T("OK");251}252break;253case CHOICE_STORAGE_ROOT:254case CHOICE_SET_MANUAL:255default:256confirmButtonText = di->T("OK");257break;258}259260rightColumnItems->Add(new UI::Choice(confirmButtonText, confirmButtonImage))->OnClick.Handle<MemStickScreen>(this, &MemStickScreen::OnConfirmClick);261rightColumnItems->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));262263if (!initialSetup_) {264rightColumnItems->Add(new UI::Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);265}266if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_TV) {267rightColumnItems->Add(new UI::Choice(ms->T("WhatsThis", "What's this?")))->OnClick.Handle<MemStickScreen>(this, &MemStickScreen::OnHelp);268}269270INFO_LOG(Log::System, "MemStickScreen: initialSetup=%d", (int)initialSetup_);271}272273UI::EventReturn MemStickScreen::OnHelp(UI::EventParams ¶ms) {274// I'm letting the old redirect handle this one, as the target is within /docs on the website,275// and that structure may change a bit.276System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/guide_storage.html");277278return UI::EVENT_DONE;279}280281UI::EventReturn MemStickScreen::OnChoiceClick(UI::EventParams ¶ms) {282// Change the confirm button to match the choice,283// and change the text that we show.284RecreateViews();285return UI::EVENT_DONE;286}287288289UI::EventReturn MemStickScreen::OnConfirmClick(UI::EventParams ¶ms) {290switch (choice_) {291case CHOICE_SET_MANUAL:292return SetFolderManually(params);293case CHOICE_STORAGE_ROOT:294return UseStorageRoot(params);295case CHOICE_PRIVATE_DIRECTORY:296return UseInternalStorage(params);297case CHOICE_BROWSE_FOLDER:298return Browse(params);299}300return UI::EVENT_DONE;301}302303UI::EventReturn MemStickScreen::SetFolderManually(UI::EventParams ¶ms) {304// The old way, from before scoped storage. Write in the full path.305#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)306auto sy = GetI18NCategory(I18NCat::SYSTEM);307System_InputBoxGetString(GetRequesterToken(), sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), false, [&](const std::string &value, int) {308auto sy = GetI18NCategory(I18NCat::SYSTEM);309auto di = GetI18NCategory(I18NCat::DIALOG);310311std::string newPath = value;312size_t pos = newPath.find_last_not_of("/");313// Gotta have at least something but a /, and also needs to start with a /.314if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') {315settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr);316return;317}318if (pos != newPath.size() - 1) {319newPath = newPath.substr(0, pos + 1);320}321322if (newPath.empty()) {323// Reuse below message instead of adding yet another string.324System_Toast(sy->T("Path does not exist!"));325return;326}327328Path pendingMemStickFolder(newPath);329330if (!File::Exists(pendingMemStickFolder)) {331// Try to fix the path string, apparently some users got used to leaving out the /.332if (newPath[0] != '/') {333newPath = "/" + newPath;334}335336pendingMemStickFolder = Path(newPath);337}338339if (!File::Exists(pendingMemStickFolder) && pendingMemStickFolder.Type() == PathType::NATIVE) {340// Still no path? Try to automatically fix the case.341std::string oldNewPath = newPath;342FixPathCase(Path(""), newPath, FixPathCaseBehavior::FPC_FILE_MUST_EXIST);343if (oldNewPath != newPath) {344NOTICE_LOG(Log::IO, "Fixed path case: %s -> %s", oldNewPath.c_str(), newPath.c_str());345pendingMemStickFolder = Path(newPath);346} else {347NOTICE_LOG(Log::IO, "Failed to fix case of path %s (result: %s)", newPath.c_str(), oldNewPath.c_str());348}349}350351if (pendingMemStickFolder == g_Config.memStickDirectory) {352// Same directory as before - all good. Nothing to do.353TriggerFinish(DialogResult::DR_OK);354return;355}356357if (!File::Exists(pendingMemStickFolder)) {358System_Toast(sy->T("Path does not exist!"));359return;360}361362screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));363});364#endif365return UI::EVENT_DONE;366}367368UI::EventReturn MemStickScreen::UseInternalStorage(UI::EventParams ¶ms) {369#if PPSSPP_PLATFORM(UWP)370Path pendingMemStickFolder = g_Config.internalDataDirectory;371#else372Path pendingMemStickFolder = Path(g_extFilesDir);373#endif374375if (initialSetup_) {376// There's not gonna be any files here in this case since it's a fresh install.377// Let's just accept it and move on. No need to move files either.378if (SwitchMemstickFolderTo(pendingMemStickFolder)) {379TriggerFinish(DialogResult::DR_OK);380} else {381// This can't really happen?? Not worth making an error message.382ERROR_LOG_REPORT(Log::System, "Could not switch memstick path in setup (internal)");383}384// Don't have a confirmation dialog that would otherwise do it for us, need to just switch directly to the main screen.385screenManager()->switchScreen(new MainScreen());386} else if (pendingMemStickFolder != g_Config.memStickDirectory) {387// Always ask for confirmation when called from the UI. Likely there's already some data.388screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));389} else {390// User chose the same directory it's already in. Let's just bail.391TriggerFinish(DialogResult::DR_OK);392}393return UI::EVENT_DONE;394}395396UI::EventReturn MemStickScreen::UseStorageRoot(UI::EventParams ¶ms) {397Path pendingMemStickFolder = Path(g_externalDir);398399if (initialSetup_) {400// There's not gonna be any files here in this case since it's a fresh install.401// Let's just accept it and move on. No need to move files either.402if (SwitchMemstickFolderTo(pendingMemStickFolder)) {403TriggerFinish(DialogResult::DR_OK);404} else {405// This can't really happen?? Not worth making an error message.406ERROR_LOG_REPORT(Log::System, "Could not switch memstick path in setup");407}408} else if (pendingMemStickFolder != g_Config.memStickDirectory) {409// Always ask for confirmation when called from the UI. Likely there's already some data.410screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));411} else {412// User chose the same directory it's already in. Let's just bail.413TriggerFinish(DialogResult::DR_OK);414}415return UI::EVENT_DONE;416}417418UI::EventReturn MemStickScreen::Browse(UI::EventParams ¶ms) {419auto mm = GetI18NCategory(I18NCat::MAINMENU);420System_BrowseForFolder(GetRequesterToken(), mm->T("Choose folder"), g_Config.memStickDirectory, [=](const std::string &value, int) {421Path pendingMemStickFolder = Path(value);422INFO_LOG(Log::System, "Got folder: '%s' (old: %s)", pendingMemStickFolder.c_str(), g_Config.memStickDirectory.c_str());423// Browse finished. Let's pop up the confirmation dialog.424if (!pendingMemStickFolder.empty() && pendingMemStickFolder == g_Config.memStickDirectory && File::IsDirectory(pendingMemStickFolder)) {425auto di = GetI18NCategory(I18NCat::DIALOG);426// Not sure how this could happen, but let's go with it.427g_OSD.Show(OSDType::MESSAGE_SUCCESS, di->T("Done!"));428done_ = true;429return;430}431errorNoticeView_->SetVisibility(UI::V_GONE);432433screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, initialSetup_));434}, [=]() {435errorNoticeView_->SetVisibility(UI::V_VISIBLE);436});437return UI::EVENT_DONE;438}439440void MemStickScreen::dialogFinished(const Screen *dialog, DialogResult result) {441if (result == DialogResult::DR_OK) {442INFO_LOG(Log::System, "Confirmation screen done - moving on.");443// There's a screen manager bug if we call TriggerFinish directly.444// Can't be bothered right now, so we pick this up in update().445done_ = true;446}447// otherwise, we just keep going.448}449450void MemStickScreen::update() {451UIDialogScreenWithBackground::update();452if (done_) {453TriggerFinish(DialogResult::DR_OK);454done_ = false;455}456}457458ConfirmMemstickMoveScreen::ConfirmMemstickMoveScreen(Path newMemstickFolder, bool initialSetup)459: newMemstickFolder_(newMemstickFolder), initialSetup_(initialSetup), progressReporter_() {460existingFilesInNewFolder_ = FolderSeemsToBeUsed(newMemstickFolder);461if (initialSetup_) {462moveData_ = false;463}464}465466ConfirmMemstickMoveScreen::~ConfirmMemstickMoveScreen() {467if (moveDataTask_) {468INFO_LOG(Log::System, "Move Data task still running, blocking on it");469moveDataTask_->BlockUntilReady();470delete moveDataTask_;471}472}473474void ConfirmMemstickMoveScreen::CreateViews() {475using namespace UI;476auto sy = GetI18NCategory(I18NCat::SYSTEM);477auto ms = GetI18NCategory(I18NCat::MEMSTICK);478479root_ = new LinearLayout(ORIENT_HORIZONTAL);480481Path oldMemstickFolder = g_Config.memStickDirectory;482483Spacer *spacerColumn = new Spacer(new LinearLayoutParams(20.0, FILL_PARENT, 0.0f));484ViewGroup *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));485ViewGroup *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));486root_->Add(spacerColumn);487root_->Add(leftColumn);488root_->Add(rightColumn);489490int64_t freeSpaceNew;491int64_t freeSpaceOld;492free_disk_space(newMemstickFolder_, freeSpaceNew);493free_disk_space(oldMemstickFolder, freeSpaceOld);494495leftColumn->Add(new TextView(ms->T("Selected PSP Data Folder"), ALIGN_LEFT, false));496if (!initialSetup_) {497leftColumn->Add(new NoticeView(NoticeLevel::WARN, ms->T("PPSSPP will restart after the change"), ""));498}499leftColumn->Add(new TextView(newMemstickFolder_.ToVisualString(), ALIGN_LEFT, false));500std::string newFreeSpaceText = std::string(ms->T("Free space")) + ": " + FormatSpaceString(freeSpaceNew);501leftColumn->Add(new TextView(newFreeSpaceText, ALIGN_LEFT, false));502if (existingFilesInNewFolder_) {503leftColumn->Add(new NoticeView(NoticeLevel::SUCCESS, ms->T("Already contains PSP data"), ""));504if (!moveData_) {505leftColumn->Add(new NoticeView(NoticeLevel::INFO, ms->T("No data will be changed"), ""));506}507}508if (!error_.empty()) {509leftColumn->Add(new TextView(error_, ALIGN_LEFT, false));510}511512if (!oldMemstickFolder.empty()) {513std::string oldFreeSpaceText = std::string(ms->T("Free space")) + ": " + FormatSpaceString(freeSpaceOld);514515rightColumn->Add(new TextView(std::string(ms->T("Current")) + ":", ALIGN_LEFT, false));516rightColumn->Add(new TextView(oldMemstickFolder.ToVisualString(), ALIGN_LEFT, false));517rightColumn->Add(new TextView(oldFreeSpaceText, ALIGN_LEFT, false));518}519520if (moveDataTask_) {521progressView_ = leftColumn->Add(new TextView(progressReporter_.Format()));522} else {523progressView_ = nullptr;524}525526if (!moveDataTask_) {527if (!initialSetup_) {528leftColumn->Add(new CheckBox(&moveData_, ms->T("Move Data")))->OnClick.Handle(this, &ConfirmMemstickMoveScreen::OnMoveDataClick);529}530531auto di = GetI18NCategory(I18NCat::DIALOG);532leftColumn->Add(new Choice(di->T("OK")))->OnClick.Handle(this, &ConfirmMemstickMoveScreen::OnConfirm);533leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);534}535}536537UI::EventReturn ConfirmMemstickMoveScreen::OnMoveDataClick(UI::EventParams ¶ms) {538RecreateViews();539return UI::EVENT_DONE;540}541542void ConfirmMemstickMoveScreen::update() {543UIDialogScreenWithBackground::update();544auto ms = GetI18NCategory(I18NCat::MEMSTICK);545546if (moveDataTask_) {547if (progressView_) {548progressView_->SetText(progressReporter_.Format());549}550551MoveResult *result = moveDataTask_->Poll();552553if (result) {554if (result->success) {555progressReporter_.SetProgress(ms->T("Done!"));556INFO_LOG(Log::System, "Move data task finished successfully!");557// Succeeded!558FinishFolderMove();559} else {560progressReporter_.SetProgress(ms->T("Failed to move some files!"));561INFO_LOG(Log::System, "Move data task failed!");562// What do we do here? We might be in the middle of a move... Bad.563RecreateViews();564}565delete moveDataTask_;566moveDataTask_ = nullptr;567}568}569}570571UI::EventReturn ConfirmMemstickMoveScreen::OnConfirm(UI::EventParams ¶ms) {572// Transfer all the files in /PSP from the original directory.573// Should probably be done on a background thread so we can show some UI.574// So we probably need another screen for this with a progress bar..575// If the directory itself is called PSP, don't go below.576577if (moveData_) {578progressReporter_.SetProgress(T(I18NCat::MEMSTICK, "Starting move..."));579580moveDataTask_ = Promise<MoveResult *>::Spawn(&g_threadManager, [&]() -> MoveResult * {581Path moveSrc = g_Config.memStickDirectory;582Path moveDest = newMemstickFolder_;583return MoveDirectoryContentsSafe(moveSrc, moveDest, progressReporter_);584}, TaskType::IO_BLOCKING, TaskPriority::HIGH);585586RecreateViews();587} else {588FinishFolderMove();589}590591return UI::EVENT_DONE;592}593594void ConfirmMemstickMoveScreen::FinishFolderMove() {595auto ms = GetI18NCategory(I18NCat::MEMSTICK);596597Path oldMemstickFolder = g_Config.memStickDirectory;598599// Successful so far, switch the memstick folder.600if (!SwitchMemstickFolderTo(newMemstickFolder_)) {601// TODO: More precise errors.602error_ = ms->T("That folder doesn't work as a memstick folder.");603return;604}605606// If the chosen folder already had a config, reload it!607g_Config.Load();608609// If the current browser directory is the old memstick folder, drop it.610if (g_Config.currentDirectory == oldMemstickFolder) {611g_Config.currentDirectory = g_Config.defaultCurrentDirectory;612}613614PostLoadConfig();615616if (!initialSetup_) {617// We restart the app here, to get the new settings.618System_RestartApp("");619} else {620// This is initial setup, we now switch to the main screen, if we were successful621// (which we better have been...)622if (g_Config.Save("MemstickPathChanged")) {623// TriggerFinish(DialogResult::DR_OK);624screenManager()->switchScreen(new MainScreen());625} else {626error_ = ms->T("Failed to save config");627RecreateViews();628}629}630}631632633