mirror of
https://github.com/ppy/osu.git
synced 2025-03-14 05:07:26 +08:00
Merge pull request #11212 from Game4all/android-multiple-import-support-suppot
Add ability to import multiple files at once on android
This commit is contained in:
commit
1dc2af57a6
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
|
||||
@ -12,13 +13,14 @@ using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Views;
|
||||
using osu.Framework.Android;
|
||||
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, LaunchMode = LaunchMode.SingleInstance)]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
||||
public class OsuGameActivity : AndroidGameActivity
|
||||
{
|
||||
@ -54,43 +56,59 @@ namespace osu.Android
|
||||
{
|
||||
case Intent.ActionDefault:
|
||||
if (intent.Scheme == ContentResolver.SchemeContent)
|
||||
handleImportFromUri(intent.Data);
|
||||
handleImportFromUris(intent.Data);
|
||||
else if (osu_url_schemes.Contains(intent.Scheme))
|
||||
game.HandleLink(intent.DataString);
|
||||
break;
|
||||
|
||||
case Intent.ActionSend:
|
||||
case Intent.ActionSendMultiple:
|
||||
{
|
||||
var content = intent.ClipData?.GetItemAt(0);
|
||||
if (content != null)
|
||||
handleImportFromUri(content.Uri);
|
||||
var uris = new List<Uri>();
|
||||
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<ImportTask>();
|
||||
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
|
@ -115,13 +115,13 @@ namespace osu.Game.Database
|
||||
return Import(notification, paths.Select(p => new ImportTask(p)).ToArray());
|
||||
}
|
||||
|
||||
public Task Import(Stream stream, string filename)
|
||||
public Task Import(params ImportTask[] tasks)
|
||||
{
|
||||
var notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
return Import(notification, new ImportTask(stream, filename));
|
||||
return Import(notification, tasks);
|
||||
}
|
||||
|
||||
protected async Task<IEnumerable<TModel>> Import(ProgressNotification notification, params ImportTask[] tasks)
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@ -19,11 +18,10 @@ namespace osu.Game.Database
|
||||
Task Import(params string[] paths);
|
||||
|
||||
/// <summary>
|
||||
/// Import the provided stream as a simple item.
|
||||
/// Import the specified files from the given import tasks.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to import files from. Should be in a supported archive format.</param>
|
||||
/// <param name="filename">The filename of the archive being imported.</param>
|
||||
Task Import(Stream stream, string filename);
|
||||
/// <param name="tasks">The import tasks from which the files should be imported.</param>
|
||||
Task Import(params ImportTask[] tasks);
|
||||
|
||||
/// <summary>
|
||||
/// An array of accepted file extensions (in the standard format of ".abc").
|
||||
|
@ -51,7 +51,7 @@ using osu.Game.Screens.Select;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Utils;
|
||||
using LogLevel = osu.Framework.Logging.LogLevel;
|
||||
using System.IO;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game
|
||||
{
|
||||
@ -438,10 +438,10 @@ namespace osu.Game
|
||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
||||
}
|
||||
|
||||
public override Task Import(Stream stream, string filename)
|
||||
public override Task Import(params ImportTask[] imports)
|
||||
{
|
||||
// encapsulate task as we don't want to begin the import process until in a ready state.
|
||||
var importTask = new Task(async () => await base.Import(stream, filename));
|
||||
var importTask = new Task(async () => await base.Import(imports));
|
||||
|
||||
waitForReady(() => this, _ => importTask.Start());
|
||||
|
||||
|
@ -419,15 +419,14 @@ namespace osu.Game
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task Import(Stream stream, string filename)
|
||||
public virtual async Task Import(params ImportTask[] tasks)
|
||||
{
|
||||
var extension = Path.GetExtension(filename)?.ToLowerInvariant();
|
||||
|
||||
foreach (var importer in fileImporters)
|
||||
var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant());
|
||||
await Task.WhenAll(tasksPerExtension.Select(taskGroup =>
|
||||
{
|
||||
if (importer.HandledExtensions.Contains(extension))
|
||||
await importer.Import(stream, Path.GetFileNameWithoutExtension(filename));
|
||||
}
|
||||
var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key));
|
||||
return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask;
|
||||
}));
|
||||
}
|
||||
|
||||
public IEnumerable<string> HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions);
|
||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task ICanAcceptFiles.Import(Stream stream, string filename) => throw new NotImplementedException();
|
||||
Task ICanAcceptFiles.Import(params ImportTask[] tasks) => throw new NotImplementedException();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user