Android Tutorial Managing deployment of multiple applications on multiple devices with B4AServer

This is an example of how we can use B4AServer to manage the deployment and updates of multiple Android applications on multiple devices.

It works in the following way:
- The device application wants to check for updates.
- It connects to the desktop server and downloads a CSV file that lists the current applications and their versions.
- The device checks the version and existence of each of the applications and downloads and installs the required applications.

The desktop 'files' folder should contain the APK files and the CSV file:
b4aserver_deplyoment.png


The content of the csv file is:
B4X:
anywheresoftware.b4a.samples.json,JSON.apk,1
anywheresoftware.b4a.samples.linkedlist,LinkedList.apk,2
anywheresoftware.b4a.charts,Charts.apk,1
anywheresoftware.b4a.samples.animation,Animation.apk,2
As you can see each row has three fields: package name, apk file and the version.

The difficult part in the code is to manage the installation intents. It is not possible to send all of the intents together. So it is sending them one by one.

To build this project you should add B4AServer and B4AServerService modules which are available here: http://www.b4x.com/forum/additional-libraries-official-updates/9679-b4aserver-files.html
The following libraries are required: HTTP, Network, RandromAccessFile, Phone (for the version check and intents) and StringUtils (for the CSV parsing).

The main Activity code:
B4X:
'Activity module
Sub Process_Globals
    Dim ServerName As String
    ServerName = "test1" '*********** Change to your server value
    Dim checkForUpdatesTask As Int
    Dim UpdateTable As List
    Dim DownloadApkTask As Int
    Dim DuringResume As Boolean
    Dim InstallationInProgress As Boolean
    Dim LastIntent As Intent
    Dim tmrResendIntent As Timer
End Sub

Sub Globals
    Dim btnCheckUpdates As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        B4AServer.Initialize("http://66.84.14.57/android/b4a_server.php", ServerName, "Main")
        UpdateTable.Initialize
        tmrResendIntent.Initialize("tmrResendIntent", 500)
    End If
    btnCheckUpdates.Initialize("btnCheckUpdates")
    btnCheckUpdates.Text = "Check for updates"
    Activity.AddView(btnCheckUpdates, 10dip, 10dip, 300dip, 100dip)
End Sub
    
Sub Activity_Resume
    tmrResendIntent.Enabled = False
    DuringResume = True
    'Raise events for tasks that completed while our activity was paused
    For i = 0 To B4AServer.TasksFinishedWhilePaused.Size - 1
        Dim Result As TaskResult
        Result = B4AServer.TasksFinishedWhilePaused.Get(i)
        Log("From resume: " & Result)
        TaskComplete(Result)
    Next
    B4AServer.TasksFinishedWhilePaused.Clear
    'Activity_Resume will be called after each installation
    'So we process the next package here (if such is available).
    ProcessNextRow
    DuringResume = False
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub btnCheckUpdates_Click
    checkForUpdatesTask = B4AServer.ReceiveFile("files.csv")
    btnCheckUpdates.Enabled = False
    InstallationInProgress = True
End Sub

Sub TaskComplete(Result As TaskResult)
    Log("Task=" & Result.Id & " completed. Success=" & Result.Success & ", Message=" & Result.Message)
    If Result.Success Then
        Select Result.Id
            Case checkForUpdatesTask
                HandleFilesTable(Result.Id) 'The task id is the file name.
            Case DownloadApkTask
                InstallPackage(Result.Id)
        End Select
    Else
        Msgbox(Result.Message, "Error occurred")
        btnCheckUpdates.Enabled = True
        InstallationInProgress = False
    End If
End Sub

Sub HandleFilesTable(FileName As String)
    Dim su As StringUtils
    UpdateTable = su.LoadCSV(B4AServer.DownloadFilesFolder, FileName, ",")
    'The next line will process the first row (which will lead to processing all rows).
    'If however the Activity was paused during the call to "check updates" call then
    'we do not want to ProcessNextRow as it will happen anyway in Sub Activity_Resume.
    If DuringResume = False Then ProcessNextRow
End Sub
Sub ProcessNextRow
    Dim pm As PackageManager
    Dim row() As String
    'Each row includes the following fields: package name, apk name, version code
    For i = UpdateTable.Size - 1 To 0 Step -1
        row = UpdateTable.Get(i)
        UpdateTable.RemoveAt(i)
        'Check if we need to install this package.
        If ShouldInstallPackage(row(0), row(2)) Then
            DownloadApkTask = B4AServer.ReceiveFile(row(1)) 'request the apk.
            Log("Requesting " & row(1) & ", task=" & DownloadApkTask)
            Return
        Else
            Log("no need to install: " & row(0))
        End If
    Next
    If InstallationInProgress Then
        ToastMessageShow("Finished updating applications.", True)
        btnCheckUpdates.Enabled = True
        InstallationInProgress = False
    End If
End Sub
Sub ShouldInstallPackage(Package As String, NewVersion As Int) As Boolean
    Try
        Dim version As Int
        Dim pm As PackageManager
        version = pm.GetVersionCode(Package)
        Return version < NewVersion
    Catch
        'An exception will be thrown if the package do not exist. In this case we need to install.
        Return True
    End Try
End Sub
Sub InstallPackage (FileName As String)
    Dim i As Intent
    i.Initialize(i.ACTION_VIEW, "file://" & File.Combine(B4AServer.DownloadFilesFolder, FileName))
    i.SetType("application/vnd.android.package-archive")
    StartActivity(i)
    'in some cases at least in the emulator the Intent is ignored.
    'we use this timer to resend the intent. If the installation has 
    'already started then this activity will paused and the timer will not tick.
    LastIntent = i
    tmrResendIntent.Enabled = True
End Sub
Sub tmrResendIntent_Tick
    Log("Resending intent")
    StartActivity(LastIntent)
End Sub
 

AllanD

Member
Licensed User
Longtime User
Hello Erel,

It looks very interesting, but I have the question..

Does the install program happen transparently to the device side? or does the user have to aprove the install?

What I am hoping to do is impliment a kiosk type application that could receive updates automatically from the server without promptings on the device side.

Thanks,
Allan
 

AllanD

Member
Licensed User
Longtime User
Can not delete package

Erel,
I downloaded the sample packages above to understand and then try to modify code so that I can delete a package on the Galaxy Tab.

The problem is that the intent does not provide ACTION_DELETE
so how can I get this included?

I have the following code but it bombs out on ACTION_DELETE

Sub UnInstallPackage (FileName As String)
Dim i As Intent
i.Initialize(i.ACTION_DELETE, FileName)
'i.SetType("application/vnd.android.package-archive")
StartActivity(i)
End Sub

Thank you,
Allan
 

splatt

Active Member
Licensed User
Longtime User
It is not possible to do "quiet" installs in Android.

Is that strictly true? If I install an app through Google Play and select "Automatically update", I only ever get prompted for permission to install if the Android Permissions change.
 
Top