androidxamarinxamarin.formslaunching-applicationandroid-storage

Xamarin.Forms save file (pdf) in local storage and open with the default viewer


Edited to fine the question

In Android, I need to download a pdf file to "Documents" folder and open it with the default program. I downloaded the document and stored it in a Byte array. Then I call this method:

    /// <summary>
    /// Save data bytes on "My documents" folder and open
    /// </summary>
    /// <param name="fileName">Filename, for example: "Test.pdf"</param>
    /// <param name="data">Array of bytes to save</param>
    /// <returns>True if success or false if error</returns>
    public bool SaveAndOpen(string fileName, byte[] data)
    {
        try
        {
            // Get my documents path
            string filePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
            // Combine with filename
            string fullName = Path.Combine(filePath, fileName);

            // If the file exist on device delete it
            if (File.Exists(fullName))
            {
                // Note: In the second run of this method, the file exists
                File.Delete(fullName);
            }

            // Write bytes on "My documents"
            File.WriteAllBytes(fullName, data);

            // Launch file as process
            Process.Start(fullName);

            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message + ex.StackTrace);
            return false;
        }
    }

The problem is to open the pdf document with the default pdf reader.

The line "Process.Start(fullName);" throws an exception

System.ComponentModel.Win32Exception (0x80004005): Access denied

Thanks for your time!


Solution

  • I found a way to save and open the file:

        /// <summary>
        /// Save data bytes on "My documents" folder and open
        /// </summary>
        /// <param name="fileName">Filename, for example: "Test.pdf"</param>
        /// <param name="data">Array of bytes to save</param>
        /// <returns>True if success or false if error</returns>
        public bool SaveAndOpen(string fileName, byte[] data)
        {
            try
            {
                // Get my downloads path
                string filePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
    
                // Combine with filename
                string fullName = Path.Combine(filePath, fileName);
    
                // If the file exist on device delete it
                if (File.Exists(fullName))
                {
                    // Note: In the second run of this method, the file exists
                    File.Delete(fullName);
                }
    
                // Write bytes on "My documents"
                File.WriteAllBytes(fullName, data);
    
                // Get the uri for the saved file
                Android.Net.Uri file = Android.Support.V4.Content.FileProvider.GetUriForFile(
                    this,
                    this.PackageName + ".fileprovider",
                    new Java.IO.File(fileName));
                Intent intent = new Intent(Intent.ActionView);
                intent.SetDataAndType(file, "application/pdf");
                intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask | ActivityFlags.GrantReadUriPermission | ActivityFlags.NewTask);
                this.ApplicationContext.StartActivity(intent);
    
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.Message + ex.StackTrace);
                return false;
            }
        }
    

    It's needed to request the permissions at runtime. I used Plugin.Permisions available on Nuget:

        Plugin.Permissions.PermissionsImplementation.Current.RequestPermissionsAsync(
             new Plugin.Permissions.Abstractions.Permission[]
             {
                 Plugin.Permissions.Abstractions.Permission.Storage,
                 Plugin.Permissions.Abstractions.Permission.MediaLibrary
             });
    

    Also add a provider in the AndroidManifest.xml file:

    <application android:label="My app" android:icon="@mipmap/icon">
        <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
        </provider>
    </application>
    

    In the same AndroidManifest add permisions for:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    

    And add an external path in Resources/xml/file_paths.xml

    <external-path name="external_files" path="."/>
    

    Don't know if I have not needed permissions, but with this way works.