Android Question PhoneEvent: BatteryChanged event problem

DavideV

Active Member
Licensed User
Longtime User
** SOLVED **
see post #4

Hello guys,
i'm trying to develop a simple app with a battery widget following the Erel's widget tutorial.
The app is working but there is a problem with the batterychanged event in the widget service:
at a certain point (after minutes, hours.... casually) the event is no more fired. I see the device's battery icon in the status bar with a different percentage value than my widget as the event is not fired. If i touch the widget it opens my app (as it is coded to do this) then the widget updates itself and it keep updating for a while before the problem appear again. I tried the app on different devices (2.3.6, 4.0, 5.0) and it always appear casually

I can't understand what's happen or what's wrong....
I'll be happy if someone can point me in the right direction... thanks in advance :)
here is the widget code:

B4X:
#Region  Service Attributes
    #StartAtBoot: False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
     Dim rv As RemoteViews
     Dim Pe As PhoneEvents
     Dim PrevLevel         As Int=0            'previous battery value
     Dim PrevPlugged     As Boolean=False    'previous plugged state  
     Dim FirstTime         As Boolean            'first widget run
     Dim Settings         As Map
   
End Sub
Sub Service_Create
    'rv = ConfigureHomeWidget("LayoutFile", "rv", 0, "Widget Name")
    rv = ConfigureHomeWidget("Smiley2x1","rv",0,"Smiley battery 2x1",False)
    Pe.Initialize("Pe")
    Log("Battery event started")
    FirstTime=True
    'read the preferences stored on main to setup the widget
    Dim Settings As Map= ReadSettings("SmileyWidget2x1.json")
    'rv_RequestUpdate
   
End Sub

Sub Service_Start (StartingIntent As Intent)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
       
End Sub

'Reads the settings from Json into a map
Sub ReadSettings (WidgetName As String) As Map
    'check if exist a settings file otherwise load the default settings file
    Dim retMap As Map
    retMap.Initialize
  
    If File.Exists(File.DirDefaultExternal,WidgetName) Then
        'load user settings
        Try
            Dim Text As String=File.ReadString(File.DirDefaultExternal,WidgetName)
            'new!!! pre-parse JSON for old devices with limited support for JSON
            Dim PP As JSONadvanced
            PP.Initialize(Text)
            'Log("Preparser user result: " & CRLF & PP.preParse(False) )
            'Log("Current user json:" & CRLF & Text)
            Dim parser As JSONParser
            parser.Initialize(PP.preParse(True))
            Dim root As Map = parser.NextObject
            retMap= root.Get("smiley2x1")  
        Catch
            Log(LastException)
        End Try
    Else
        'load default settings
        Try
            Dim Text As String=File.ReadString(File.DirAssets, "resources/" & WidgetName)
            'new!!! pre-parse JSON for old devices with limited support for JSON
            Dim PP As JSONadvanced
            PP.Initialize(Text)
            'Log("Preparser default result: " & CRLF & PP.preParse(False) )
            'Log("Current default json:" & CRLF & Text)
            Dim parser As JSONParser
            parser.Initialize(PP.preParse(True))
            Dim root As Map = parser.NextObject
            retMap= root.Get("smiley2x1")  
        Catch
            Log(LastException)
        End Try
    End If
  
    Return retMap
  
End Sub

'update the widget
Sub rv_RequestUpdate
    'do something...
    rv.UpdateWidget
End Sub

Sub rv_Disabled
    Pe.StopListening
    StopService("")
End Sub

'click the image:
Sub ImgSmiley_click
    'StartActivity(Main)
    ExecFromWidget("OpenMain")
End Sub

'click the label:
'the label shows the actual function
Sub LblBattery_click
    'StartActivity(Main)
    ExecFromWidget("OpenMain")
  
End Sub

'click the battery status:
'the label shows the actual function
Sub ImgBattery_click
    'StartActivity(Main)
    ExecFromWidget("OpenMain")
  
End Sub

'execute commands from WidgetHandler service.
'pass the command as string
Sub ExecFromWidget(Command As String)
    Log("SBW> exec command: " & Command)
    Select Case Command.ToLowerCase
        Case "action1"
            '
        Case "action2"      
            '
        Case "action3"
            '  
        Case "action4"  
        '
        Case "action5"  
            '
        Case "action6"
            '      
        Case "action7"
            '
        Case "OpenMain"
            StartActivity(Main)
        Case Else
            StartActivity(Main)
    End Select

End Sub

Sub PE_BatteryChanged(Level As Int, Scale As Int, Plugged As Boolean, Intent As Intent)
    'it seem that this event is called also when there aren't changes, almost when plugged,
    'save some battery by checking for changes...
    If FirstTime Then
        FirstTime=False
        UpdateBatteryStatus(Level,Scale,Plugged)
    Else
        If PrevPlugged=Plugged And PrevLevel=Level Then
              Return    'no changes
          Else
            'save current state
            PrevLevel=Level
            PrevPlugged=Plugged
            UpdateBatteryStatus(Level,Scale,Plugged)
        End If
     End If

End Sub

Sub Service_Destroy
  
End Sub

'update the widget
Sub UpdateBatteryStatus(Level As Int, Scale As Int, Plugged As Boolean)
    Dim Icon, battery As String
  
    'adjust scale from whatever to 100%
    Dim BattValue As Int= Level * 100 /Scale
    Dim Smiley, batt As Bitmap
    Log("Battery level  : " & Level & "%" & ", Plugged: " & Plugged & ", Scale: " & Scale)
  
    'choose icon:
    Select True
        Case BattValue> 89
            '90-100
            Icon="90.png"
            battery="b_100.png"
        Case BattValue> 79
            '80-89
            Icon="80.png"
            battery="b_100.png"
        Case BattValue> 69
            '70-79  
            Icon="70.png"
            battery="b_80.png"  
        Case BattValue> 59
            '60-69
            Icon="60.png"
            battery="b_80.png"
        Case BattValue> 49
            '50-59
            Icon="50.png"
            battery="b_60.png"
        Case BattValue> 39
            '40-49  
            Icon="40.png"
            battery="b_60.png"
        Case BattValue> 29
            '30-39
            Icon="30.png"
            battery="b_40.png"      
        Case BattValue> 19
            '20-29
            Icon="20.png"
            battery="b_40.png"
        Case BattValue> 9
            '10-19
            Icon="10.png"
            battery="b_20.png"
        Case BattValue> 4
            '5 -9
            Icon="5.png"
            battery="b_10.png"
        Case BattValue> 0
            'from 0 to 4
            Icon="0.png"
            battery="b_empty.png"
        Case Else
            'unknown state?
            Icon="un.png"
            battery="b_unknown.png"  
    End Select
  
    'check if connected to power
    If Plugged Then
        battery="usb_plugged.png"
    Else
        battery="usb_unplugged.png"
    End If
  
    'color
    Dim col As Int
    'get the key, it's used as an index
    Select Case Settings.Get("text_color")
        Case "color1"
            col=Colors.Black
        Case "color2"
            col=Colors.White
        Case "color4"
            col=Colors.Cyan
        Case "color3"
            col=Colors.Red
        Case "color5"
            col=Colors.Yellow
        Case "color6"
            col=Colors.Green
        Case Else
            col=Colors.Black  
    End Select
  
    'update widget
    'Log("icon: " & Icon)
    batt.Initialize(File.DirAssets,"battery/" & battery)
    Smiley.initialize(File.DirAssets,Settings.Get("folder") & "/" & Icon)
    rv.SetImage("ImgSmiley",Smiley)
    rv.SetImage("ImgBattery",batt)
    rv.SetTextSize("LblBattery",Settings.Get("text_size"))
    rv.SetTextColor("LblBattery",Colors.Black)
    rv.SetText("LblBattery",BattValue & "%")
    rv.UpdateWidget
  
End Sub
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

DavideV

Active Member
Licensed User
Longtime User
Thanks Erel,
After reading those threads i'll go with :
B4X:
#StartCommandReturnValue: android.app.Service.START_STICKY

and will see in a few days
 
Upvote 0

DavideV

Active Member
Licensed User
Longtime User
Hi, thanks Erel, problem solved.
I added the line as in post #3:

B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #StartCommandReturnValue: android.app.Service.START_STICKY
#End Region

in the widget service and now the system restarts it when need.
But i had to change also the Service start sub because when the service is started by the system there isn't an intent and the app crashes with 'intent not initialized' error.
Here is the new sub:

B4X:
Sub Service_Start (StartingIntent As Intent)
    'check if service is started by an intent or by system...
    'Dim LogTxt As String
    If StartingIntent.IsInitialized=True Then
        'started by an intent
        If rv.HandleWidgetEvents(StartingIntent) Then Return
    Else
        'started by system
    End If
   
   
        
End Sub
 
Upvote 0
Top