1

I can't seem to get my App to save an image, outside of my App folder. I can save internally (image will save to app folder) but not externally. I want to see photos taken with my App in the default device gallery. I believe i have all the required App permissions. Here is my code:

\\MainActivity
    using System;
    using TCmCRS_20;
    using Android.App;
    using Android.Content.PM;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using Android.OS;
    using SQLite;
    using System.IO;
    using Xamarin.Essentials;
    using Acr.UserDialogs;
    using Plugin.Media;
    
    
    namespace TCmCRS_20.Droid
    {
        [Activity(Label = "TCmCRS_20", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
        public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
    
            protected override async void OnCreate(Bundle savedInstanceState)
            {
    
                TabLayoutResource = Resource.Layout.Tabbar;
                ToolbarResource = Resource.Layout.Toolbar;
    
                base.OnCreate(savedInstanceState);
    
                Xamarin.Essentials.Platform.Init(this, savedInstanceState);
                await CrossMedia.Current.Initialize();
    
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                Xamarin.FormsMaps.Init(this, savedInstanceState);
                UserDialogs.Init(this);
    
    
                string fileName = "TCmCRS_20_db.db3";
                string folderPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
                string completePath = System.IO.Path.Combine(folderPath,fileName);
    
                LoadApplication(new App(completePath));
    
            }
            public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
            {
                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }

//Android Manifest

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.TCmCRS_20" android:installLocation="preferExternal">
    <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="29" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature android:name="android.hardware.location" android:required="false" />
    <uses-feature android:name="android.hardware.location.gps" android:required="false" />
    <uses-feature android:name="android.hardware.location.network" android:required="false" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application android:label="TCmCRS_20.Android">
        <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>
        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyADDISS9WBQw8XYyWGkIF0Q5TtiAW9b_ao" />
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
        <uses-library android:name="org.apache.http.legacy" android:required="false" />
    </application>
</manifest>



        

private async void TakePhotoButton_OnClicked(object sender, EventArgs e)

        {
            var location = await Geolocation.GetLocationAsync();

            if (location != null)
            {
                PhotoLatCoord.Text = location.Latitude.ToString();
                PhotoLonCoord.Text = location.Longitude.ToString();
            }
            else
            {
                ;
            }

            string gID = Guid.NewGuid().ToString();
            string fN = "RI-" + Asset_ID.Text + " " + gID + ".jpg";

            if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
            {
                await DisplayAlert("No Camera", "No camera available", "OK");
                return;
            }

            await CrossMedia.Current.Initialize();
            var current = Connectivity.NetworkAccess;


            if (current != NetworkAccess.Internet)
            {
                var file = await CrossMedia.Current.TakePhotoAsync(new 
                   Plugin.Media.Abstractions.StoreCameraMediaOptions
                {
                    Name = fN,
                    Directory = "All_Photos",
                    SaveToAlbum = true,
                });
            }
            else
            {
                var file = await CrossMedia.Current.TakePhotoAsync(new 
                Plugin.Media.Abstractions.StoreCameraMediaOptions
                {
                    Name = fN,
                    Directory = "Photos_for_Upload",
                    SaveToAlbum = true,
                });



                //Get the public album path
                var aPpath = file.AlbumPath;

                //Get private path
                var path = file.Path;

                //! added using Microsoft.WindowsAzure.Storage;
                var account = CloudStorageAccount.Parse("xxxhiddenxxx");
                var blobClient = account.CreateCloudBlobClient();

                var container = blobClient.GetContainerReference("xxx");

                var blockBlob = container.GetBlockBlobReference(fN);

                if (file == null)
                    return;
                {
                    MainImage.Source = ImageSource.FromStream(() => file.GetStream());
                    await blockBlob.UploadFromStreamAsync(file.GetStream());
                }
            }
        }

Any help would be greatly appreciated

Tricky035
  • 107
  • 1
  • 11

1 Answers1

2

I think adding android:requestLegacyExternalStorage="true" to your manifest <application> tag as mentioned here: Xamarin-System.UnauthorizedAccessException: Access to the path is denied in combination with the native android path for pictures Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath; will help you.

Tom0x
  • 36
  • 3
  • Thank you for pointing me in the right direction @Tom0x it was doing my head in, Cheers – Tricky035 Oct 27 '20 at 01:55
  • This solution will not work post-Android 10 I hope you guys are aware! Legacy external storage is like a cop-out that only works on Android 10, will not work on the latest Android though!!! – FreakyAli Oct 27 '20 at 03:51
  • @FreakyAli, Indeed. But for Android 11 you dont need it as you can write to that Pictures directory just fine then. – blackapps Oct 27 '20 at 08:56
  • @blackapps Are you saying that on Android 11 the above legacy issue won't exist? Because I am pretty sure it will! – FreakyAli Oct 27 '20 at 13:46
  • 1
    @FreakyAli, On an Android 11 emulator the Pictures directory is readable and writable for all apps. Like Download, Documents, DCIM, Alarms folder and so on. Easy to test for your self. The root of external storage is again readable for all apps. – blackapps Oct 27 '20 at 14:22
  • @FreakyAli Could this be reason that I cannot seem to delete images with my App? I've tried system.io and using an interface with dependency injection but photos will not delete? – Tricky035 Oct 29 '20 at 22:01