Android Question Persistent App

ElliotHC

Active Member
Licensed User
I have an app which is using SMB and FTP which needs to carry on what it's doing in the background. Is this a simple command?

Thanks
 

Brandsum

Well-Known Member
Licensed User
  1. Move all your SMB and FTP code to a service
  2. Set service foreground to always (Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS). This will show a default style notification.
    OR
    Set service foreground to never (Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_NEVER) and create your own notification with NB6 and call Service.StartForeground
  3. Call Service.StopAutomaticForeground when you want to stop the service.
  4. You can add #StartAtBoot: True to start that service after a system reboot
 

ElliotHC

Active Member
Licensed User
:eek: I have a lot of subs in this project. It's basically some from the External Storage example, SMB and FTP.. I'm not sure exactly how I'd do that.
 

ElliotHC

Active Member
Licensed User
upload_2019-8-10_18-50-34.png

It doesn't seem to like the Listfiles sub which is also in the service I've created.
 

Brandsum

Well-Known Member
Licensed User
Maybe the declaration of the storage variable is incorrect that's why the IDE could not able to find ListFiles method. That you need to figure out. I have told you how you can communicate in the background and without activating the app.
 

ElliotHC

Active Member
Licensed User
I've literally just copied everything, perhaps there is an issue with it accessing the external storage?
 

ElliotHC

Active Member
Licensed User
I'm not sure the best way about doing this..

Basically, I need to use this project. https://www.b4x.com/android/forum/attachments/externalstorage-zip.76775/

Select a folder.

This is the bit that I can't seem to get to work..

So up to now it's exactly that project above, no changes or additions. I need to have a timer running every say 1000ms which then loads the file names in to a list.. Sort the list! But I need that list built up and sorted running as a service.

Easy to mod?
 

Brandsum

Well-Known Member
Licensed User
So up to now it's exactly that project above, no changes or additions. I need to have a timer running every say 1000ms which then loads the file names in to a list.. Sort the list! But I need that list built up and sorted running as a service.
So basically you want to monitor a folder for file changes and if any file change detected you want to get notified?

Then use this FileObserver Library
https://www.b4x.com/android/forum/threads/fileobserver.16709/
 
Last edited:

ElliotHC

Active Member
Licensed User
Sorry, I don't think I explained it well. I can't seem to be able to get that example app to run a service that checks the file periodically on the external storage. I don't think it's possible.
 

ElliotHC

Active Member
Licensed User
I'm at first trying to move everything over in to the 'Main' as there is a separate module. After doing this I'll move the complete project in to a service and remove the listview in place of a list.

Now that I've done that it doesn't like the storage element.

I'm trying to upload the project but the uploader keeps telling me that the file is too large. It's 1.45Mb
 

ElliotHC

Active Member
Licensed User
B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example External Storage Service
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: portrait
    #CanInstallToExternalStorage: False

#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

#AdditionalJar: com.android.support:support-core-utils
Sub Process_Globals
    Private Storage As ExternalStorage
    Private FoldersStack As List
    Private UpItem As ExternalFile
End Sub

Sub Globals
    'Private chkUsePreviouslySelected As CheckBox
    Private Previous As Boolean
    Private ListView1 As ListView ' Change this to a list
    Private ImageView1 As ImageView
    Private lblPath As Label
    
    
    
    '>>>>>>>>>>>>>>>>>>>>> Below is from Module
    Private ion As Object
    Private PersistantUri As String
    Private const FileName As String = "PersistantUri"
    Private ctxt As JavaObject
    Private mCallback As Object
    Private mEventName As String
    Public Root As ExternalFile
    Type ExternalFile (Name As String, Length As Long, LastModified As Long, IsFolder As Boolean, Native As JavaObject)
    '>>>>>>>>>>>>>>>>>>>>> Above is from Module
    
    
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        Storage.Initialize("Storage")
        FoldersStack.Initialize
        UpItem.Initialize
    End If
    'Activity.LoadLayout("1")
    'chkUsePreviouslySelected.Checked = True
    Previous = True
    Storage.SelectDir(Previous)
    Wait For Storage_ExternalFolderAvailable
    FoldersStack.Clear
    EnterFolder(Storage.Root)
End Sub

'>>>>>>>>>>>>>>>>>>>>> Below is from Module
Public Sub Initialize (Callback As Object, EventName As String)
    mCallback = Callback
    mEventName = EventName
    ctxt.InitializeContext
End Sub
'>>>>>>>>>>>>>>>>>>>>> Above is from Module

Private Sub EnterFolder (folder As ExternalFile)
    FoldersStack.Add(folder)
    Dim sb As StringBuilder
    sb.Initialize
    For Each f As ExternalFile In FoldersStack
        If f = Storage.Root Then
            sb.Append("Root")
        Else
            sb.Append(" / ").Append(f.Name)
        End If
    Next
    lblPath.Text = sb.ToString
    ListView1.Clear ' Change this to a list
    
    If FoldersStack.Size > 1 Then
        ListView1.AddSingleLine2("..", UpItem) ' Change this to a list
    End If
    For Each f As ExternalFile In Storage.ListFiles(folder)
        If f.IsFolder Then
            ListView1.AddSingleLine2($"[${f.Name}]"$, f) ' Change this to a list
        Else If IsImageFile(f.Name) Then
            Dim cs As CSBuilder
            cs.Initialize.Append(f.Name).Append(" ").Color(0xFF00EEFF).Append("(click)").PopAll
            ListView1.AddSingleLine2(cs, f) ' Change this to a list
        Else
            ListView1.AddSingleLine2(f.Name, f) ' Change this to a list
        End If
    Next
End Sub

Private Sub ListView1_ItemClick (Position As Int, Value As Object)
    Dim f As ExternalFile = Value
    If f = UpItem Then
        'remove the current folder
        FoldersStack.RemoveAt(FoldersStack.Size - 1)
        'get the parent folder which is now the topmost folder
        Dim folder As ExternalFile = GetCurrentFolder
        'remove it and enter it again
        FoldersStack.RemoveAt(FoldersStack.Size - 1)
        EnterFolder(folder)
        
    Else
        'The ExternalFile returned from ListFiles cannot be used directly.
        'We need to first call FindFile.
        f = Storage.FindFile(GetCurrentFolder, f.Name)
        If f.IsFolder Then
            EnterFolder(f)
        Else If IsImageFile(f.Name) Then
            Dim in As InputStream = Storage.OpenInputStream(f)
            'We can open the image directly with Bitmap.Initialize2 however it will not allow us to use LoadBitmapResize
            'so instead we copy it to a temporary file.
            Dim out As OutputStream = File.OpenOutput(File.DirInternal, "temp", False)
            File.Copy2(in, out)
            out.Close
            ImageView1.SetBackgroundImage(LoadBitmapResize(File.DirInternal, "temp", ImageView1.Width, ImageView1.Height, True))
        End If
    End If
End Sub

Private Sub GetCurrentFolder As ExternalFile
    Return FoldersStack.Get(FoldersStack.Size - 1) 'get the last item
End Sub


Private Sub IsImageFile (Name As String) As Boolean
    Dim n As String = Name.ToLowerCase
    For Each extension As String In Array(".jpg", ".png", ".gif", ".bmp")
        If n.EndsWith(extension) Then Return True
    Next
    Return False
End Sub


Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub


'>>>>>>>>>>>>>>>>>>>>> Below is from Module
'Lets the user pick a folder.
'Optionally using the previous selected folder.
Public Sub SelectDir (UsePreviouslySelectedIfAvaiable As Boolean)
    If UsePreviouslySelectedIfAvaiable And File.Exists(File.DirInternal, FileName) Then
        PersistantUri = File.ReadString(File.DirInternal, FileName)
        Dim list As List = ctxt.RunMethodJO("getContentResolver", Null).RunMethod("getPersistedUriPermissions", Null)
        If list.IsInitialized Then
            For Each uripermission As JavaObject In list
                Dim u As Uri = uripermission.RunMethod("getUri", Null)
                Dim temp As Object = u
                Dim s As String = temp
                If s = PersistantUri And uripermission.RunMethod("isWritePermission", Null) = True Then
                    Log("Can use persistant uri!")
                    SetPickedDir
                    Return
                End If
            Next
        End If
    End If
    Dim i As Intent
    i.Initialize("android.intent.action.OPEN_DOCUMENT_TREE", "")
    i.PutExtra("android.content.extra.SHOW_ADVANCED", True)
    StartActivityForResult(i)
End Sub

'List all files in the given folder.
Public Sub ListFiles (Folder As ExternalFile) As List
    Dim files() As Object = Folder.Native.RunMethod("listFiles", Null)
    Dim res As List
    res.Initialize
    For Each o As Object In files
        Dim f As JavaObject = o
        res.Add(DocumentFileToExternalFile(f))
    Next
    Return res
End Sub
'Finds the file with the given name.
'Returns an uninitialized ExternalFile if not found.
Public Sub FindFile (Parent As ExternalFile, Name As String) As ExternalFile
    Dim f As JavaObject = Parent.Native.RunMethod("findFile", Array(Name))
    Return DocumentFileToExternalFile(f)
End Sub

'Creates a new file.
Public Sub CreateNewFile (Parent As ExternalFile, Name As String) As ExternalFile
    Return DocumentFileToExternalFile(Parent.Native.RunMethod("createFile", Array("", Name)))
End Sub

'Deletes the file.
Public Sub DeleteFile (EF As ExternalFile) As Boolean
    If EF.IsInitialized = False Then Return False
    Return EF.Native.RunMethod("delete", Null)
End Sub

'Open an output stream that writes to the file.
Public Sub OpenOutputStream(EF As ExternalFile) As OutputStream
    Return ctxt.RunMethodJO("getContentResolver", Null).RunMethod("openOutputStream", Array(EF.Native.RunMethod("getUri", Null)))
End Sub
'Open an input stream that reads from the file.
Public Sub OpenInputStream(EF As ExternalFile) As InputStream
    Return ctxt.RunMethodJO("getContentResolver", Null).RunMethod("openInputStream", Array(EF.Native.RunMethod("getUri", Null)))
End Sub

'Finds the file with the given name. If not found creates a new file.
Public Sub FindFileOrCreate (Parent As ExternalFile, Name As String) As ExternalFile
    Dim f As ExternalFile = FindFile(Parent, Name)
    If f.IsInitialized = False Then
        Return CreateNewFile(Parent, Name)
    Else
        Return f
    End If
End Sub


Private Sub DocumentFileToExternalFile (DocumentFile As JavaObject) As ExternalFile
    Dim ef As ExternalFile
    If DocumentFile.IsInitialized = False Then
        Return ef
    End If
    ef.Initialize
    ef.Name = DocumentFile.RunMethod("getName", Null)
    ef.Length = DocumentFile.RunMethod("length", Null)
    ef.IsFolder = DocumentFile.RunMethod("isDirectory", Null)
    ef.Native = DocumentFile
    ef.LastModified = DocumentFile.RunMethod("lastModified", Null)
    Return ef
End Sub

Private Sub SetPickedDir
    Root = DocumentFileToExternalFile(GetPickedDir(PersistantUri))
    CallSubDelayed(mCallback, mEventName & "_ExternalFolderAvailable")
End Sub

Private Sub ion_Event (MethodName As String, Args() As Object) As Object
    If Args(0) = -1 Then 'resultCode = RESULT_OK
        Dim i As Intent = Args(1)
        Dim jo As JavaObject = i
        Dim treeUri As Uri = jo.RunMethod("getData", Null)
        Dim takeFlags As Int = Bit.And(i.Flags, 3)
        ctxt.RunMethodJO("getContentResolver", Null).RunMethod("takePersistableUriPermission", Array(treeUri, takeFlags))
        Dim temp As Object = treeUri
        PersistantUri = temp
        File.WriteString(File.DirInternal, FileName, PersistantUri)
        Log(PersistantUri)
        SetPickedDir
    End If
    Return Null
End Sub



Private Sub GetPickedDir (uri As String) As JavaObject
    Dim DocumentFileStatic As JavaObject
    Dim treeUri As Uri
    treeUri.Parse(uri)
    Dim PickedDir As JavaObject = DocumentFileStatic.InitializeStatic("android.support.v4.provider.DocumentFile").RunMethod("fromTreeUri", Array(ctxt, treeUri))
    Return PickedDir
End Sub

Private Sub StartActivityForResult(i As Intent)
    Dim jo As JavaObject = GetBA
    ion = jo.CreateEvent("anywheresoftware.b4a.IOnActivityResult", "ion", Null)
    jo.RunMethod("startActivityForResult", Array As Object(ion, i))
End Sub

Private Sub GetBA As Object
    Dim jo As JavaObject = Me
    Return jo.RunMethod("getBA", Null)
End Sub
'>>>>>>>>>>>>>>>>>>>>> Above is from Module
 

Brandsum

Well-Known Member
Licensed User
Answer point wise,
  1. Did you copied external storage class code to main module? If yes then you are doing wrong.
  2. Did the original external storage example worked?
 

ElliotHC

Active Member
Licensed User
Answer point wise,
  1. Did you copied external storage class code to main module? If yes then you are doing wrong.
  2. Did the original external storage example worked?
The original external storage app works just fine, the issue is that I really don't know how to make it run as a service.

I can remove the interactions, I can put a timer, change listviews for lists..
 

DonManfred

Expert
Licensed User
I can remove the interactions
No, you can´t. The interactions are needed. The user must initially select the External SD. This can only be initiated from an Activity.
Once you know the persistenturi you can store it and then use it to further interact with the SDCard.
 

ElliotHC

Active Member
Licensed User
No, you can´t. The interactions are needed. The user must initially select the External SD. This can only be initiated from an Activity.
Once you know the persistenturi you can store it and then use it to further interact with the SDCard.
Ok, so how do I take that project and add in a service that every second, checks and puts the names of the files in that folder in a list? Is it even possible? It has to run in the background!
 

Brandsum

Well-Known Member
Licensed User
, I can put a timer, change listviews for lists..
Why do you need the timer? Do you need the file name that will be created or deleted over time? A timer running every second to get file list and put them in listview will make your app consume more memory and unresponsive. Use fileobserver.
 
Top