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,051
Last edited:

NMiguel

Member
Licensed User
Hi there Erel. Great work.

I can't put it to work with listviews when the user rotates the screen.
It works pressing the home button and going back to the app.

What am I doing wrong?

There's a sample attached.

Thanks.
 

Attachments

  • TestStateManager.zip
    9.7 KB · Views: 207

NMiguel

Member
Licensed User
StateManager doesn't do anything with listviews (you can see the code).

Ok Erel. I don't loose the data when I press home because because the activity only pauses. Correct?

Will it do something with the listviews?

Thanks
 

Mark Zraik

Member
Licensed User
NMiguel,

I looked at your sample code and it looks as though you are just getting startied on this app.

If you are going to be using short lists (Under 20 items in the list), just rerun the listing function on a rotation.
If the list are long, you may have to create your own way of retaining the list in a file on the SD Card area.
You can use the savesettings routine to set a marker or placeholder if you need to.

A simple fix for now would be to select an single orientation for the app and move forward with your coding. You can come back to your rotation issue later.

If you need to, place a
B4X:
Log("Loading from ...")
statement for Activity_Create, Resume and Pause to see the different steps the app goes through when rotated and when you use the Home button to resume, and the back button to close out of the app. You can also place a conditional to show if FirstTime is true or UserClosed is true. This can give a clearer picture of how you can approach your code.
 

NMiguel

Member
Licensed User
Thanks Mark.

I will follow your advice.
I'll set the screen rotation to horizontal and move on with the code for now and look to this issue latter.

Who knows if someone adds the ListView feature to the StateManager. :)

Best regards.
NMiguel
 

jayel

Active Member
Licensed User
Did the changes but not working.
B4X:
Sub innerSaveState(v As View, list1 As List)
    Dim data() As Object
    If v Is EditText Then
        Dim edit As EditText
        edit = v
        data = Array As Object(edit.Text, edit.SelectionStart)
    Else If v Is Spinner Then
        Dim spinner1 As Spinner
        spinner1 = v
        data = Array As Object(spinner1.SelectedIndex)
    Else If v Is CheckBox Then
        Dim check As CheckBox
        check = v
        data = Array As Object(check.Checked)
    Else If v Is RadioButton Then
        Dim radio As RadioButton
        radio = v
        data = Array As Object(radio.Checked)   
    Else If v Is ToggleButton Then
        Dim toggle As ToggleButton
        toggle = v
        data = Array As Object(toggle.Checked)
    Else If v Is SeekBar Then
        Dim seek As SeekBar
        seek = v
        data = Array As Object(seek.Value)
    Else If v Is TabHost Then
        Dim th As TabHost
        th = v
        data = Array As Object(th.CurrentTab)
        For i = 0 To th.TabCount - 1
            th.CurrentTab = i
        Next
        list1.Add(data)
        Dim data() As Object
        Dim r As Reflector
        r.Target = th
        Dim tabParentPanel As Panel
        tabParentPanel = r.RunMethod("getTabContentView")
        For i = 0 To tabParentPanel.NumberOfViews - 1
            innerSaveState(tabParentPanel.GetView(i), list1)
        Next
    Else If v Is ScrollView Then
        Dim sv As ScrollView
        sv = v
        data = Array As Object(sv.ScrollPosition)
        list1.Add(data)
        Dim data() As Object
        innerSaveState(sv.Panel, list1)
    Else If v Is HorizontalScrollView Then
        Dim hsv As HorizontalScrollView
        hsv = v
        data = Array As Object(hsv.ScrollPosition)
        list1.Add(data)
        Dim data() As Object
        innerSaveState(hsv.Panel, list1)
    Else If v Is Panel Then
        Dim panel1 As Panel
        panel1 = v
        For i = 0 To panel1.NumberOfViews - 1
            innerSaveState(panel1.GetView(i), list1)
        Next
    End If
    If data.Length > 0 Then list1.Add(data)
End Sub
Sub innerRestoreState(v As View, list1 As List)
    Dim data() As Object
    If v Is EditText Then
        Dim edit As EditText
        edit = v
        data = getNextItem(list1)
        edit.Text = data(0)
        edit.SelectionStart = data(1)
    Else If v Is Spinner Then
        Dim spinner1 As Spinner
        spinner1 = v
        data = getNextItem(list1)
        spinner1.SelectedIndex = data(0)
    Else If v Is CheckBox Then
        Dim check As CheckBox
        check = v
        data = getNextItem(list1)
        check.Checked = data(0)
    Else If v Is RadioButton Then
        Dim radio As RadioButton
        radio = v
        data = getNextItem(list1)
        radio.Checked = data(0)
    Else If v Is ToggleButton Then
        Dim toggle As ToggleButton
        toggle = v
        data = getNextItem(list1)
        toggle.Checked = data(0)
    Else If v Is SeekBar Then
        Dim seek As SeekBar
        seek = v
        data = getNextItem(list1)
        seek.Value = data(0)
    Else If v Is TabHost Then
        Dim th As TabHost
        th = v
        data = getNextItem(list1)
        For i = 0 To th.TabCount - 1
            th.CurrentTab = i
        Next
        th.CurrentTab = data(0)
        Dim r As Reflector
        r.Target = th
        Dim tabParentPanel As Panel
        tabParentPanel = r.RunMethod("getTabContentView")
        For i = 0 To tabParentPanel.NumberOfViews - 1
            innerRestoreState(tabParentPanel.GetView(i), list1)
        Next
    Else If v Is ScrollView Then
        Dim sv As ScrollView
        sv = v
        data = getNextItem(list1)
        sv.ScrollPosition = data(0)
        DoEvents
        sv.ScrollPosition = data(0)
        innerRestoreState(sv.Panel, list1)
    Else If v Is HorizontalScrollView Then
        Dim hsv As HorizontalScrollView
        hsv = v
        data = getNextItem(list1)
        hsv.ScrollPosition = data(0)
        DoEvents
        sv.ScrollPosition = data(0)
        innerRestoreState(hsv.Panel, list1)
    Else If v Is Panel Then
        Dim panel1 As Panel
        panel1 = v
        For i = 0 To panel1.NumberOfViews - 1
            innerRestoreState(panel1.GetView(i), list1)
        Next
    End If
End Sub
 

jayel

Active Member
Licensed User
There is a typo in your code. It should be hsv instead of sv. However you can remove this DoEvents call and use ScrollToNow instead of setting ScrollPosition.

Ok, I changed that :
B4X:
Else If v Is HorizontalScrollView Then
        Dim hsv As HorizontalScrollView
        hsv = v
        data = getNextItem(list1)
        hsv.ScrollPosition = data(0)
        DoEvents
        'hsv.ScrollPosition = data(0)
        hsv.ScrollToNow(data(0))
        innerRestoreState(hsv.Panel, list1)

But not working, The HSV doens't save my panels inside !
 

bogdanc

Active Member
Licensed User
I tried to use this great tutorial but I have problem on line 14:

If Settings.IsInitialized = False Then

Unknow member: IsInitialized
 
Status
Not open for further replies.
Top