Esempio n. 1
0
        public static void OnLoad()
        {
            try
            {
                var _levelListViewController = Resources.FindObjectsOfTypeAll <SelectLevelCategoryViewController>().Last();

                _levelListViewController.didActivateEvent += _levelListViewController_didActivateEvent;

                if (_levelListViewController)
                {
                    // move the icon control
                    var iconSegmentedControl = _levelListViewController.GetField <IconSegmentedControl, SelectLevelCategoryViewController>("_levelFilterCategoryIconSegmentedControl");
                    ((RectTransform)iconSegmentedControl.transform).anchoredPosition = new Vector2(0, 4.5f);

                    _requestButton = _levelListViewController.CreateUIButton("SRMButton", "PracticeButton", new Vector2(14, -4.5f), new Vector2(15f, 105f),
                                                                             () =>
                    {
                        _requestButton.interactable = false;
                        SRMButtonPressed();
                        _requestButton.interactable = true;
                    },
                                                                             "SRM");


                    _requestButton.ToggleWordWrapping(false);
                    _requestButton.SetButtonTextSize(5f);

                    UIHelper.AddHintText(_requestButton.transform as RectTransform, "Manage the current request queue");

                    UpdateRequestUI();
                    Plugin.Log("Created request button!");
                }
            }
            catch
            {
                Plugin.Log("Unable to create request button");
            }

            // check if flow coordinator has been setup yet
            if (_flowCoordinator == null)
            {
                _flowCoordinator = BeatSaberMarkupLanguage.BeatSaberUI.CreateFlowCoordinator <RequestFlowCoordinator>();
            }

            SongListUtils.Initialize();

            ChatHandler.instance.Init();

            WriteQueueSummaryToFile();
            WriteQueueStatusToFile(QueueMessage(RequestBotConfig.Instance.RequestQueueOpen));

            if (Instance)
            {
                return;
            }
            new GameObject("SongRequestManager").AddComponent <RequestBot>();
        }
        public static void OnLoad()
        {
            try
            {
                var _levelListViewController = Resources.FindObjectsOfTypeAll <LevelCollectionViewController>().First();
                if (_levelListViewController)
                {
                    _requestButton = UIHelper.CreateUIButton(_levelListViewController.rectTransform, "OkButton", new Vector2(66, -3.5f),
                                                             new Vector2(9f, 5.5f), () => { _requestButton.interactable = false; SRMButtonPressed(); _requestButton.interactable = true; }, "SRM");

                    (_requestButton.transform as RectTransform).anchorMin = new Vector2(1, 1);
                    (_requestButton.transform as RectTransform).anchorMax = new Vector2(1, 1);

                    _requestButton.ToggleWordWrapping(false);
                    _requestButton.SetButtonTextSize(3.5f);
                    UIHelper.AddHintText(_requestButton.transform as RectTransform, "Manage the current request queue");

                    UpdateRequestUI();
                    Plugin.Log("Created request button!");
                }
            }
            catch
            {
                Plugin.Log("Unable to create request button");
            }

            // check if flow coordinator has been setup yet
            if (_flowCoordinator == null)
            {
                _flowCoordinator = BeatSaberMarkupLanguage.BeatSaberUI.CreateFlowCoordinator <RequestFlowCoordinator>();
            }

            SongListUtils.Initialize();

            ChatHandler.instance.Init();

            WriteQueueSummaryToFile();
            WriteQueueStatusToFile(QueueMessage(RequestBotConfig.Instance.RequestQueueOpen));

            if (Instance)
            {
                return;
            }
            new GameObject("SongRequestManager").AddComponent <RequestBot>();
        }
Esempio n. 3
0
            public KEY(KEYBOARD kb, Vector2 position, string text, float width, float height, Color color)
            {
                value   = text;
                this.kb = kb;

                name = text;

                mybutton = Button.Instantiate(kb.BaseButton, kb.container, false);

                (mybutton.transform as RectTransform).anchorMin = new Vector2(0.5f, 0.5f);
                (mybutton.transform as RectTransform).anchorMax = new Vector2(0.5f, 0.5f);

                TMP_Text txt = mybutton.GetComponentInChildren <TMP_Text>();

                mybutton.ToggleWordWrapping(false);

                mybutton.transform.localScale = new Vector3(kb.scale, kb.scale, 1.0f);
                mybutton.SetButtonTextSize(5f);
                mybutton.SetButtonText(text);
                mybutton.name = $"SRM_{text}";
                mybutton.GetComponentInChildren <Image>().color = color;

                if (width == 0)
                {
                    Vector2 v = txt.GetPreferredValues(text);
                    v.x  += 10f;
                    v.y  += 2f;
                    width = v.x;
                }

                // Adjust starting position so button aligns to upper left of current drawing position

                position.x += kb.scale * width / 2;
                position.y -= kb.scale * height / 2;
                (mybutton.transform as RectTransform).anchoredPosition = position;

                (mybutton.transform as RectTransform).sizeDelta = new Vector2(width, height);

                kb.currentposition.x += width * kb.scale + kb.padding;

                mybutton.onClick.RemoveAllListeners();

                mybutton.onClick.AddListener(delegate()
                {
                    if (keyaction != null)
                    {
                        keyaction(this);
                        kb.DrawCursor();
                        return;
                    }

                    if (value.EndsWith("%CR%"))
                    {
                        kb.KeyboardText.text += value.Substring(0, value.Length - 4);
                        kb.Enter(this);
                        kb.DrawCursor();

                        return;
                    }

                    {
                        string x = kb.Shift ? shifted : value;
                        if (x == "")
                        {
                            x = value;
                        }
                        if (kb.Caps)
                        {
                            x = value.ToUpper();
                        }
                        kb.KeyboardText.text += x;
                        kb.DrawCursor();
                    }
                });
                HoverHint _MyHintText = UIHelper.AddHintText(mybutton.transform as RectTransform, value);
            }
Esempio n. 4
0
        private async void SetDataFromLevelAsync(SongRequest request, LevelListTableCell _tableCell, int row)
        {
            var favouritesBadge = _tableCell.GetField <Image, LevelListTableCell>("_favoritesBadgeImage");

            favouritesBadge.enabled = false;

            var highlight = (request.requestInfo.Length > 0) && (request.requestInfo[0] == '!');

            var msg = highlight ? "MSG" : "";

            var hasMessage  = (request.requestInfo.Length > 0) && (request.requestInfo[0] == '!');
            var isChallenge = request.requestInfo.IndexOf("!challenge", StringComparison.OrdinalIgnoreCase) >= 0;

            var pp      = "";
            var ppvalue = request.song["pp"].AsInt;

            if (ppvalue > 0)
            {
                pp = $" {ppvalue} PP";
            }

            var dt = new RequestBot.DynamicText().AddSong(request.song).AddUser(ref request.requestor); // Get basic fields

            dt.Add("Status", request.status.ToString());
            dt.Add("Info", (request.requestInfo != "") ? " / " + request.requestInfo : "");
            dt.Add("RequestTime", request.requestTime.ToLocalTime().ToString("hh:mm"));

            var songDurationText = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_songDurationText");

            songDurationText.text = request.song["songlength"].Value;

            var songBpm = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_songBpmText");

            (songBpm.transform as RectTransform).anchoredPosition = new Vector2(-2.5f, -1.8f);
            (songBpm.transform as RectTransform).sizeDelta       += new Vector2(15f, 0f);

            var k = new List <string>();

            if (hasMessage)
            {
                k.Add("MSG");
            }
            if (isChallenge)
            {
                k.Add("VS");
            }
            k.Add(request.song["id"]);
            songBpm.text = string.Join(" - ", k);

            var songBmpIcon = _tableCell.GetComponentsInChildren <Image>().LastOrDefault(c => string.Equals(c.name, "BpmIcon", StringComparison.OrdinalIgnoreCase));

            if (songBmpIcon != null)
            {
                Destroy(songBmpIcon);
            }

            var songName = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_songNameText");

            songName.richText = true;
            songName.text     = $"{request.song["songName"].Value} <size=50%>{RequestBot.GetRating(ref request.song)} <color=#3fff3f>{pp}</color></size>";

            var author = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_songAuthorText");

            author.richText = true;
            author.text     = dt.Parse(RequestBot.QueueListRow2);

            var image    = _tableCell.GetField <Image, LevelListTableCell>("_coverImage");
            var imageSet = false;

            if (SongCore.Loader.AreSongsLoaded)
            {
                var level = CustomLevelForRow(row);
                if (level != null)
                {
                    // set image from song's cover image
                    var sprite = await level.GetCoverImageAsync(System.Threading.CancellationToken.None);

                    image.sprite = sprite;
                    imageSet     = true;
                }
            }

            if (!imageSet)
            {
                var url = request.song["coverURL"].Value;

                if (!_cachedTextures.TryGetValue(url, out var tex))
                {
                    var b = await Plugin.WebClient.DownloadImage($"{url}", System.Threading.CancellationToken.None);

                    tex = new Texture2D(2, 2);
                    tex.LoadImage(b);

                    try
                    {
                        _cachedTextures.Add(url, tex);
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                }

                image.sprite = Base64Sprites.Texture2DToSprite(tex);
            }

            UIHelper.AddHintText(_tableCell.transform as RectTransform, dt.Parse(RequestBot.SongHintText));
        }
Esempio n. 5
0
        protected override void DidActivate(bool firstActivation, bool addedToHierarchy, bool screenSystemEnabling)
        {
            if (firstActivation)
            {
                if (!SongCore.Loader.AreSongsLoaded)
                {
                    SongCore.Loader.SongsLoadedEvent += SongLoader_SongsLoadedEvent;
                }

                Plugin.Log("DidActivate 001");

                // get table cell instance
                _requestListTableCellInstance = Resources.FindObjectsOfTypeAll <LevelListTableCell>().First((LevelListTableCell x) => x.name == "LevelListTableCell");

                // initialize Yes/No modal
                YesNoModal.instance.Setup();

                Plugin.Log("DidActivate 002");

                _songPreviewPlayer = Resources.FindObjectsOfTypeAll <SongPreviewPlayer>().FirstOrDefault();

                RectTransform container = new GameObject("RequestBotContainer", typeof(RectTransform)).transform as RectTransform;
                container.SetParent(rectTransform, false);

                #region TableView Setup and Initialization
                var go = new GameObject("SongRequestTableView", typeof(RectTransform));
                go.SetActive(false);

                go.AddComponent <ScrollRect>();
                go.AddComponent <Touchable>();
                go.AddComponent <EventSystemListener>();

                ScrollView scrollView = go.AddComponent <ScrollView>();

                _songListTableView = go.AddComponent <TableView>();
                go.AddComponent <RectMask2D>();
                _songListTableView.transform.SetParent(container, false);

                _songListTableView.SetField("_preallocatedCells", new TableView.CellsGroup[0]);
                _songListTableView.SetField("_isInitialized", false);
                _songListTableView.SetField("_scrollView", scrollView);

                var viewport = new GameObject("Viewport").AddComponent <RectTransform>();
                viewport.SetParent(go.GetComponent <RectTransform>(), false);
                go.GetComponent <ScrollRect>().viewport         = viewport;
                (viewport.transform as RectTransform).sizeDelta = new Vector2(70f, 70f);

                RectTransform content = new GameObject("Content").AddComponent <RectTransform>();
                content.SetParent(viewport, false);

                scrollView.SetField("_contentRectTransform", content);
                scrollView.SetField("_viewport", viewport);

                _songListTableView.SetDataSource(this, false);

                _songListTableView.LazyInit();

                go.SetActive(true);

                (_songListTableView.transform as RectTransform).sizeDelta        = new Vector2(70f, 70f);
                (_songListTableView.transform as RectTransform).anchoredPosition = new Vector2(3f, 0f);

                _songListTableView.didSelectCellWithIdxEvent += DidSelectRow;

                _pageUpButton = UIHelper.CreateUIButton("SRMPageUpButton",
                                                        container,
                                                        "PracticeButton",
                                                        new Vector2(0f, 38.5f),
                                                        new Vector2(15f, 7f),
                                                        () => { scrollView.PageUpButtonPressed(); },
                                                        "˄");
                Destroy(_pageUpButton.GetComponentsInChildren <ImageView>().FirstOrDefault(x => x.name == "Underline"));

                _pageDownButton = UIHelper.CreateUIButton("SRMPageDownButton",
                                                          container,
                                                          "PracticeButton",
                                                          new Vector2(0f, -38.5f),
                                                          new Vector2(15f, 7f),
                                                          () => { scrollView.PageDownButtonPressed(); },
                                                          "˅");
                Destroy(_pageDownButton.GetComponentsInChildren <ImageView>().FirstOrDefault(x => x.name == "Underline"));
                #endregion

                CenterKeys = new KEYBOARD(container, "", false, -15, 15);

#if UNRELEASED
                // BUG: Need additional modes disabling one shot buttons
                // BUG: Need to make sure the buttons are usable on older headsets

                Plugin.Log("DidActivate 005");

                _CurrentSongName                    = BeatSaberUI.CreateText(container, "", new Vector2(-35, 37f));
                _CurrentSongName.fontSize           = 3f;
                _CurrentSongName.color              = Color.cyan;
                _CurrentSongName.alignment          = TextAlignmentOptions.Left;
                _CurrentSongName.enableWordWrapping = false;
                _CurrentSongName.text               = "";

                _CurrentSongName2                    = BeatSaberUI.CreateText(container, "", new Vector2(-35, 34f));
                _CurrentSongName2.fontSize           = 3f;
                _CurrentSongName2.color              = Color.cyan;
                _CurrentSongName2.alignment          = TextAlignmentOptions.Left;
                _CurrentSongName2.enableWordWrapping = false;
                _CurrentSongName2.text               = "";

                //CenterKeys.AddKeys(SONGLISTKEY);
                if (!RequestBot.AddKeyboard(CenterKeys, "mainpanel.kbd"))
                {
                    CenterKeys.AddKeys(SONGLISTKEY);
                }

                ColorDeckButtons(CenterKeys, Color.white, Color.magenta);
#endif

                RequestBot.AddKeyboard(CenterKeys, "CenterPanel.kbd");

                CenterKeys.DefaultActions();

                #region History button
                // History button
                _historyButton = UIHelper.CreateUIButton("SRMHistory", container, "PracticeButton", new Vector2(53f, 30f),
                                                         new Vector2(25f, 15f),
                                                         () =>
                {
                    isShowingHistory = !isShowingHistory;
                    RequestBot.SetTitle(isShowingHistory ? "Song Request History" : "Song Request Queue");
                    if (NumberOfCells() > 0)
                    {
                        _songListTableView.ScrollToCellWithIdx(0, TableView.ScrollPositionType.Beginning, false);
                        _songListTableView.SelectCellWithIdx(0);
                        _selectedRow = 0;
                    }
                    else
                    {
                        _selectedRow = -1;
                    }
                    UpdateRequestUI(true);
                    SetUIInteractivity();
                    _lastSelection = -1;
                }, "History");

                _historyButton.ToggleWordWrapping(false);
                _historyHintText = UIHelper.AddHintText(_historyButton.transform as RectTransform, "");
                #endregion

                #region Blacklist button
                // Blacklist button
                _blacklistButton = UIHelper.CreateUIButton("SRMBlacklist", container, "PracticeButton", new Vector2(53f, 10f),
                                                           new Vector2(25f, 15f),
                                                           () =>
                {
                    if (NumberOfCells() > 0)
                    {
                        void _onConfirm()
                        {
                            RequestBot.Blacklist(_selectedRow, isShowingHistory, true);
                            if (_selectedRow > 0)
                            {
                                _selectedRow--;
                            }
                            confirmDialogActive = false;
                        }

                        // get song
                        var song = SongInfoForRow(_selectedRow).song;

                        // indicate dialog is active
                        confirmDialogActive = true;

                        // show dialog
                        YesNoModal.instance.ShowDialog("Blacklist Song Warning", $"Blacklisting {song["songName"].Value} by {song["authorName"].Value}\r\nDo you want to continue?", _onConfirm, () => { confirmDialogActive = false; });
                    }
                }, "Blacklist");

                _blacklistButton.ToggleWordWrapping(false);
                UIHelper.AddHintText(_blacklistButton.transform as RectTransform, "Block the selected request from being queued in the future.");
                #endregion

                #region Skip button
                // Skip button
                _skipButton = UIHelper.CreateUIButton("SRMSkip", container, "PracticeButton", new Vector2(53f, 0f),
                                                      new Vector2(25f, 15f),
                                                      () =>
                {
                    if (NumberOfCells() > 0)
                    {
                        // get song
                        var song = SongInfoForRow(_selectedRow).song;

                        void _onConfirm()
                        {
                            // get selected song
                            currentsong = SongInfoForRow(_selectedRow);

                            // skip it
                            RequestBot.Skip(_selectedRow);

                            // select previous song if not first song
                            if (_selectedRow > 0)
                            {
                                _selectedRow--;
                            }

                            // indicate dialog is no longer active
                            confirmDialogActive = false;
                        }

                        // indicate dialog is active
                        confirmDialogActive = true;

                        // show dialog
                        YesNoModal.instance.ShowDialog("Skip Song Warning", $"Skipping {song["songName"].Value} by {song["authorName"].Value}\r\nDo you want to continue?", _onConfirm, () => { confirmDialogActive = false; });
                    }
                }, "Skip");

                _skipButton.ToggleWordWrapping(false);
                UIHelper.AddHintText(_skipButton.transform as RectTransform, "Remove the selected request from the queue.");
                #endregion

                #region Play button
                // Play button
                _playButton = UIHelper.CreateUIButton("SRMPlay", container, "ActionButton", new Vector2(53f, -10f),
                                                      new Vector2(25f, 15f),
                                                      () =>
                {
                    if (NumberOfCells() > 0)
                    {
                        currentsong = SongInfoForRow(_selectedRow);
                        RequestBot.played.Add(currentsong.song);
                        RequestBot.WriteJSON(RequestBot.playedfilename, ref RequestBot.played);

                        SetUIInteractivity(false);
                        RequestBot.Process(_selectedRow, isShowingHistory);
                        _selectedRow = -1;
                    }
                }, "Play");

                _playButton.ToggleWordWrapping(false);
                _playButton.interactable = ((isShowingHistory && RequestHistory.Songs.Count > 0) || (!isShowingHistory && RequestQueue.Songs.Count > 0));
                UIHelper.AddHintText(_playButton.transform as RectTransform, "Download and scroll to the currently selected request.");
                #endregion

                #region Queue button
                // Queue button
                _queueButton = UIHelper.CreateUIButton("SRMQueue", container, "PracticeButton", new Vector2(53f, -30f),
                                                       new Vector2(25f, 15f),
                                                       () =>
                {
                    RequestBotConfig.Instance.RequestQueueOpen = !RequestBotConfig.Instance.RequestQueueOpen;
                    RequestBotConfig.Instance.Save();
                    RequestBot.WriteQueueStatusToFile(RequestBotConfig.Instance.RequestQueueOpen ? "Queue is open." : "Queue is closed.");
                    RequestBot.Instance.QueueChatMessage(RequestBotConfig.Instance.RequestQueueOpen ? "Queue is open." : "Queue is closed.");
                    UpdateRequestUI();
                }, RequestBotConfig.Instance.RequestQueueOpen ? "Queue Open" : "Queue Closed");

                _queueButton.ToggleWordWrapping(true);
                _queueButton.SetButtonUnderlineColor(RequestBotConfig.Instance.RequestQueueOpen ? Color.green : Color.red);
                _queueButton.SetButtonTextSize(3.5f);
                UIHelper.AddHintText(_queueButton.transform as RectTransform, "Open/Close the queue.");
                #endregion

                // Set default RequestFlowCoordinator title
                RequestBot.SetTitle(isShowingHistory ? "Song Request History" : "Song Request Queue");
            }
            base.DidActivate(firstActivation, addedToHierarchy, screenSystemEnabling);

            if (addedToHierarchy)
            {
                _selectedRow = -1;
                _songListTableView.ClearSelection();
            }

            UpdateRequestUI();
            SetUIInteractivity(true);
        }
        private async void SetDataFromLevelAsync(SongRequest request, LevelListTableCell _tableCell, int row)
        {
            var favouritesBadge = _tableCell.GetField <RawImage, LevelListTableCell>("_favoritesBadgeImage");

            favouritesBadge.enabled = false;

            bool highlight = (request.requestInfo.Length > 0) && (request.requestInfo[0] == '!');

            string msg = highlight ? "MSG" : "";

            var hasMessage  = (request.requestInfo.Length > 0) && (request.requestInfo[0] == '!');
            var isChallenge = request.requestInfo.IndexOf("!challenge", StringComparison.OrdinalIgnoreCase) >= 0;

            var beatmapCharacteristicImages = _tableCell.GetField <UnityEngine.UI.Image[], LevelListTableCell>("_beatmapCharacteristicImages"); // NEW VERSION

            foreach (var i in beatmapCharacteristicImages)
            {
                i.enabled = false;
            }

            // causing a nullex?
            //_tableCell.SetField("_beatmapCharacteristicAlphas", new float[5] { 1f, 1f, 1f, 1f, 1f });

            // set message icon if request has a message // NEW VERSION
            if (hasMessage)
            {
                beatmapCharacteristicImages.Last().sprite  = Base64Sprites.InfoIcon;
                beatmapCharacteristicImages.Last().enabled = true;
            }

            // set challenge icon if song is a challenge
            if (isChallenge)
            {
                var el = beatmapCharacteristicImages.ElementAt(beatmapCharacteristicImages.Length - 2);

                el.sprite  = Base64Sprites.VersusChallengeIcon;
                el.enabled = true;
            }

            string pp      = "";
            int    ppvalue = request.song["pp"].AsInt;

            if (ppvalue > 0)
            {
                pp = $" {ppvalue} PP";
            }

            var dt = new RequestBot.DynamicText().AddSong(request.song).AddUser(ref request.requestor); // Get basic fields

            dt.Add("Status", request.status.ToString());
            dt.Add("Info", (request.requestInfo != "") ? " / " + request.requestInfo : "");
            dt.Add("RequestTime", request.requestTime.ToLocalTime().ToString("hh:mm"));

            var songName = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_songNameText");

            //songName.text = $"{request.song["songName"].Value} <size=50%>{RequestBot.GetRating(ref request.song)} <color=#3fff3f>{pp}</color></size> <color=#ff00ff>{msg}</color>";
            songName.text = $"{request.song["songName"].Value} <size=50%>{RequestBot.GetRating(ref request.song)} <color=#3fff3f>{pp}</color></size>"; // NEW VERSION

            var author = _tableCell.GetField <TextMeshProUGUI, LevelListTableCell>("_authorText");

            author.text = dt.Parse(RequestBot.QueueListRow2);

            var image    = _tableCell.GetField <RawImage, LevelListTableCell>("_coverRawImage");
            var imageSet = false;

            if (SongCore.Loader.AreSongsLoaded)
            {
                CustomPreviewBeatmapLevel level = CustomLevelForRow(row);
                if (level != null)
                {
                    //Plugin.Log("custom level found");
                    // set image from song's cover image
                    var tex = await level.GetCoverImageTexture2DAsync(System.Threading.CancellationToken.None);

                    image.texture = tex;
                    imageSet      = true;
                }
            }

            if (!imageSet)
            {
                string url = request.song["coverURL"].Value;

                if (!_cachedTextures.TryGetValue(url, out var tex))
                {
                    var b = await Plugin.WebClient.DownloadImage($"https://beatsaver.com{url}", System.Threading.CancellationToken.None);

                    tex = new Texture2D(2, 2);
                    tex.LoadImage(b);

                    try
                    {
                        _cachedTextures.Add(url, tex);
                    }
                    catch (Exception)
                    {
                    }
                }

                image.texture = tex;
            }

            UIHelper.AddHintText(_tableCell.transform as RectTransform, dt.Parse(RequestBot.SongHintText));
        }
        protected override void DidActivate(bool firstActivation, ActivationType type)
        {
            if (firstActivation)
            {
                if (!SongCore.Loader.AreSongsLoaded)
                {
                    SongCore.Loader.SongsLoadedEvent += SongLoader_SongsLoadedEvent;
                }

                // get table cell instance
                _requestListTableCellInstance = Resources.FindObjectsOfTypeAll <LevelListTableCell>().First((LevelListTableCell x) => x.name == "LevelListTableCell");

                // initialize Yes/No modal
                YesNoModal.instance.Setup();

                _songPreviewPlayer = Resources.FindObjectsOfTypeAll <SongPreviewPlayer>().FirstOrDefault();

                RectTransform container = new GameObject("RequestBotContainer", typeof(RectTransform)).transform as RectTransform;
                container.SetParent(rectTransform, false);
                container.sizeDelta = new Vector2(60f, 0f);

                #region TableView Setup and Initialization
                var go = new GameObject("SongRequestTableView", typeof(RectTransform));
                go.SetActive(false);
                _songListTableView = go.AddComponent <TableView>();
                _songListTableView.gameObject.AddComponent <RectMask2D>();
                _songListTableView.transform.SetParent(container, false);

                _songListTableView.SetField("_preallocatedCells", new TableView.CellsGroup[0]);
                _songListTableView.SetField("_isInitialized", false);

                var viewport = new GameObject("Viewport").AddComponent <RectTransform>();
                viewport.SetParent(go.GetComponent <RectTransform>(), false);
                (viewport.transform as RectTransform).sizeDelta = new Vector2(0, 0);
                (viewport.transform as RectTransform).anchorMin = new Vector2(0, 0);
                (viewport.transform as RectTransform).anchorMax = new Vector2(1, 1);
                go.GetComponent <ScrollRect>().viewport         = viewport;

                _songListTableView.InvokeMethod <object, TableView>("Init");

                _songListTableView.dataSource = this;

                go.SetActive(true);

                (_songListTableView.transform as RectTransform).anchorMin        = new Vector2(0f, 0f);
                (_songListTableView.transform as RectTransform).anchorMax        = new Vector2(1f, 1f);
                (_songListTableView.transform as RectTransform).sizeDelta        = new Vector2(0f, 60f);
                (_songListTableView.transform as RectTransform).anchoredPosition = new Vector2(0f, -3f);

                _songListTableView.didSelectCellWithIdxEvent += DidSelectRow;

                rectTransform.anchorMin = new Vector2(0.5f, 0f);
                rectTransform.anchorMax = new Vector2(0.5f, 1f);
                rectTransform.sizeDelta = new Vector2(74f, 0f);
                rectTransform.pivot     = new Vector2(0.4f, 0.5f);

                var _songListTableViewScroller = _songListTableView.GetField <TableViewScroller, TableView>("_scroller");

                _pageUpButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().Last(x => (x.name == "PageUpButton")), container, false);
                (_pageUpButton.transform as RectTransform).anchoredPosition = new Vector2(0f, 35f);
                _pageUpButton.interactable = true;
                _pageUpButton.onClick.AddListener(delegate()
                {
                    _songListTableViewScroller.PageScrollUp();
                });

                _pageDownButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(x => (x.name == "PageDownButton")), container, false);
                (_pageDownButton.transform as RectTransform).anchoredPosition = new Vector2(0f, -41f);
                _pageDownButton.interactable = true;
                _pageDownButton.onClick.AddListener(delegate()
                {
                    _songListTableViewScroller.PageScrollDown();
                });
                #endregion

                CenterKeys = new KEYBOARD(container, "", false, -15, 15);

#if UNRELEASED
                // BUG: Need additional modes disabling one shot buttons
                // BUG: Need to make sure the buttons are usable on older headsets

                _CurrentSongName                    = BeatSaberUI.CreateText(container, "", new Vector2(-35, 37f));
                _CurrentSongName.fontSize           = 3f;
                _CurrentSongName.color              = Color.cyan;
                _CurrentSongName.alignment          = TextAlignmentOptions.Left;
                _CurrentSongName.enableWordWrapping = false;
                _CurrentSongName.text               = "";

                _CurrentSongName2                    = BeatSaberUI.CreateText(container, "", new Vector2(-35, 34f));
                _CurrentSongName2.fontSize           = 3f;
                _CurrentSongName2.color              = Color.cyan;
                _CurrentSongName2.alignment          = TextAlignmentOptions.Left;
                _CurrentSongName2.enableWordWrapping = false;
                _CurrentSongName2.text               = "";

                //CenterKeys.AddKeys(SONGLISTKEY);
                if (!RequestBot.AddKeyboard(CenterKeys, "mainpanel.kbd"))
                {
                    CenterKeys.AddKeys(SONGLISTKEY);
                }

                ColorDeckButtons(CenterKeys, Color.white, Color.magenta);
#endif

                RequestBot.AddKeyboard(CenterKeys, "CenterPanel.kbd");

                CenterKeys.DefaultActions();

                #region History button
                // History button
                _historyButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(o => (o.name == "OkButton")), container, false);
                _historyButton.ToggleWordWrapping(false);
                (_historyButton.transform as RectTransform).anchoredPosition = new Vector2(90f, 30f);
                _historyButton.SetButtonText("History");
                _historyButton.onClick.RemoveAllListeners();
                _historyButton.onClick.AddListener(delegate()
                {
                    isShowingHistory = !isShowingHistory;
                    RequestBot.SetTitle(isShowingHistory ? "Song Request History" : "Song Request Queue");
                    UpdateRequestUI(true);
                    SetUIInteractivity();
                    _lastSelection = -1;
                });
                _historyHintText = UIHelper.AddHintText(_historyButton.transform as RectTransform, "");
                #endregion

                #region Blacklist button
                // Blacklist button
                _blacklistButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(o => (o.name == "OkButton")), container, false);
                _blacklistButton.ToggleWordWrapping(false);
                (_blacklistButton.transform as RectTransform).anchoredPosition = new Vector2(90f, 10f);
                _blacklistButton.SetButtonText("Blacklist");
                //_blacklistButton.GetComponentInChildren<Image>().color = Color.red;
                _blacklistButton.onClick.RemoveAllListeners();
                _blacklistButton.onClick.AddListener(delegate()
                {
                    if (NumberOfCells() > 0)
                    {
                        void _onConfirm()
                        {
                            RequestBot.Blacklist(_selectedRow, isShowingHistory, true);
                            if (_selectedRow > 0)
                            {
                                _selectedRow--;
                            }
                            confirmDialogActive = false;
                        }

                        // get song
                        var song = SongInfoForRow(_selectedRow).song;

                        // indicate dialog is active
                        confirmDialogActive = true;

                        // show dialog
                        YesNoModal.instance.ShowDialog("Blacklist Song Warning", $"Blacklisting {song["songName"].Value} by {song["authorName"].Value}\r\nDo you want to continue?", _onConfirm, () => { confirmDialogActive = false; });
                    }
                });
                UIHelper.AddHintText(_blacklistButton.transform as RectTransform, "Block the selected request from being queued in the future.");
                #endregion

                #region Skip button
                // Skip button
                _skipButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(o => (o.name == "OkButton")), container, false);
                _skipButton.ToggleWordWrapping(false);
                (_skipButton.transform as RectTransform).anchoredPosition = new Vector2(90f, 0f);
                _skipButton.SetButtonText("Skip");
                //_skipButton.GetComponentInChildren<Image>().color = Color.yellow;
                _skipButton.onClick.RemoveAllListeners();
                _skipButton.onClick.AddListener(delegate()
                {
                    if (NumberOfCells() > 0)
                    {
                        void _onConfirm()
                        {
                            // get selected song
                            currentsong = SongInfoForRow(_selectedRow);

                            // skip it
                            RequestBot.Skip(_selectedRow);

                            // select previous song if not first song
                            if (_selectedRow > 0)
                            {
                                _selectedRow--;
                            }

                            // indicate dialog is no longer active
                            confirmDialogActive = false;
                        }

                        // get song
                        var song = SongInfoForRow(_selectedRow).song;

                        // indicate dialog is active
                        confirmDialogActive = true;

                        // show dialog
                        YesNoModal.instance.ShowDialog("Skip Song Warning", $"Skipping {song["songName"].Value} by {song["authorName"].Value}\r\nDo you want to continue?", _onConfirm, () => { confirmDialogActive = false; });
                    }
                });
                UIHelper.AddHintText(_skipButton.transform as RectTransform, "Remove the selected request from the queue.");
                #endregion

                #region Play button
                // Play button
                _playButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(o => (o.name == "OkButton")), container, false);
                _playButton.ToggleWordWrapping(false);
                (_playButton.transform as RectTransform).anchoredPosition = new Vector2(90f, -10f);
                _playButton.SetButtonText("Play");
                _playButton.GetComponentInChildren <Image>().color = Color.green;
                _playButton.onClick.RemoveAllListeners();
                _playButton.onClick.AddListener(delegate()
                {
                    if (NumberOfCells() > 0)
                    {
                        currentsong = SongInfoForRow(_selectedRow);
                        RequestBot.played.Add(currentsong.song);
                        RequestBot.WriteJSON(RequestBot.playedfilename, ref RequestBot.played);

                        SetUIInteractivity(false);
                        RequestBot.Process(_selectedRow, isShowingHistory);
                        _selectedRow = -1;
                    }
                });
                UIHelper.AddHintText(_playButton.transform as RectTransform, "Download and scroll to the currently selected request.");
                #endregion

                #region Queue button
                // Queue button
                _queueButton = Instantiate(Resources.FindObjectsOfTypeAll <Button>().First(o => (o.name == "OkButton")), container, false);
                _queueButton.ToggleWordWrapping(false);
                _queueButton.SetButtonTextSize(3.5f);
                (_queueButton.transform as RectTransform).anchoredPosition = new Vector2(90f, -30f);
                _queueButton.SetButtonText(RequestBotConfig.Instance.RequestQueueOpen ? "Queue Open" : "Queue Closed");
                _queueButton.GetComponentInChildren <Image>().color = RequestBotConfig.Instance.RequestQueueOpen ? Color.green : Color.red;;
                _queueButton.interactable = true;
                _queueButton.onClick.RemoveAllListeners();
                _queueButton.onClick.AddListener(delegate()
                {
                    RequestBotConfig.Instance.RequestQueueOpen = !RequestBotConfig.Instance.RequestQueueOpen;
                    RequestBotConfig.Instance.Save();
                    RequestBot.WriteQueueStatusToFile(RequestBotConfig.Instance.RequestQueueOpen ? "Queue is open." : "Queue is closed.");
                    RequestBot.Instance.QueueChatMessage(RequestBotConfig.Instance.RequestQueueOpen ? "Queue is open." : "Queue is closed.");
                    UpdateRequestUI();
                });
                UIHelper.AddHintText(_queueButton.transform as RectTransform, "Open/Close the queue.");
                #endregion

                // Set default RequestFlowCoordinator title
                RequestBot.SetTitle(isShowingHistory ? "Song Request History" : "Song Request Queue");
            }
            base.DidActivate(firstActivation, type);
            UpdateRequestUI();
            SetUIInteractivity(true);
        }