Added DownloadController Added FloatingNotificationpull/11/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"?> | |||
<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 |