Android Question Is possible to draw on Canvas while app is paused ?

max123

Active Member
Licensed User
Hi all,

I've read this:
https://www.b4x.com/android/forum/threads/callsub-when-activity-paused.55533/

but I can't figure if is possible to draw on Canvas (on background) while the app stay paused.

I'm explain better here:

I've developed an app that load gcode files, parse it, draw on Canvas and then send over USB to printer controller line by line, every line wait an acknowledgment from controller "ok" before send next line.

To do this I've used an Activity (default Main activity) and wrote some classes, like Connector to connect over USB, Viewer that have a big Canvas on one panel and then all is inside a ScrollView2D to scoll it, this class shows gcode X,Y,Z movements on a canvas, and a Service that manage all app and USB, so even the data arrival (ACK from printer)

Because a USB (Connector Class) need continue to send and receive from, even if app is pauses, I've declared it on the Service, but my Viewer class I put it on the Main activity because contains some views like a Canvas.

If I switch and send app in Pause the USB continue to send and receive, but how to draw on Canvas on the same time?

What I need is not to show the Activity, but continue to draw on Canvas in background, so when I Resume the app and I see the Activity I can see the draw updated as the Activity never go in pause.

I've tried with CallSub, but because the Activity is paused, it do nothing.
I've tried CallSubDelay, but it just send some calls to a queue, and then if I resume the Activity I can't see Canvas updated.

Is there a way to do this?

Many thanks
 
Last edited:

Semen Matusovskiy

Well-Known Member
Licensed User
Never used a canvas inside the service. As I understand, it's possible to create mutable bitmap and to use in canvas.Initialize2

Method_636.png
Initialize2 (Bitmap As android.graphics.Bitmap)

Initializes the canvas for drawing on this bitmap.
The bitmap must be mutable. Bitmaps created from files or input streams are NOT mutable.
 
Upvote 0

max123

Active Member
Licensed User
Hi @Semen Matusovskiy

Thanks for your reply, the Canvas is not in the Service, I created a Class to view gcode (Viewer class) and I initialize it on main Activity because it contains Canvas and some views as I explained, so can't declare this class on the Service.

The Service now has CallSub or CallSubDelayed that call the drawing methods on the main Activity where there are all controls of the app, so even a Viewer class.

But I need to draw on Canvas while the app stay paused (in background), I do not need to see it, just I want the Canvas stay updated when i resume the Activity.

For this reason I don't think that Mutable Bitmap can help.

The Canvas need to stay visible to draw on it?

This way while I print I can use other apps, this is a problem because eg. if I receive a notification or just go back to android launcher, the app stops to draw on Canvas.

Any suggestion?
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
If you use in Activity Canvas.Initialize (Target As android.view.View), imagine what happens, when user rotates a phone.
All views will be simply destroyed.

IMO, correct way - to build a bitmap inside the service and to use it inside Activity
 
Upvote 0

max123

Active Member
Licensed User
In my app this not heppen, the layout is fixed Landscape and if I draw on canvas, then pause it, when I resume the Canvas already has draws on it, the problem seem to be only if I draw on It while the app (main activity) is paused
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
As @Semen Matusovskiy wrote, you should use a mutable bitmap instead of drawing over a view directly.

Canvas is marked as an activity object. The compiler will not let you declare a process global Canvas. You can trick the compiler:
B4X:
'starter service
Sub Process_Globals
   Private ocvs As Object
   Public bmp As Bitmap
End Sub

Sub Service_Create
   bmp.InitializeMutable(200dip, 200dip)
   Dim cvs As Canvas
   cvs.Initialize2(bmp)
   Dim o As Object = cvs
   ocvs = o
End Sub

Private Sub GetCvs As Canvas
   Return ocvs
End Sub


Sub DrawSomething
   GetCvs.DrawLine(10dip, 10dip, 200dip, 200dip, Colors.Red, 5dip)
End Sub

Get the bitmap with Starter.bmp
 
Upvote 0

max123

Active Member
Licensed User
Ok, thanks Erel, ;) and @Semen Matusovskiy

I will try it..... But my Canvas is on Viewer class, in the main Activity i just declare a class, initialize it and use its methods.

So I need to do this inside a Viewer class?
 
Upvote 0

max123

Active Member
Licensed User
I've tried your solutions but not clear how to adapt it on my project.

@Erel Is not clear what I need to do, based on your example code I think that when the app is paused, the Service continue to draw on the Mutable Bitmap, right?
And then what I need to do when the app resumes? I need just to use Canvas.DrawBitmap to update it?

The app draw line by line, eg. 100.000 line of gcode file, in the app by default the Canvas invalidate itself every line, but I can change a buffer size with a SeekBar, so eg. if the buffer size is 100, it draw 100 lines and then invalidate, this way the user can decide the speed of simulation (because the app not only print but even simulate).

So if I just draw on the Mutable Bitmap it is not visible until I use the Canvas.DrawBitmap to update it, if example the buffersize is 1, need to use Canvas.DrawBitmap and draw the full bitmap every line I draw to view an update? I think this slow down a lot...

Is not clear...

Anyway the Object trick worked well, I've tried this code, it draw a red line on the mutable bitmap and then show on the canvas, works, but not sure how to implement it on my app, is this a right way?

Thanks

Main:
B4X:
Sub Process_Globals

End Sub

Sub Globals
    Dim Canvas1 As Canvas
    Dim Panel1 As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
 
   Panel1.Initialize("Panel1")
    Activity.AddView(Panel1, 10dip,10dip, 500dip, 500dip)
 
    Canvas1.Initialize(Panel1)

    Dim DestRect As Rect
    DestRect.Initialize(10dip, 10dip, 10dip + 100dip, 10dip + 100dip)
    Canvas1.DrawBitmap(Starter.bmp, Null, DestRect)  ' draws the bitmap to the destination rectangle.

    Activity.Invalidate
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Starter Service:
B4X:
Sub Process_Globals
    Private ocvs As Object
    Public bmp As Bitmap
End Sub

Sub Service_Create
    bmp.InitializeMutable(200dip, 200dip)
    Dim cvs As Canvas
    cvs.Initialize2(bmp)
    Dim o As Object = cvs
    ocvs = o
 
    DrawSomething
End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_Destroy

End Sub

Private Sub GetCvs As Canvas
    Return ocvs
End Sub

Sub DrawSomething
    Log("Draw line")
    GetCvs.DrawLine(10dip, 10dip, 200dip, 200dip, Colors.Red, 5dip)
End Sub
 
Last edited:
Upvote 0

max123

Active Member
Licensed User
The problem is that I use this class to draw on the Canvas, it use some calculations to make the view like 3D, so I can't just declare another Canvas to draw on it this way:
B4X:
    Private ocvs As Object
Public bmp As Bitmap

bmp.InitializeMutable(200dip, 200dip)
Dim cvs As Canvas
cvs.Initialize2(bmp)
Dim o As Object = cvs
ocvs = o
 
DrawSomething
... and draw on it in a simple way.

I declare it this way:
B4X:
Dim Viewer As GCodeViewer3D
I need to draw using class functions, so I presume that I have manage Mutable Bitmap directly inside the class?
 

Attachments

  • GCodeViewer3D.bas
    13.2 KB · Views: 156
Last edited:
Upvote 0

max123

Active Member
Licensed User
But only when I resume or need to do every line?
I need to place the Imageview on top of my canvas?

So maybe I not explained in the right way. :(

The app while stay visible process about 50 lines every second (parse from a List filled from file and draw it) if invalidate every line and show lines one by one, if I increase the buffer to 1000 it draw these lines and then invalidate, this way is very fast (about 300 lines every second), but if I draw on a mutable bitmap and then every line I use ImageView.Bitmap = Starter.YourBitmap this slow down the render process.
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
It's not necessary to redraw whole bitmap per each line. Bitmaps offer getPixels / setpixels, so it's possible to redraw the changed parts very fast.
 
Upvote 0

max123

Active Member
Licensed User
Thanks @Semen Matusovskiy

seem to be very difficult to know the real portion to redraw, you tried to read my class?

The class has a big panel 2100dip x1300dip on witch I've initialized the Canvas,, all this is inside a ScrollView2D to scroll on X and Y axis.
 
Last edited:
Upvote 0

max123

Active Member
Licensed User
What I want to test is to draw a Bitmap file if app is paused, draw it on normal Canvas if the app is not paused, if app go to pause, just the Service write on the mutable bitmap (declared as global), only when tha app resumes in the Resume sub load a Bitmap (maybe as Erel sayd add an ImageView, i think on background of the Canvas) and continue to draw on normal mode to the Canvas on top on it, if the app goes another time to paused the cycle repeat until the last line.

But use an ImageView of 2100dip x1300dip on top on already same dimensions Canvas consume much memory and because I can set the Zoom, I need even to increase this space.

Is this fattible?
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
It's hard to understand even own logic ... Ok, let's imagine that you want to save your initial code of Activity, where you draw over View.

In this case in Activity_Pause it's possible to assign Starter.Bitmap = CanvasInActivity.Bitmap. I guess that in this case Android will not destroy a bitmap.
If I am wrong and Android destroys a bitmap, it's possible to use getPixels function and to create a copy of bitmap, which was used in Activity.

When Service will receive a control it can create CanvasInService over Starter.bitmap and you will be able to draw as usual.

When Activity will return a control (Activity_Resume), you can restore a View, where your draw : getPixels from Starter.Bitmap; setpixels for CanvasInActivity.bitmap

Well, wait a little, I will write small demo.
 
Last edited:
Upvote 0

udg

Expert
Licensed User
Longtime User
I quickly read the whole thread so maybe my comment is not applicable or even not useful at all..
What if you have a service that "produces" new graphics commands (line or whatever) and write them in a list; this goes the same whether the activity is paused or not.
Then the Viewer activity when resumed/active just operates on the list and draws what it needs to (everything, from point item x, last entries..).
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
This is simple sample. In first time activity draws a circle. You can add something (for first and other times).

Click Overview button. Your activity will loose a control and service begins to wook. When you activate app again, you will see some lines near a circle. This lines are drawn by in Starter service
 

Attachments

  • test.zip
    8 KB · Views: 160
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
The app while stay visible process about 50 lines every second (parse from a List filled from file and draw it) if invalidate every line and show lines one by one, if I increase the buffer to 1000 it draw these lines and then invalidate, this way is very fast (about 300 lines every second), but if I draw on a mutable bitmap and then every line I use ImageView.Bitmap = Starter.YourBitmap this slow down the render process.
Have you tried it? Was it really slow?

You only need to call ImageView.Invalidate. You don't need to set the bitmap every time.
 
Upvote 0
Top