Android Tutorial StateManager - Helps managing Android applications settings and state

Status
Not open for further replies.
Edit: StateManager was written in 2011. I don't recommend using it for new projects. Use B4XPages + KVS2 instead.

StateManager is a code module which takes care of handling the application UI state and settings.

Settings
Settings are the application configurable settings. These settings should be permanently kept.

The methods for handling settings are pretty simple:
StateManager.GetSetting (Key As String) As String: gets the value associated with the given key. An empty string will return if the key is not available. The settings will be loaded from a file if they were not loaded before.

StateManager.GetSetting2 (Key As String, DefaultValue As String) As String: similar to GetSetting. The DefaultValue will return if the key was not found.

StateManager.SetSetting(Key As String, Value As String): Associates the given value with the gives key. Note that there is no need to call SaveSettings after each call to SetSetting.

StateManager.SaveSettings: saves the settings to a file. Usually you will want to call this method in Activity_Pause.

UI State
The UI state is a bit more interesting. In some cases Android may destroy our activity and then recreate it when needed. This happens for example when the user changes the screen orientation. If the user has entered some text in an EditText view then we want to keep this text. So instead of resetting the UI we are first saving the state and then we will restore it.

Not all the elements are saved. Only elements which the user interacts with (like EditText text, Spinner chosen item, SeekBar value...).
Using StateManager to handle the state is simple:
B4X:
Sub Activity_Create(FirstTime As Boolean)

    ...
    'Load the previous state
    If StateManager.RestoreState(Activity, "Main", 60) = False Then
        'set the default values
        EditText1.Text = "Default text"
        EditText2.Text = "Default text"
    End If
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed Then
        StateManager.ResetState("Main")
    Else
        StateManager.SaveState(Activity, "Main")
    End If
    StateManager.SaveSettings
End Sub
When the activity is paused we check if the user chose to close the activity (by pressing on the Back key). In that case we reset the state. The string parameter is the ActivityName value. StateManager can manage the state of multiple activities so the name is used to differentiate between the activities.
If UserClosed=False then we want to save the state.
The settings are saved in both cases.

When the activity is created we call: StateManager.RestoreState. The last parameter is the validity period for this state. The state will not be loaded if more than the specified minutes have passed. Pass 0 for an unlimited period.
RestoreState returns a boolean value. It returns true if the state was loaded. If the state wasn't loaded it is your responsibility to set the default value. This will be the case when the user runs the application for the first time.

To use StateManager you should choose Project - Add Existing Module and add StateManager.bas which is included in the attached example. You should also add a reference to the RandomAccessFile library and Reflection library.

statemanager_1.png


Version 1.11 is attached. It adds support for saving and restoring TabHost views with their internal views.
 

Attachments

  • StateManager.zip
    11.7 KB · Views: 1,873
Last edited:

Orcino Borges

Member
Licensed User
Longtime User
Save button colors

Hi Erel

In my application when a button is pressed it changes the color and text.

So is saved with the StateManger, but the color of the button is not saved.

How to do this ?

Thank`s

Orcino
 

Vader

Well-Known Member
Licensed User
Longtime User
Updated version [unofficial]

Erel,

I have modified your version 1.11 and am using it as v 1.12 (attached).

Changes:
Public and Private Sub scope added
Spacing around Sub layout (as I prefer a single line between each Sub)

Additions:
Logging (May not be complete at this time)

Variables
Public SettingsDeleteResult As Boolean = False
Public StateDeleteResult As Boolean = False

Subs
Public Sub DeleteState() as Boolean
Public Sub DeleteSettings() as Boolean
Public Sub DeleteStateAndSettings() as Boolean

I hope you don't mind. It adds a few things I wanted/needed, plus I think one or more people may have asked for the DeleteState or DeleteSettings functionality, and it made sense to share.

Dave
 

Attachments

  • Statemanagerv1.12.zip
    10.3 KB · Views: 603

padvou

Active Member
Licensed User
Longtime User
Hi Erel

In my application when a button is pressed it changes the color and text.

So is saved with the StateManger, but the color of the button is not saved.

How to do this ?

Thank`s

Orcino

B4X:
Else If v Is Button Then
        Dim bttn1 As Button
        bttn1 = v
        data = Array As Object(bttn1.Enabled, bttn1.Visible, bttn1.Text, bttn1.TextColor)

B4X:
Else If v Is Button Then
        Dim bttn1 As Button
        bttn1 = v
        data = getNextItem(list1)
        bttn1.Enabled = data(0)
       bttn1.Visible = data(1)
        bttn1.Text = data(2)
      bttn1.TextColor=data(3)
 

luke2012

Well-Known Member
Licensed User
Longtime User
You should not catch the Back button. Just change Activity_Paused to:
B4X:
Sub Activity_Pause (UserClosed As Boolean)
        StateManager.SaveState(Activity, "Main")
        StateManager.SaveSettings
End Sub

In this case all the UI Activity views are saved ?
 

JakeBullet70

Well-Known Member
Licensed User
Longtime User
Trying to save an ImageView background and I am getting a index out of range error. Basically data only comes back with a single element.

Is it not possible to save the background?


'---save data
B4X:
     Else If v Is ImageView Then
      Dim img As ImageView = v
      data = Array As Object(img.Tag,img.Background)
      list1.Add(data)


'--- load data back

B4X:
     Else If TheView Is ImageView Then
      Dim img As ImageView
      img = TheView
      data = getNextItem(list1)
      img.Tag = data(0)
      img.Background = data(1) '--- FAILS HERE
 

jeronimovilar2

Member
Licensed User
Longtime User
I just need not pause my timer when the home button is pressed :(
please. How can i do this?

B4X:
Sub Activity_Pause (UserClosed As Boolean)
   If UserClosed Then
      log("OK. stop the countdown")
   Else
      log("not pause my count down")
   End If

End Sub
 

jeronimovilar2

Member
Licensed User
Longtime User
What can i do to my timer NOT pause when touch in the home button? (Similar to the clock alarm/timer from android samsung).

My code:
B4X:
Sub Process_Globals
   Dim Timer1 As Timer 
   Dim CountDown1 As Int 
   Dim CountDown2 As Int
   Dim mp As MediaPlayer
   Dim p As Phone
   Dim vibrate As PhoneVibrate
   Dim Timer2 As Timer 
End Sub
Sub Activity_KeyPress (KeyCode As Int) As Boolean
   Dim Answ As Int
   If KeyCode = KeyCodes.KEYCODE_BACK Then
      Answ = Msgbox2("Sair do programa???", "ATENÇÂO!!!", "Yes", "", "No", Null)
      If Answ = DialogResponse.NEGATIVE Then
         Return True
      Else
         ToastMessageShow("Saindo...", False)
      End If
   End If
      Return False
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 LblCountDown1 As Label
   Dim conta_tempo As Int
   Dim Panel1 As Panel   
   Dim TEMPO As Int
   Dim VarLinguagem As String
   Dim corPainel As String
   Dim IMG1 As ImageView
   Dim TAM_tela As Int
   Dim RB2 As RadioButton
   Dim RB1 As RadioButton
   Dim RB3 As RadioButton
   Dim RB4 As RadioButton   
End Sub
Sub Activity_Pause (UserClosed As Boolean)
   If UserClosed Then
      Log("RESET")
   Else
      Log(IsPaused(""))
   End If
End Sub
Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")
   p.SetScreenBrightness(99) ' aumento o brilho ao máxino
   TAM_tela = 0
   Activity.Title = "-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. Cronômetro 2.1 .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-."
   
   Dim LY As LayoutValues
   Dim X, Y As Int
   LY = GetDeviceLayoutValues
   X = LY.Width
   Y=  LY.Height
   
   '----------- Cronometro -------------------
   Timer1.Initialize("Timer1", 1000)    
   CountDown2 = 0
   CountDown1 = 0
   TEMPO = RB1.Text
   Timer1.Enabled = False
   mp.Initialize2("mp")
   mp.Stop
   '------------------------------------------
End Sub

Sub Timer1_Tick
      If CountDown1 = 5 AND CountDown2 = 1 Then  ' ------- 5 min
         Vibra_Toca (False, True, 5)
      End If
      If CountDown1 = 1 AND CountDown2 = 1 Then ' ----------- 1 min
         Vibra_Toca (False, True, 2)      
      End If
      If CountDown1 = 0 AND (CountDown2 = 6 OR CountDown2 = 5 OR CountDown2 = 4 OR CountDown2 = 3 OR CountDown2 = 2)  Then  ' ------- últimos 5 segundos
         Vibra_Toca (False, True, 2) ' ------- últimos 5 seg
      End If
   If CountDown2 = 0 Then
      CountDown2 = 59
      CountDown1 = CountDown1 - 1
      If CountDown1 < 0 Then
         Timer1.Enabled = False
         CountDown2 = 0
         CountDown1 = 0
      End If
   Else
      CountDown2 = CountDown2 -1
   End If
   If CountDown2 < 10 Then
      LblCountDown1.Text = CountDown1 & ":0" & CountDown2
   Else
      LblCountDown1.Text = CountDown1 & ":" & CountDown2
   End If
   If CountDown2 = 0 AND CountDown1 <= 0 Then
      Timer1.Enabled = False
      Vibra_Toca (True, True, 0)
      CountDown2 = 0
      CountDown1 = 0
   End If
End Sub
Sub Vibra_Toca (vibra As Boolean, toca As Boolean, Musica As Int)
   mp.Initialize2("mp")
   mp.Stop
   Dim SOM As String
   vibrate.vibrate(250)
   If Musica = 5 Then
      SOM = "Alarme_05min.mp3"
      LblCountDown1.TextColor = Colors.Yellow
   Else If Musica = 0 Then  'END
      SOM = "ALARME_FINAL.mp3"
      LblCountDown1.TextColor = Colors.Red
   Else If Musica = 2 Then  '1 min ou 5s
      SOM = "ALARM_1S.mp3"
      LblCountDown1.TextColor = Colors.Red
   Else '  START
      SOM = "ALARME_INICIO.mp3"
      LblCountDown1.TextColor = Colors.White
   End If
   If toca = True Then
      p.SetRingerMode(p.RINGER_NORMAL)
      p.SetVolume(p.VOLUME_MUSIC,15,False)
      Dim r As Reflector
      r.Target = "ContentDir"
      r.Target = r.RunMethod("intern")
      mp.Load(File.DirAssets,SOM)
      mp.Play
      vibrate.vibrate(500)
      mp.Play
   End If
End Sub
Sub LblCountDown1_Click
   Dim SQL As String
   If CountDown2 = 0 AND CountDown1 <= 0 Then
      CountDown2 = 00
      '-----tempo do BUTTON
      CountDown1 = TEMPO
      Vibra_Toca(True, True, 15)
      Timer1.Enabled = True
   End If   
End Sub
Sub Timer2_Tick
   conta_tempo = conta_tempo + 1
   Timer1.Enabled = False
   CountDown2 = 0
   CountDown1 = 0
   If conta_tempo = 3 Then
      Timer2.Enabled = False
   End If
   LblCountDown1.Text = CountDown1 & ":0" & CountDown2
End Sub
Sub LblCountDown1_LongClick
   mp.Stop
   Timer1.Enabled = False
   CountDown2 = 0
   CountDown1 = 0
   LblCountDown1.Text = CountDown1 & ":0" & CountDown2
End Sub
Sub IMG1_LongClick
   ToastMessageShow("REDIMENSIONANDO...",False)
   If TAM_tela < 2 Then
      ' PAINEL
      Panel1.Height = Panel1.Height * 1.5
      Panel1.Width = Panel1.Width * 1.5
      Panel1.Top = Panel1.Top * 1.5
      ' TEMPO
      LblCountDown1.Height = LblCountDown1.Height * 1.5
      LblCountDown1.Width = LblCountDown1.Width * 1.5
      LblCountDown1.TextSize = LblCountDown1.TextSize * 1.5
      LblCountDown1.Top = LblCountDown1.Top * 1.5
      'IMAGEM
      IMG1.Top = IMG1.Top * 1.5
      IMG1.Width = IMG1.Width * 1.5
      IMG1.Height = IMG1.Height * 1.5
      '
      TAM_tela = TAM_tela + 1 
   Else
      Dim i As Int
      For i=1 To 2
      ' PAINEL
      Panel1.Height = Panel1.Height / 1.5
      Panel1.Width = Panel1.Width / 1.5
      Panel1.Top = Panel1.Top / 1.5
      ' TEMPO
      LblCountDown1.Height = LblCountDown1.Height / 1.5
      LblCountDown1.Width = LblCountDown1.Width / 1.5
      LblCountDown1.TextSize = LblCountDown1.TextSize / 1.5
      LblCountDown1.Top = LblCountDown1.Top / 1.5
      'IMAGEM
      IMG1.Top = IMG1.Top / 1.5
      IMG1.Width = IMG1.Width / 1.5
      IMG1.Height = IMG1.Height / 1.5
      '
      Next
      TAM_tela = 0
   End If
   
End Sub
Sub ListView1_ItemClick (Position As Int, Value As Object)
   ToastMessageShow("click",False)
End Sub
Sub RB1_CheckedChange(Checked As Boolean)
   If RB1.Checked = True Then
      TEMPO = RB1.Text
   End If
End Sub
Sub RB2_CheckedChange(Checked As Boolean)
   If RB2.Checked = True Then
      TEMPO = RB2.Text
   End If
End Sub
Sub RB3_CheckedChange(Checked As Boolean)
   If RB3.Checked = True Then
      TEMPO = RB3.Text
   End If
End Sub
Sub RB4_CheckedChange(Checked As Boolean)
   If RB4.Checked = True Then
      TEMPO = RB4.Text
   End If
End Sub
 

Felix Maria

Member
Licensed User
Longtime User
Hi,
I have 4 layouts in my main Activity. These layouts often call each other and has many views in each of the layout.
How can I preserve the state of the layouts like the activity state while navigating between the layouts.
Thank You!

Maria
 
Status
Not open for further replies.
Top