/// <summary> /// Initializes a new instance of the <see cref="DownloadThread"/> class. /// </summary> /// <param name="info"> /// The info. /// </param> /// <param name="service"> /// The service. /// </param> /// <param name="notification"> /// The notification. /// </param> internal DownloadThread(DownloadInfo info, DownloaderService service, DownloadNotification notification) { this.context = service; this.downloadInfo = info; this.downloaderService = service; this.downloadNotification = notification; this.UserAgent = string.Format("APKXDL (Linux; U; Android {0};{1}; {2}/{3}){4}", Build.VERSION.Release, System.Threading.Thread.CurrentThread.CurrentCulture.Name, Build.Device, Build.Id, this.downloaderService.PackageName); }
/// <summary> /// Initializes a new instance of the <see cref="DownloadThread"/> class. /// </summary> /// <param name="info"> /// The info. /// </param> /// <param name="service"> /// The service. /// </param> /// <param name="notification"> /// The notification. /// </param> internal DownloadThread(DownloadInfo info, DownloaderService service, DownloadNotification notification) { this.context = service; this.downloadInfo = info; this.downloaderService = service; this.downloadNotification = notification; this.UserAgent = string.Format("APKXDL (Linux; U; Android {0};{1}; {2}/{3}){4}", Build.VERSION.Release, System.Threading.Thread.CurrentThread.CurrentCulture.Name, Build.Device, Build.Id, this.downloaderService.PackageName); }
/// <summary> /// The on create. /// </summary> public override void OnCreate() { base.OnCreate(); try { this.packageInfo = this.PackageManager.GetPackageInfo(this.PackageName, 0); string applicationLabel = this.PackageManager.GetApplicationLabel(this.ApplicationInfo); this.downloadNotification = new DownloadNotification(this, applicationLabel); } catch (PackageManager.NameNotFoundException e) { Log.Error(Tag, e, "Oh oh!"); } }
/// <summary> /// Downloads a beatmap. /// This will post notifications tracking progress. /// </summary> /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param> /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param> /// <returns>Downloading can happen</returns> public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) { var existing = GetExistingDownload(beatmapSetInfo); if (existing != null || api == null) { return(false); } var downloadNotification = new DownloadNotification { Text = $"Downloading {beatmapSetInfo}", }; var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); request.DownloadProgressed += progress => { downloadNotification.State = ProgressNotificationState.Active; downloadNotification.Progress = progress; }; request.Success += filename => { Task.Factory.StartNew(() => { // This gets scheduled back to the update thread, but we want the import to run in the background. Import(downloadNotification, filename); currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += error => { BeatmapDownloadFailed?.Invoke(request); if (error is OperationCanceledException) { return; } downloadNotification.State = ProgressNotificationState.Cancelled; Logger.Error(error, "Beatmap download failed!"); currentDownloads.Remove(request); }; downloadNotification.CancelRequested += () => { request.Cancel(); currentDownloads.Remove(request); downloadNotification.State = ProgressNotificationState.Cancelled; return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(downloadNotification); // don't run in the main api queue as this is a long-running task. Task.Factory.StartNew(() => { try { request.Perform(api); } catch { // no need to handle here as exceptions will filter down to request.Failure above. } }, TaskCreationOptions.LongRunning); BeatmapDownloadBegan?.Invoke(request); return(true); }
/// <summary> /// Begin a download for the requested <typeparamref name="TModel"/>. /// </summary> /// <param name="model">The <typeparamref name="TModel"/> to be downloaded.</param> /// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param> /// <returns>Whether the download was started.</returns> public bool Download(TModel model, bool minimiseDownloadSize = false) { if (!canDownload(model)) { return(false); } var request = CreateDownloadRequest(model, minimiseDownloadSize); DownloadNotification notification = new DownloadNotification { Text = $"Downloading {request.Model}", }; request.DownloadProgressed += progress => { notification.State = ProgressNotificationState.Active; notification.Progress = progress; }; request.Success += filename => { Task.Factory.StartNew(async() => { // This gets scheduled back to the update thread, but we want the import to run in the background. var imported = await Import(notification, filename); // for now a failed import will be marked as a failed download for simplicity. if (!imported.Any()) { downloadFailed.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request); } currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += triggerFailure; notification.CancelRequested += () => { request.Cancel(); return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(notification); api.PerformAsync(request); downloadBegan.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request); return(true); void triggerFailure(Exception error) { currentDownloads.Remove(request); downloadFailed.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request); notification.State = ProgressNotificationState.Cancelled; if (!(error is OperationCanceledException)) { Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!"); } } }
/// <summary> /// Downloads a beatmap. /// This will post notifications tracking progress. /// </summary> /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param> /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param> /// <returns>Downloading can happen</returns> public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) { var existing = GetExistingDownload(beatmapSetInfo); if (existing != null || api == null) { return(false); } if (!api.LocalUser.Value.IsSupporter) { PostNotification?.Invoke(new SimpleNotification { Icon = FontAwesome.fa_superpowers, Text = "You gotta be an osu!supporter to download for now 'yo" }); return(false); } var downloadNotification = new DownloadNotification { CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", }; var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); request.DownloadProgressed += progress => { downloadNotification.State = ProgressNotificationState.Active; downloadNotification.Progress = progress; }; request.Success += data => { downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; Task.Factory.StartNew(() => { BeatmapSetInfo importedBeatmap; // This gets scheduled back to the update thread, but we want the import to run in the background. using (var stream = new MemoryStream(data)) using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString())) importedBeatmap = Import(archive); downloadNotification.CompletionClickAction = () => { PresentCompletedImport(importedBeatmap.Yield()); return(true); }; downloadNotification.State = ProgressNotificationState.Completed; currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += error => { BeatmapDownloadFailed?.Invoke(request); if (error is OperationCanceledException) { return; } downloadNotification.State = ProgressNotificationState.Cancelled; Logger.Error(error, "Beatmap download failed!"); currentDownloads.Remove(request); }; downloadNotification.CancelRequested += () => { request.Cancel(); currentDownloads.Remove(request); downloadNotification.State = ProgressNotificationState.Cancelled; return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(downloadNotification); // don't run in the main api queue as this is a long-running task. Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); BeatmapDownloadBegan?.Invoke(request); return(true); }
/// <summary> /// The on create. /// </summary> public override void OnCreate() { base.OnCreate(); try { this.packageInfo = this.PackageManager.GetPackageInfo(this.PackageName, 0); string applicationLabel = this.PackageManager.GetApplicationLabel(this.ApplicationInfo); this.downloadNotification = new DownloadNotification(this, applicationLabel); } catch (PackageManager.NameNotFoundException e) { Log.Error(Tag, e, "Oh oh!"); } }
/// <summary> /// Downloads a beatmap. /// This will post notifications tracking progress. /// </summary> /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param> /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param> /// <returns>Downloading can happen</returns> public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) { var existing = GetExistingDownload(beatmapSetInfo); if (existing != null || api == null) { return(false); } var downloadNotification = new DownloadNotification { CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", }; var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); request.DownloadProgressed += progress => { downloadNotification.State = ProgressNotificationState.Active; downloadNotification.Progress = progress; }; request.Success += filename => { downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; Task.Factory.StartNew(() => { // This gets scheduled back to the update thread, but we want the import to run in the background. var importedBeatmap = Import(filename); downloadNotification.CompletionClickAction = () => { PresentCompletedImport(importedBeatmap.Yield()); return(true); }; downloadNotification.State = ProgressNotificationState.Completed; currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += error => { BeatmapDownloadFailed?.Invoke(request); if (error is OperationCanceledException) { return; } downloadNotification.State = ProgressNotificationState.Cancelled; Logger.Error(error, "Beatmap download failed!"); currentDownloads.Remove(request); }; downloadNotification.CancelRequested += () => { request.Cancel(); currentDownloads.Remove(request); downloadNotification.State = ProgressNotificationState.Cancelled; return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(downloadNotification); // don't run in the main api queue as this is a long-running task. Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); BeatmapDownloadBegan?.Invoke(request); return(true); }
/// <summary> /// Begin a download for the requested <see cref="TModel"/>. /// </summary> /// <param name="model">The <see cref="TModel"/> to be downloaded.</param> /// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param> /// <returns>Whether the download was started.</returns> public bool Download(TModel model, bool minimiseDownloadSize = false) { if (!canDownload(model)) { return(false); } var request = CreateDownloadRequest(model, minimiseDownloadSize); DownloadNotification notification = new DownloadNotification { Text = $"Downloading {request.Model}", }; request.DownloadProgressed += progress => { notification.State = ProgressNotificationState.Active; notification.Progress = progress; }; request.Success += filename => { Task.Factory.StartNew(async() => { // This gets scheduled back to the update thread, but we want the import to run in the background. await Import(notification, filename); currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += error => { DownloadFailed?.Invoke(request); if (error is OperationCanceledException) { return; } notification.State = ProgressNotificationState.Cancelled; Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!"); currentDownloads.Remove(request); }; notification.CancelRequested += () => { request.Cancel(); currentDownloads.Remove(request); notification.State = ProgressNotificationState.Cancelled; return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(notification); Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); DownloadBegan?.Invoke(request); return(true); }