diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/PageManager.java b/FCL/src/main/java/com/tungsten/fcl/ui/PageManager.java index 3ab7dbdc..b111be6e 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/PageManager.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/PageManager.java @@ -123,6 +123,22 @@ public abstract class PageManager { } } + public void dismissAllTempPagesCreatedByPage(int id) { + FCLCommonPage commonPage = getPageById(id); + commonPage.getCurrentTempPage().dismiss(); + commonPage.getAllTempPages().clear(); + commonPage.setCurrentTempPage(null); + if (currentPage == commonPage) { + commonPage.onStart(); + } + } + + public void dismissAllTempPages() { + for (FCLCommonPage page : allPages) { + dismissAllTempPagesCreatedByPage(page.getId()); + } + } + public void onPause() { for (FCLCommonPage page : allPages) { page.onPause(); diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/manage/InstallerListPage.java b/FCL/src/main/java/com/tungsten/fcl/ui/manage/InstallerListPage.java index 3b65f1a2..d7660aa6 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/manage/InstallerListPage.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/manage/InstallerListPage.java @@ -1,15 +1,64 @@ package com.tungsten.fcl.ui.manage; -import android.content.Context; +import static com.tungsten.fcl.ui.download.InstallersPage.alertFailureMessage; +import android.app.Activity; +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +import androidx.appcompat.app.AppCompatDialog; +import androidx.appcompat.widget.LinearLayoutCompat; + +import com.tungsten.fcl.R; +import com.tungsten.fcl.setting.DownloadProviders; import com.tungsten.fcl.setting.Profile; +import com.tungsten.fcl.ui.InstallerItem; +import com.tungsten.fcl.ui.PageManager; +import com.tungsten.fcl.ui.TaskDialog; +import com.tungsten.fcl.ui.download.InstallerVersionPage; +import com.tungsten.fcl.util.AndroidUtils; +import com.tungsten.fcl.util.RequestCodes; +import com.tungsten.fcl.util.TaskCancellationAction; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Schedulers; import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.task.TaskExecutor; +import com.tungsten.fclcore.task.TaskListener; +import com.tungsten.fcllibrary.browser.FileBrowser; +import com.tungsten.fcllibrary.browser.options.LibMode; +import com.tungsten.fcllibrary.browser.options.SelectionMode; +import com.tungsten.fcllibrary.component.dialog.FCLAlertDialog; import com.tungsten.fcllibrary.component.ui.FCLCommonPage; import com.tungsten.fcllibrary.component.view.FCLUILayout; +import com.tungsten.fcllibrary.util.ConvertUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; public class InstallerListPage extends FCLCommonPage implements ManageUI.VersionLoadable { + + private Profile profile; + private String versionId; + private Version version; + private String gameVersion; + + private ScrollView scrollView; + private LinearLayoutCompat parent; + public InstallerListPage(Context context, int id, FCLUILayout parent, int resId) { super(context, id, parent, resId); + create(); + } + + private void create() { + scrollView = findViewById(R.id.scroll); } @Override @@ -18,7 +67,179 @@ public class InstallerListPage extends FCLCommonPage implements ManageUI.Version } @Override - public void loadVersion(Profile profile, String version) { + public void loadVersion(Profile profile, String versionId) { + this.profile = profile; + this.versionId = versionId; + this.version = profile.getRepository().getVersion(versionId); + this.gameVersion = null; + CompletableFuture.supplyAsync(() -> { + gameVersion = profile.getRepository().getGameVersion(version).orElse(null); + + return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId)); + }).thenAcceptAsync(analyzer -> { + Function removeAction = libraryId -> () -> profile.getDependency().removeLibraryAsync(version, libraryId) + .thenComposeAsync(profile.getRepository()::saveAsync) + .withComposeAsync(profile.getRepository().refreshVersionsAsync()) + .withRunAsync(Schedulers.androidUIThread(), () -> loadVersion(this.profile, this.versionId)) + .start(); + + clear(); + + InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup(getContext()); + + // Conventional libraries: game, fabric, quilt, forge, liteloader, optifine + for (InstallerItem installerItem : group.getLibraries()) { + String libraryId = installerItem.getLibraryId(); + String libraryVersion = analyzer.getVersion(libraryId).orElse(null); + installerItem.libraryVersion.set(libraryVersion); + installerItem.upgradable.set(libraryVersion != null); + installerItem.installable.set(true); + installerItem.action.set(() -> { + InstallerVersionPage page = new InstallerVersionPage(getContext(), PageManager.PAGE_ID_TEMP, getParent(), R.layout.page_install_version, gameVersion, libraryId, remoteVersion -> { + if (libraryVersion == null) { + finish(profile, remoteVersion); + } else { + FCLAlertDialog.Builder builder = new FCLAlertDialog.Builder(getContext()); + builder.setCancelable(false); + builder.setAlertLevel(FCLAlertDialog.AlertLevel.INFO); + builder.setTitle(getContext().getString(R.string.install_change_version)); + builder.setMessage(AndroidUtils.getLocalizedText(getContext(), "install_change_version_confirm", AndroidUtils.getLocalizedText(getContext(), "install_installer_" + libraryId), libraryVersion, remoteVersion.getSelfVersion())); + builder.setPositiveButton(() -> finish(profile, remoteVersion)); + builder.setNegativeButton(null); + } + }); + ManagePageManager.getInstance().showTempPage(page); + }); + boolean removable = !"game".equals(libraryId) && libraryVersion != null; + installerItem.removable.set(removable); + if (removable) { + Runnable action = removeAction.apply(libraryId); + installerItem.removeAction.set(action); + } + addView(installerItem); + } + }, Schedulers.androidUIThread()); + } + + public void installOffline() { + ArrayList suffix = new ArrayList<>(); + suffix.add(".jar"); + FileBrowser.Builder builder = new FileBrowser.Builder(getContext()); + builder.setTitle(getContext().getString(R.string.install_installer_install_offline_extension)); + builder.setLibMode(LibMode.FILE_CHOOSER); + builder.setSelectionMode(SelectionMode.SINGLE_SELECTION); + builder.setSuffix(suffix); + builder.create().browse(getActivity(), RequestCodes.SELECT_AUTO_INSTALLER_CODE, (requestCode, resultCode, data) -> { + if (requestCode == RequestCodes.SELECT_AUTO_INSTALLER_CODE && resultCode == Activity.RESULT_OK && data != null) { + String path = FileBrowser.getSelectedFiles(data).get(0); + if (new File(path).exists()) { + doInstallOffline(new File(path)); + } + } + }); + } + + private void doInstallOffline(File file) { + Task task = profile.getDependency().installLibraryAsync(version, file.toPath()) + .thenComposeAsync(profile.getRepository()::saveAsync) + .thenComposeAsync(profile.getRepository().refreshVersionsAsync()); + task.setName(getContext().getString(R.string.install_installer_install_offline)); + TaskExecutor executor = task.executor(new TaskListener() { + @Override + public void onStop(boolean success, TaskExecutor executor) { + Schedulers.androidUIThread().execute(() -> { + if (success) { + loadVersion(profile, versionId); + FCLAlertDialog.Builder builder = new FCLAlertDialog.Builder(getContext()); + builder.setAlertLevel(FCLAlertDialog.AlertLevel.INFO); + builder.setCancelable(false); + builder.setMessage(getContext().getString(R.string.install_success)); + builder.setNegativeButton(getContext().getString(com.tungsten.fcllibrary.R.string.dialog_positive), null); + builder.create().show(); + } else { + if (executor.getException() == null) + return; + alertFailureMessage(getContext(), executor.getException(), () -> {}); + } + }); + } + }); + TaskDialog dialog = new TaskDialog(getContext(), TaskCancellationAction.NO_CANCEL); + dialog.setTitle(getContext().getString(R.string.install_installer_install_offline)); + dialog.setExecutor(executor); + dialog.show(); + executor.start(); + } + + private void clear() { + if (parent == null) { + parent = new LinearLayoutCompat(getContext()); + parent.setOrientation(LinearLayoutCompat.VERTICAL); + scrollView.addView(parent); + ViewGroup.LayoutParams layoutParams = scrollView.getChildAt(0).getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + scrollView.getChildAt(0).setLayoutParams(layoutParams); + } + parent.removeAllViews(); + } + + private void addView(InstallerItem installerItem) { + if (parent == null) { + parent = new LinearLayoutCompat(getContext()); + parent.setOrientation(LinearLayoutCompat.VERTICAL); + scrollView.addView(parent); + ViewGroup.LayoutParams layoutParams = scrollView.getChildAt(0).getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + scrollView.getChildAt(0).setLayoutParams(layoutParams); + } + View view = installerItem.createView(); + if (parent.getChildCount() > 0) { + view.setPadding(0, ConvertUtils.dip2px(getContext(), 10), 0, 0); + } + parent.addView(view); + } + + private void finish(Profile profile, RemoteVersion remoteVersion) { + // We remove library but not save it, + // so if installation failed will not break down current version. + Task ret = Task.supplyAsync(() -> version); + List stages = new ArrayList<>(); + ret = ret.thenComposeAsync(version -> profile.getDependency(DownloadProviders.getDownloadProvider()).installLibraryAsync(version, remoteVersion)); + stages.add(String.format("fcl.install.%s:%s", remoteVersion.getLibraryId(), remoteVersion.getSelfVersion())); + + Task task = ret.thenComposeAsync(profile.getRepository()::saveAsync).thenComposeAsync(profile.getRepository().refreshVersionsAsync()).withStagesHint(stages); + + TaskDialog pane = new TaskDialog(getContext(), new TaskCancellationAction(AppCompatDialog::dismiss)); + + pane.setTitle(getContext().getString(R.string.install_change_version)); + + Schedulers.androidUIThread().execute(() -> { + TaskExecutor executor = task.executor(new TaskListener() { + @Override + public void onStop(boolean success, TaskExecutor executor) { + Schedulers.androidUIThread().execute(() -> { + if (success) { + FCLAlertDialog.Builder builder1 = new FCLAlertDialog.Builder(getContext()); + builder1.setAlertLevel(FCLAlertDialog.AlertLevel.INFO); + builder1.setCancelable(false); + builder1.setMessage(getContext().getString(R.string.install_success)); + builder1.setNegativeButton(getContext().getString(com.tungsten.fcllibrary.R.string.dialog_positive), () -> ManagePageManager.getInstance().dismissCurrentTempPage()); + builder1.create().show(); + } else { + if (executor.getException() == null) + return; + alertFailureMessage(getContext(), executor.getException(), () -> {}); + } + + }); + } + }); + pane.setExecutor(executor); + pane.show(); + executor.start(); + }); } } diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/manage/ManageUI.java b/FCL/src/main/java/com/tungsten/fcl/ui/manage/ManageUI.java index 11c50aaf..a83cc0f1 100644 --- a/FCL/src/main/java/com/tungsten/fcl/ui/manage/ManageUI.java +++ b/FCL/src/main/java/com/tungsten/fcl/ui/manage/ManageUI.java @@ -18,6 +18,7 @@ import com.tungsten.fclcore.game.GameRepository; import com.tungsten.fclcore.task.Schedulers; import com.tungsten.fclcore.task.Task; import com.tungsten.fcllibrary.component.ui.FCLBasePage; +import com.tungsten.fcllibrary.component.ui.FCLCommonPage; import com.tungsten.fcllibrary.component.ui.FCLMultiPageUI; import com.tungsten.fcllibrary.component.view.FCLTabLayout; import com.tungsten.fcllibrary.component.view.FCLUILayout; @@ -55,6 +56,9 @@ public class ManageUI extends FCLMultiPageUI implements TabLayout.OnTabSelectedL @Override public void onStart() { super.onStart(); + for (FCLCommonPage page : pageManager.getAllPages()) { + ((VersionLoadable) page).loadVersion(getProfile(), getVersion()); + } } @Override @@ -171,6 +175,7 @@ public class ManageUI extends FCLMultiPageUI implements TabLayout.OnTabSelectedL setVersion(version, profile); preferredVersionName = version; + pageManager.dismissAllTempPages(); pageManager.loadVersion(profile, version); } diff --git a/FCL/src/main/java/com/tungsten/fcl/util/RequestCodes.java b/FCL/src/main/java/com/tungsten/fcl/util/RequestCodes.java index 4b6ed8a0..04e870cf 100644 --- a/FCL/src/main/java/com/tungsten/fcl/util/RequestCodes.java +++ b/FCL/src/main/java/com/tungsten/fcl/util/RequestCodes.java @@ -9,4 +9,8 @@ public class RequestCodes { public static final int SELECT_PROFILE_CODE = 100; public static final int SELECT_SKIN_CODE = 150; + + public static final int SELECT_AUTO_INSTALLER_CODE = 200; + + public static final int SELECT_MANUAL_INSTALLER_CODE = 250; } diff --git a/FCL/src/main/res/layout/page_installer_list.xml b/FCL/src/main/res/layout/page_installer_list.xml index 3e401dd6..10585bca 100644 --- a/FCL/src/main/res/layout/page_installer_list.xml +++ b/FCL/src/main/res/layout/page_installer_list.xml @@ -1,7 +1,19 @@ + + \ No newline at end of file