Android Question Silly question - how to save an image file to internal SD card with Fileprovider ?

Phayao

Active Member
Licensed User
I hesitated long time to post this question - but honestly I'm unable to figure it out.
I can create a bitmap file with the intent camera and show it in an imageview.
But to save it with fileprovider to a file in the internal SD Card (SDK version 26) i could not do.
The fileprovider examples show how to copy from dir.assets to the shared folder and share it - but not the other way around. I tried with File.copy(imagefolder,tempfile,file.dirrootexternal,"mypic.jpg") only to get java error "file not found 'mypic.jpg' access denied".
(of course I included fileprovider.bas and modified manifest according to the fileprovider example).
I guess the solution is super-easy, please forgive my ignorance :oops:

Thanks a lot,
Chris
 

agraham

Expert
Licensed User
FileProvider is not concerned with file copying but only with file sharing.

This is how I copy a file from the SD Card to a file in the Download folder using a Stream opened by Erel's ExternalStorage class.

It is confusing, at least to me, that External Storage seems to mean either 'internal external storage' which needs PERMISSION_WRITE_EXTERNAL_STORAGE or an SD Card or USB stick which need to use the Storage Access Framework as implemented in the ExternalStorage class module. In fact I find the whole Android permission system totally baffling as Google seems to keep changing it. But then I loathe Google and Android anyway!

B4X:
' in Activity_Create
    Dim rp As RuntimePermissions   
   rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE) ' Implicit read capability if granted
   Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
   Log($"PERMISSION_WRITE_EXTERNAL_STORAGE = ${Result}"$)

' then later
' Starter.MapTempDestination, = "/Download/OS Search Map.jpg"
           Dim out As OutputStream = File.OpenOutput(File.DirRootExternal, Starter.MapTempDestination, False)
           File.Copy2(Storage.OpenInputStream(extfile), out)
           out.Close
 

Phayao

Active Member
Licensed User
Thanks a lot, that helps but does not solve the problem 100%.
I am using the ExternalStorage class, though the critical command now is:
B4X:
File.Copy2(Storage.OpenInputStream(extFile), out)
I do not get where the extFile should come from - it is a kind of stream object i guess, but what I have is a bitmap which I want to save as file on the internal SD card. How to bring the bitmap into this extFile ? Or did i get it totally wrong ? o_O
Thanks in advance again.
 

agraham

Expert
Licensed User
You said you could create the file and wanted to copy it to the SD Card so I showed you how to get permission and use two Streams to copy it. Now I don't understand what you mean. If you know how to create the file in External storage then using Storage.OpenInputStream should be obvious. extFile is the name of the file to copy.
 

Phayao

Active Member
Licensed User
Thanks to your hints I was able to figure it out, it's actually very easy - I got confused about the Fileprovider class.
It is as you said not necessary here.
The solution is:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
   Dim rp As RuntimePermissions
   rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE) ' Implicit read capability if granted
   Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
   Log($"PERMISSION_WRITE_EXTERNAL_STORAGE = ${Result}"$)
   imageFolder = rp.GetSafeDirDefaultExternal("shared") 'shared folder as set in the manifest editor
   Log("imagefolder: "&imageFolder)
   If lastPicture.IsInitialized Then ImageView1.Bitmap = lastPicture
End Sub

Sub Button1_Click
   TakePicture
End Sub

Sub TakePicture
   Dim i As Intent
   i.Initialize("android.media.action.IMAGE_CAPTURE", "")
   File.Delete(imageFolder, tempImageFile)
   File.Delete(myFolder,"camintentTest.jpg")
   Dim p As Phone
   Dim u As Object
   Log("SDK Version: "&p.SdkVersion)
   If p.SdkVersion < 24 Then
       Dim uri As Uri
       uri.Parse("file://" & File.Combine(imageFolder, tempImageFile))
       u = uri
   Else
       u = CreateFileProviderUri(imageFolder, tempImageFile)
   End If
   i.PutExtra("output", u) 'the image will be saved to this path
   Try
       StartActivityForResult(i)
   Catch
       Msgbox("Camera is not available."&CRLF&LastException, "Camera ERROR")
   End Try
End Sub

'result arrives here
Sub ion_Event (MethodName As String, Args() As Object) As Object
   If Args(0) = -1 Then
       Try
           Dim in As Intent = Args(1)
           If File.Exists(imageFolder, tempImageFile) Then
               lastPicture = LoadBitmapSample(imageFolder, tempImageFile, ImageView1.Width, ImageView1.Height)
               ImageView1.Bitmap = lastPicture
           Else If in.HasExtra("data") Then 'try to get thumbnail instead
               Dim jo As JavaObject = in
               lastPicture = jo.RunMethodJO("getExtras", Null).RunMethod("get", Array("data"))
           End If
       Catch
           Msgbox( LastException,"ERROR")
       End Try

       ' copy to directory
       Try
           File.Copy(imageFolder,tempImageFile,myFolder,"camintentTest.jpg") ' <==== THIS IS THE WHOLE "TRICK" !!
           ' NO FILEPROVIDER OR EXTFILE NECESSARY !

           If File.Exists(myFolder,"camintentTest.jpg") Then _
               Msgbox("Image written to: camintentTest.jpg ","IMAGE SAVE")
       Catch
           Msgbox( LastException,"ERROR in copy file")
       End Try
   End If
   If lastPicture.IsInitialized Then ImageView1.Bitmap = lastPicture

   Return Null
End Sub

Sub CreateFileProviderUri (Dir As String, FileName As String) As Object
   Dim fp As JavaObject
   Dim context As JavaObject
   context.InitializeContext
   fp.InitializeStatic("android.support.v4.content.FileProvider")
   Dim f As JavaObject
   f.InitializeNewInstance("java.io.File", Array(Dir, FileName))
   Log("Packagename: "&Application.PackageName)
   Return fp.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))
End Sub


Sub StartActivityForResult(i As Intent)
   Dim jo As JavaObject = GetBA
   ion = jo.CreateEvent("anywheresoftware.b4a.IOnActivityResult", "ion", Null)
   jo.RunMethod("startActivityForResult", Array As Object(ion, i))
End Sub

Sub GetBA As Object
   Dim jo As JavaObject
   Dim cls As String = Me
   cls = cls.SubString("class ".Length)
   jo.InitializeStatic(cls)
   Return jo.GetField("processBA")
End Sub
I hope that helps another beginner like me !
Thanks a lot.
 
Top