Skip to content

Commit

Permalink
Feat download directory (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShortDevelopment authored Sep 1, 2023
1 parent d6f1394 commit 60250d3
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 74 deletions.
42 changes: 42 additions & 0 deletions Nearby Sharing Windows/FileUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Android.Content;
using Android.Provider;
using ShortDev.Microsoft.ConnectedDevices.NearShare;
using Environment = Android.OS.Environment;

namespace Nearby_Sharing_Windows;

internal static class FileUtils
{
public static CdpFileProvider CreateNearShareFileFromContentUriAsync(this ContentResolver contentResolver, AndroidUri contentUri)
{
var fileName = contentResolver.QueryContentName(contentUri);

using var fd = contentResolver.OpenAssetFileDescriptor(contentUri, "r") ?? throw new IOException("Could not open file");
var stream = fd.CreateInputStream() ?? throw new IOException("Could not open input stream");

return CdpFileProvider.FromStream(fileName, stream);
}

public static string QueryContentName(this ContentResolver resolver, AndroidUri contentUri)
{
using var returnCursor = resolver.Query(contentUri, new[] { IOpenableColumns.DisplayName }, null, null, null) ?? throw new InvalidOperationException("Could not open content cursor");
returnCursor.MoveToFirst();
return returnCursor.GetString(0) ?? throw new IOException("Could not query content name");
}

public static Stream CreateDownloadFile(this Activity activity, string fileName, ulong size)
{
var filePath = Path.Combine(activity.GetDownloadDirectory().FullName, fileName);
return File.Create(filePath);
}

public static DirectoryInfo GetDownloadDirectory(this Activity activity)
{
var publicDownloadDir = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDownloads)?.AbsolutePath;
DirectoryInfo downloadDir = new(publicDownloadDir ?? Path.Combine(activity.GetExternalMediaDirs()?.FirstOrDefault()?.AbsolutePath ?? "/sdcard/", "Download"));
if (!downloadDir.Exists)
downloadDir.Create();

return downloadDir;
}
}
28 changes: 3 additions & 25 deletions Nearby Sharing Windows/ReceiveActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,7 @@ void onCompleted()
acceptButton.Visibility = ViewStates.Gone;
loadingProgressIndicator.Visibility = ViewStates.Gone;
openButton.Visibility = ViewStates.Visible;
openButton.SetOnClickListener(new DelegateClickListener((s, e) =>
{
var firstFilePath = GetFilePath(fileTransfer.Files[0].Name);
if (fileTransfer.Files.Count == 1)
UIHelper.OpenFile(this, firstFilePath);
// ToDo: UIHelper.OpenDirectory(this, Path.GetDirectoryName(firstFilePath));
}));
openButton.Click += (_, _) => this.ViewDownloads();
}
if (fileTransfer.IsTransferComplete)
onCompleted();
Expand Down Expand Up @@ -190,23 +183,8 @@ void InitializeCDP()
);
}

string GetFilePath(string name)
=> Path.Combine(
this.GetDownloadDirectory().FullName,
name
);

IReadOnlyList<FileStream> CreateFiles(FileTransferToken token)
{
List<FileStream> streams = new((int)token.TotalFilesToSend);
foreach (var file in token)
{
var path = GetFilePath(file.Name);
Log($"Saving file to \"{path}\"");
streams.Add(File.Create(path));
}
return streams;
}
IReadOnlyList<Stream> CreateFiles(FileTransferToken token)
=> token.Select(file => this.CreateDownloadFile(file.Name, file.Size)).ToArray();

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
Expand Down
20 changes: 2 additions & 18 deletions Nearby Sharing Windows/SendActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,23 +159,7 @@ private void UpdateUI()
}
#endregion

async Task<CdpFileProvider> CreateNearShareFileFromContentUriAsync(AndroidUri contentUri)
{
var fileName = QueryContentName(ContentResolver!, contentUri);

using var contentStream = ContentResolver!.OpenInputStream(contentUri) ?? throw new InvalidOperationException("Could not open input stream");
var buffer = await Task.Run(() => EndianReader.ReadToEnd(contentStream));

return CdpFileProvider.FromBuffer(fileName, buffer);
}

static string QueryContentName(ContentResolver resolver, AndroidUri contentUri)
{
using var returnCursor = resolver.Query(contentUri, null, null, null, null) ?? throw new InvalidOperationException("Could not open content cursor");
int nameIndex = returnCursor.GetColumnIndex(IOpenableColumns.DisplayName);
returnCursor.MoveToFirst();
return returnCursor.GetString(nameIndex) ?? throw new InvalidOperationException("Could not query content name");
}

readonly CancellationTokenSource _fileSendCancellationTokenSource = new();
private async void SendData(CdpDevice remoteSystem)
Expand All @@ -197,7 +181,7 @@ private async void SendData(CdpDevice remoteSystem)
AndroidUri file = Intent.GetParcelableExtra<AndroidUri>(Intent.ExtraStream)!;
fileTransferOperation = NearShareSender.SendFileAsync(
remoteSystem,
await CreateNearShareFileFromContentUriAsync(file),
ContentResolver!.CreateNearShareFileFromContentUriAsync(file),
fileSendProgress,
_fileSendCancellationTokenSource.Token
);
Expand Down Expand Up @@ -228,7 +212,7 @@ await CreateNearShareFileFromContentUriAsync(file),
var files = Intent.GetParcelableArrayListExtra<AndroidUri>(Intent.ExtraStream) ?? throw new InvalidDataException("Could not get extra files from intent");
fileTransferOperation = NearShareSender.SendFilesAsync(
remoteSystem,
await Task.WhenAll(files.Select(CreateNearShareFileFromContentUriAsync)),
files.Select(x => ContentResolver!.CreateNearShareFileFromContentUriAsync(x)).ToArray(),
fileSendProgress,
_fileSendCancellationTokenSource.Token
);
Expand Down
26 changes: 4 additions & 22 deletions Nearby Sharing Windows/UIHelper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Text;
using Android.Views;
using AndroidX.AppCompat.App;
using AndroidX.Browser.CustomTabs;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using Google.Android.Material.Dialog;
using Nearby_Sharing_Windows.Settings;
using CompatToolbar = AndroidX.AppCompat.Widget.Toolbar;
Expand Down Expand Up @@ -62,7 +60,7 @@ public static void DisplayWebSite(Activity activity, string url)
{
CustomTabsIntent intent = new CustomTabsIntent.Builder()
.Build();
intent.LaunchUrl(activity, Android.Net.Uri.Parse(url));
intent.LaunchUrl(activity, AndroidUri.Parse(url));
}

public static void OpenLocaleSettings(Activity activity)
Expand Down Expand Up @@ -91,20 +89,10 @@ public static void OpenLocaleSettings(Activity activity)
}
}

public static void OpenFile(Activity activity, string path)
public static void ViewDownloads(this Activity activity)
{
Intent intent = new(Intent.ActionView);
var contentUri = FileProvider.GetUriForFile(activity, "de.shortdev.nearshare.FileProvider", new Java.IO.File(path))!;

var mimeType = activity.ContentResolver?.GetType(contentUri);
if (string.IsNullOrEmpty(mimeType))
intent.SetData(contentUri);
else
intent.SetDataAndType(contentUri, mimeType);

intent.SetFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.NewTask);
var chooserIntent = Intent.CreateChooser(intent, $"Open {Path.GetFileName(path)}");
activity.StartActivity(chooserIntent);
Intent intent = new(DownloadManager.ActionViewDownloads);
activity.StartActivity(intent);
}

public static void SetupToolBar(AppCompatActivity activity, string? subtitle = null)
Expand Down Expand Up @@ -163,10 +151,4 @@ public static ISpanned LoadHtmlAsset(Activity activity, string assetPath)

public static string Localize(this Activity activity, int resId, params object[] args)
=> string.Format(activity.GetString(resId), args);

public static DirectoryInfo GetDownloadDirectory(this Activity activity)
{
DirectoryInfo rootDir = new(Path.Combine(activity.GetExternalMediaDirs()?.FirstOrDefault()?.AbsolutePath ?? "/sdcard/"));
return rootDir.CreateSubdirectory("Download");
}
}
37 changes: 31 additions & 6 deletions ShortDev.Microsoft.ConnectedDevices.NearShare/CdpFileProvider.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text;
using System.IO;
using System.Text;

namespace ShortDev.Microsoft.ConnectedDevices.NearShare;

public sealed class CdpFileProvider
public sealed class CdpFileProvider : IDisposable
{
readonly ReadOnlyMemory<byte> _buffer;
private CdpFileProvider(string fileName, ReadOnlyMemory<byte> buffer)
readonly Stream _buffer;
private CdpFileProvider(string fileName, Stream buffer)
{
FileName = fileName;
_buffer = buffer;
Expand All @@ -21,13 +22,37 @@ public static CdpFileProvider FromContent(string fileName, string content, Encod
}

public static CdpFileProvider FromBuffer(string fileName, ReadOnlyMemory<byte> buffer)
=> new(fileName, buffer);
{
MemoryStream stream = new();
stream.Write(buffer.Span);
return FromStream(fileName, stream);
}

public static CdpFileProvider FromStream(string fileName, Stream stream)
{
if (!stream.CanSeek)
throw new ArgumentException("Stream can't seek", nameof(stream));
if (!stream.CanRead)
throw new ArgumentException("Stream can't read", nameof(stream));

return new(fileName, stream);
}

public string FileName { get; }

public ulong FileSize
=> (ulong)_buffer.Length;

public ReadOnlySpan<byte> ReadBlob(ulong start, uint length)
=> _buffer.Slice((int)start, (int)length).Span;
{
Span<byte> buffer = new byte[length];
_buffer.Position = (long)start;
_buffer.Read(buffer);
return buffer;
}

public void Dispose()
{
_buffer.Dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ static decimal CalcSize(ulong size, uint unit)
#endregion

#region Acceptance
readonly TaskCompletionSource<IReadOnlyList<FileStream>> _promise = new();
readonly TaskCompletionSource<IReadOnlyList<Stream>> _promise = new();
internal async ValueTask AwaitAcceptance()
=> await _promise.Task;

public bool IsAccepted
=> _promise.Task.IsCompletedSuccessfully;

public void Accept(IReadOnlyList<FileStream> fileStream)
public void Accept(IReadOnlyList<Stream> fileStream)
{
if (fileStream.Count != TotalFilesToSend)
throw new ArgumentException("Invalid number of streams", nameof(fileStream));
Expand All @@ -58,7 +58,7 @@ public void Accept(IReadOnlyList<FileStream> fileStream)
public void Cancel()
=> _promise.SetCanceled();

internal FileStream GetStream(uint contentId)
internal Stream GetStream(uint contentId)
{
for (int i = 0; i < Files.Count; i++)
{
Expand Down

0 comments on commit 60250d3

Please sign in to comment.