Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

GH-130 & GH-129: Android support for safe shareable file URI’s #416

Merged
merged 47 commits into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d330940
Android: Support for safe shareable file URI’s
Redth Jul 26, 2018
b377ce5
Fix absolute type naming
Redth Jul 26, 2018
6695cb9
Add a user interaction test for File Provider
Redth Jul 26, 2018
f40ce30
Fix vibration code
Redth Jul 26, 2018
3147070
Reorder using statements
Redth Jul 26, 2018
e2efa96
Fix test attribute
Redth Jul 26, 2018
ef1be9d
Get provider authority properly
Redth Jul 26, 2018
333d0a0
Added external storage permission
Redth Jul 26, 2018
5d2d142
Change file provider path
Redth Jul 26, 2018
4ba6735
Copy file into temp folder instead of file
Redth Jul 26, 2018
3281aaa
Resgen
Redth Jul 26, 2018
9413620
Merge branch 'master' into feature/android-file-provider
Redth Jul 26, 2018
036bbc1
Merge branch 'master' into feature/android-file-provider
Redth Aug 15, 2018
1f6d48d
Merge branch 'master' into feature/android-file-provider
Redth Aug 16, 2018
f3a22cc
Merge branch 'master' into feature/android-file-provider
mattleibow Sep 19, 2018
ec7ca41
Merge branch 'master' into feature/android-file-provider
jamesmontemagno Sep 19, 2018
5c5b3f2
Permissions may need to be checked to control functionality
mattleibow Sep 20, 2018
652c6a7
The Android FileProvider now can detect permissions
mattleibow Sep 20, 2018
8abd65e
Added support for email attachments
mattleibow Sep 20, 2018
f587716
Added attachments to the sample app
mattleibow Sep 20, 2018
8df5c98
Updated the docs with the new types
mattleibow Sep 20, 2018
494d2c6
Some fixes for iOS
mattleibow Sep 21, 2018
ba85b64
Merge branch 'dev/1.1.0' into feature/android-file-provider
mattleibow Dec 18, 2018
fbf0b8f
Fix the mdoc target
mattleibow Dec 18, 2018
8257a18
regen docs
mattleibow Dec 18, 2018
c4c2c96
remove the obsolete armeabi ABI
mattleibow Dec 18, 2018
5c7b54a
Reworked the file logic to try and use public folders first
mattleibow Dec 20, 2018
00a6a6f
Be more specific with the external storage permission name
mattleibow Dec 20, 2018
b557743
Added some more depth to the comments here
Redth Mar 12, 2019
a56c942
Unnecessary else
Redth Mar 12, 2019
b1ed40e
Added base file info class
Redth Mar 12, 2019
a6044b5
EmailAttachment now derives from FileBase
Redth Mar 12, 2019
3561832
Added File Sharing
Redth Mar 12, 2019
f71f741
Keep track of IStorageFile internally
Redth Mar 12, 2019
13f7896
Prefer internal IStorageFile in UWP
Redth Mar 12, 2019
6c6f1e1
Use attachment name properly in UWP
Redth Mar 12, 2019
267ec59
Add ctor to create from existing FileBase
Redth Mar 12, 2019
880ff39
Add ctors for FileBase
Redth Mar 12, 2019
14cea84
Add ctors for ShareFileRequest
Redth Mar 12, 2019
b8037f0
Merge branch 'dev/1.1.0' into feature/android-file-provider
mattleibow Mar 12, 2019
7ceee2e
We can't use N on pre-N platforms
mattleibow Mar 12, 2019
7fc76d6
Updated the docs
mattleibow Mar 12, 2019
4f4ad8f
Update some docs.
Redth Mar 12, 2019
cf38175
Bump
Redth Mar 12, 2019
4af2507
Gate Email/Share files with feature flags
Redth Mar 13, 2019
2f0c268
Add sample for ShareFileRequest
Redth Mar 13, 2019
e5c1671
Added test for share method in netstandard
Redth Mar 13, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/MainTheme"></application>
</manifest>
3,910 changes: 2,014 additions & 1,896 deletions DeviceTests/DeviceTests.Android/Resources/Resource.designer.cs

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions DeviceTests/DeviceTests.Shared/Android_FileProvider_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Xamarin.Essentials;
using Xunit;

namespace DeviceTests.Shared
{
#if __ANDROID__
public class Android_FileProvider_Tests
{
[Fact]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public void Share_Simple_Text_File_Test()
{
const string TestStr = "Test Text File";

// Save a local cache data directory file
var file = Path.Combine(FileSystem.CacheDirectory, "test.txt");
File.WriteAllText(file, TestStr);

// Actually get a safe shareable file uri
var shareableUri = Platform.GetShareableFileUri(file);

// Launch an intent to let tye user pick where to open this content
var intent = new Android.Content.Intent(Android.Content.Intent.ActionSend);
intent.SetType("text/plain");
intent.PutExtra(Android.Content.Intent.ExtraStream, shareableUri);
intent.PutExtra(Android.Content.Intent.ExtraTitle, TestStr);
intent.SetFlags(Android.Content.ActivityFlags.GrantReadUriPermission);

var intentChooser = Android.Content.Intent.CreateChooser(intent, TestStr);

Platform.AppContext.StartActivity(intentChooser);
}
}
#endif
}
3 changes: 3 additions & 0 deletions Xamarin.Essentials/Permissions/Permissions.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ internal static IEnumerable<string> ToAndroidPermissions(this PermissionType per
case PermissionType.Vibrate:
permissions.Add((Manifest.Permission.Vibrate, true));
break;
case PermissionType.ExternalStorage:
permissions.Add((Manifest.Permission.WriteExternalStorage, false));
break;
}

if (onlyRuntimePermissions)
Expand Down
1 change: 1 addition & 0 deletions Xamarin.Essentials/Permissions/Permissions.shared.enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ enum PermissionType
LocationWhenInUse,
NetworkState,
Vibrate,
ExternalStorage,
}
}
41 changes: 41 additions & 0 deletions Xamarin.Essentials/Platform/Platform.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using Android.Net;
using Android.Net.Wifi;
using Android.OS;
using Android.Support.V4.Content;
using AndroidUri = Android.Net.Uri;

namespace Xamarin.Essentials
{
Expand Down Expand Up @@ -61,6 +63,45 @@ internal static bool IsIntentSupported(Intent intent)
return activities.Any();
}

internal static AndroidUri GetShareableFileUri(string filename)
{
// We require external storage permissions for this API
Permissions.EnsureDeclared(PermissionType.ExternalStorage);

var extDir = AppContext.GetExternalFilesDir("2203693cc04e0be7f4f024d5f9499e13");
var fileInfo = new System.IO.FileInfo(filename);
var tmpDir = System.IO.Path.Combine(extDir.AbsolutePath, Guid.NewGuid().ToString("N"));
var tmpFile = System.IO.Path.Combine(tmpDir, fileInfo.Name);

// Create inner temp dir if we need to
// We create a subdirectory so we can still have a unique path
// but keep the original filename the same
System.IO.Directory.CreateDirectory(tmpDir);

// Copy the original file to a known externals dir that we can share
System.IO.File.Copy(filename, tmpFile);

// Mark the file and directory for deletion on exit
var javaFile = new Java.IO.File(tmpFile);
javaFile.DeleteOnExit();
var javaDir = new Java.IO.File(tmpDir);
javaDir.DeleteOnExit();

var providerAuthority = AppContext.PackageName + ".fileProvider";

if (HasApiLevel(BuildVersionCodes.N))
{
return FileProvider.GetUriForFile(
AppContext.ApplicationContext,
providerAuthority,
javaFile);
}
else
{
return AndroidUri.FromFile(new Java.IO.File(filename));
}
}

internal static bool HasApiLevel(BuildVersionCodes versionCode) =>
(int)Build.VERSION.SdkInt >= (int)versionCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<paths>
<external-path name="2203693cc04e0be7f4f024d5f9499e13" path="" />
</paths>
20 changes: 20 additions & 0 deletions Xamarin.Essentials/Types/FileProvider.android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.App;
using Android.Content;

namespace Xamarin.Essentials.Types
{
[ContentProvider(
new[] { "${applicationId}.fileProvider" },
Name = "xamarin.essentials.fileProvider",
Exported = false,
GrantUriPermissions = true)]
[MetaData(
"android.support.FILE_PROVIDER_PATHS",
Resource = "@xml/xamarin_essentials_fileprovider_file_paths")]
public class FileProvider : global::Android.Support.V4.Content.FileProvider
{
}
}
4 changes: 4 additions & 0 deletions Xamarin.Essentials/Xamarin.Essentials.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,21 @@
<Compile Include="**\*.android.*.cs" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors" />
<AndroidResource Include="Resources\xml\*.xml" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid81')) ">
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="27.0.2" />
<PackageReference Include="Xamarin.Android.Support.Compat" Version="27.0.2" />
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="27.0.2" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid80')) ">
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="26.1.0.1" />
<PackageReference Include="Xamarin.Android.Support.Compat" Version="26.1.0.1" />
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="26.1.0.1" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid71')) ">
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="25.4.0.2" />
<PackageReference Include="Xamarin.Android.Support.Compat" Version="25.4.0.2" />
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="25.4.0.2" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
Expand Down