Android Question [Solved] Wait For file to be saved.

Roger Daley

Well-Known Member
Licensed User
Longtime User
Hi All,

I've done the search the Forum, read the docs/tuts etc and all seems good until I write the code.

In short a couple of subs capture the Gmap on the screen and save it to Dirinternal. Then it is read from that location for use. Unfortunately the Sub tries to read the file before it is written.
The simple solution is Sleep(100) but different phones operate at different speeds, so waiting for the "save" to finish is the best option.

Below is the code which is pretty well used around B4A. I have tried several variations of Wait For examples without success.

Any help much appreciated.

Regards Roger

[
B4X:
Sub BtnSave_Click
    VibBip
   
    Private Obj1, Obj2 As Reflector
    Private args(1) As Object
    Private types(1) As String
    Private Out As OutputStream
    Private Rect1 As Rect
    Private Canvas3 As Canvas
    Private bmp1 As Bitmap
    bmp1.InitializeMutable(Activity.Width,Activity.Height)
    Canvas3.Initialize2(bmp1)
    'No way to take screen shot and show map in one operation
    'SOLUTION: Take picture of Map.  Take screen shot.  Merge both images onto a canvas
   
    'Create folder in DirInternal if required
    If File.Exists(File.Dirinternal&"/ABT/", "") = False Then File.MakeDir(File.Dirinternal&"/ABT/", "")
       
'******Take ScreenShot of Google map, save as map.png
    MapEx.Snapshot(gmap, "gmap")          'Triggers Sub gmap_SnapshotReady  and save map.png
   
'******Take a screenshot and Save as ScrnShotTemp.png
    Obj1.Target = Obj1.GetActivityBA
    Obj1.Target = Obj1.GetField("vg")
    Obj2.Target = Canvas3              
    Obj2.Target = Obj2.GetField("canvas")
    args(0) = Obj2.Target
    types(0) = "android.graphics.Canvas"
    Obj1.RunMethod4("draw", args, types)
    If File.Exists(File.Dirinternal&"/ABT/", "ScrnShotTemp.png") Then File.Delete(File.Dirinternal&"/ABT/", "ScrnShotTemp.png")
    Out = File.OpenOutput(File.Dirinternal&"/ABT/", "ScrnShotTemp.png", False)
    bmp1.WriteToStream(Out, 100, "PNG")
    Out.Close
 
    '********Place Map on Canvas
         Sleep(100)                                                                  'This works but is the crude appoach

    'Wait For gmap_SnapshotReady                                     'This throws an error
   
   
    Rect1.Initialize(0, pnlDispLatLng.Height, 100%x, 100%y)  
    MapCanvas.DrawBitmap(LoadBitmap(File.Dirinternal&"/ABT/", "map.png"), Null, Rect1)          'This is the line that throws the error,  map.png is not found unless Sleep(100) is used.



B4X:
Sub gmap_SnapshotReady (Bitmap1 As Bitmap)
    Private out As OutputStream
    If File.Exists(File.DirInternal&"/ABT/", "map.png") Then File.Delete(File.DirInternal&"/ABT/", "map.png")
 
    out = File.OpenOutput(File.DirInternal&"/ABT/", "map.png", False)
    Bitmap1.WriteToStream(out, 100, "PNG")
    out.Close
    'wait for (out) Complete(Comp1 As Boolean)                                    'Putting Wait For here leaves the same problem.
End Sub


The error log.
main$ResumableSub_BtnSave_Clickresume (java line: 3721)
java.io.FileNotFoundException: /data/user/0/horsetrailer.B4A.AntennaBearingTool/files/ABT/map.png: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:492)
at java.io.FileInputStream.<init>(FileInputStream.java:160)
at anywheresoftware.b4a.objects.streams.File.OpenInput(File.java:215)
at anywheresoftware.b4a.objects.drawable.CanvasWrapper$BitmapWrapper.Initialize(CanvasWrapper.java:516)
at anywheresoftware.b4a.keywords.Common.LoadBitmap(Common.java:1342)
at horsetrailer.B4A.AntennaBearingTool.main$ResumableSub_BtnSave_Click.resume(main.java:3721)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:207)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
at uk.co.martinpearman.b4a.googlemapsextras.GoogleMapsExtras$1.onSnapshotReady(GoogleMapsExtras.java:263)
at com.google.android.gms.maps.zzq.zzc(com.google.android.gms:play-services-maps@@17.0.1:1)
at com.google.android.gms.maps.internal.zzbt.zza(com.google.android.gms:play-services-maps@@17.0.1:2)
 
Last edited:
Solution
try
B4X:
Sub BtnSave_Click
    VibBip
 
    Private Obj1, Obj2 As Reflector
    Private args(1) As Object
    Private types(1) As String
    Private Out As OutputStream
    Private Rect1 As Rect
    Private Canvas3 As Canvas
    Private bmp1 As Bitmap
    bmp1.InitializeMutable(Activity.Width,Activity.Height)
    Canvas3.Initialize2(bmp1)
    'No way to take screen shot and show map in one operation
    'SOLUTION: Take picture of Map.  Take screen shot.  Merge both images onto a canvas
 
    'Create folder in DirInternal if required
    If File.Exists(File.Dirinternal&"/ABT/", "") = False Then File.MakeDir(File.Dirinternal&"/ABT/", "")
     

 
'******Take a screenshot and Save as ScrnShotTemp.png
    Obj1.Target = Obj1.GetActivityBA...

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
Why re-load the bitmap you already have it in memory

how about changing line
B4X:
MapCanvas.DrawBitmap(LoadBitmap(File.Dirinternal&"/ABT/", "map.png"), Null, Rect1)

to
B4X:
MapCanvas.DrawBitmap(bmp1, Null, Rect1)

or are those 2 pieces of code in separate places.
 
Upvote 0

emexes

Expert
Licensed User
Often, to be sure, to be sure, I get the data onto disk first and then do a filename shuffle eg for MyFile.dat:

- save data to new temporary file eg MyFile.$d$
- if MyFile.dat already exists then
--- if MyFile.bak already exists then delete it
--- rename existing MyFile.dat to MyFile.bak
- rename new temporary file MyFile.$d$ to MyFile.dat

Has nice bonus features of
(i) not corrupting existing file if the save process doesn't work properly eg out of disk space
(ii) keeping previous version of file to compare against
(iii) other programs never see a half-written file (or interfere with the writing of a new file, although they might trip up the shuffle)
 
Upvote 0

Roger Daley

Well-Known Member
Licensed User
Longtime User
Andrew,

First I've fixed the faulty Code Tag in the first post. This should make it easier to read.
You can now see there are two subs. MapEx.Snapshot(gmap, "gmap") in Sub BtnSave_Click triggers Sub gmap_SnapshotReady.

Unfortunately I can't find a way to do what you suggest after trying many variations. I think this is what I tried in the dim dark past and failed then.

Many thanks Roger
 
Upvote 0

Roger Daley

Well-Known Member
Licensed User
Longtime User
Often, to be sure, to be sure, I get the data onto disk first and then do a filename shuffle eg for MyFile.dat:

- save data to new temporary file eg MyFile.$d$
- if MyFile.dat already exists then
--- if MyFile.bak already exists then delete it
--- rename existing MyFile.dat to MyFile.bak
- rename new temporary file MyFile.$d$ to MyFile.dat

Has nice bonus features of
(i) not corrupting existing file if the save process doesn't work properly eg out of disk space
(ii) keeping previous version of file to compare against
(iii) other programs never see a half-written file (or interfere with the writing of a new file, although they might trip up the shuffle)
emexes

Thanks for the reply. I think this will take me some time to get my head around.

Thanks Roger
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
Now I don't have the patience (and health) to read your source well.

Just a few considerations:

1 - I don't think there are any "Wait For" problems, as the methods used (WriteToStream) are almost certainly synchronous, so the execution of subsequent lines only occurs at the end of the operation.

2 - it is always better to use "B4X objects"; for example B4XBitmap instead of Bitmap.

3 - this line:
'Wait For gmap_SnapshotReady 'This throws an error
throws an error because it is written wrong and, mainly, the called routine, gmap_SnapshotReady, is not of type Resumable (it is not even a function, it does not return values).

4 - I repeat that I have not looked at the source well but the problem could be in the paths of the files to be saved (tip: use variables)
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Try calling Flush before calling Close on out
B4X:
out.Flush
out.Close
 
Upvote 0

Roger Daley

Well-Known Member
Licensed User
Longtime User
Now I don't have the patience (and health) to read your source well.

Just a few considerations:

1 - I don't think there are any "Wait For" problems, as the methods used (WriteToStream) are almost certainly synchronous, so the execution of subsequent lines only occurs at the end of the operation.

2 - it is always better to use "B4X objects"; for example B4XBitmap instead of Bitmap.

3 - this line:

throws an error because it is written wrong and, mainly, the called routine, gmap_SnapshotReady, is not of type Resumable (it is not even a function, it does not return values).

4 - I repeat that I have not looked at the source well but the problem could be in the paths of the files to be saved (tip: use variables)
Thanks LucaMs
Stay well.

Roger
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
I just noticed:
You realize you are saving the file as ScrnShotTemp.png, but then you are trying to open map.png
 
Upvote 0

Roger Daley

Well-Known Member
Licensed User
Longtime User
I just noticed:
You realize you are saving the file as ScrnShotTemp.png, but then you are trying to open map.png
OliverA,

Sorry, two different files. Map.png is saved in Sub gmap_SnapshotReady which is a Googlemapextras function. This Sub is triggered by
B4X:
'******Take ScreenShot of Google map, save as map.png
    MapEx.Snapshot(gmap, "gmap")          'Triggers Sub gmap_SnapshotReady  and save map.png
in BtnSave Sub.

ScrnShotTemp.png is saved in the BtnSave Sub by seperate code.

The two files are needed where the screen contains a Google Map and stuff that is not Google Map. The screenshot code does not capture Google Map.

Regards Roger
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
I finally get what you are trying to do. Do you actually need to save the bitmap to map.png or are you just doing it so that you can show it in the MapCanvas object?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
try
B4X:
Sub BtnSave_Click
    VibBip
 
    Private Obj1, Obj2 As Reflector
    Private args(1) As Object
    Private types(1) As String
    Private Out As OutputStream
    Private Rect1 As Rect
    Private Canvas3 As Canvas
    Private bmp1 As Bitmap
    bmp1.InitializeMutable(Activity.Width,Activity.Height)
    Canvas3.Initialize2(bmp1)
    'No way to take screen shot and show map in one operation
    'SOLUTION: Take picture of Map.  Take screen shot.  Merge both images onto a canvas
 
    'Create folder in DirInternal if required
    If File.Exists(File.Dirinternal&"/ABT/", "") = False Then File.MakeDir(File.Dirinternal&"/ABT/", "")
     

 
'******Take a screenshot and Save as ScrnShotTemp.png
    Obj1.Target = Obj1.GetActivityBA
    Obj1.Target = Obj1.GetField("vg")
    Obj2.Target = Canvas3            
    Obj2.Target = Obj2.GetField("canvas")
    args(0) = Obj2.Target
    types(0) = "android.graphics.Canvas"
    Obj1.RunMethod4("draw", args, types)
    If File.Exists(File.Dirinternal&"/ABT/", "ScrnShotTemp.png") Then File.Delete(File.Dirinternal&"/ABT/", "ScrnShotTemp.png")
    Out = File.OpenOutput(File.Dirinternal&"/ABT/", "ScrnShotTemp.png", False)
    bmp1.WriteToStream(Out, 100, "PNG")
    Out.Close

'******Take ScreenShot of Google map
    MapEx.Snapshot(gmap, "gmap")          'Triggers Sub gmap_SnapshotReady
    '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    'Note: Do NOT have a separate gmap_SnapshotReady Sub in your code module!
    '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    Wait For gmap_SnapshotReady (Bitmap1 As Bitmap)

    '********Place Map on Canvas
 
    Rect1.Initialize(0, pnlDispLatLng.Height, 100%x, 100%y)
    'We don't need to read Bitmap1 from file, since we received it straight from gmap_SnapshotReady above
    MapCanvas.DrawBitmap(Bitmap1, Null, Rect1)
 
Upvote 8
Solution

Roger Daley

Well-Known Member
Licensed User
Longtime User
OliverA,

You are a genius. It worked perfectly and I've done similar for the ScreenShot, all working.

Regards Roger

EDIT: I have just done similar to the Sub that Loads the saved images. Works great of course.:)
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
1631079426820.png
 
Upvote 0
Top