Xamarin.Android:使用 Android 10+ 中的 Intents 打开 Excel 文档失败
Xamarin.Android: Opening an Excel document using Intents in Android 10+ fails
我有以下导出 Excel 文件的代码。最终结果在ExcelApp中打开。这是打开文件的代码:
if (file.Exists())
Intent intent = new Intent(Intent.ActionView);
if (Build.VERSION.SdkInt < BuildVersionCodes.N)
{
intent.SetDataAndType(Android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
else
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.SetDataAndType(Android.Net.Uri.Parse(file.Path), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));
}
我知道文件已创建,因为我没有收到任何错误并正确地到达了这一点。此外,我已经添加了标志 FLAG_GRANT_READ_URI_PERMISSION
/GrantReadUriPermission
,当我尝试打开文件时,我收到此消息:
Can't open file | Try saving the file on the device and then opening
it
这是我的一部分 AndroidManifest.xml
<application android:theme="@style/Launcher" android:icon="@drawable/icon" android:label="@string/app_name" android:requestLegacyExternalStorage="true">
<activity android:name="tk.supernova.tmtimer.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity>
</application>
而且我已经申请了 Read/Write 权限:
private readonly string[] PermissionToCheck = {
Android.Manifest.Permission.WriteExternalStorage,
Android.Manifest.Permission.ReadExternalStorage
};
private const int REQUEST_ID = 0;
private const int STORAGE_PERMISSION_CODE = 23;
protected override void OnCreate(Bundle savedInstanceState)
{
Platform.Init(this, savedInstanceState);
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
RequestPermissions(PermissionToCheck, REQUEST_ID);
}
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
try
{
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
.PenaltyDeathOnFileUriExposure()
.DetectFileUriExposure()
.Build();
StrictMode.SetVmPolicy(policy);
}
catch { }
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
知道我做错了什么吗?谢谢。
根据@blackapps 的建议,我做了以下修改。
#1。要在 AndroidManifest.xml
中添加新部分:
<provider android:name="androidx.core.content.FileProvider" android:authorities="tk.supernova.tmtimer.tk.supernova.tmtimer.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
#2。在名为 file_paths 的 XML 文件夹中创建一个新文件,其中包含:
<?xml version="1.0" encoding="UTF-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_meetings" path="meetings/" />
</paths>
#3。这会更改为正确打开和保存文件:
private File CreateDirFile(string fileName)
{
var meetingPath = new File(Xamarin.Essentials.FileSystem.AppDataDirectory, "meetings");
meetingPath.Mkdir();
var file = new File(meetingPath, fileName);
if (file.Exists())
{
file.Delete();
}
file.CreateNewFile();
return file;
}
private void Save(string fileName, string contentType, MemoryStream stream, Context context)
{
var file = CreateDirFile(fileName);
try
{
FileOutputStream outs = new FileOutputStream(file, false);
outs.Write(stream.ToArray());
outs.Flush();
outs.Close();
}
catch
{
Toast.MakeText(context, context.GetString(Resource.String.LblStorageIssue), ToastLength.Long).Show();
}
if (file.Exists() && contentType != APP_TYPE)
{
Intent intent = new Intent(Intent.ActionView);
if (Build.VERSION.SdkInt < BuildVersionCodes.N)
{
intent.SetDataAndType(Android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
else
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
var contentUri = FileProvider.GetUriForFile(Application.Context, FILE_PROVIDER, file);
intent.SetDataAndType(contentUri, MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));
}
}
#4。我删除了严格的部分。
我有以下导出 Excel 文件的代码。最终结果在ExcelApp中打开。这是打开文件的代码:
if (file.Exists())
Intent intent = new Intent(Intent.ActionView);
if (Build.VERSION.SdkInt < BuildVersionCodes.N)
{
intent.SetDataAndType(Android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
else
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.SetDataAndType(Android.Net.Uri.Parse(file.Path), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));
}
我知道文件已创建,因为我没有收到任何错误并正确地到达了这一点。此外,我已经添加了标志 FLAG_GRANT_READ_URI_PERMISSION
/GrantReadUriPermission
,当我尝试打开文件时,我收到此消息:
Can't open file | Try saving the file on the device and then opening it
这是我的一部分 AndroidManifest.xml
<application android:theme="@style/Launcher" android:icon="@drawable/icon" android:label="@string/app_name" android:requestLegacyExternalStorage="true">
<activity android:name="tk.supernova.tmtimer.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity>
</application>
而且我已经申请了 Read/Write 权限:
private readonly string[] PermissionToCheck = {
Android.Manifest.Permission.WriteExternalStorage,
Android.Manifest.Permission.ReadExternalStorage
};
private const int REQUEST_ID = 0;
private const int STORAGE_PERMISSION_CODE = 23;
protected override void OnCreate(Bundle savedInstanceState)
{
Platform.Init(this, savedInstanceState);
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
RequestPermissions(PermissionToCheck, REQUEST_ID);
}
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
try
{
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
.PenaltyDeathOnFileUriExposure()
.DetectFileUriExposure()
.Build();
StrictMode.SetVmPolicy(policy);
}
catch { }
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
知道我做错了什么吗?谢谢。
根据@blackapps 的建议,我做了以下修改。
#1。要在 AndroidManifest.xml
中添加新部分:
<provider android:name="androidx.core.content.FileProvider" android:authorities="tk.supernova.tmtimer.tk.supernova.tmtimer.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
#2。在名为 file_paths 的 XML 文件夹中创建一个新文件,其中包含:
<?xml version="1.0" encoding="UTF-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_meetings" path="meetings/" />
</paths>
#3。这会更改为正确打开和保存文件:
private File CreateDirFile(string fileName)
{
var meetingPath = new File(Xamarin.Essentials.FileSystem.AppDataDirectory, "meetings");
meetingPath.Mkdir();
var file = new File(meetingPath, fileName);
if (file.Exists())
{
file.Delete();
}
file.CreateNewFile();
return file;
}
private void Save(string fileName, string contentType, MemoryStream stream, Context context)
{
var file = CreateDirFile(fileName);
try
{
FileOutputStream outs = new FileOutputStream(file, false);
outs.Write(stream.ToArray());
outs.Flush();
outs.Close();
}
catch
{
Toast.MakeText(context, context.GetString(Resource.String.LblStorageIssue), ToastLength.Long).Show();
}
if (file.Exists() && contentType != APP_TYPE)
{
Intent intent = new Intent(Intent.ActionView);
if (Build.VERSION.SdkInt < BuildVersionCodes.N)
{
intent.SetDataAndType(Android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
else
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
var contentUri = FileProvider.GetUriForFile(Application.Context, FILE_PROVIDER, file);
intent.SetDataAndType(contentUri, MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
}
context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));
}
}
#4。我删除了严格的部分。