GW_PICK_FILE_VER$ = "0.2" % 09-DEC-2020
% This is the third party lib "GW_PICK_FILE.bas"
% http://mougino.free.fr/tmp/GW/third-party-libs/
%
% This lib requires the main lib "GW.bas" and of course Android RFO-BASIC!
% To download the lib when you have "GW.bas" on your device, do:
% GW_DOWNLOAD_THIRD_PARTY("GW_PICK_FILE.bas")
%
% USAGE: in your program do: INCLUDE "GW_PICK_FILE.bas"
% Then call: folder$ = GW_PICK_FOLDER$()
% or: file$ = GW_PICK_FILE$(filter$)
% where filter$ contains all the file extensions separated by comas
% e.g. ?GW_PICK_FILE$("*.java,*.c,*.bas")
% ?GW_PICK_FILE$("*.*") (or "") for all files (no filter)
INCLUDE "GW.bas"
% Utils functions needed:
%== == == == == == == == == == == ==
FN.DEF TALLY(main$, sub$)
i=IS_IN(sub$, main$)
WHILE i
n++
i=IS_IN(sub$, main$, i+1)
REPEAT
FN.RTN n
FN.END
FN.DEF BUILD$(e$, n)
DIM tmp$[n+1]
JOIN.ALL tmp$[], r$, e$
FN.RTN r$
FN.END
% Declare functions:
%== == == == == == == == == == == ==
FN.DEF SortNoCase(a$[])
ARRAY.LENGTH al, a$[]
FOR i=1 TO al
FOR j=i+1 TO al
IF LOWER$(a$[j]) < LOWER$(a$[i]) THEN SWAP a$[i], a$[j]
NEXT
NEXT
FN.END
FN.DEF Short$(e$,nc)
IF RIGHT$(e$,1)="/" & e$<>"/" THEN e$=LEFT$(e$,-1)
IF LEN(e$)>nc
e$=LEFT$(e$,FLOOR(nc/2))+"..."+RIGHT$(e$,CEIL(nc/2))
ENDIF
FN.RTN e$
FN.END
FN.DEF FileNameIsSafe(e$)
safe=1
FOR i=1 TO 31
IF IS_IN(CHR$(i),e$) THEN safe=0: F_N.BREAK
NEXT
FN.RTN safe
FN.END
FN.DEF FinishWith(file$, ext$)
IF ext$="" | IS_IN(".*",ext$) THEN FN.RTN 1
ext$=REPLACE$(ext$, "*.", ".")
IF IS_IN(",", ext$) THEN SPLIT ext$[], ext$, "," ELSE ARRAY.LOAD ext$[], ext$
ARRAY.LENGTH noe, ext$[]
FOR i=1 TO noe
IF LOWER$(RIGHT$(file$, LEN(ext$[i])))=LOWER$(ext$[i]) THEN found=1 : F_N.BREAK
NEXT
FN.RTN found
FN.END
FN.DEF ListFiles(path$, extensions$, list) % use extensions$="/" to list folders
FILE.ROOT sd$
root$=BUILD$("../", TALLY(sd$, "/"))
FOR i=1 TO 2: sd$=LEFT$(sd$, IS_IN("/", sd$, -2)): NEXT
FILE.DIR root$+LTRIM$(path$,"/"), all$[], "/" % list all folders + files
ARRAY.LENGTH nall, all$[]
FOR nfolders=1 TO nall % find separation folders/files
IF RIGHT$(all$[nfolders],1)<>"/" THEN F_N.BREAK % found first file > everything before are folders
NEXT
IF --nfolders=0 & IS_IN(path$, sd$) % fix for Lollipop
e$=REPLACE$(sd$, path$, "") + "/"
ARRAY.LOAD all$[], LEFT$(e$, IS_IN("/",e$))
nfolders++
ENDIF
LIST.CLEAR list
IF nfolders>0
ARRAY.COPY all$[1, nfolders], folders$[]
SortNoCase(folders$[]) % sort regardless of case: aAbBcC...xXyYzZ
LIST.ADD.ARRAY list, folders$[]
FOR i=1 TO nfolders
IF LEFT$(folders$[i],1)="." THEN cl$="hfo" ELSE cl$="fo" % show differently hidden (system) folders
e$ = "📁 "
e$ += LTRIM$(LEFT$(folders$[i], -1), root$)
e$ += " >^" + folders$[i]
LIST.REPLACE list, i, e$
NEXT
ENDIF
IF extensions$<>"/" & nall<>nfolders & LEN(all$[nall]) % now go through the files
ARRAY.COPY all$[nfolders+1], files$[]
SortNoCase(files$[])
LIST.ADD.ARRAY list, files$[]
FOR i=nall-nfolders TO 1 STEP -1
IF !FileNameIsSafe(files$[i]) | !FinishWith(files$[i], extensions$)
LIST.REMOVE list, nfolders+i
F_N.CONTINUE
ENDIF
IF LEFT$(files$[i],1)="." THEN cl$="hfi" ELSE cl$="" % show differently hidden (system) files
e$ = "📄 "
IF LEN(cl$) THEN e$ += ""
e$ += LTRIM$(files$[i], root$)
IF LEN(cl$) THEN e$ += ""
e$ += ">^" + files$[i]
LIST.REPLACE list, nfolders+i, e$
NEXT
ENDIF
IF TALLY(path$, "/") > 2 THEN LIST.INSERT list, 1, "..>^.." % add parent folder ".." if we can
FN.END
FN.DEF GW_PICK_FILE$(filter$)
% Init
%== == == == == == == == == == == ==
BUNDLE.GET 1, "GW_PF_PAGE", GW_PF_PAGE
BUNDLE.GET 1, "GW_PF_TB", GW_PF_TB
BUNDLE.GET 1, "GW_PF_LV", GW_PF_LV
BUNDLE.GET 1, "GW_FOLDER", GW_FOLDER$
BUNDLE.GET 1, "HISTO", HISTO
LIST.CREATE s, GW_FILES
GOSUB GW_PF_UPDATE
% Main Loop
%== == == == == == == == == == == ==
GW_PFI_LOOP:
r$=GW_WAIT_ACTION$()
IF r$="CANCEL"
FN.RTN ""
ELSEIF LEFT$(r$,1)="^"
IF r$="^.."
STACK.PUSH HISTO, GW_FOLDER$
GW_FOLDER$=LEFT$(GW_FOLDER$, IS_IN("/", GW_FOLDER$, -2))
ELSEIF RIGHT$(r$, 1)="/"
STACK.PUSH HISTO, GW_FOLDER$
GW_FOLDER$+=MID$(r$,2)
ELSE % user picked a file
BUNDLE.PUT 1, "GW_FOLDER", GW_FOLDER$
FILE.ROOT sd$ % return relative path
root$=BUILD$("../", TALLY(sd$, "/"))
FN.RTN root$+LTRIM$(GW_FOLDER$,"/")+MID$(r$,2)
ENDIF
GOSUB GW_PF_UPDATE
ELSEIF r$="SEL" % user picked a folder
BUNDLE.PUT 1, "GW_FOLDER", GW_FOLDER$
FILE.ROOT sd$ % return relative path
root$=BUILD$("../", TALLY(sd$, "/"))
FN.RTN root$+LTRIM$(GW_FOLDER$,"/")
ELSEIF r$="BACK"
STACK.ISEMPTY HISTO, empty
IF !empty
STACK.POP HISTO, GW_FOLDER$
GOSUB GW_PF_UPDATE
ELSEIF ++BackTapped=1
IF filter$="/" THEN e$="Folder" ELSE e$="File"
POPUP "Tap Back key again\n to exit "+e$+" Picker"
ELSE % user tapped Back key twice with no histo > exit
FN.RTN ""
ENDIF
ENDIF
GOTO GW_PFI_LOOP
% Update SUB
%== == == == == == == == == == == ==
GW_PF_UPDATE:
BackTapped=0
ListFiles(GW_FOLDER$, filter$, GW_FILES)
UNDIM GW_FILES$[]
LIST.TOARRAY GW_FILES, GW_FILES$[]
LIST.CLEAR GW_FILES
IF !main_page_displayed
GW_RENDER(GW_PF_PAGE)
IF filter$<>"/" THEN JS("$('.ui-btn-right').hide()") % keep "select" button only in folder picker
main_page_displayed=1
ENDIF
GW_MODIFY(GW_PF_TB, "title", Short$(GW_FOLDER$,24))
GW_AMODIFY(GW_PF_LV, "content", GW_FILES$[])
RETURN
FN.END
FN.DEF GW_PICK_FOLDER$()
FN.RTN GW_PICK_FILE$("/")
FN.END
% GW_PICK_FILE/FOLDER Page Definition:
%== == == == == == == == == == == == ==
GW_PF_PAGE=GW_NEW_PAGE()
GW_USE_THEME_CUSTO_ONCE("iconpos=notext icon=back")
lbar$=GW_ADD_BAR_LBUTTON$(">CANCEL")
rbar$=GW_ADD_BAR_RBUTTON$("Select>SEL")
GW_PF_TB=GW_ADD_TITLEBAR(GW_PF_PAGE, lbar$+GW_ADD_BAR_TITLE$(GW_FOLDER$)+rbar$)
ARRAY.LOAD GW_FOLDERS$[], ".."
GW_PF_LV=GW_ADD_LISTVIEW(GW_PF_PAGE, GW_FOLDERS$[])
GW_INJECT_HTML(GW_PF_PAGE, "")
FILE.ROOT GW_FOLDER$
FOR i=1 TO 2 % start at /sdcard i.e. myapp/data/../..
GW_FOLDER$=LEFT$(GW_FOLDER$, IS_IN("/", GW_FOLDER$, -2))
NEXT
STACK.CREATE s, HISTO
BUNDLE.PUT 1, "GW_PF_PAGE", GW_PF_PAGE
BUNDLE.PUT 1, "GW_PF_TB", GW_PF_TB
BUNDLE.PUT 1, "GW_PF_LV", GW_PF_LV
BUNDLE.PUT 1, "GW_FOLDER", GW_FOLDER$
BUNDLE.PUT 1, "HISTO", HISTO