Merge branch 'main' into pojav

This commit is contained in:
ShirosakiMio 2024-09-10 19:18:41 +08:00
commit 96a63ffb2c
25 changed files with 473 additions and 377 deletions

View File

@ -44,8 +44,8 @@ android {
applicationId "com.tungsten.fcl" applicationId "com.tungsten.fcl"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 1177 versionCode 1178
versionName "1.1.7.7" versionName "1.1.7.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -95,5 +95,11 @@ class AnimUtil {
this.interpolator = interpolator this.interpolator = interpolator
return this return this
} }
@JvmStatic
fun ObjectAnimator.startAfter(delayTime: Long) {
this.startDelay = delayTime
this.start()
}
} }
} }

View File

@ -0,0 +1,61 @@
package com.mio.util
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import android.util.Printer
class PerfUtil : Printer {
companion object {
@JvmStatic
fun install() {
Looper.getMainLooper().setMessageLogging(PerfUtil())
}
}
private val sampler = StackSampler(300)
private var isStarted = false
private var startTime = 0L
override fun println(x: String?) {
if (!isStarted) {
isStarted = true
startTime = System.currentTimeMillis()
sampler.startDump()
} else {
isStarted = false
val endTime = System.currentTimeMillis()
if (endTime - startTime > 300) {
Log.e("FCL PerfUtil", "block time = ${endTime - startTime}")
}
sampler.stopDump()
}
}
inner class StackSampler(val interval: Long) {
private val handler: Handler
private val runnable = Runnable {
val sb = StringBuilder()
Looper.getMainLooper().thread.stackTrace.forEach {
sb.append(it.toString())
sb.append("\n")
}
Log.e("FCL PerfUtil", sb.toString())
}
init {
val handlerThread = HandlerThread("")
handlerThread.start()
handler = Handler(handlerThread.looper)
}
fun startDump() {
handler.postDelayed(runnable, interval)
}
fun stopDump() {
handler.removeCallbacks(runnable)
}
}
}

View File

@ -8,6 +8,8 @@ import android.os.StrictMode;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.mio.util.PerfUtil;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
public class FCLApplication extends Application implements Application.ActivityLifecycleCallbacks { public class FCLApplication extends Application implements Application.ActivityLifecycleCallbacks {
@ -18,6 +20,7 @@ public class FCLApplication extends Application implements Application.ActivityL
// enabledStrictMode(); // enabledStrictMode();
super.onCreate(); super.onCreate();
this.registerActivityLifecycleCallbacks(this); this.registerActivityLifecycleCallbacks(this);
PerfUtil.install();
} }
public static Activity getCurrentActivity() { public static Activity getCurrentActivity() {
@ -28,9 +31,9 @@ public class FCLApplication extends Application implements Application.ActivityL
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectNetwork() StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectNetwork()
.detectCustomSlowCalls() .detectCustomSlowCalls()
.detectDiskReads() .detectDiskReads()
.detectDiskWrites() .detectDiskWrites()
.detectAll() .detectAll()
.penaltyLog() .penaltyLog()
.build()); .build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects() StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects()
@ -73,7 +76,7 @@ public class FCLApplication extends Application implements Application.ActivityL
@Override @Override
public void onActivityDestroyed(@NonNull Activity activity) { public void onActivityDestroyed(@NonNull Activity activity) {
if (currentActivity.get() == activity) { if (currentActivity != null && currentActivity.get() == activity) {
currentActivity = null; currentActivity = null;
} }
} }

View File

@ -9,10 +9,13 @@ import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.BounceInterpolator
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import com.mio.util.AnimUtil
import com.mio.util.AnimUtil.Companion.interpolator
import com.tungsten.fcl.R import com.tungsten.fcl.R
import com.tungsten.fcl.databinding.ActivityMainBinding import com.tungsten.fcl.databinding.ActivityMainBinding
import com.tungsten.fcl.game.JarExecutorHelper import com.tungsten.fcl.game.JarExecutorHelper
@ -225,6 +228,7 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
setupVersionDisplay() setupVersionDisplay()
UpdateChecker.getInstance().checkAuto(this@MainActivity).start() UpdateChecker.getInstance().checkAuto(this@MainActivity).start()
} }
playAnim()
} }
} }
} }
@ -256,7 +260,7 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
} }
manage -> { manage -> {
val version = Profiles.getSelectedVersion() val version = Profiles.getSelectedProfile().selectedVersion
if (version == null) { if (version == null) {
refreshMenuView(null) refreshMenuView(null)
title.setTextWithAnim(getString(R.string.version)) title.setTextWithAnim(getString(R.string.version))
@ -381,6 +385,7 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
@SuppressLint("UseCompatLoadingForDrawables") @SuppressLint("UseCompatLoadingForDrawables")
private fun loadVersion(version: String?) { private fun loadVersion(version: String?) {
bind.versionProgress.visibility = View.VISIBLE
if (Profiles.getSelectedProfile() != profile) { if (Profiles.getSelectedProfile() != profile) {
profile = Profiles.getSelectedProfile() profile = Profiles.getSelectedProfile()
if (profile != null) { if (profile != null) {
@ -394,42 +399,45 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
version version
) )
) { ) {
val game = Profiles.getSelectedProfile().repository.getGameVersion(version) Schedulers.defaultScheduler().execute {
.orElse(getString(R.string.message_unknown)) val game = Profiles.getSelectedProfile().repository.getGameVersion(version)
val libraries = StringBuilder(game) .orElse(getString(R.string.message_unknown))
val analyzer = LibraryAnalyzer.analyze( val libraries = StringBuilder(game)
Profiles.getSelectedProfile().repository.getResolvedPreservingPatchesVersion(version) val analyzer = LibraryAnalyzer.analyze(
) Profiles.getSelectedProfile().repository.getResolvedPreservingPatchesVersion(version)
for (mark in analyzer) { )
val libraryId = mark.libraryId for (mark in analyzer) {
val libraryVersion = mark.libraryVersion val libraryId = mark.libraryId
if (libraryId == LibraryType.MINECRAFT.patchId) continue val libraryVersion = mark.libraryVersion
if (AndroidUtils.hasStringId( if (libraryId == LibraryType.MINECRAFT.patchId) continue
this, if (AndroidUtils.hasStringId(
"install_installer_" + libraryId.replace("-", "_")
)
) {
libraries.append(", ").append(
AndroidUtils.getLocalizedText(
this, this,
"install_installer_" + libraryId.replace("-", "_") "install_installer_" + libraryId.replace("-", "_")
) )
) ) {
if (libraryVersion != null) libraries.append(": ").append( libraries.append(", ").append(
libraryVersion.replace( AndroidUtils.getLocalizedText(
"(?i)$libraryId".toRegex(), "" this,
"install_installer_" + libraryId.replace("-", "_")
)
) )
) if (libraryVersion != null) libraries.append(": ").append(
libraryVersion.replace(
"(?i)$libraryId".toRegex(), ""
)
)
}
}
val drawable = Profiles.getSelectedProfile().repository.getVersionIconImage(version)
Schedulers.androidUIThread().execute {
bind.versionProgress.visibility = View.GONE
bind.versionName.text = version
bind.versionHint.text = libraries.toString()
bind.icon.setBackgroundDrawable(drawable)
} }
} }
bind.versionName.text = version
bind.versionHint.text = libraries.toString()
bind.icon.setBackgroundDrawable(
Profiles.getSelectedProfile().repository.getVersionIconImage(
version
)
)
} else { } else {
bind.versionProgress.visibility = View.GONE
bind.versionName.text = getString(R.string.version_no_version) bind.versionName.text = getString(R.string.version_no_version)
bind.versionHint.text = getString(R.string.version_manage) bind.versionHint.text = getString(R.string.version_manage)
bind.icon.setBackgroundDrawable(getDrawable(R.drawable.img_grass)) bind.icon.setBackgroundDrawable(getDrawable(R.drawable.img_grass))
@ -454,4 +462,21 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
}) })
} }
} }
private fun playAnim() {
bind.apply {
AnimUtil.playTranslationX(
leftMenu,
ThemeEngine.getInstance().getTheme().animationSpeed * 100L,
-100f,
0f
).interpolator(BounceInterpolator()).start()
AnimUtil.playTranslationX(
rightMenu,
ThemeEngine.getInstance().getTheme().animationSpeed * 100L,
100f,
0f
).interpolator(BounceInterpolator()).start()
}
}
} }

View File

@ -41,9 +41,6 @@ public class SplashActivity extends FCLActivity {
public ConstraintLayout background; public ConstraintLayout background;
private EulaFragment eulaFragment;
private RuntimeFragment runtimeFragment;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -103,21 +100,15 @@ public class SplashActivity extends FCLActivity {
FCLPath.loadPaths(this); FCLPath.loadPaths(this);
transFile(); transFile();
Logging.start(Paths.get(FCLPath.LOG_DIR)); Logging.start(Paths.get(FCLPath.LOG_DIR));
initFragments();
start(); start();
} }
private void initFragments() {
eulaFragment = new EulaFragment();
runtimeFragment = new RuntimeFragment();
}
public void start() { public void start() {
SharedPreferences sharedPreferences = getSharedPreferences("launcher", MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences("launcher", MODE_PRIVATE);
if (sharedPreferences.getBoolean("isFirstLaunch", true)) { if (sharedPreferences.getBoolean("isFirstLaunch", true)) {
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, eulaFragment).commit(); getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, EulaFragment.class, null).commit();
} else { } else {
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, runtimeFragment).commit(); getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, RuntimeFragment.class, null).commit();
} }
} }

View File

@ -1,6 +1,5 @@
package com.tungsten.fcl.fragment package com.tungsten.fcl.fragment
import android.annotation.SuppressLint
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -12,11 +11,9 @@ import com.tungsten.fcl.activity.SplashActivity
import com.tungsten.fcl.databinding.FragmentRuntimeBinding import com.tungsten.fcl.databinding.FragmentRuntimeBinding
import com.tungsten.fcl.util.RuntimeUtils import com.tungsten.fcl.util.RuntimeUtils
import com.tungsten.fclauncher.utils.FCLPath import com.tungsten.fclauncher.utils.FCLPath
import com.tungsten.fclcore.task.Schedulers
import com.tungsten.fclcore.util.io.FileUtils import com.tungsten.fclcore.util.io.FileUtils
import com.tungsten.fcllibrary.component.FCLFragment import com.tungsten.fcllibrary.component.FCLFragment
import com.tungsten.fcllibrary.component.view.FCLButton
import com.tungsten.fcllibrary.component.view.FCLImageView
import com.tungsten.fcllibrary.component.view.FCLProgressBar
import com.tungsten.fcllibrary.util.LocaleUtils import com.tungsten.fcllibrary.util.LocaleUtils
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -41,10 +38,12 @@ class RuntimeFragment : FCLFragment(), View.OnClickListener {
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_runtime, container, false) val view = inflater.inflate(R.layout.fragment_runtime, container, false)
bind = FragmentRuntimeBinding.bind(view) bind = FragmentRuntimeBinding.bind(view)
initState()
refreshDrawables()
check()
bind.install.setOnClickListener(this) bind.install.setOnClickListener(this)
Schedulers.defaultScheduler().execute {
initState()
refreshDrawables()
check()
}
return view return view
} }

View File

@ -243,7 +243,7 @@ public final class Accounts {
*/ */
static void init() { static void init() {
if (initialized) if (initialized)
throw new IllegalStateException("Already initialized"); return;
loadGlobalAccountStorages(); loadGlobalAccountStorages();

View File

@ -62,7 +62,7 @@ public final class ConfigHolder {
public synchronized static void init() throws IOException { public synchronized static void init() throws IOException {
if (configInstance != null) { if (configInstance != null) {
throw new IllegalStateException("Configuration is already loaded"); return;
} }
configInstance = loadConfig(); configInstance = loadConfig();

View File

@ -10,6 +10,7 @@ import com.tungsten.fclcore.fakefx.beans.Observable;
import com.tungsten.fclcore.fakefx.beans.property.ReadOnlyListProperty; import com.tungsten.fclcore.fakefx.beans.property.ReadOnlyListProperty;
import com.tungsten.fclcore.fakefx.beans.property.ReadOnlyListWrapper; import com.tungsten.fclcore.fakefx.beans.property.ReadOnlyListWrapper;
import com.tungsten.fclcore.fakefx.collections.ObservableList; import com.tungsten.fclcore.fakefx.collections.ObservableList;
import com.tungsten.fclcore.task.Schedulers;
import com.tungsten.fclcore.util.Logging; import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.gson.fakefx.factories.JavaFxPropertyTypeAdapterFactory; import com.tungsten.fclcore.util.gson.fakefx.factories.JavaFxPropertyTypeAdapterFactory;
import com.tungsten.fclcore.util.io.FileUtils; import com.tungsten.fclcore.util.io.FileUtils;
@ -27,10 +28,12 @@ public class Controllers {
private Controllers() { private Controllers() {
} }
private static final ObservableList<Controller> controllers = observableArrayList(controller -> new Observable[] { controller }); private static final ObservableList<Controller> controllers = observableArrayList(controller -> new Observable[]{controller});
private static final ReadOnlyListWrapper<Controller> controllersWrapper = new ReadOnlyListWrapper<>(controllers); private static final ReadOnlyListWrapper<Controller> controllersWrapper = new ReadOnlyListWrapper<>(controllers);
public static Controller DEFAULT_CONTROLLER; public static Controller DEFAULT_CONTROLLER;
private static final List<Runnable> CALLBACKS = new ArrayList<>();
public static void checkControllers() { public static void checkControllers() {
if (controllers.contains(null)) { if (controllers.contains(null)) {
controllers.remove(null); controllers.remove(null);
@ -92,12 +95,16 @@ public class Controllers {
public static void init() { public static void init() {
if (initialized) if (initialized)
throw new IllegalStateException("Already initialized"); return;
controllers.addAll(getControllersFromDisk()); controllers.addAll(getControllersFromDisk());
checkControllers(); checkControllers();
initialized = true; initialized = true;
CALLBACKS.forEach(callback -> {
Schedulers.androidUIThread().execute(callback);
});
CALLBACKS.clear();
} }
private static ArrayList<Controller> getControllersFromDisk() { private static ArrayList<Controller> getControllersFromDisk() {
@ -150,4 +157,12 @@ public class Controllers {
return controllers.stream().filter(it -> it.getName().equals(name)).findFirst().orElse(controllers.get(0)); return controllers.stream().filter(it -> it.getName().equals(name)).findFirst().orElse(controllers.get(0));
} }
public static void addCallback(Runnable callback) {
if (initialized) {
callback.run();
return;
}
CALLBACKS.add(callback);
}
} }

View File

@ -135,7 +135,7 @@ public final class Profiles {
static void init() { static void init() {
if (initialized) if (initialized)
throw new IllegalStateException("Already initialized"); return;
HashSet<String> names = new HashSet<>(); HashSet<String> names = new HashSet<>();
config().getConfigurations().forEach((name, profile) -> { config().getConfigurations().forEach((name, profile) -> {

View File

@ -19,6 +19,7 @@ package com.tungsten.fcl.setting;
import com.tungsten.fcl.game.FCLCacheRepository; import com.tungsten.fcl.game.FCLCacheRepository;
import com.tungsten.fclauncher.utils.FCLPath; import com.tungsten.fclauncher.utils.FCLPath;
import com.tungsten.fclcore.task.Schedulers;
import com.tungsten.fclcore.util.CacheRepository; import com.tungsten.fclcore.util.CacheRepository;
public final class Settings { public final class Settings {
@ -43,7 +44,7 @@ public final class Settings {
DownloadProviders.init(); DownloadProviders.init();
Accounts.init(); Accounts.init();
Profiles.init(); Profiles.init();
Controllers.init(); Schedulers.defaultScheduler().execute(Controllers::init);
AuthlibInjectorServers.init(); AuthlibInjectorServers.init();
CacheRepository.setInstance(FCLCacheRepository.REPOSITORY); CacheRepository.setInstance(FCLCacheRepository.REPOSITORY);

View File

@ -359,14 +359,15 @@ public final class VersionSetting implements Cloneable {
} }
public void checkController() { public void checkController() {
Controllers.checkControllers(); Controllers.addCallback(() -> {
Controllers.checkControllers();
Controller controller = Controllers.getControllers().stream()
.filter(it -> it.getName().equals(getController()))
.findFirst()
.orElse(Controllers.getControllers().get(0));
Controller controller = Controllers.getControllers().stream() setController(controller.getName());
.filter(it -> it.getName().equals(getController())) });
.findFirst()
.orElse(Controllers.getControllers().get(0));
setController(controller.getName());
} }
public void addPropertyChangedListener(InvalidationListener listener) { public void addPropertyChangedListener(InvalidationListener listener) {

View File

@ -18,7 +18,7 @@ public abstract class PageManager {
private final FCLUILayout parent; private final FCLUILayout parent;
private final int defaultPageId; private final int defaultPageId;
private final ArrayList<FCLCommonPage> allPages; public final ArrayList<FCLCommonPage> allPages;
private FCLCommonPage currentPage; private FCLCommonPage currentPage;
public PageManager (Context context, FCLUILayout parent, int defaultPageId, UIListener listener) { public PageManager (Context context, FCLUILayout parent, int defaultPageId, UIListener listener) {
@ -53,6 +53,10 @@ public abstract class PageManager {
public abstract ArrayList<FCLCommonPage> getAllPages(); public abstract ArrayList<FCLCommonPage> getAllPages();
public FCLCommonPage createPageById(int id){
return null;
}
public FCLCommonPage getPageById(int id) { public FCLCommonPage getPageById(int id) {
for (FCLCommonPage page : allPages) { for (FCLCommonPage page : allPages) {
if (page.getId() == id) { if (page.getId() == id) {
@ -66,7 +70,10 @@ public abstract class PageManager {
if (allPages.size() > 0) { if (allPages.size() > 0) {
FCLCommonPage targetPage = getPageById(id); FCLCommonPage targetPage = getPageById(id);
if (targetPage == null) { if (targetPage == null) {
throw new IllegalStateException("Wrong page id, this should not happen!"); targetPage = createPageById(id);
if (targetPage == null){
throw new IllegalStateException("Wrong page id, this should not happen!");
}
} }
if (currentPage != null && currentPage != targetPage) { if (currentPage != null && currentPage != targetPage) {
if (currentPage.isShowing()) { if (currentPage.isShowing()) {

View File

@ -1,169 +0,0 @@
package com.tungsten.fcl.ui;
import android.content.Context;
import com.tungsten.fcl.R;
import com.tungsten.fcl.ui.account.AccountUI;
import com.tungsten.fcl.ui.controller.ControllerUI;
import com.tungsten.fcl.ui.download.DownloadUI;
import com.tungsten.fcl.ui.manage.ManageUI;
import com.tungsten.fcl.ui.main.MainUI;
import com.tungsten.fcl.ui.multiplayer.MultiplayerUI;
import com.tungsten.fcl.ui.setting.SettingUI;
import com.tungsten.fcl.ui.version.VersionUI;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fcllibrary.component.ui.FCLBaseUI;
import com.tungsten.fcllibrary.component.ui.FCLCommonUI;
import com.tungsten.fcllibrary.component.view.FCLUILayout;
import java.util.logging.Level;
public class UIManager {
private static UIManager instance;
public static UIManager getInstance() {
if (instance == null) {
throw new IllegalStateException("UIManager not initialized!");
}
return instance;
}
private final Context context;
private final FCLUILayout parent;
private MainUI mainUI;
private AccountUI accountUI;
private VersionUI versionUI;
private ManageUI manageUI;
private DownloadUI downloadUI;
private ControllerUI controllerUI;
private MultiplayerUI multiplayerUI;
private SettingUI settingUI;
private FCLBaseUI[] allUIs;
private FCLBaseUI currentUI;
private int loadedUI = 0;
public UIManager(Context context, FCLUILayout parent) {
this.context = context;
this.parent = parent;
}
public void init(UIListener listener) {
if (instance != null) {
Logging.LOG.log(Level.WARNING, "UIManager already initialized!");
return;
}
instance = this;
mainUI = new MainUI(context, parent, R.layout.ui_main);
accountUI = new AccountUI(context, parent, R.layout.ui_account);
versionUI = new VersionUI(context, parent, R.layout.ui_version);
manageUI = new ManageUI(context, parent, R.layout.ui_manage);
downloadUI = new DownloadUI(context, parent, R.layout.ui_download);
controllerUI = new ControllerUI(context, parent, R.layout.ui_controller);
multiplayerUI = new MultiplayerUI(context, parent, R.layout.ui_multiplayer);
settingUI = new SettingUI(context, parent, R.layout.ui_setting);
allUIs = new FCLBaseUI[] {
mainUI,
accountUI,
versionUI,
manageUI,
downloadUI,
controllerUI,
multiplayerUI,
settingUI
};
for (FCLBaseUI ui : allUIs) {
((FCLCommonUI) ui).addLoadingCallback(() -> {
loadedUI++;
if (loadedUI == allUIs.length) {
listener.onLoad();
}
});
}
}
public Context getContext() {
return context;
}
public FCLUILayout getParent() {
return parent;
}
public MainUI getMainUI() {
return mainUI;
}
public AccountUI getAccountUI() {
return accountUI;
}
public VersionUI getVersionUI() {
return versionUI;
}
public ManageUI getManageUI() {
return manageUI;
}
public DownloadUI getDownloadUI() {
return downloadUI;
}
public ControllerUI getControllerUI() {
return controllerUI;
}
public MultiplayerUI getMultiplayerUI() {
return multiplayerUI;
}
public SettingUI getSettingUI() {
return settingUI;
}
public FCLBaseUI getCurrentUI() {
return currentUI;
}
public void switchUI(FCLBaseUI ui) {
for (FCLBaseUI baseUI : allUIs) {
if (ui == baseUI) {
if (currentUI != null) {
currentUI.onStop();
}
ui.onStart();
currentUI = ui;
break;
}
}
}
public void registerDefaultBackEvent(Runnable runnable) {
FCLBaseUI.setDefaultBackEvent(runnable);
}
public void onBackPressed() {
if (currentUI != null) {
currentUI.onBackPressed();
}
}
public void onPause() {
for (FCLBaseUI baseUI : allUIs) {
baseUI.onPause();
}
}
public void onResume() {
for (FCLBaseUI baseUI : allUIs) {
baseUI.onResume();
}
}
}

View File

@ -0,0 +1,98 @@
package com.tungsten.fcl.ui
import android.content.Context
import com.tungsten.fcl.R
import com.tungsten.fcl.ui.account.AccountUI
import com.tungsten.fcl.ui.controller.ControllerUI
import com.tungsten.fcl.ui.download.DownloadUI
import com.tungsten.fcl.ui.main.MainUI
import com.tungsten.fcl.ui.manage.ManageUI
import com.tungsten.fcl.ui.multiplayer.MultiplayerUI
import com.tungsten.fcl.ui.setting.SettingUI
import com.tungsten.fcl.ui.version.VersionUI
import com.tungsten.fclcore.util.Logging
import com.tungsten.fcllibrary.component.ui.FCLBaseUI
import com.tungsten.fcllibrary.component.ui.FCLCommonUI
import com.tungsten.fcllibrary.component.view.FCLUILayout
import java.util.logging.Level
class UIManager(val context: Context, val parent: FCLUILayout) {
companion object {
@JvmStatic
lateinit var instance: UIManager
}
private var initialized = false
lateinit var mainUI: MainUI
val accountUI: AccountUI by lazy { AccountUI(context, parent, R.layout.ui_account) }
val versionUI: VersionUI by lazy { VersionUI(context, parent, R.layout.ui_version) }
val manageUI: ManageUI by lazy { ManageUI(context, parent, R.layout.ui_manage) }
val downloadUI: DownloadUI by lazy { DownloadUI(context, parent, R.layout.ui_download) }
val controllerUI: ControllerUI by lazy { ControllerUI(context, parent, R.layout.ui_controller) }
val multiplayerUI: MultiplayerUI by lazy {
MultiplayerUI(
context,
parent,
R.layout.ui_multiplayer
)
}
val settingUI: SettingUI by lazy { SettingUI(context, parent, R.layout.ui_setting) }
private val allUIList = mutableListOf<FCLBaseUI>()
var currentUI: FCLBaseUI? = null
fun init(listener: UIListener) {
if (initialized) {
Logging.LOG.log(Level.WARNING, "UIManager already initialized!")
return
}
instance = this
mainUI = MainUI(context, parent, R.layout.ui_main)
allUIList.add(mainUI)
mainUI.addLoadingCallback {
listener.onLoad()
}
}
fun switchUI(ui: FCLCommonUI) {
var isFirstAdd = false
if (!allUIList.contains(ui)) {
isFirstAdd = true
allUIList.add(ui)
}
for (baseUI in allUIList) {
if (ui === baseUI) {
currentUI?.onStop()
if (isFirstAdd) {
ui.addLoadingCallback {
ui.onStart()
}
} else {
ui.onStart()
}
currentUI = ui
break
}
}
}
fun registerDefaultBackEvent(runnable: Runnable?) {
FCLBaseUI.setDefaultBackEvent(runnable)
}
fun onBackPressed() {
currentUI?.onBackPressed()
}
fun onPause() {
for (baseUI in allUIList) {
baseUI.onPause()
}
}
fun onResume() {
for (baseUI in allUIList) {
baseUI.onResume()
}
}
}

View File

@ -42,33 +42,14 @@ import com.tungsten.fcllibrary.component.view.FCLUILayout;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
public class ControllerUI extends FCLCommonUI implements View.OnClickListener { public class ControllerUI extends FCLCommonUI implements View.OnClickListener {
private final BooleanProperty refreshProperty = new SimpleBooleanProperty(false); private final BooleanProperty refreshProperty = new SimpleBooleanProperty(false);
private final ObjectProperty<Controller> selectedController = new SimpleObjectProperty<Controller>() { private ObjectProperty<Controller> selectedController;
{
Controllers.getControllers().addListener(onInvalidating(this::invalidated));
}
@Override
protected void invalidated() {
if (!Controllers.isInitialized()) return;
Controller controller = get();
if (Controllers.getControllers().isEmpty()) {
if (controller != null) {
set(null);
}
} else {
if (!Controllers.getControllers().contains(controller)) {
set(Controllers.getControllers().get(0));
}
}
}
};
public Controller getSelectedController() { public Controller getSelectedController() {
return selectedController.get(); return selectedController.get();
@ -94,8 +75,32 @@ public class ControllerUI extends FCLCommonUI implements View.OnClickListener {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Controllers.addCallback(this::init);
}
if (Controllers.controllersProperty().size() != 0) { private void init() {
selectedController = new SimpleObjectProperty<Controller>() {
{
Controllers.getControllers().addListener(onInvalidating(this::invalidated));
}
@Override
protected void invalidated() {
if (!Controllers.isInitialized()) return;
Controller controller = get();
if (Controllers.getControllers().isEmpty()) {
if (controller != null) {
set(null);
}
} else {
if (!Controllers.getControllers().contains(controller)) {
set(Controllers.getControllers().get(0));
}
}
}
};
if (!Controllers.controllersProperty().isEmpty()) {
selectedController.set(Controllers.controllersProperty().get(0)); selectedController.set(Controllers.controllersProperty().get(0));
} else { } else {
selectedController.set(Controllers.DEFAULT_CONTROLLER); selectedController.set(Controllers.DEFAULT_CONTROLLER);

View File

@ -1,80 +0,0 @@
package com.tungsten.fcl.ui.manage;
import android.content.Context;
import com.tungsten.fcl.R;
import com.tungsten.fcl.setting.Profile;
import com.tungsten.fcl.ui.PageManager;
import com.tungsten.fcl.ui.UIListener;
import com.tungsten.fcl.ui.download.DownloadPage;
import com.tungsten.fcl.ui.download.InstallVersionPage;
import com.tungsten.fcllibrary.component.ui.FCLCommonPage;
import com.tungsten.fcllibrary.component.view.FCLUILayout;
import java.util.ArrayList;
public class ManagePageManager extends PageManager {
public static final int PAGE_ID_MANAGE_MANAGE = 15000;
public static final int PAGE_ID_MANAGE_SETTING = 15001;
public static final int PAGE_ID_MANAGE_INSTALL = 15002;
public static final int PAGE_ID_MANAGE_MOD = 15003;
public static final int PAGE_ID_MANAGE_WORLD = 15004;
private static ManagePageManager instance;
private ManagePage managePage;
private VersionSettingPage versionSettingPage;
private InstallerListPage installerListPage;
private ModListPage modListPage;
private WorldListPage worldListPage;
public static ManagePageManager getInstance() {
if (instance == null) {
throw new IllegalStateException("ManagePageManager not initialized!");
}
return instance;
}
public ManagePageManager(Context context, FCLUILayout parent, int defaultPageId, UIListener listener) {
super(context, parent, defaultPageId, listener);
instance = this;
}
@Override
public void init(UIListener listener) {
managePage = new ManagePage(getContext(), PAGE_ID_MANAGE_MANAGE, getParent(), R.layout.page_manage);
versionSettingPage = new VersionSettingPage(getContext(), PAGE_ID_MANAGE_SETTING, getParent(), R.layout.page_version_setting, false);
installerListPage = new InstallerListPage(getContext(), PAGE_ID_MANAGE_INSTALL, getParent(), R.layout.page_installer_list);
modListPage = new ModListPage(getContext(), PAGE_ID_MANAGE_MOD, getParent(), R.layout.page_mod_list);
worldListPage = new WorldListPage(getContext(), PAGE_ID_MANAGE_WORLD, getParent(), R.layout.page_world_list);
if (listener != null) {
listener.onLoad();
}
}
@Override
public ArrayList<FCLCommonPage> getAllPages() {
ArrayList<FCLCommonPage> pages = new ArrayList<>();
pages.add(managePage);
pages.add(versionSettingPage);
pages.add(installerListPage);
pages.add(modListPage);
pages.add(worldListPage);
return pages;
}
public void loadVersion(Profile profile, String version) {
managePage.loadVersion(profile, version);
versionSettingPage.loadVersion(profile, version);
installerListPage.loadVersion(profile, version);
modListPage.loadVersion(profile, version);
worldListPage.loadVersion(profile, version);
}
public void onRunDirectoryChange(Profile profile, String version) {
modListPage.loadVersion(profile, version);
worldListPage.loadVersion(profile, version);
}
}

View File

@ -0,0 +1,109 @@
package com.tungsten.fcl.ui.manage
import android.content.Context
import com.tungsten.fcl.R
import com.tungsten.fcl.setting.Profile
import com.tungsten.fcl.ui.PageManager
import com.tungsten.fcl.ui.UIListener
import com.tungsten.fcl.ui.manage.ManageUI.VersionLoadable
import com.tungsten.fcllibrary.component.ui.FCLCommonPage
import com.tungsten.fcllibrary.component.view.FCLUILayout
class ManagePageManager(
context: Context,
parent: FCLUILayout,
defaultPageId: Int,
val listener: UIListener?
) : PageManager(context, parent, defaultPageId, listener) {
companion object {
@JvmStatic
var instance: ManagePageManager? = null
const val PAGE_ID_MANAGE_MANAGE: Int = 15000
const val PAGE_ID_MANAGE_SETTING: Int = 15001
const val PAGE_ID_MANAGE_INSTALL: Int = 15002
const val PAGE_ID_MANAGE_MOD: Int = 15003
const val PAGE_ID_MANAGE_WORLD: Int = 15004
}
var profile: Profile? = null
var version: String? = null
lateinit var managePage: ManagePage
val versionSettingPage: VersionSettingPage by lazy {
VersionSettingPage(
context,
PAGE_ID_MANAGE_SETTING,
parent,
R.layout.page_version_setting,
false
)
}
val installerListPage: InstallerListPage by lazy {
InstallerListPage(
context,
PAGE_ID_MANAGE_INSTALL,
parent,
R.layout.page_installer_list
)
}
val modListPage: ModListPage by lazy {
ModListPage(
context,
PAGE_ID_MANAGE_MOD,
parent,
R.layout.page_mod_list
)
}
val worldListPage: WorldListPage by lazy {
WorldListPage(
context,
PAGE_ID_MANAGE_WORLD,
parent,
R.layout.page_world_list
)
}
init {
instance = this
}
override fun init(listener: UIListener?) {
managePage =
ManagePage(context, PAGE_ID_MANAGE_MANAGE, parent, R.layout.page_manage)
listener?.onLoad()
}
override fun getAllPages(): ArrayList<FCLCommonPage> {
return ArrayList<FCLCommonPage>().apply {
add(managePage)
}
}
override fun createPageById(id: Int): FCLCommonPage? {
val page: FCLCommonPage? = when (id) {
PAGE_ID_MANAGE_SETTING -> versionSettingPage
PAGE_ID_MANAGE_INSTALL -> installerListPage
PAGE_ID_MANAGE_MOD -> modListPage
PAGE_ID_MANAGE_WORLD -> worldListPage
else -> null
}
if (page != null) {
allPages.add(page)
(page as VersionLoadable).loadVersion(profile, version)
}
return page
}
fun loadVersion(profile: Profile?, version: String?) {
this.profile = profile
this.version = version
allPages.forEach {
(it as VersionLoadable).loadVersion(profile, version)
}
}
fun onRunDirectoryChange(profile: Profile?, version: String?) {
modListPage.loadVersion(profile, version)
worldListPage.loadVersion(profile, version)
}
}

View File

@ -47,28 +47,28 @@ public class ManageUI extends FCLMultiPageUI implements TabLayout.OnTabSelectedL
container = findViewById(R.id.container); container = findViewById(R.id.container);
tabLayout.addOnTabSelectedListener(this); tabLayout.addOnTabSelectedListener(this);
container.post(this::initPages); initPages();
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST)); listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
// If we jumped to game list page and deleted this version addLoadingCallback(()->{
// and back to this page, we should return to main page. // If we jumped to game list page and deleted this version
if (!getProfile().getRepository().isLoaded() || // and back to this page, we should return to main page.
!getProfile().getRepository().hasVersion(getVersion())) { if (!getProfile().getRepository().isLoaded() ||
Schedulers.androidUIThread().execute(() -> { !getProfile().getRepository().hasVersion(getVersion())) {
if (isShowing()) { Schedulers.androidUIThread().execute(() -> {
MainActivity.getInstance().refreshMenuView(null); if (isShowing()) {
MainActivity.getInstance().bind.home.setSelected(true); MainActivity.getInstance().refreshMenuView(null);
} MainActivity.getInstance().bind.home.setSelected(true);
}); }
return; });
} return;
}
loadVersion(getVersion(), getProfile()); loadVersion(getVersion(), getProfile());
});
} }
@Override @Override

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
</shape>

View File

@ -261,6 +261,16 @@
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<com.tungsten.fcllibrary.component.view.FCLProgressBar
android:layout_marginTop="10dp"
android:id="@+id/version_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
app:layout_constraintTop_toBottomOf="@id/view2"
app:layout_constraintStart_toStartOf="parent"/>
<com.tungsten.fcllibrary.component.view.FCLButton <com.tungsten.fcllibrary.component.view.FCLButton
android:id="@+id/execute_jar" android:id="@+id/execute_jar"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -10,14 +10,26 @@ import java.util.List;
public class LibFilter { public class LibFilter {
private static final String ASM_ALL_5_2_STRING = private static final String ASM_ALL_5_0_4_STRING = "{\n" +
"{\n" + " \"name\": \"org.ow2.asm:asm-all:5.0.4\",\n" +
" \"name\": \"org.ow2.asm:asm-all:5.2\"\n" + " \"downloads\": {\n" +
"}"; " \"artifact\": {\n" +
private static final String JNA_5_13_STRING = " \"path\": \"org/ow2/asm/asm-all/5.0.4/asm-all-5.0.4.jar\",\n" +
"{\n" + " \"sha1\": \"e6244859997b3d4237a552669279780876228909\",\n" +
" \"name\": \"net.java.dev.jna:jna:5.13.0\"\n" + " \"url\": \"https://repo1.maven.org/maven2/org/ow2/asm/asm-all/5.0.4/asm-all-5.0.4.jar\"\n" +
"}"; " }\n" +
" }\n" +
" }";
private static final String JNA_5_13_STRING = "{\n" +
" \"name\": \"net.java.dev.jna:jna:5.13.0\",\n" +
" \"downloads\": {\n" +
" \"artifact\": {\n" +
" \"path\": \"net/java/dev/jna/jna/5.13.0/jna-5.13.0.jar\",\n" +
" \"sha1\": \"1200e7ebeedbe0d10062093f32925a912020e747\",\n" +
" \"url\": \"https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.13.0/jna-5.13.0.jar\"\n" +
" }\n" +
" }\n" +
" }";
private static final String OSHI_6_3_STRING = "{\n" + private static final String OSHI_6_3_STRING = "{\n" +
" \"name\": \"com.github.oshi:oshi-core:6.3.0\",\n" + " \"name\": \"com.github.oshi:oshi-core:6.3.0\",\n" +
" \"downloads\": {\n" + " \"downloads\": {\n" +
@ -29,7 +41,7 @@ public class LibFilter {
" }\n" + " }\n" +
" }"; " }";
private static final Library ASM_ALL_5_2 = GSON.fromJson(ASM_ALL_5_2_STRING, Library.class); private static final Library ASM_ALL_5_0_4 = GSON.fromJson(ASM_ALL_5_0_4_STRING, Library.class);
private static final Library JNA_5_13 = GSON.fromJson(JNA_5_13_STRING, Library.class); private static final Library JNA_5_13 = GSON.fromJson(JNA_5_13_STRING, Library.class);
private static final Library OSHI_6_3 = GSON.fromJson(OSHI_6_3_STRING, Library.class); private static final Library OSHI_6_3 = GSON.fromJson(OSHI_6_3_STRING, Library.class);
@ -42,8 +54,8 @@ public class LibFilter {
for (Library library : libraries) { for (Library library : libraries) {
if (!library.getName().contains("org.lwjgl") && !library.getName().contains("jinput-platform") && !library.getName().contains("twitch-platform")) { if (!library.getName().contains("org.lwjgl") && !library.getName().contains("jinput-platform") && !library.getName().contains("twitch-platform")) {
String[] version = library.getName().split(":")[2].split("\\."); String[] version = library.getName().split(":")[2].split("\\.");
if (library.getArtifactId().equals("asm-all") && library.getVersion().equals("4.1")) { if (library.getArtifactId().equals("asm-all") && Integer.parseInt(version[0]) < 5) {
newLibraries.add(ASM_ALL_5_2); newLibraries.add(ASM_ALL_5_0_4);
} else if (library.getName().startsWith("net.java.dev.jna:jna:")) { } else if (library.getName().startsWith("net.java.dev.jna:jna:")) {
if (Integer.parseInt(version[0]) >= 5 && Integer.parseInt(version[1]) >= 13) { if (Integer.parseInt(version[0]) >= 5 && Integer.parseInt(version[1]) >= 13) {
newLibraries.add(library); newLibraries.add(library);

View File

@ -17,11 +17,14 @@ public abstract class FCLCommonUI extends FCLBaseUI {
private UILoadingCallback callback; private UILoadingCallback callback;
private boolean init = false;
public FCLCommonUI(Context context, FCLUILayout parent, @LayoutRes int id) { public FCLCommonUI(Context context, FCLUILayout parent, @LayoutRes int id) {
super(context); super(context);
this.parent = parent; this.parent = parent;
setContentView(id, () -> { setContentView(id, () -> {
onCreate(); onCreate();
init = true;
if (callback != null) { if (callback != null) {
callback.onLoad(); callback.onLoad();
} }
@ -78,5 +81,8 @@ public abstract class FCLCommonUI extends FCLBaseUI {
public void addLoadingCallback(UILoadingCallback callback) { public void addLoadingCallback(UILoadingCallback callback) {
this.callback = callback; this.callback = callback;
if (init) {
callback.onLoad();
}
} }
} }

View File

@ -1,20 +1,20 @@
[ [
{ {
"type": "release", "type": "release",
"versionCode": 1177, "versionCode": 1178,
"versionName": "1.1.7.7", "versionName": "1.1.7.8",
"date": "2024.08.27", "date": "2024.09.04",
"description": [ "description": [
{ {
"lang": "en", "lang": "en",
"text": "1.Fix oshi core.\n2.Fix windows scale.\n3.Add custom cursor.\n4.Fix some bug.\nSee github for more details." "text": "1. Fix bug that triggers error return when deleting text\n2. Fix bug that Microsoft login cannot be used when the language is not Chinese or English\n3. Update HolyGL4ES\n4. Add the function of adjusting cursor offset in the game"
}, },
{ {
"lang": "zh_CN", "lang": "zh_CN",
"text": "FCL 1.1.7.7更新内容(若启动器内下载缓慢请使用网盘下载,64位的请下载arm64不知道该下哪个的请下载all\n1.修复oshi core\n2.修复窗口缩放\n3.添加自定义光标\n4.添加更多动画效果" "text": "FCL 1.1.7.8更新内容(若启动器内下载缓慢请使用网盘下载,64位的请下载arm64不知道该下哪个的请下载all\n1.修复删除文本时错误触发返回的bug\n2.修复语言为非中文或英文时无法使用微软登录的bug\n3.更新HolyGL4ES\n4.增加游戏内光标偏移调节的功能"
} }
], ],
"netdiskUrl": "https://pan.quark.cn/s/5fac965e464b", "netdiskUrl": "https://pan.quark.cn/s/5fa82d8bb512",
"url": "https://github.com/FCL-Team/FoldCraftLauncher/releases/download/1.1.7.7/FCL-release-1.1.7.7-all.apk" "url": "https://github.com/FCL-Team/FoldCraftLauncher/releases/download/1.1.7.8/FCL-release-1.1.7.8-all.apk"
} }
] ]