From 9d8906924580cc3886759de27dbbb05fb5460b00 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 16 Dec 2020 20:33:29 +0100 Subject: [PATCH] Add ability to import multiple files at once on android. --- osu.Android/OsuGameActivity.cs | 63 ++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index bd5523f0e2..bf73f33b74 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Android.App; using Android.Content; @@ -16,7 +18,7 @@ using osu.Game.Database; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { private OsuGameAndroid game; @@ -49,41 +51,64 @@ namespace osu.Android { case Intent.ActionDefault: if (intent.Scheme == ContentResolver.SchemeContent) - handleImportFromUri(intent.Data); + handleImportFromUris(intent.Data); break; case Intent.ActionSend: { var content = intent.ClipData?.GetItemAt(0); if (content != null) - handleImportFromUri(content.Uri); + handleImportFromUris(content.Uri); + break; + } + + case Intent.ActionSendMultiple: + { + var uris = new List(); + for (int i = 0; i < intent.ClipData?.ItemCount; i++) + { + var content = intent.ClipData?.GetItemAt(i); + if (content != null) + uris.Add(content.Uri); + } + handleImportFromUris(uris.ToArray()); break; } } } - private void handleImportFromUri(Uri uri) => Task.Factory.StartNew(async () => + private void handleImportFromUris(params Uri[] uris) => Task.Factory.StartNew(async () => { - // there are more performant overloads of this method, but this one is the most backwards-compatible - // (dates back to API 1). - var cursor = ContentResolver?.Query(uri, null, null, null, null); + var tasks = new List(); - if (cursor == null) - return; + await Task.WhenAll(uris.Select(async uri => + { + // there are more performant overloads of this method, but this one is the most backwards-compatible + // (dates back to API 1). + var cursor = ContentResolver?.Query(uri, null, null, null, null); - cursor.MoveToFirst(); + if (cursor == null) + return; - var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); - string filename = cursor.GetString(filenameColumn); + cursor.MoveToFirst(); - // SharpCompress requires archive streams to be seekable, which the stream opened by - // OpenInputStream() seems to not necessarily be. - // copy to an arbitrary-access memory stream to be able to proceed with the import. - var copy = new MemoryStream(); - using (var stream = ContentResolver.OpenInputStream(uri)) - await stream.CopyToAsync(copy); + var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); + string filename = cursor.GetString(filenameColumn); - await game.Import(new ImportTask(copy, filename)); + // SharpCompress requires archive streams to be seekable, which the stream opened by + // OpenInputStream() seems to not necessarily be. + // copy to an arbitrary-access memory stream to be able to proceed with the import. + var copy = new MemoryStream(); + using (var stream = ContentResolver.OpenInputStream(uri)) + await stream.CopyToAsync(copy); + + lock (tasks) + { + tasks.Add(new ImportTask(copy, filename)); + } + })); + + await game.Import(tasks.ToArray()); }, TaskCreationOptions.LongRunning); } }