【问题标题】:Android: How do I construct *full* path to pass to Intent.CreateChooserAndroid:如何构造 *full* 路径以传递给 Intent.CreateChooser
【发布时间】:2017-06-02 08:02:55
【问题描述】:

试图让 Android 选择器显示可用的操作以供用户启动存储在我的本地文件夹中的 PDF 文件。 当我传递 /data/user/0/myappappname/files/output.pdf 之类的文件名(当然存在)时,我会得到一个不错的选择器,其中包含所有可以接受的应用程序pdf文件。但是当我选择其中任何一个时,我收到一个错误(来自外部应用程序)文档路径无效。没有抛出异常。

然后我尝试(出于测试目的)将 fname 设置为 /storage/emulated/0/Download/TLCL.pdf (文件也存在),并且一切正常。

起初,我认为这与文件权限有关(因为第一个路径是我的应用程序私有的),但后来我发现标记 ActivityFlags.GrantReadUriPermission 正是为了临时授予而构建的文件访问其他应用程序。还是一样的结果。

由于这是一个 Xamarin.forms 项目,我在文件创建位置的选择上受到限制(我使用 PCLStorage,它总是写入 app-private 本地文件夹),所以我没有在/文档、/下载等。

我显然做错了什么。任何想法表示赞赏。

是否有从系统获取完整路径的选项,包括 /storage/emulated/0 部分(或其他设备上的任何内容)?也许这会有所帮助?


一段代码:

(之前mimeType被定义为“application/pdf”)

 public async Task<bool> LaunchFile(string fname, string mimeType)
        {
            var uri = Android.Net.Uri.Parse("file://" + fname );
            var intent = new Intent(Intent.ActionView);
            intent.SetDataAndType(uri, mimeType);

            intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask | ActivityFlags.GrantReadUriPermission );

            try
            {
                Forms.Context.StartActivity(Intent.CreateChooser(intent, "ChooseApp"));
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("LaunchFile: " + ex.Message);
                return false;
            }

【问题讨论】:

    标签: c# android xamarin.android xamarin.forms


    【解决方案1】:

    我的解决方案可能不是您想要的,它是生成一个文件(在我的情况下是一个 zip 文件),将其导出到公共文件夹,然后将该文件用于选择器。

    使用这些:

    private readonly string PublicDocsPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/AppName";
    private readonly string PrivateDocsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
    

    还有一些基本功能:

        public Stream GetOutputStream(string destFilePath)
        {
            string destFolderPath = Path.GetDirectoryName(destFilePath);
            if (!Directory.Exists(destFolderPath))
                Directory.CreateDirectory(destFolderPath);
            return new FileStream(destFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
        }
        public Stream GetInputStream(string sourceFilePath)
        {
            if (!File.Exists(sourceFilePath)) throw new FileNotFoundException();
            string sourceFolderPath = Path.GetDirectoryName(sourceFilePath);
            return new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
    

    您可以将文件复制到您的公共文件夹(或子文件夹,您只需组装路径)并将该文件用于您的选择器:

        public void SendEmail(string subject, string body, string recipient, string mimeType, string attachmentFilePath, string activityTitle)
        {
            var emailIntent = new Intent(Intent.ActionSendMultiple);
            if (string.IsNullOrEmpty(subject)) throw new ArgumentException();
            emailIntent.PutExtra(Intent.ExtraSubject, subject);
            if (!string.IsNullOrEmpty(recipient))
                emailIntent.PutExtra(Intent.ExtraEmail, new[] { recipient });
            if (!string.IsNullOrEmpty(body))
                emailIntent.PutExtra(Intent.ExtraText, body);
            if (!string.IsNullOrEmpty(attachmentFilePath))
            {
                var file = new Java.IO.File(attachmentFilePath);
                file.SetReadable(true, true);
                var uri = Android.Net.Uri.FromFile(file);
                emailIntent.PutParcelableArrayListExtra(Intent.ExtraStream, new List<IParcelable>(){uri});                              
            }
            emailIntent.SetType(mimeType);
            _activity.StartActivity(Intent.CreateChooser(emailIntent, activityTitle));
        }
    

    这个选择器专门让用户通过电子邮件或谷歌驱动器发送他们的文件,但你可以随意组装它。这个函数的attachmentFilePath和上面GetOutputStream函数传入的字符串是一样的。

    【讨论】:

    • 嘿,这看起来是个不错的解决方法。我明天第一件事就试试看。感谢您花时间和精力。我会等待一段时间,看看是否有人可以解决我原来的问题(不导出到公用文件夹),如果没有 - 这就是我要做的。
    【解决方案2】:

    我们使用的是 Acr.IO 而不是 PCLStorage,我记得它有一个属性可以为您返回完整路径。 我们正在使用的代码如下,但我想知道您是否只是在路径的开头丢失了“file://”,因为我注意到我们的代码中出现了这一点,以及之前的 stackoverflow 对类似问题的回答作为这个,open a PDF in Xamarin.Forms (Android) 我们在 Android 上使用依赖 FileService 并使用此代码打开 PDF:

     public void OpenNatively(string filePath) {
            Android.Net.Uri uri;
            if (filePath.StartsWithHTTP()) {
                uri = Android.Net.Uri.Parse(filePath);
            }
            else {
                 uri = Android.Net.Uri.Parse("file:///" + filePath);
            }
            Intent intent = new Intent(Intent.ActionView);
            var extension = filePath.Substring(filePath.LastIndexOf(".")+1);
            if (extension == "ppt" || extension == "pptx") {
                extension = "vnd.ms-powerpoint";
            }
            var docType = "application/" + extension;
            intent.SetDataAndType(uri, docType);
            intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);
    
            try {
                Xamarin.Forms.Forms.Context.StartActivity(intent);
            }
            catch (Exception e) {
                Toast.MakeText(Xamarin.Forms.Forms.Context, "No Application found to view " + extension.ToUpperInvariant() + " files.", ToastLength.Short).Show();
            }
        }
    

    【讨论】:

    • 谢谢。这和我做的基本一样。问题肯定在于应用程序私有文件夹中创建的文件的访问权限。乍一看,Acr.IO 似乎不错,感谢您提供的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多