B4A Library AppUpdating 2.0 - update non-market apps

Time goes by, things change and old tools need to be updated.

AppUpdating 2.0 is a partial rewrite of AppUpdating, a lib written back in 2014 to allow the remote update of non-market applications hosted on a webserver.

Why AU 2.0? Main reason is Google's introduction of Android 8 and the profound changes on a few key points coming with it. But it was time to leverage B4x's new features too. And, finally, a bit of clean up is always due.. :)

On next two posts I'll describe the steps needed to make the lib work with your code.
Edit: see post#43 below for installing instructions related to version 2.05 and higher.

What the lib does is simply to check whether the version number reported by reading an info text file on your webserver is greater than the one showed in the running copy of your app. If it finds indication of a newer version, it downloads it, then it asks the user to install it.
Since we can't know if the user agrees to update the app, we simply go on with our app, knowing that a service burned in the lib will fire when the OS will signal that the user accepted to install the newly downloaded copy of the app. In this latter case, the same service will reload the app when ready.

Files attacched:
AppUpdating.b4xlib - version 2.05 packed following the new b4xlib standard
AU200_src - source code for the lib
AU200_demo - example program using the lib
AU200_lib - lib file compiled with B4A 8.30

Versions changelog
2.05 - made it compatible with simultaneous use of NB6
2.00 - initial release of AppUpdating 2.0
 

Attachments

  • AU200_demo.zip
    31.6 KB · Views: 1,498
  • AU200_src.zip
    14.9 KB · Views: 1,258
  • AU200_lib.zip
    25.3 KB · Views: 1,472
  • AppUpdating.b4xlib
    7.3 KB · Views: 1,002
Last edited:

udg

Expert
Licensed User
Longtime User
Hi all, sorry for the long delay.
In order to stop the ever growing confusion about versions of the lib, please find in post #1 the latest one.
It's labeled 2.05 altough its code base is identical to version 2.03. You find it published in the form of the newer b4xlib, so to access its code just rename it to a zip file and open it: you'll find the class c_appupdate and the service newinst2 as always.

For those who simply want to use the lib w/o concern on how is built or what's inside it, just copy the b4xlib file to your Additional Libraries folder (I put it in the sub-folder B4X) and check AppUdating in your IDE's Library Manager. Then prepare the Manifest of your app as follows (this means "forget instructions from post#2 above):
B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="28"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddReceiverText(newinst2,
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>)

' Starting from Android 7 (API 24) we pass a file uri using a FileProvider
AddApplicationText(
  <provider
  android:name="android.support.v4.content.FileProvider"
  android:authorities="$PACKAGE$.provider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
  android:name="android.support.FILE_PROVIDER_PATHS"
  android:resource="@xml/provider_paths"/>
  </provider>
)

'new external-files-path ensures compatibility with NB6
CreateResource(xml, provider_paths,
<paths>
   <files-path name="name" path="shared" />
   <external-files-path name="name1" path="shared1" />
</paths>
)

AddManifestText(<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="19" />
)

AddPermission(android.permission.REQUEST_INSTALL_PACKAGES)
AddPermission(android.permission.INTERNET)

Edit: section Main Activity from post #2 is still needed.

As soon as possible I will update the demo and hopefully add a B4xPages demo too.
 
Last edited:

DonManfred

Expert
Licensed User
Longtime User

udg

Expert
Licensed User
Longtime User
AppUpdating 2.05 - installation

Lib files installation

As usual with any contributed library published in the b4xlib format, download the lib file (AppUpdatingxxx.b4xlib, you need just this one), and place it in your Additional Libraries path (see menu option "Tools/Configure paths" in your B4A installation). Best if you place it in the B4X sub-folder.
To access its source code, just rename its extension to "zip" and open it as any zipped file; in it you'll find the class c_appupdate and the service newinst2 as in older versions.
The above point is also useful if you need to recompile the lib with a more recent version of B4A.

Manifest
For any app that you'd like to use this lib with, go to its Manifest editor ("Project/Manifest Editor") and add the following code:
B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="28"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddReceiverText(newinst2,
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>)

' Starting from Android 7 (API 24) we pass a file uri using a FileProvider
AddApplicationText(
  <provider
  android:name="android.support.v4.content.FileProvider"
  android:authorities="$PACKAGE$.provider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
  android:name="android.support.FILE_PROVIDER_PATHS"
  android:resource="@xml/provider_paths"/>
  </provider>
)

'new external-files-path ensures compatibility with NB6
CreateResource(xml, provider_paths,
<paths>
   <files-path name="name" path="shared" />
   <external-files-path name="name1" path="shared1" />
</paths>
)

AddManifestText(<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="19" />
)

AddPermission(android.permission.REQUEST_INSTALL_PACKAGES)
AddPermission(android.permission.INTERNET)

Main activity
Don't forget to check AppUdating in your IDE's Library Manager.
Add to your Main activity the following code and call it before issuing an "install" command to the lib.
B4X:
'Check whether we already have permission for install other apps.
'If not we open the relevant settings page
'then wait for Activity_Resume and check the value of CanRequestPackageInstalls again
Private Sub CheckInstallationRequirements As ResumableSub
    If File.ExternalWritable = False Then
        MsgboxAsync("Storage card not available. Make sure that your device is not connected in USB storage mode.", "")
        Return False
    Else If phone.SdkVersion >= 26 And apkupdt.CanRequestPackageInstalls = False Then
        MsgboxAsync("Please allow me to install applications.", "")
        Wait For Msgbox_Result(Result As Int)
        Dim in As Intent
        in.Initialize("android.settings.MANAGE_UNKNOWN_APP_SOURCES", "package:" & Application.PackageName)
        StartActivity(in)
        Wait For Activity_Resume '<-- wait for Activity_Resume
        Return apkupdt.CanRequestPackageInstalls
    Else If apkupdt.CheckNonMarketAppsEnabled = False Then
        MsgboxAsync("Please enable installation of non-market applications." & CRLF & "Under Settings - Security - Unknown sources" _
        & CRLF & "Or Settings - Applications - Unknown sources", "")
        Return False
    Else
        Return True
    End If
End Sub

Note: code above assumes you named the instance of the class cl_appupdate as apkupdt (Dim apkupdt As cl_appupdate). If you used a different name, change the above code accordingly.

That's it. You're now ready to use the lib.
You may want to refer to the demo soon to be attached to post#1 in order to see how the methods from the lib are used in a couple of scenarios.
 
Last edited:

Alex_Puz

Member
Licensed User
Longtime User
I try to use your library 2.05 and found that library ignore update when APP paused.
After App comback event never appear.
Maybe need modify service in library to allow work in background.
At moment I use Activity_resume to invoke update again. I guess it is little bit incorrect.
Also library ignore event app download with status =5
 

udg

Expert
Licensed User
Longtime User
Hi Alex,
I'm not sure to fully understand the paused problem.
AppUpdating, in its Manifest, declares a service that the OS is expected to call when the main app is ready with a new package. This last event is raised when the app is running as part of the "check for a new package" sub. This means that the main app is active and in the foreground.
Did you expect something like the PlayStore where a background task searches for any updated app then list them in a notification so you can accept to download (and then install) each of them?

Also library ignore event app download with status =5
Thank you, I'll check the source code for this point. Sorry if it caused any inconvenience.
 

Alex_Puz

Member
Licensed User
Longtime User
Hi Alex,
I'm not sure to fully understand the paused problem.
AppUpdating, in its Manifest, declares a service that the OS is expected to call when the main app is ready with a new package. This last event is raised when the app is running as part of the "check for a new package" sub. This means that the main app is active and in the foreground.
Did you expect something like the PlayStore where a background task searches for any updated app then list them in a notification so you can accept to download (and then install) each of them?


Thank you, I'll check the source code for this point. Sorry if it caused any inconvenience.
Thank You for your attention and help to improve library and support my lovely b4x.
Could you check assign shared directory, in most examples
shared folder:
if p.sdkVersion>= 24 then
Dim Sharedfolder As String = rp.GetSafeDirDefaultExternal("shared")
....
in your library
shared folder:
If p.SdkVersion >= 24 Or File.ExternalWritable = False Then
        UseFileProvider = True
        SharedFolder = File.Combine(File.DirInternal, "shared")
        File.MakeDir("", SharedFolder)
    Else
        UseFileProvider = False
        SharedFolder = rp.GetSafeDirDefaultExternal("shared")
    End If
if I use share folder
shared directory:
  SharedFolder = File.Combine(File.DirInternal, "shared")
invoike in NB6
b4x:
Return FileProvider.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))
then I got error
manifest:
CreateResource(xml, provider_paths,
<files-path name="name" path="shared" />
)
if use
manifest:
CreateResource(xml, provider_paths,
<external-files-path name="name" path="shared" />
)
then got error in you library
in NB6
NB6:
 Notification_WithCustomSound(content As String,id As Int)
    Dim rp As RuntimePermissions
    Dim folder As String = rp.GetSafeDirDefaultExternal("shared")
I guess it can be solve with
NB6:
 Notification_WithCustomSound(content As String,id As Int)
    Dim folder As String  = File.Combine(File.DirInternal, "shared")
about notification: I will double check. but in your library sdk
sub DownloadApk:
If j.Success Then
        'save to temporary file "tmp.apk" in SharedFolder
        Dim out As OutputStream
        out = File.OpenOutput(SharedFolder,"tmp.apk",False)
        File.Copy2(J.GetInputStream, out)
        out.Close
        sStatusCode = OK_DOWNLOAD ' = 5
        If sVerbose Then Log($"${TAB}new apk version downloaded and ready to install"$)
    Else
        Log($"${TAB}Error: ${J.ErrorMessage}"$)
        sStatusCode = ERR_HTTP
        If sVerbose Then Log($"${TAB}error in httputils2"$)
        ToastMessageShow("Error: " & J.ErrorMessage, True)
    End If
Thank you for help
 
Last edited:

udg

Expert
Licensed User
Longtime User
Hi Alex,
I'm not at my dev machine now, but I found a thread where a conflict between AU and NB6 was solved. Read here.
So, if AU uses <files-path name="name" path="shared1" /> then in cl_upappdate.bas you should have
B4X:
Dim Sharedfolder As String = rp.GetSafeDirDefaultExternal("shared1")
leaving the "keyword" shared to NB6.

Please, try it with this sole change. If it works we know that I should amend my code in order to align it with the suggested Manifest while all the NB6 code remains unchanged.
 

Alex_Puz

Member
Licensed User
Longtime User
Hi Alex,
I'm not at my dev machine now, but I found a thread where a conflict between AU and NB6 was solved. Read here.
So, if AU uses <files-path name="name" path="shared1" /> then in cl_upappdate.bas you should have
B4X:
Dim Sharedfolder As String = rp.GetSafeDirDefaultExternal("shared1")
leaving the "keyword" shared to NB6.

Please, try it with this sole change. If it works we know that I should amend my code in order to align it with the suggested Manifest while all the NB6 code remains unchanged.
Thank you for reply
I just try my previous solution
NB6:
 Notification_WithCustomSound(content As String,id As Int)
    Dim folder As String  = File.Combine(File.DirInternal, "shared")
and it works for me
 

vecino

Well-Known Member
Licensed User
Longtime User
Hello friends, I am reading your comments and I have a question: what is UA and BN6?
Thank you.
 

udg

Expert
Licensed User
Longtime User
AU (not UA) stands for AppUpdating (the lib at this thread) while NB6 (not BN6) is the internal library Notification library.
 

udg

Expert
Licensed User
Longtime User
It happens all the time to me too but it's always better than mine hand-writing :)
 

warwound

Expert
Licensed User
Longtime User
I just wanted to point out that the copy of newinst2.bas contained in AU200_src.zip is not the same as the version in AppUpdating.b4xlib

AU200_src.zip > newinst2.bas > lines 33 to 40 read:
B4X:
    If StartingIntent.Action = "android.intent.action.PACKAGE_REPLACED" Then
        If svcVerbose Then Log($"${TAB}Package REPLACED intent received!"$)
        pkg = GetPackageName
        If svcVerbose Then Log($"${TAB}package: ${pkg}"$)
        If StartingIntent.GetData = "package:" & pkg Then
            MyAppReload
        End If
    End If

AppUpdating.b4xlib > newinst2.bas > lines 33 to 38 read:
B4X:
    If StartingIntent.Action = "android.intent.action.MY_PACKAGE_REPLACED" Then
        If svcVerbose Then Log($"${TAB}Intent MY_PACKAGE_REPLACED received!"$)
        pkg = GetPackageName
        If svcVerbose Then Log($"${TAB}package: ${pkg}"$)
        MyAppReload
    End If

AU200_src.zip version will always fail as android.intent.action.PACKAGE_REPLACED is the incorrect action.
Also in my tests the line:
B4X:
If StartingIntent.GetData = "package:" & pkg Then
Is not always true after an update.

Otherwise thanks for a great library.
 

udg

Expert
Licensed User
Longtime User
You are right. My_package_replaced is newer and the suggested intent.
I planned a newer and reviseted release for the 10th anniversary of the lib, bit unfortunately my mother needs some close assistance so my time is for her these days.
 
Top