Android Question cameraEx : java exception : method called after relase()

aeropic

Active Member
Licensed User
Longtime User
Hi all,

I'm trying (only trying ...) to develop a small application that would allow to take a picture with the internal camera when the device is pin locked.
I fully succeed to make the phone vibrate after a double press on the power button (power off, wait to go to sleep, then power ON, power OFF in less than 2 sec, this makes the phone vibrate).

Now I would want to add some code in order to take a picture in place of phone vibrate...)

For this, I've cut and pasted the cameraex example but I get this exception :

** Activity (main) Pause, UserClosed = false **
sending message to waiting queue (CallSubDelayed - TakePicture)
running waiting messages (1)
cameraexclass_takepicture (B4A line: 102)
cam.TakePicture
java.lang.RuntimeException: Method called after release()
at android.hardware.Camera.native_takePicture(Native Method)
at android.hardware.Camera.takePicture(Camera.java:1103)
at android.hardware.Camera.takePicture(Camera.java:1048)
at anywheresoftware.b4a.objects.CameraW.TakePicture(CameraW.java:287)
at test.power.cameraexclass._takepicture(cameraexclass.java:1130)
at test.power.main._takepicture(main.java:653)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
at anywheresoftware.b4a.keywords.Common$5$1.run(Common.java:943)
at anywheresoftware.b4a.BA.setActivityPaused(BA.java:382)
at test.power.main$ResumeMessage.run(main.java:252)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
at dalvik.system.NativeStart.main(Native Method)

here is the service code : the service takes the picture with callsubdelay...
B4X:
Sub Process_Globals
  Dim TimerService As Timer
  Dim Counter As Int
  Counter=0 
' Dim p As Phone
  Dim Vibrate As PhoneVibrate
  Dim PE As PhoneEvents
 
  Dim mydate As Long
  Dim sNotif As Notification
 
End Sub

Sub Service_Create
'  TimerService.Initialize("TimerService",1000)
'  TimerService.Enabled=True
  PE.Initialize("PE")
  sNotif.Initialize
sNotif.Icon = "icon"

sNotif.SetInfo("MyApp","Service Running",Main)
sNotif.Sound = False
sNotif.Notify(1)
Service.StartForeground(1,sNotif)
End Sub

Sub Service_Start
' Counter=Counter+1
End Sub

Sub TimerService_Tick
' StartServiceAt("", DateTime.Now, True)
End Sub

Sub PE_ScreenOff (Intent As Intent)
    If CheckLock Then ' si le tel est verouillé ...
        If DateTime.Now - mydate < 2000 Then
            Vibrate.Vibrate(200)
            ' <============ add the camera take picture here
           
            CallSubDelayed(Main , "TakePicture")
        End If
    End If
'    Log ("screen off")
    mydate = DateTime.Now
End Sub

Sub PE_ScreenON (Intent As Intent)
'Log ("screen on")
mydate = DateTime.Now

   
   
   
End Sub

Sub CheckLock As Boolean ' permet de tester l'etat locked ou non du téléphone
    Dim r As Reflector
    r.Target = r.GetContext
    r.Target = r.RunMethod2("getSystemService", "keyguard", "java.lang.String")
    Return  r.RunMethod("inKeyguardRestrictedInputMode")
End Sub

what I am missing ?

thanks
Alain
 

aeropic

Active Member
Licensed User
Longtime User
The camera is released when the activity is paused (assuming that your code is based on CameraEx example).
Thanks Erel,
Yes it is based on CameraEx example. But in this example too, I understand the camera is initialized back in activity resume isn't it ?

I modified a bit the activity resume routine in order to wait for the camera ready status and take the image and I get another runtime exception :


PackageAdded: package:test.power
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
90
[Height=720, IsInitialized=false, Width=960
]
camera ready
** Service (test_service) Create **
** Service (test_service) Start **
** Activity (main) Pause, UserClosed = false **
sending message to waiting queue (CallSubDelayed - activity_resume)
running waiting messages (1)
90
[Height=720, IsInitialized=false, Width=960
]
camera ready
** Activity (main) Resume **
cameraexclass_takepicture (B4A line: 102)
cam.TakePicture
java.lang.RuntimeException: takePicture failed
at android.hardware.Camera.native_takePicture(Native Method)
at android.hardware.Camera.takePicture(Camera.java:1103)
at android.hardware.Camera.takePicture(Camera.java:1048)
at anywheresoftware.b4a.objects.CameraW.TakePicture(CameraW.java:287)
at test.power.cameraexclass._takepicture(cameraexclass.java:1130)
at test.power.main._takepicture(main.java:682)
at test.power.main._activity_resume(main.java:392)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:154)
at test.power.main$ResumeMessage.run(main.java:254)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
at dalvik.system.NativeStart.main(Native Method)

The occurence of this error is when I turn off the screen. The services sends a callsubdelayed main activity resume which is supposed to take the picture:
Here is the service piece of code:
B4X:
Sub PE_ScreenOff (Intent As Intent)
    'If CheckLock Then ' si le tel est verouillé ...
    '    If DateTime.Now - mydate < 2000 Then
            Vibrate.Vibrate(200)
            ' <============ add the camera take picture here
            CallSubDelayed(Main,"activity_resume")
            'CallSubDelayed(Main,"InitializeCamera")
            'CallSubDelayed(Main , "TakePicture")
           
    '    End If
    'End If
'    Log ("screen off")
    mydate = DateTime.Now
End Sub

Here is the main code:
B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("1")
    PE.Initialize("PE")
End Sub   

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim p As PhoneSms
Dim PE As PhoneEvents
    Dim phonepower As String
    Dim Button1 As Button
    Dim label1 As Label
    Dim Panel1 As Panel
   
    Private camEx As CameraExClass
   
End Sub
   
    Sub Process_Globals
  Dim Timer1 As Timer
  Timer1.Initialize("Timer1",1000)
  Private frontCamera As Boolean = False
  Dim camera_prete As Boolean = False
End Sub

Sub Activity_Resume
        'Activity.LoadLayout("1")               
    InitializeCamera
    'ToastMessageShow(camera_prete, False)
    Do While camera_prete = False

'Log(camera_prete)

DoEvents
Loop
ToastMessageShow(camera_prete, False)
    TakePicture
End Sub

Sub Button1_Click
  Timer1.Enabled=True
  StartService(test_service)
 
' p.Send("+33688941445","helloworld : " & DateTime.Now)
End Sub

Sub Timer1_Tick
  label1.Text=test_service.Counter
End Sub

Sub Activity_KeyPress (keycode As Int) As Boolean
'Return True to consume the event
   
Msgbox(keycode, "")
    Return True
 
End Sub

Sub PE_BatteryChanged(Level As Int, Scale As Int, Plugged As Boolean, Intent As Intent)
If Plugged Then
    phonepower = " sur secteur"
    Else
    phonepower = " pas de secteur"
End If
Msgbox(phonepower,"")
'    p.Send("+33688941445","helloworld : " & phonepower)
End Sub

Private Sub InitializeCamera
    camEx.Initialize(Panel1, frontCamera, Me, "Camera1")
    frontCamera = camEx.Front
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    camEx.Release
    camera_prete = False
End Sub

Sub Camera1_Ready (Success As Boolean)
    If Success Then
        camEx.SetJpegQuality(90)
        camEx.CommitParameters
        camEx.StartPreview
        Log(camEx.GetPreviewSize)
        Log("camera ready")
        camera_prete = True
    Else
    Log("camera not ready")
        ToastMessageShow("Cannot open camera.", True)
    End If
End Sub

Sub TakePicture
    camEx.TakePicture
End Sub



Sub Camera1_PictureTaken (Data() As Byte)
    Dim filename As String = "1.jpg"
    Dim dir As String = File.DirRootExternal
   
    camEx.SavePictureToFile(Data, dir, filename)
    camEx.StartPreview 'restart preview
   
    'send a broadcast intent to the media scanner to force it to scan the saved file.
    Dim Phone As Phone
    Dim i As Intent
    i.Initialize("android.intent.action.MEDIA_SCANNER_SCAN_FILE", _
        "file://" & File.Combine(dir, filename))
    Phone.SendBroadcastIntent(i)
    ToastMessageShow("Picture saved." & CRLF  & "File size: " & File.Size(dir, filename), True)
End Sub



Sub Delay(nMilliSecond As Long)
Dim nBeginTime As Long
Dim nEndTime As Long
nEndTime = DateTime.Now + nMilliSecond
nBeginTime = DateTime.Now
Do While nBeginTime < nEndTime
nBeginTime = DateTime.Now
Log(nBeginTime)
If nEndTime < nBeginTime Then
Return
End If
DoEvents
Loop
End Sub

It seems the problem is coming from the fact the panel1 is not displayed on the screen ?
I wonder how I can put the main activity back on screen from the service ?

Alain
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
You should not initialize Timer1 in sub Process_Globals. Though this is not the cause of this error.

This code will not work:
B4X:
DoWhile camera_prete = False

'Log(camera_prete)

DoEvents

Loop

DoEvents only handles UI messages.


Do you want to take a picture when the screen turns off? This will not work. The activity will be paused.
 
Upvote 0

aeropic

Active Member
Licensed User
Longtime User
You should not initialize Timer1 in sub Process_Globals. Though this is not the cause of this error.

This code will not work:
B4X:
DoWhile camera_prete = False

'Log(camera_prete)

DoEvents

Loop

DoEvents only handles UI messages.

Ok; thanks I will fix all this.


Do you want to take a picture when the screen turns off? This will not work. The activity will be paused.

Yes that was the basic idea... What I wanted first to do was detecting a double key press on a physical button while the device is pin locked. As key presses cannot be detected from a service, I thought the best way to detect a power button press was catching the screen ON/OFF generated events.
This works pretty well as I can vibrate the phone after a double press on the power button (screen OFF --> ON --> OFF).
Now changing the vibration by an image capture is another chalenge!
It would be ideal if image capture could be done from a service.. I understand this is not achievable since cameraEX is an activity class only (I tried from a service it failed)

You're right; I understand that if the screen turns off the activity will be paused... Is there a way to forbid the screen to turn off when the "PE_ScreenOff" event is fired ?
Could I try something like:
B4X:
Do While ecranON == False 
            wakelock.KeepAlive(False) 'keep alive screen dimmed : switch ON the screen
            Log ("wake")
            DoEvents  ' <<<<<<< useless if i understand your previous comment ?
        Loop
then do my stuff to use the camera, then release the wake lock ?
 
Upvote 0

aeropic

Active Member
Licensed User
Longtime User
Try to acquire the wake lock a few seconds after the screen turns off (with a timer).
Hi Erel,
Thanks for your advice. I get some progress but still some weird behavior.
I've supressed anaything connected with the camera. Now my code is as simple as that :
B4X:
Sub TimerService_Tick
Vibrate.Vibrate(50)
StartActivity(Main)
CallSubDelayed(Main , "wake")
  TimerService.Enabled=False
End Sub

Sub PE_ScreenOff (Intent As Intent)
   
        If DateTime.Now - mydate < 2000 then
        Vibrate.Vibrate(20)
            ' <============ add the camera take picture here
            'CallSubDelayed(Main,"activity_resume")
            'CallSubDelayed(Main,"InitializeCamera")
              TimerService.Enabled=True
            'CallSubDelayed(Me , "wake")
        ' wakelock.ReleaseKeepAlive 'Releases the power lock and allows the device to go to sleep.
    End If

'    Log ("screen off")
    mydate = DateTime.Now
End Sub

When running this, the wakelock is acquireds only if the screen, before turning it off, does not display the Main activity...
In other words, I launch the application, prsse the button that starts the service, press the back button to display anything else, then turn OOF the screen, ON and OFF ==> the screen displays the Main in dimmed light as expected.
If I don't press the back button : the screen never turns ON. (But the timer ticks as I get the 50ms vibration !)
Something to do wi(th "StartActivty(main)" in the timer routine maybe ?
 
Upvote 0

aeropic

Active Member
Licensed User
Longtime User
You should acquire the lock in the service. The activity will not be able to resume until the screen turns on.

Hi Erel, good news, I've found the way to do exactly what I wanted :

an unlock service includes:
B4X:
Sub Service_Destroy
    WS.ReleaseKeepAlive
End Sub

Sub PE_ScreenOff (Intent As Intent)
    
        If DateTime.Now - mydate < 2000 Then
            WS.KeepAlive(True)
            If IsPaused(second) Then
                StartActivity("second")
            End If
            CallSub(second,"SetShowWhenLocked")
            WS.ReleaseKeepAlive
    End If

'    Log ("screen off")
    mydate = DateTime.Now
End Sub

Sub PE_ScreenON (Intent As Intent)
       'Log ("screen on")
       mydate = DateTime.Now
End Sub

This service is started by Main

an other activity "second" (basically cameraEX example) includes: see http://www.b4x.com/android/forum/threads/unlock-password-locked-screen.14870/#post-84636
B4X:
Sub Activity_Resume
SetShowWhenLocked
    InitializeCamera
End Sub

Sub SetShowWhenLocked
  Dim r As Reflector
  r.Target = r.GetActivity
  r.Target = r.RunMethod("getWindow")
  r.RunMethod2("addFlags", 6815872, "java.lang.int")
End Sub

and all this does the job.
Many thanks for your help
Alain
 
Upvote 0

aeropic

Active Member
Licensed User
Longtime User
The code above can even be simplified as setting the flags "keep screen ON" and "Turn screen On" is redundant with "keep_alive" (except the screen brightness), it is better to suppress the lines dealing with keepalive and releasekeepalive

B4X:
Sub PE_ScreenOff (Intent As Intent)
    If CheckLock Then ' si le tel est verouillé ...
        If DateTime.Now - mydate < 2000 Then
            'WS.KeepAlive(True)    <<<<<<<<<<<<<<<<<<< USELESS
            If IsPaused(second) Then
                StartActivity("second")
            End If
            CallSub(second,"SetShowWhenLocked")
            'WS.ReleaseKeepAlive  <<<<<<<<<<<<<<<<<< USELESS
        End If
      
    End If
'    Log ("screen off")
    mydate = DateTime.Now
End Sub

Moreover, for my purpose, the SetShowWhenLocked subroutine in second activity needs only to set FLAG_TURN_SCREEN_ON and FLAG_SHOW_WHEN_LOCKED which gives :
B4X:
Sub SetShowWhenLocked
  Dim r As Reflector
  r.Target = r.GetActivity
  r.Target = r.RunMethod("getWindow")
'  r.RunMethod2("addFlags", 6815872, "java.lang.int") 'combination of: FLAG_TURN_SCREEN_ON, FLAG_DISMISS_KEYGUARD, FLAG_SHOW_WHEN_LOCKED and FLAG_KEEP_SCREEN_ON.
  r.RunMethod2("addFlags", 2621440, "java.lang.int") 'combination of: FLAG_TURN_SCREEN_ON,  FLAG_SHOW_WHEN_LOCKED .
End Sub
 
Upvote 0
Top