private async Task ReadFromFile() { Log(Type.Event, "Started ReadFromFile method"); #region UI Variables ImageView savedView = FindViewById <ImageView>(Resource.Id.savedView); TextView infoTxt = FindViewById <TextView>(Resource.Id.infoTxt); ProgressBar lyricsLoadingWheel = FindViewById <ProgressBar>(Resource.Id.lyricsLoadingWheel); ImageButton fabMore = FindViewById <ImageButton>(Resource.Id.fabMore); SwipeRefreshLayout refreshLayout = FindViewById <SwipeRefreshLayout>(Resource.Id.swipeRefreshLayout); #endregion songInfo = await Database.ReadLyrics(songInfo.Normal.Id); UpdateSong(songInfo, true, true, imagesOnDisk: true); RunOnUiThread(() => { lyricsLoadingWheel.Visibility = ViewStates.Gone; fabMore.Visibility = ViewStates.Visible; savedView.Visibility = ViewStates.Visible; infoTxt.Visibility = ViewStates.Visible; refreshLayout.Refreshing = false; }); shouldCheck = true; Log(Type.Info, "Done reading from file!"); Analytics.TrackEvent("Finished reading song from file", new Dictionary <string, string> { { "SongID", songInfo.Normal.Id.ToString() } }); }
protected override async void OnResume() { base.OnResume(); Log(Type.Info, "OnResume started"); bool fromNotification = Intent.HasExtra("NotificationSong"); if (fromNotification) { Log(Type.Event, "Attempting to load song from notification"); Analytics.TrackEvent("Opening song from notification", new Dictionary <string, string> { { "NotificationSong", JsonConvert.SerializeObject(notificationSong) } }); notificationSong = JsonConvert.DeserializeObject <SongBundle>(Intent.GetStringExtra("NotificationSong") !); NotificationManagerCompat ntfManager = NotificationManagerCompat.From(this); ntfManager.Cancel(NotificationId); songInfo = notificationSong; previousNtfSong = notificationSong; shouldCheck = false; nowPlayingMode = true; LoadSong(); } }
private async Task OpenInMainActivity(SongBundle song) { Intent intent = new Intent(this, typeof(MainActivity)).SetFlags(ActivityFlags.ReorderToFront); intent.PutExtra("SavedSong", JsonConvert.SerializeObject(song)); StartActivityForResult(intent, 1); }
private void CreateNotification(SongBundle song) { Log(Type.Info, "Creating notification"); Intent info = new Intent(Application.Context, typeof(MainActivity)); info.PutExtra("NotificationSong", JsonConvert.SerializeObject(song)); TaskStackBuilder stackBuilder = TaskStackBuilder.Create(Application.Context); stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(MainActivity))); stackBuilder.AddNextIntent(info); PendingIntent resultIntent = stackBuilder.GetPendingIntent(0, (int)PendingIntentFlags.UpdateCurrent); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ChannelId) .SetAutoCancel(true) .SetContentTitle(Resources?.GetString(Resource.String.app_name)) .SetContentText(song.Normal.Artist + " - " + song.Normal.Title) .SetSmallIcon(Resource.Drawable.ic_stat_name) .SetContentIntent(resultIntent) .SetPriority(-1); ntfManager = NotificationManagerCompat.From(Application.Context); ntfManager.Notify(NotificationId, builder.Build()); Log(Type.Event, "Notification made!"); }
public static async Task <SongBundle> ReadLyrics(int id) { SongBundle song = await GetSongFromTable(id); string path = Path.Combine(Globals.ApplicationPath, Globals.SavedLyricsLocation + song.Normal.Id); //songInfo.Normal is already loaded (from SavedLyrics activity), load lyrics and images from disk //Load songInfo.Romanized lyrics if songInfo.Romanized lyrics were saved StreamReader sr = File.OpenText(path + Globals.LyricsExtension); song.Normal.Lyrics = await sr.ReadToEndAsync(); if (song.Romanized != null) { sr.Dispose(); sr = File.OpenText(path + Globals.RomanizedExtension); song.Romanized.Lyrics = await sr.ReadToEndAsync(); } Logging.Log(Logging.Type.Event, "Read lyrics from file"); sr.Dispose(); //Dispose/close manually since we're not using "using" return(song); }
public static async Task <string> GetSongLyrics(SongBundle song) { HtmlWeb web = new HtmlWeb(); HtmlDocument doc = await web.LoadFromWebAsync("https://genius.com" + song.Normal.Path); Log(Logging.Type.Processing, "Parsing HTML..."); return(await HtmlParsing.ParseHtml(doc)); }
protected override void OnStop() { base.OnStop(); Log(Type.Info, "OnStop started"); notificationSong = new SongBundle(); previousNtfSong = notificationSong; }
private async Task ShowSongDetails() { songInfo = await Genius.GetSongDetails(songInfo); UpdateSong(songInfo, true, true); Log(Type.Info, "Finished getting info from Genius"); Analytics.TrackEvent("Finished getting info from Genius", new Dictionary <string, string> { { "SongID", songInfo.Normal.Id.ToString() } }); }
private async void CheckIfSongIsPlaying() { if (!shouldCheck || !MiscTools.IsInForeground()) { return; } if (notificationSong.Normal.Id == previousNtfSong.Normal.Id) { return; } Log(Type.Event, "Song playing is different, updating..."); nowPlayingMode = true; songInfo = notificationSong; previousNtfSong = notificationSong; bool autoUpdate = Prefs.GetBoolean("auto_update_page", false); if (autoUpdate && nowPlayingMode) { shouldCheck = false; LoadSong(); } else if (nowPlayingMode) { shouldCheck = false; Android.Views.Animations.Animation anim = Animations.BlinkingAnimation(700, 3); npTxt.StartAnimation(anim); Log(Type.Event, "Playing animation on npTxt"); } else if (!nowPlayingMode) { shouldCheck = false; Android.Views.Animations.Animation anim = Animations.BlinkingImageAnimation(500, 4); shineView.Visibility = ViewStates.Visible; shineView.StartAnimation(anim); Log(Type.Event, "Playing animation on shineView"); } }
private async void SearchResults_ItemClick(object sender, AdapterView.ItemClickEventArgs e) { if (welcomeView != null) { dynamicLayout.RemoveView(welcomeView); welcomeView = null; InflateSong(); } RunOnUiThread(() => { dynamicLayout.Visibility = ViewStates.Visible; }); #region UI Variables ProgressBar lyricsLoadingWheel = FindViewById <ProgressBar>(Resource.Id.lyricsLoadingWheel); ListView searchResults = FindViewById <ListView>(Resource.Id.searchResults); #endregion imm.HideSoftInputFromWindow(searchTxt.WindowToken, 0); nowPlayingMode = false; ClearLabels(); songInfo ??= new SongBundle(); songInfo = resultsToView.ElementAt(e.Position); Log(Type.Action, $"Attempting to display song at search position {e.Position}."); Analytics.TrackEvent("Attempting to display song from search", new Dictionary <string, string> { { "SongID", songInfo.Normal.Id.ToString() }, { "ListPosition", e.Position.ToString() } }); RunOnUiThread(() => { lyricsLoadingWheel.Visibility = ViewStates.Visible; searchResults.Visibility = ViewStates.Gone; }); LoadSong(); }
protected override async void OnNewIntent(Intent intent) { base.OnNewIntent(intent); //This should be called when a notification is opened, but that's not happening //Probably because it's not a new intent from a loaded activity, but a new one, you dumb ass if (intent.HasExtra("SavedSong")) { SongBundle saved = JsonConvert.DeserializeObject <SongBundle>(intent.GetStringExtra("SavedSong") ?? Resources.GetString(Resource.String.lyricsErrorOcurred)); songInfo = saved; Log(Type.Event, "Received SavedSongs intent, loading song from disk"); Analytics.TrackEvent("Received SavedSongs intent", new Dictionary <string, string> { { "SongID", saved.Normal.Id.ToString() } }); LoadSong(); } Log(Type.Info, "OnNewIntent started"); }
public static async Task <SongBundle> GetSongDetails(SongBundle song) { Log(Logging.Type.Info, "Starting GetSongDetails operation"); string results = await HttpRequests.GetRequest(GeniusApiUrl + song.Normal.ApiPath, GeniusAuthHeader); JObject parsed = JObject.Parse(results); parsed = (JObject)parsed["response"]?["song"]; //Change root to song Song fromJson = new Song { Title = (string)parsed?.SelectToken("title") ?? "", Artist = (string)parsed?.SelectToken("primary_artist.name") ?? "", Album = (string)parsed?.SelectToken("album.name") ?? "", Header = (string)parsed?.SelectToken("header_image_url") ?? "", Cover = (string)parsed?.SelectToken("song_art_image_url") ?? "", ApiPath = (string)parsed?.SelectToken("api_path") ?? "", Path = (string)parsed?.SelectToken("path") ?? "" }; song.Normal = fromJson; if (parsed != null && parsed["featured_artists"].HasValues) { IList <JToken> parsedList = parsed["featured_artists"].Children().ToList(); song.Normal.FeaturedArtist = "feat. "; foreach (JToken artist in parsedList) { if (song.Normal.FeaturedArtist == "feat. ") { song.Normal.FeaturedArtist += artist["name"]?.ToString(); } else { song.Normal.FeaturedArtist += ", " + artist["name"]; } } Log(Logging.Type.Processing, "Added featured artists to song"); } else { song.Normal.FeaturedArtist = ""; } //Execute all Japanese transliteration tasks at once if (Prefs.GetBoolean("auto_romanize_details", true) && song.Normal.Title.ContainsJapanese() || song.Normal.Artist.ContainsJapanese() || song.Normal.Album.ContainsJapanese()) { Task <string> awaitTitle = song.Normal.Title.StripJapanese(); Task <string> awaitArtist = song.Normal.Artist.StripJapanese(); Task <string> awaitAlbum = song.Normal.Album.StripJapanese(); await Task.WhenAll(awaitTitle, awaitArtist, awaitAlbum); RomanizedSong romanized = new RomanizedSong(); // This snippet is the same in GetAndShowLyrics song.Romanized ??= romanized; romanized.Title = await awaitTitle; romanized.Artist = await awaitArtist; romanized.Album = await awaitAlbum; romanized.Id = song.Normal.Id; song.Romanized = romanized; song.Normal.Romanized = true; Log(Logging.Type.Event, "Romanized song info with ID " + song.Normal.Id); Analytics.TrackEvent("Romanized song info", new Dictionary <string, string> { { "SongID", song.Normal.Id.ToString() } }); } else { song.Romanized = null; } return(song); }
private async void UpdateSong(SongBundle song, bool updateImages, bool updateDetails, bool imagesOnDisk = false) { Log(Type.Info, "Started UpdateSong method for ID " + song.Normal.Id); #region UI Variables TextView songLyrics = FindViewById <TextView>(Resource.Id.songLyrics); TextView songTitle = FindViewById <TextView>(Resource.Id.songTitle); TextView songArtist = FindViewById <TextView>(Resource.Id.songArtist); TextView songAlbum = FindViewById <TextView>(Resource.Id.songAlbum); TextView songFeat = FindViewById <TextView>(Resource.Id.songFeat); #endregion RunOnUiThread(() => { Song toShow = song.Normal; if (song.Normal.Romanized && song.Romanized != null && Prefs.GetBoolean("auto_romanize_details", false)) { Log(Type.Processing, $"Song with ID {songInfo.Normal.Id} has romanization data, using it for display"); toShow = (Song)song.Romanized; } if (!string.IsNullOrEmpty(song.Normal.Lyrics)) { bool romanizedIsValid = songInfo.Romanized != null && !string.IsNullOrEmpty(songInfo.Romanized.Lyrics) && Prefs.GetBoolean("auto_romanize", false); string lyricsToShow = romanizedIsValid ? songInfo.Romanized.Lyrics : songInfo.Normal.Lyrics; songLyrics.TextFormatted = Build.VERSION.SdkInt >= BuildVersionCodes.N ? Html.FromHtml(lyricsToShow, FromHtmlOptions.ModeCompact) #pragma warning disable 618 : Html.FromHtml(lyricsToShow); #pragma warning restore 618 } if (updateDetails) { songTitle.Text = toShow.Title; songArtist.Text = toShow.Artist; songAlbum.Text = toShow.Album; if (string.IsNullOrEmpty(toShow.FeaturedArtist) || string.IsNullOrWhiteSpace(toShow.FeaturedArtist)) { songFeat.Visibility = ViewStates.Gone; } else { songFeat.Visibility = ViewStates.Visible; songFeat.Text = toShow.FeaturedArtist; } } Log(Type.Processing, "Updated labels"); }); if (updateImages) //TODO: Add timer to retry after enough time with images not updated { ImageButton coverView = FindViewById <ImageButton>(Resource.Id.coverView); ImageView headerView = FindViewById <ImageView>(Resource.Id.headerView); ImageView searchView = FindViewById <ImageView>(Resource.Id.searchView); if (imagesOnDisk) { string coverPath = Path.Combine(ApplicationPath + "/" + SavedImagesLocation, song.Normal.Id + CoverSuffix); string headerPath = Path.Combine(ApplicationPath + "/" + SavedImagesLocation, song.Normal.Id + HeaderSuffix); RunOnUiThread(() => { ImageService.Instance.LoadFile(coverPath) .Transform(new RoundedTransformation(CoverRadius)) .Into(coverView); if (File.Exists(headerPath)) { ImageService.Instance.LoadFile(headerPath) .Transform(new BlurredTransformation(HeaderBlur)) .Into(headerView); ImageService.Instance.LoadFile(headerPath) .Transform(new CropTransformation(3, 0, 0)) .Transform(new BlurredTransformation(SearchBlur)) .Transform(new BlurredTransformation(SearchBlur)) .Into(searchView); } else { ImageService.Instance.LoadFile(coverPath) .Transform(new BlurredTransformation(HeaderBlur)) .Into(headerView); ImageService.Instance.LoadFile(coverPath) .Transform(new CropTransformation(3, 0, 0)) .Transform(new BlurredTransformation(SearchBlur)) .Transform(new BlurredTransformation(SearchBlur)) .Into(searchView); } }); Log(Type.Processing, "Updated images from disk"); } else { RunOnUiThread(() => { ImageService.Instance.LoadUrl(song.Normal.Cover) .Transform(new RoundedTransformation(CoverRadius)) .Into(coverView); ImageService.Instance.LoadUrl(song.Normal.Header) .Transform(new BlurredTransformation(HeaderBlur)) .Into(headerView); ImageService.Instance.LoadUrl(song.Normal.Header) .Transform(new CropTransformation(3, 0, 0)) .Transform(new BlurredTransformation(SearchBlur)) .Transform(new BlurredTransformation(SearchBlur)) .Into(searchView); }); Log(Type.Processing, "Updated images from the internet"); } } }
private bool nonGrouped; //TODO: Add persistency to grouping option #region Standard Activity Shit protected override async void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.main_saved); CrossCurrentActivity.Current.Activity = this; //don't remove this, permission stuff needs it #region UI Variables DrawerLayout drawer = FindViewById <DrawerLayout>(Resource.Id.drawer_layout); ImageButton drawerBtn = FindViewById <ImageButton>(Resource.Id.drawerBtn); NavigationView navigationView = FindViewById <NavigationView>(Resource.Id.nav_view); ExpandableListView savedList = FindViewById <ExpandableListView>(Resource.Id.savedList); ImageButton selectGroupingBtn = FindViewById <ImageButton>(Resource.Id.selectGroupingBtn); ListView savedListNonGrouped = FindViewById <ListView>(Resource.Id.savedListNonGrouped); #endregion navigationView.NavigationItemSelected += NavigationView_NavigationViewSelected; drawerBtn.Click += delegate { drawer.OpenDrawer(navigationView); }; selectGroupingBtn.Click += async delegate { if (!nonGrouped) { nonGrouped = true; savedList.Visibility = ViewStates.Gone; savedListNonGrouped.Visibility = ViewStates.Visible; selectGroupingBtn.SetImageDrawable(GetDrawable(Resource.Drawable.ic_ungrouped)); await ShowSavedSongs(); } else { nonGrouped = false; savedList.Visibility = ViewStates.Visible; savedListNonGrouped.Visibility = ViewStates.Gone; selectGroupingBtn.SetImageDrawable(GetDrawable(Resource.Drawable.ic_grouped)); await ShowSavedSongs(); } }; savedList.ChildClick += async delegate(object sender, ExpandableListView.ChildClickEventArgs e) { SongBundle selection = artistSongs.ElementAt(e.GroupPosition).Value[e.ChildPosition]; Log(Type.Action, "Clicked on item from grouped list"); Analytics.TrackEvent("Clicked on item from non-grouped list", new Dictionary <string, string> { { "SongID", selection.Normal.Id.ToString() } }); await OpenInMainActivity(selection); }; savedListNonGrouped.ItemClick += async delegate(object sender, AdapterView.ItemClickEventArgs e) { SongBundle selection = allSongs[e.Position]; Log(Type.Action, "Clicked on item from non-grouped list"); Analytics.TrackEvent("Clicked on item from non-grouped list", new Dictionary <string, string> { { "SongID", selection.Normal.Id.ToString() } }); await OpenInMainActivity(selection); }; }
// Writes a song to the saved lyrics database. // Returns true if successful. //TODO: Add ability to return an error, song already saved or saved successfully messages public static async Task <bool> WriteInfoAndLyrics(SongBundle song) { await MiscTools.CheckAndCreateAppFolders(); InitializeTables(); db = await ReadDatabaseFile(DbPath); rdb = await ReadRomanizedDatabaseFile(RomanizedDbPath); try { if (await GetSongFromTable(song.Normal.Id) == null) { // Write lyrics to file string filepath = Path.Combine(LyricsPath, song.Normal.Id + Globals.LyricsExtension); File.WriteAllText(filepath, song.Normal.Lyrics); if (song.Romanized != null) { string romanizedFilepath = Path.Combine(LyricsPath, song.Normal.Id + Globals.RomanizedExtension); File.WriteAllText(romanizedFilepath, song.Romanized.Lyrics); song.Normal.Romanized = true; rdb.Rows.Add( song.Romanized.Id, song.Romanized.Title, song.Romanized.Artist, song.Romanized.Album, song.Romanized.FeaturedArtist); rdb.WriteXml(RomanizedDbPath); } await WriteImages(song.Normal); db.Rows.Add( song.Normal.Id, song.Normal.Title, song.Normal.Artist, song.Normal.Album, song.Normal.FeaturedArtist, song.Normal.Cover, song.Normal.Header, song.Normal.Romanized, song.Normal.ApiPath, song.Normal.Path); db.WriteXml(DbPath); Logging.Log(Logging.Type.Event, $"Wrote song {song.Normal.Id} to file"); Analytics.TrackEvent("Wrote song to file", new Dictionary <string, string> { { "SongID", song.Normal.Id.ToString() }, { "DBSize", db.Rows.Count.ToString() }, { "RomanizedDBSize", rdb.Rows.Count.ToString() } }); return(true); } else { return(false); } } catch (IOException ex) { Crashes.TrackError(ex, new Dictionary <string, string> { { "SongID", song.Normal.Id.ToString() }, { "Exception", ex.ToString() } }); Logging.Log(Logging.Type.Error, "Exception while writing lyrics to disk!\n" + ex); return(false); } catch (NullReferenceException ex) { Crashes.TrackError(ex, new Dictionary <string, string> { { "SongID", song.Normal.Id.ToString() }, { "Exception", ex.ToString() } }); Logging.Log(Logging.Type.Error, "NullReferenceException while writing lyrics to disk! Reload song and try again.\n" + ex); return(false); } catch (Exception ex) { Crashes.TrackError(ex, new Dictionary <string, string> { { "SongID", song.Normal.Id.ToString() }, { "Exception", ex.ToString() } }); Logging.Log(Logging.Type.Error, "Unkown error while writing song to disk!\n" + ex); return(false); } }