Added DownloadController Added FloatingNotificationpull/46/head
@ -0,0 +1,216 @@ | |||||
using IPA.Config; | |||||
using IPA.Updating.BeatMods; | |||||
using System; | |||||
using System.Collections; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using UnityEngine; | |||||
using static IPA.Updating.BeatMods.Updater; | |||||
namespace BSIPA_ModList | |||||
{ | |||||
internal class DownloadObject | |||||
{ | |||||
public enum States | |||||
{ | |||||
ToDownload, Downloading, Installing, Failed, Completed | |||||
} | |||||
public DependencyObject Mod; | |||||
public Sprite Icon; | |||||
public States State = States.ToDownload; | |||||
public double Progress = 0; | |||||
} | |||||
internal class DownloadController : MonoBehaviour | |||||
{ | |||||
private static DownloadController _instance; | |||||
public static DownloadController Instance | |||||
{ | |||||
get | |||||
{ | |||||
if (_instance == null) | |||||
_instance = Create(); | |||||
return _instance; | |||||
} | |||||
} | |||||
public static DownloadController Create() | |||||
{ | |||||
var inst = new GameObject("BSIPA Modlist Download Controller").AddComponent<DownloadController>(); | |||||
if (SelfConfig.SelfConfigRef.Value.Updates.AutoCheckUpdates) | |||||
inst.StartCoroutine(inst.StartUpdateCheck()); | |||||
return inst; | |||||
} | |||||
private IEnumerator StartUpdateCheck() | |||||
{ | |||||
yield return null; | |||||
CheckForUpdates(); | |||||
} | |||||
private readonly List<DownloadObject> downloads = new List<DownloadObject>(); | |||||
private readonly Dictionary<DependencyObject, DownloadObject> lookup = new Dictionary<DependencyObject, DownloadObject>(); | |||||
internal IReadOnlyList<DownloadObject> Downloads => downloads; | |||||
public event Action OnCheckForUpdates; | |||||
public event Action<int> OnCheckForUpdatesComplete; | |||||
public event Action OnDownloadStateChanged; | |||||
public event Action OnDownloaderListChanged; | |||||
private enum States | |||||
{ | |||||
Start, Checking, UpdatesFound, Downloading, Done | |||||
} | |||||
private States _state = States.Start; | |||||
private States State | |||||
{ | |||||
get => _state; | |||||
set | |||||
{ | |||||
_state = value; | |||||
OnDownloadStateChanged?.Invoke(); | |||||
} | |||||
} | |||||
public bool CanCheck => State == States.Start || State == States.Done; | |||||
public bool CanDownload => State == States.UpdatesFound; | |||||
public bool CanReset => State == States.UpdatesFound; | |||||
public bool IsChecking => State == States.Checking; | |||||
public bool IsDownloading => State == States.Downloading; | |||||
public bool IsDone => State == States.Done; | |||||
public void Awake() => DontDestroyOnLoad(this); | |||||
public void CheckForUpdates() | |||||
{ | |||||
if (!CanCheck) | |||||
throw new InvalidOperationException("Invalid state for CheckForUpdates to be called"); | |||||
State = States.Checking; | |||||
OnCheckForUpdates?.Invoke(); | |||||
Updater.Instance.CheckForUpdates(UpdateCheckComplete); | |||||
} | |||||
public void ResetCheck(bool resetCache = false) | |||||
{ | |||||
if (!CanReset) | |||||
throw new InvalidOperationException("Invalid state for ResetCheck to be called"); | |||||
Clear(); | |||||
State = States.Start; | |||||
if (resetCache) | |||||
ResetRequestCache(); | |||||
} | |||||
private void Clear() | |||||
{ | |||||
downloads.Clear(); | |||||
lookup.Clear(); | |||||
OnDownloaderListChanged?.Invoke(); | |||||
} | |||||
private void Add(DownloadObject obj) | |||||
{ | |||||
downloads.Add(obj); | |||||
lookup.Add(obj.Mod, obj); | |||||
} | |||||
private void Remove(DependencyObject obj) | |||||
{ | |||||
downloads.Remove(lookup[obj]); | |||||
lookup.Remove(obj); | |||||
OnDownloaderListChanged?.Invoke(); | |||||
} | |||||
private void UpdateCheckComplete(List<DependencyObject> found) | |||||
{ | |||||
State = States.UpdatesFound; | |||||
OnCheckForUpdatesComplete?.Invoke(found.Count); | |||||
foreach (var dep in found) | |||||
Add(new DownloadObject | |||||
{ | |||||
Mod = dep, | |||||
Icon = Utilities.GetIcon(dep.LocalPluginMeta?.Metadata), | |||||
State = DownloadObject.States.ToDownload, | |||||
Progress = 0 | |||||
}); | |||||
OnDownloaderListChanged?.Invoke(); | |||||
if (SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate) | |||||
StartDownloads(); | |||||
} | |||||
public void StartDownloads() | |||||
{ | |||||
if (!CanDownload) | |||||
throw new InvalidOperationException("Invalid state for StartDownloads to be called"); | |||||
State = States.Downloading; | |||||
Updater.Instance.StartDownload(downloads.Select(d => d.Mod), _DownloadStart, _DownloadProgress, | |||||
_DownloadFailed, _DownloadFinished, _InstallFailed, _InstallFinished); | |||||
if (downloads.Count == 0) | |||||
OnAllDownloadsCompleted(); | |||||
} | |||||
private void _DownloadStart(DependencyObject obj) | |||||
{ | |||||
var dl = lookup[obj]; | |||||
dl.Progress = 0; | |||||
dl.State = DownloadObject.States.Downloading; | |||||
} | |||||
private void _DownloadProgress(DependencyObject obj, long totalBytes, long currentBytes, double progress) | |||||
{ | |||||
lookup[obj].Progress = progress; | |||||
} | |||||
private void _DownloadFailed(DependencyObject obj, string error) | |||||
{ | |||||
lookup[obj].State = DownloadObject.States.Failed; | |||||
} | |||||
private void _DownloadFinished(DependencyObject obj) | |||||
{ | |||||
lookup[obj].State = DownloadObject.States.Installing; | |||||
} | |||||
private void _InstallFailed(DependencyObject obj, Exception error) | |||||
{ | |||||
lookup[obj].State = DownloadObject.States.Failed; | |||||
} | |||||
private void _InstallFinished(DependencyObject obj, bool didError) | |||||
{ | |||||
if (!didError) | |||||
lookup[obj].State = DownloadObject.States.Completed; | |||||
StartCoroutine(RemoveModFromList(obj)); | |||||
} | |||||
private IEnumerator RemoveModFromList(DependencyObject obj) | |||||
{ | |||||
yield return new WaitForSeconds(0.25f); | |||||
Remove(obj); | |||||
if (downloads.Count == 0) | |||||
OnAllDownloadsCompleted(); | |||||
} | |||||
private void OnAllDownloadsCompleted() | |||||
{ | |||||
State = States.Done; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,98 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using TMPro; | |||||
using System.Threading.Tasks; | |||||
using IPA.Updating.BeatMods; | |||||
using UnityEngine; | |||||
namespace BSIPA_ModList.UI | |||||
{ | |||||
// originally ripped verbatim from Andruzz's BeatSaverDownloader | |||||
internal class DownloadProgressCell : LevelListTableCell | |||||
{ | |||||
private DownloadObject mod; | |||||
protected override void Awake() | |||||
{ | |||||
base.Awake(); | |||||
} | |||||
public void Init(DownloadObject mod) | |||||
{ | |||||
Destroy(GetComponent<LevelListTableCell>()); | |||||
reuseIdentifier = "DownloadCell"; | |||||
this.mod = mod; | |||||
_authorText = GetComponentsInChildren<TextMeshProUGUI>().First(x => x.name == "Author"); | |||||
_authorText.enableWordWrapping = false; | |||||
_authorText.overflowMode = TextOverflowModes.Overflow; | |||||
_songNameText = GetComponentsInChildren<TextMeshProUGUI>().First(x => x.name == "SongName"); | |||||
_songNameText.enableWordWrapping = false; | |||||
_songNameText.overflowMode = TextOverflowModes.Overflow; | |||||
_coverImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "CoverImage"); | |||||
_bgImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "BG"); | |||||
_highlightImage = GetComponentsInChildren<UnityEngine.UI.Image>().First(x => x.name == "Highlight"); | |||||
_beatmapCharacteristicAlphas = new float[0]; | |||||
_beatmapCharacteristicImages = new UnityEngine.UI.Image[0]; | |||||
_bought = true; | |||||
foreach (var icon in GetComponentsInChildren<UnityEngine.UI.Image>().Where(x => x.name.StartsWith("LevelTypeIcon"))) | |||||
Destroy(icon.gameObject); | |||||
_songNameText.text = $"{mod.Mod.Name} <size=60%>v{mod.Mod.ResolvedVersion}</size>"; | |||||
_authorText.text = ""; | |||||
_coverImage.sprite = mod.Icon; | |||||
_bgImage.enabled = true; | |||||
_bgImage.sprite = Sprite.Create(new Texture2D(1, 1), new Rect(0, 0, 1, 1), Vector2.one / 2f); | |||||
_bgImage.type = UnityEngine.UI.Image.Type.Filled; | |||||
_bgImage.fillMethod = UnityEngine.UI.Image.FillMethod.Horizontal; | |||||
Update(); | |||||
} | |||||
public void Update() | |||||
{ | |||||
_bgImage.enabled = true; | |||||
switch (mod.State) | |||||
{ | |||||
case DownloadObject.States.ToDownload: | |||||
{ | |||||
_bgImage.color = new Color(1f, 1f, 1f, 0.35f); | |||||
_bgImage.fillAmount = 0; | |||||
} | |||||
break; | |||||
case DownloadObject.States.Downloading: | |||||
{ | |||||
_bgImage.color = new Color(1f, 1f, 1f, 0.35f); | |||||
_bgImage.fillAmount = (float)mod.Progress; | |||||
} | |||||
break; | |||||
case DownloadObject.States.Installing: | |||||
{ | |||||
_bgImage.color = new Color(0f, 1f, 1f, 0.35f); | |||||
_bgImage.fillAmount = 1f; | |||||
} | |||||
break; | |||||
case DownloadObject.States.Completed: | |||||
{ | |||||
_bgImage.color = new Color(0f, 1f, 0f, 0.35f); | |||||
_bgImage.fillAmount = 1f; | |||||
} | |||||
break; | |||||
case DownloadObject.States.Failed: | |||||
{ | |||||
_bgImage.color = new Color(1f, 0f, 0f, 0.35f); | |||||
_bgImage.fillAmount = 1f; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,218 @@ | |||||
using CustomUI.BeatSaber; | |||||
using System.Collections; | |||||
using TMPro; | |||||
using UnityEngine; | |||||
using UnityEngine.SceneManagement; | |||||
using UnityEngine.UI; | |||||
namespace BSIPA_ModList.UI | |||||
{ | |||||
internal class FloatingNotification : MonoBehaviour | |||||
{ | |||||
private Canvas _canvas; | |||||
private TMP_Text _authorNameText; | |||||
private TMP_Text _pluginNameText; | |||||
private TMP_Text _headerText; | |||||
private Image _loadingBackg; | |||||
private Image _loadingBar; | |||||
private static readonly Vector3 Position = new Vector3(2.3f, 2.3f, 1.35f); | |||||
private static readonly Vector3 Rotation = new Vector3(0, 60, 0); | |||||
private static readonly Vector3 Scale = new Vector3(0.01f, 0.01f, 0.01f); | |||||
private static readonly Vector2 CanvasSize = new Vector2(100, 50); | |||||
private const string AuthorNameText = "BSIPA"; | |||||
private const float AuthorNameFontSize = 7f; | |||||
private static readonly Vector2 AuthorNamePosition = new Vector2(10, 31); | |||||
private const string PluginNameText = "Mod Updater"; | |||||
private const float PluginNameFontSize = 9f; | |||||
private static readonly Vector2 PluginNamePosition = new Vector2(10, 23); | |||||
private static readonly Vector2 HeaderPosition = new Vector2(10, 15); | |||||
private static readonly Vector2 HeaderSize = new Vector2(100, 20); | |||||
private const string HeaderText = "Checking for updates..."; | |||||
private const float HeaderFontSize = 15f; | |||||
private static readonly Vector2 LoadingBarSize = new Vector2(100, 10); | |||||
private static readonly Color BackgroundColor = new Color(0, 0, 0, 0.2f); | |||||
private bool _showingMessage; | |||||
public static FloatingNotification Create() | |||||
{ | |||||
return new GameObject("Mod List Floating Notification").AddComponent<FloatingNotification>(); | |||||
} | |||||
public void ShowMessage(string message, float time) | |||||
{ | |||||
StopAllCoroutines(); | |||||
_showingMessage = true; | |||||
_headerText.text = message; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
_canvas.enabled = true; | |||||
StartCoroutine(DisableCanvasRoutine(time)); | |||||
} | |||||
public void ShowMessage(string message) | |||||
{ | |||||
StopAllCoroutines(); | |||||
_showingMessage = true; | |||||
_headerText.text = message; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
_canvas.enabled = true; | |||||
} | |||||
protected void OnEnable() | |||||
{ | |||||
SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged; | |||||
DownloadController.Instance.OnDownloadStateChanged += DownloaderStateChanged; | |||||
DownloadController.Instance.OnCheckForUpdates += CheckForUpdatesStart; | |||||
DownloadController.Instance.OnCheckForUpdatesComplete += CheckForUpdatesDone; | |||||
} | |||||
protected void OnDisable() | |||||
{ | |||||
SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged; | |||||
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged; | |||||
DownloadController.Instance.OnCheckForUpdates -= CheckForUpdatesStart; | |||||
DownloadController.Instance.OnCheckForUpdatesComplete -= CheckForUpdatesDone; | |||||
} | |||||
private void CheckForUpdatesStart() | |||||
{ | |||||
_showingMessage = false; | |||||
_headerText.text = HeaderText; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
_canvas.enabled = true; | |||||
} | |||||
private void CheckForUpdatesDone(int count) | |||||
{ | |||||
_showingMessage = false; | |||||
_headerText.text = $"{count} updates found"; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
_canvas.enabled = true; | |||||
StartCoroutine(DisableCanvasRoutine(5f)); | |||||
} | |||||
private void SceneManagerOnActiveSceneChanged(Scene oldScene, Scene newScene) | |||||
{ | |||||
if (newScene.name == "MenuCore") | |||||
{ | |||||
if (_showingMessage) | |||||
{ | |||||
_canvas.enabled = true; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
_canvas.enabled = false; | |||||
} | |||||
} | |||||
private void DownloaderStateChanged() | |||||
{ | |||||
if (DownloadController.Instance.IsDownloading) | |||||
{ | |||||
StopAllCoroutines(); | |||||
_showingMessage = false; | |||||
_headerText.text = "Downloading updates..."; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
_canvas.enabled = true; | |||||
} | |||||
if (DownloadController.Instance.IsDone) | |||||
{ | |||||
_showingMessage = false; | |||||
_headerText.text = "Updates complete"; | |||||
_loadingBar.enabled = false; | |||||
_loadingBackg.enabled = false; | |||||
StartCoroutine(DisableCanvasRoutine(5f)); | |||||
} | |||||
} | |||||
private IEnumerator DisableCanvasRoutine(float time) | |||||
{ | |||||
yield return new WaitForSecondsRealtime(time); | |||||
_canvas.enabled = false; | |||||
_showingMessage = false; | |||||
} | |||||
private static FloatingNotification instance; | |||||
protected void Awake() | |||||
{ | |||||
if (instance != null) | |||||
{ | |||||
Destroy(this); | |||||
return; | |||||
} | |||||
instance = this; | |||||
gameObject.transform.position = Position; | |||||
gameObject.transform.eulerAngles = Rotation; | |||||
gameObject.transform.localScale = Scale; | |||||
_canvas = gameObject.AddComponent<Canvas>(); | |||||
_canvas.renderMode = RenderMode.WorldSpace; | |||||
_canvas.enabled = false; | |||||
var rectTransform = _canvas.transform as RectTransform; | |||||
rectTransform.sizeDelta = CanvasSize; | |||||
_authorNameText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, AuthorNameText, AuthorNamePosition); | |||||
rectTransform = _authorNameText.transform as RectTransform; | |||||
rectTransform.SetParent(_canvas.transform, false); | |||||
rectTransform.anchoredPosition = AuthorNamePosition; | |||||
rectTransform.sizeDelta = HeaderSize; | |||||
_authorNameText.text = AuthorNameText; | |||||
_authorNameText.fontSize = AuthorNameFontSize; | |||||
_pluginNameText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, PluginNameText, PluginNamePosition); | |||||
rectTransform = _pluginNameText.transform as RectTransform; | |||||
rectTransform.SetParent(_canvas.transform, false); | |||||
rectTransform.sizeDelta = HeaderSize; | |||||
rectTransform.anchoredPosition = PluginNamePosition; | |||||
_pluginNameText.text = PluginNameText; | |||||
_pluginNameText.fontSize = PluginNameFontSize; | |||||
_headerText = BeatSaberUI.CreateText(_canvas.transform as RectTransform, HeaderText, HeaderPosition); | |||||
rectTransform = _headerText.transform as RectTransform; | |||||
rectTransform.SetParent(_canvas.transform, false); | |||||
rectTransform.anchoredPosition = HeaderPosition; | |||||
rectTransform.sizeDelta = HeaderSize; | |||||
_headerText.text = HeaderText; | |||||
_headerText.fontSize = HeaderFontSize; | |||||
_loadingBackg = new GameObject("Background").AddComponent<Image>(); | |||||
rectTransform = _loadingBackg.transform as RectTransform; | |||||
rectTransform.SetParent(_canvas.transform, false); | |||||
rectTransform.sizeDelta = LoadingBarSize; | |||||
_loadingBackg.color = BackgroundColor; | |||||
_loadingBar = new GameObject("Loading Bar").AddComponent<Image>(); | |||||
rectTransform = _loadingBar.transform as RectTransform; | |||||
rectTransform.SetParent(_canvas.transform, false); | |||||
rectTransform.sizeDelta = LoadingBarSize; | |||||
var tex = Texture2D.whiteTexture; | |||||
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f, 100, 1); | |||||
_loadingBar.sprite = sprite; | |||||
_loadingBar.type = Image.Type.Filled; | |||||
_loadingBar.fillMethod = Image.FillMethod.Horizontal; | |||||
_loadingBar.color = new Color(1, 1, 1, 0.5f); | |||||
DontDestroyOnLoad(gameObject); | |||||
} | |||||
/*private void Update() | |||||
{ | |||||
if (!_canvas.enabled) return; | |||||
_loadingBar.fillAmount = SongLoader.LoadingProgress; | |||||
}*/ | |||||
} | |||||
} |
@ -0,0 +1,150 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using CustomUI.BeatSaber; | |||||
using CustomUI.Utilities; | |||||
using HMUI; | |||||
using IPA.Updating.BeatMods; | |||||
using TMPro; | |||||
using UnityEngine; | |||||
using UnityEngine.UI; | |||||
using VRUI; | |||||
namespace BSIPA_ModList.UI.ViewControllers | |||||
{ | |||||
// originally ripped verbatim from Andruzz's BeatSaverDownloader | |||||
internal class DownloadProgressViewController : VRUIViewController, TableView.IDataSource | |||||
{ | |||||
private TextMeshProUGUI _titleText; | |||||
private Button _checkForUpdates; | |||||
private Button _downloadUpdates; | |||||
private TableView _currentlyUpdatingTableView; | |||||
private LevelListTableCell _songListTableCellInstance; | |||||
private Button _pageUpButton; | |||||
private Button _pageDownButton; | |||||
protected override void DidActivate(bool firstActivation, ActivationType type) | |||||
{ | |||||
if (firstActivation && type == ActivationType.AddedToHierarchy) | |||||
{ | |||||
DownloadController.Instance.OnDownloaderListChanged -= Refresh; | |||||
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged; | |||||
_songListTableCellInstance = Resources.FindObjectsOfTypeAll<LevelListTableCell>().First(x => (x.name == "LevelListTableCell")); | |||||
_titleText = BeatSaberUI.CreateText(rectTransform, "DOWNLOAD QUEUE", new Vector2(0f, 35f)); | |||||
_titleText.alignment = TextAlignmentOptions.Top; | |||||
_titleText.fontSize = 6f; | |||||
_pageUpButton = Instantiate(Resources.FindObjectsOfTypeAll<Button>().Last(x => (x.name == "PageUpButton")), rectTransform, false); | |||||
(_pageUpButton.transform as RectTransform).anchorMin = new Vector2(0.5f, 1f); | |||||
(_pageUpButton.transform as RectTransform).anchorMax = new Vector2(0.5f, 1f); | |||||
(_pageUpButton.transform as RectTransform).anchoredPosition = new Vector2(0f, -14f); | |||||
(_pageUpButton.transform as RectTransform).sizeDelta = new Vector2(40f, 10f); | |||||
_pageUpButton.interactable = true; | |||||
_pageUpButton.onClick.AddListener(delegate () | |||||
{ | |||||
_currentlyUpdatingTableView.PageScrollUp(); | |||||
}); | |||||
_pageDownButton = Instantiate(Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "PageDownButton")), rectTransform, false); | |||||
(_pageDownButton.transform as RectTransform).anchorMin = new Vector2(0.5f, 0f); | |||||
(_pageDownButton.transform as RectTransform).anchorMax = new Vector2(0.5f, 0f); | |||||
(_pageDownButton.transform as RectTransform).anchoredPosition = new Vector2(0f, 8f); | |||||
(_pageDownButton.transform as RectTransform).sizeDelta = new Vector2(40f, 10f); | |||||
_pageDownButton.interactable = true; | |||||
_pageDownButton.onClick.AddListener(delegate () | |||||
{ | |||||
_currentlyUpdatingTableView.PageScrollDown(); | |||||
}); | |||||
var gobj = new GameObject("DownloadTable"); | |||||
gobj.SetActive(false); | |||||
_currentlyUpdatingTableView = gobj.AddComponent<TableView>(); | |||||
_currentlyUpdatingTableView.transform.SetParent(rectTransform, false); | |||||
_currentlyUpdatingTableView.SetPrivateField("_isInitialized", false); | |||||
_currentlyUpdatingTableView.SetPrivateField("_preallocatedCells", new TableView.CellsGroup[0]); | |||||
_currentlyUpdatingTableView.Init(); | |||||
RectMask2D viewportMask = Instantiate(Resources.FindObjectsOfTypeAll<RectMask2D>().First(), _currentlyUpdatingTableView.transform, false); | |||||
viewportMask.transform.DetachChildren(); | |||||
_currentlyUpdatingTableView.GetComponentsInChildren<RectTransform>().First(x => x.name == "Content").transform.SetParent(viewportMask.rectTransform, false); | |||||
(_currentlyUpdatingTableView.transform as RectTransform).anchorMin = new Vector2(0.3f, 0.5f); | |||||
(_currentlyUpdatingTableView.transform as RectTransform).anchorMax = new Vector2(0.7f, 0.5f); | |||||
(_currentlyUpdatingTableView.transform as RectTransform).sizeDelta = new Vector2(0f, 60f); | |||||
(_currentlyUpdatingTableView.transform as RectTransform).anchoredPosition = new Vector3(0f, -3f); | |||||
ReflectionUtil.SetPrivateField(_currentlyUpdatingTableView, "_pageUpButton", _pageUpButton); | |||||
ReflectionUtil.SetPrivateField(_currentlyUpdatingTableView, "_pageDownButton", _pageDownButton); | |||||
_currentlyUpdatingTableView.selectionType = TableViewSelectionType.None; | |||||
_currentlyUpdatingTableView.dataSource = this; | |||||
gobj.SetActive(true); | |||||
_checkForUpdates = BeatSaberUI.CreateUIButton(rectTransform, "CreditsButton", new Vector2(36f, -30f), new Vector2(20f, 10f), CheckUpdates, "Check for updates"); | |||||
_checkForUpdates.interactable = DownloadController.Instance.CanCheck || DownloadController.Instance.CanReset; | |||||
_checkForUpdates.ToggleWordWrapping(false); | |||||
_downloadUpdates = BeatSaberUI.CreateUIButton(rectTransform, "CreditsButton", new Vector2(36f, -15f), new Vector2(20f, 10f), DownloadUpdates, "Download Updates"); | |||||
_downloadUpdates.interactable = DownloadController.Instance.CanDownload; | |||||
_downloadUpdates.ToggleWordWrapping(false); | |||||
DownloadController.Instance.OnDownloaderListChanged += Refresh; | |||||
DownloadController.Instance.OnDownloadStateChanged += DownloaderStateChanged; | |||||
} | |||||
} | |||||
private void DownloadUpdates() | |||||
{ | |||||
if (DownloadController.Instance.CanDownload) | |||||
DownloadController.Instance.StartDownloads(); | |||||
} | |||||
private void CheckUpdates() | |||||
{ | |||||
Updater.ResetRequestCache(); | |||||
if (DownloadController.Instance.CanReset) | |||||
DownloadController.Instance.ResetCheck(false); | |||||
if (DownloadController.Instance.CanCheck) | |||||
DownloadController.Instance.CheckForUpdates(); | |||||
} | |||||
private void DownloaderStateChanged() | |||||
{ | |||||
_checkForUpdates.interactable = DownloadController.Instance.CanCheck || DownloadController.Instance.CanReset; | |||||
_downloadUpdates.interactable = DownloadController.Instance.CanDownload; | |||||
} | |||||
protected override void DidDeactivate(DeactivationType type) | |||||
{ | |||||
DownloadController.Instance.OnDownloaderListChanged -= Refresh; | |||||
DownloadController.Instance.OnDownloadStateChanged -= DownloaderStateChanged; | |||||
} | |||||
private void Refresh() | |||||
{ | |||||
_currentlyUpdatingTableView.ReloadData(); | |||||
} | |||||
public float CellSize() | |||||
{ | |||||
return 10f; | |||||
} | |||||
public int NumberOfCells() | |||||
{ | |||||
return DownloadController.Instance.Downloads.Count; | |||||
} | |||||
public TableCell CellForIdx(int row) | |||||
{ | |||||
LevelListTableCell _tableCell = Instantiate(_songListTableCellInstance); | |||||
DownloadProgressCell _queueCell = _tableCell.gameObject.AddComponent<DownloadProgressCell>(); | |||||
_queueCell.Init(DownloadController.Instance.Downloads[row]); | |||||
return _queueCell; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,79 @@ | |||||
using CustomUI.Utilities; | |||||
using IPA.Loader; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using UnityEngine; | |||||
namespace BSIPA_ModList | |||||
{ | |||||
internal static class Utilities | |||||
{ | |||||
private static Sprite _defaultBsipaIcon; | |||||
public static Sprite DefaultBSIPAIcon | |||||
{ | |||||
get | |||||
{ | |||||
if (_defaultBsipaIcon == null) | |||||
_defaultBsipaIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_bsipa.png"); | |||||
return _defaultBsipaIcon; | |||||
} | |||||
} | |||||
private static Sprite _defaultLibraryIcon; | |||||
public static Sprite DefaultLibraryIcon | |||||
{ | |||||
get | |||||
{ | |||||
if (_defaultLibraryIcon == null) | |||||
_defaultLibraryIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.library.png"); | |||||
return _defaultLibraryIcon; | |||||
} | |||||
} | |||||
private static Sprite _defaultIpaIcon; | |||||
public static Sprite DefaultIPAIcon | |||||
{ | |||||
get | |||||
{ | |||||
if (_defaultIpaIcon == null) | |||||
_defaultIpaIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.mod_ipa.png"); | |||||
return _defaultIpaIcon; | |||||
} | |||||
} | |||||
public static Sprite GetIcon(this PluginLoader.PluginMetadata meta) | |||||
{ | |||||
if (meta == null) return DefaultBSIPAIcon; | |||||
if (meta.IsBare) return DefaultLibraryIcon; | |||||
else return GetEmbeddedIcon(meta) ?? DefaultBSIPAIcon; | |||||
} | |||||
private static Dictionary<PluginLoader.PluginMetadata, Sprite> embeddedIcons = new Dictionary<PluginLoader.PluginMetadata, Sprite>(); | |||||
public static Sprite GetEmbeddedIcon(this PluginLoader.PluginMetadata meta) | |||||
{ | |||||
if (embeddedIcons.TryGetValue(meta, out var sprite)) return sprite; | |||||
var icon = GetEmbeddedIconNoCache(meta); | |||||
embeddedIcons.Add(meta, icon); | |||||
return icon; | |||||
} | |||||
private static Sprite GetEmbeddedIconNoCache(PluginLoader.PluginMetadata meta) | |||||
{ | |||||
if (meta.Assembly == null) return null; | |||||
if (meta.Manifest.IconPath == null) return null; | |||||
try | |||||
{ | |||||
return UIUtilities.LoadSpriteRaw(UIUtilities.GetResource(meta.Assembly, meta.Manifest.IconPath)); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logger.log.Error($"Error loading icon for {meta.Name}"); | |||||
Logger.log.Error(e); | |||||
return null; | |||||
} | |||||
} | |||||
} | |||||
} |
@ -1,3 +1,3 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
<configuration> | <configuration> | ||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration> | |||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration> |
@ -1 +1 @@ | |||||
b1078c6b90a14bd4bd5a5e7f04507fbb89e2b3a7 | |||||
2ad64d7c1386fd6d6aca4a6ef5b1ea0e603afd9c |