unit FlyFilesUtils;
(* ************************************************ *)
(* *)
(* 设计:爱吃猪头肉 & Flying Wang 2013-11-30 *)
(* 上面的版权声明请不要移除。 *)
(* Ver 1.0.2014.808 *)
(* *)
(* ************************************************ *)
//1.0.2014.1108
//支持 UTF8 的检查。
//1.0.2014.908
//支持 XE7。
//1.0.2014.808
//增加函数 IsPadOrPC。
//1.0.2014.805
//增加安卓下的获取 内存 SD 卡空间的函数。
//1.0.2014.419
//增加对 XE6 的支持。
//1.0.2014.225
//增加对 JNI 的接口查找功能。不完善。
//1.0.2013.1219
//在 亚瑟(Arthur) 3140223 的启发下,增加了更多的 SD 目录。
//1.0.2013.1217
//增加一个 FindSDCardSubPath 函数,用于查找指定的目录。
//1.0.2013.1206
//增加 [佛山]N.E(1024317) 9:36:38 提供的几个 SD 卡的路径。
interface
uses System.SysUtils,
System.Classes,
{$IFDEF ANDROID}
Androidapi.JNIBridge,
Androidapi.IOUtils,
{$ENDIF ANDROID}
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
Posix.Dlfcn, Posix.Fcntl, Posix.SysStat, Posix.SysTime, Posix.SysTypes, Posix.Locale,
{$ENDIF POSIX}
{$IFDEF PC_MAPPED_EXCEPTIONS}
System.Internal.Unwinder,
{$ENDIF PC_MAPPED_EXCEPTIONS}
{$IFDEF MACOS}
Macapi.Mach, Macapi.CoreServices, Macapi.CoreFoundation,
{$ENDIF MACOS}
System.SysConst,
System.IOUtils;
var
Error_NotFoundFileManager_Str: string = 'Not Found FileManager.';
///
/// 返回大小写敏感的文件或路径名称
///
///
/// 文件或路径名
///
///
///
/// 检查路径的根目录
///
///
/// 当大小写检查到此目录时停止,不再继续检查。
///
///
function GetCaseSensitiveFileName(const FileName: string; RootPath: string = ''): string;
const
///
/// 外置设备的数量
///
OTGDeivceCount = 16;
///
/// USB 磁盘,例如 U 盘、移动硬盘等
///
UsbDiskStartIndex = 255;
///
/// 外置光驱
///
CDROMStartIndex = 255 + OTGDeivceCount;
const
///
/// 默认的删除等待时间,单位微秒
///
DeleteDirectories_WaitMinSecond = 2000;
///
/// 检查 SD 卡或路径是否可用
///
function isPathCanUseNow(const PathOrDir: string; const Default: Boolean = True): Boolean;
///
/// 检查 SD 卡或路径是否写入
///
function TestPathCanWrite(const PathOrDir: string): Boolean;
///
/// 获取 手机存储 或 SD 卡的路径
///
///
/// 0 为 手机存储 1 为 SD 卡
///
///
///
/// 如果找到,返回路径。带 / 或 \
///
///
/// 没找到,返回一个错误的路径。
///
///
function GetSDCardPath(Index: Integer = 0): string;
///
/// 查找 手机存储 或 SD 卡上的某个路径
///
///
/// 被查找的子路径
///
///
/// 0 为 手机存储 1 为 SD 卡
///
///
/// 如果找到,返回路径。带 / 或 \ 没找到,返回一个错误的路径。
///
function FindSDCardSubPath(SubPath: string; Index: Integer = 0): string;
///
/// 获取当成工程的运行路径
///
function GetAppPath: string;
///
/// 查找一个路径下的指定格式的文件
///
///
/// 路径,必须用通配符结束。例如 /*
///
///
/// 需要查找的文件的属性
///
///
/// 返回一个文件名或目录名的列表
///
///
/// 是否只查找文件
///
///
/// 无意义
///
function BuildFileListInAPath(const Path: string; const Attr: Integer; const List: TStrings;
JustFile: Boolean = False): Boolean; overload;
///
/// 查找一个路径下的指定格式的文件
///
///
/// 路径,必须用通配符结束。例如 /*
///
///
/// 需要查找的文件的属性
///
///
/// 是否只查找文件
///
///
/// 返回换行分割的文件名或目录的列表
///
function BuildFileListInAPath(const Path: string; const Attr: Integer;
JustFile: Boolean = False): string; overload;
///
/// 查找指定路径下的文件
///
///
/// 路径
///
///
/// 通配符组成的查找格式
///
///
/// 需要查找的文件的属性
///
///
/// 是否包含子目录的名字
///
///
/// 是否递归找子目录
///
///
/// 是否返回完整路径
///
///
/// 返回换行分割的文件名或目录的列表
///
function GetFileNamesFromDirectory(const DirName: string; const SearchFilter: string = '*';
const FileAttribs: Integer = faAnyFile; const isIncludeSubDirName: Boolean = False; const Recursion: Boolean = False;
const FullName: Boolean = False): string;
//可以用 TDirectory.Delete 代替下面的功能。
///
/// 删除目录下指定的文件
///
///
/// 被删除的文件路径,可以使用通配符
///
///
/// 失败时是否退出
///
///
/// 删掉所有文件,包括只读的。仅 WIN32 下有效。
///
///
/// 检查文件删除的等待时间,单位 微秒
///
///
/// 是否删除完成
///
function DeleteDirectoryByEcho(const Source: string;
AbortOnFailure: Boolean = False; YesToAll: Boolean = True;
WaitMinSecond: Integer = DeleteDirectories_WaitMinSecond): Boolean;
///
/// 获取指定路径的总存储大小
///
function GetTotalSpaceSize(Path: string = PathDelim): UInt64;
///
/// 获取指定路径的可以使用的存储大小
///
function GetAvailableSpaceSize(Path: string = PathDelim): UInt64;
///
/// 获取指定路径的剩余(包括不可使用的)存储大小
///
function GetFreeSpaceSize(Path: string = PathDelim): UInt64;
///
/// 获取总内存大小
{$IFDEF ANDROID}
/// 感谢[上海]故国(370620516)
{$ENDIF}
///
function GetTotalMemorySize: UInt64;
///
/// 获取剩余内存大小
{$IFDEF ANDROID}
/// 感谢[上海]故国(370620516)
{$ENDIF}
///
function GetFreeMemorySize: UInt64;
///
/// 安卓 IOS 返回是否是 PAD(平板)
/// 其他平台,返回 True
/// 很多手机的 DPI 是错的,所以获取的尺寸也就不正常了,
/// 所以个别手机会被识别成 PAD。
///
function IsPadOrPC: Boolean;
//function IsPadOrPC(MiniScreenInches: Single = 6.2): Boolean;
//function IsPad: Boolean;
///
/// 在其他 APP 中打开文件。
///
function OpenFileOnExtApp(const FileName: string; Https: Boolean = True): Boolean;
function NowGMT_UTC: TDateTime;
///
/// 获取完整 URL 的 Encode 结果。
/// Just UTF8
///
function EncodeURLWithSchemeOrProtocol(const URL: string): string;
//上面是跨平台函数。
//下面是平台函数。
{$IFDEF ANDROID}
function GetVolumePaths: string;
function GetExternalStoragePath: string;
//var
// ExterStoragePathCanRead: Boolean = True;
// ExterStoragePathCanWrite: Boolean = True;
// SDMountedMessageReceived: Boolean = False;
function GetExterStoragePath: string;
function GetInnerStoragePath: string;
///
/// It check SDCard0 Removable
///
function GetIsExternalStorageRemovable: Boolean;
///
/// 很多手机的 DPI 是错的,所以获取的尺寸也就不正常了。
///
function GetScreenClientInches: Single;
///
/// 获取安卓下剩余内存大小
///
//function GetActiveMemorySize: UInt64;
///
/// 查找一个 JAVA 类是否可以使用
///
///
/// 类的全路径
///
function IsCanFindJavaClass(const NamePath: string): Boolean;
function IsCanFindJavaMethod(const MethodName, Signature: string; const CalssNamePath: string = ''): Boolean;
function IsCanFindJavaStaticMethod(const MethodName, Signature: string; const CalssNamePath: string = ''): Boolean;
type
TGetFileNameListener = reference to procedure(const IsOK: Boolean; const FileName:string);
TGetFileNameLIsternerMethod = procedure (const IsOK: Boolean; const FileName:string) of object;
function OpenFileDialog(Title, FileExtension:string; GetFileNameCallBack: TGetFileNameListener): Boolean; overload;
function OpenFileDialog(Title, FileExtension:string; GetFileNameCallBack: TGetFileNameLIsternerMethod): Boolean; overload;
function CheckPermission(const APermissionName: string): Boolean;
const
C_android_permission_EXTERNAL_STORAGE = 'android.permission.WRITE_EXTERNAL_STORAGE';
// C_android_permission_WRITE_MEDIA = 'android.permission.WRITE_MEDIA_STORAGE';
function CanWriteExterStorage: Boolean;
///
/// 更新相册
///
procedure UpdateAlbum(FileNames: string);
function ReadNoSizeFileToString(const AFileName: string): string;
function ReadFileToString(const AFileName: string): string;
{$ENDIF}
implementation
uses
{$IFDEF ANDROID}
{$IF CompilerVersion >= 27.0} // >= XE6
Androidapi.Helpers,
// FMX.Helpers.Android,
{$ENDIF}
{$IF CompilerVersion < 28.0} // < XE7
FMX.Helpers.Android,
{$ENDIF}
Androidapi.Jni,
Androidapi.JNI.Environment,
Androidapi.JNI.StatFs,
Androidapi.JNI.Stream2,
Androidapi.JNI.ActivityManager,
Androidapi.JNI.JavaTypes,
Androidapi.NativeActivity,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Util,
Androidapi.JNI.android.os.storage.StorageManager,
Androidapi.JNI.java.lang.FlyUtils,
Androidapi.JNI.Webkit,
// Androidapi.JNI.Embarcadero,
Androidapi.JNI.App,
Androidapi.JNI.Net,
Androidapi.JNI.Media,
Androidapi.JNI.Provider,
{$ENDIF}
{$IF DEFINED(IOS) or DEFINED(MACOS)}
iOSapi.Foundation,
Macapi.ObjectiveC,
FMX.Helpers.iOS,
// iOSapi.UIDevice2,
{$ENDIF}
{$IFDEF MSWINDOWS}
Winapi.ShellApi,
{$ENDIF}
// FMX.Dialogs,
{$IF CompilerVersion >= 29.0} // XE8
System.NetEncoding,
{$ELSE}
IdURI,
{$ENDIF}
System.Rtti,
System.TypInfo,
System.Messaging,
System.Math;
//来自 inc 内部的 类型。
{$IF DEFINED(IOS) or DEFINED(MACOS)}
type
{ Used by other time functions. }
tm = record
tm_sec: Integer; // Seconds. [0-60] (1 leap second)
tm_min: Integer; // Minutes. [0-59]
tm_hour: Integer; // Hours.[0-23]
tm_mday: Integer; // Day.[1-31]
tm_mon: Integer; // Month.[0-11]
tm_year: Integer; // Year since 1900
tm_wday: Integer; // Day of week [0-6] (Sunday = 0)
tm_yday: Integer; // Days of year [0-365]
tm_isdst: Integer; // Daylight Savings flag [-1/0/1]
tm_gmtoff: LongInt; // Seconds east of UTC
tm_zone: MarshaledAString; // Timezone abbreviation
end;
{$EXTERNALSYM tm}
Ptm = ^tm;
{$ELSEIF DEFINED(ANDROID)}
{ Used by other time functions. }
type
tm = record
tm_sec: Integer; // Seconds. [0-60] (1 leap second)
tm_min: Integer; // Minutes. [0-59]
tm_hour: Integer; // Hours.[0-23]
tm_mday: Integer; // Day.[1-31]
tm_mon: Integer; // Month.[0-11]
tm_year: Integer; // Year since 1900
tm_wday: Integer; // Day of week [0-6] (Sunday = 0)
tm_yday: Integer; // Days of year [0-365]
tm_isdst: Integer; // Daylight Savings flag [-1/0/1]
tm_gmtoff: LongInt; // Seconds east of UTC
tm_zone: MarshaledAString; // Timezone abbreviation
end;
{$EXTERNALSYM tm}
Ptm = ^tm;
{$ENDIF}
//来自 inc 的函数定义。
{$IFDEF POSIX}
const
{$IFDEF UNDERSCOREIMPORTNAME}
_PU = '_';
{$ELSE}
_PU = '';
{$ENDIF}
const
libc = '/usr/lib/libc.dylib';
libpthread = '/usr/lib/libpthread.dylib';
libiconv = '/usr/lib/libiconv.dylib';
libdl = '/usr/lib/libdl.dylib';
{$IF not Declared(_PU)}
const
// On Mac OSX, cdecl names have a preceeding underscore
// if x86 native backend.
{$IF Defined(UNDERSCOREIMPORTNAME)}
_PU = '_';
{$ELSE}
_PU = '';
{$ENDIF}
{$EXTERNALSYM _PU}
{$ENDIF}
const
{$IFNDEF IOS}
_INODE_SUFFIX = '$INODE64';
{$ELSE IOS}
_INODE_SUFFIX = '';
{$ENDIF !IOS}
{$EXTERNALSYM _INODE_SUFFIX}
//具体函数定义开始。
function _system(Name: MarshaledAString): Integer; cdecl;
external libc name _PU + 'system';
function tempnam(const Path: MarshaledAString; const Prefix: MarshaledAString): MarshaledAString; cdecl;
external libc name _PU + 'tempnam';
{$EXTERNALSYM tempnam}
procedure free(p: Pointer); cdecl;
external libc name _PU + 'free';
{$EXTERNALSYM free}
function gettimeofday(var timeval: timeval; timezone: Pointer): Integer; cdecl;
external libc name _PU + 'gettimeofday';
{$EXTERNALSYM gettimeofday}
function gmtime_r(var Timer: time_t; var UnixTime: tm): Ptm; cdecl;
external libc name _PU + 'gmtime_r';
{$EXTERNALSYM gmtime_r}
{$ENDIF}
//可以开始写函数了。
function NowGMT_UTC: TDateTime;
{$IFDEF MSWINDOWS}
var
SystemTime: TSystemTime;
begin
GetSystemTime(SystemTime);
Result := EncodeDate(SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay) +
EncodeTime(SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds);
end;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
var
T: time_t;
TV: timeval;
UT: tm;
begin
gettimeofday(TV, nil);
T := TV.tv_sec;
gmtime_r(T, UT);
Result := EncodeDate(UT.tm_year + 1900, UT.tm_mon + 1, UT.tm_mday) +
EncodeTime(UT.tm_hour, UT.tm_min, UT.tm_sec, TV.tv_usec div 1000);
end;
{$ENDIF POSIX}
function EncodeURLWithSchemeOrProtocol(const URL: string): string;
var
Protocol: string;
AURL: string;
AIndex: Integer;
begin
{$IF CompilerVersion >= 29.0} // XE8
AURL := URL.Trim;
Protocol := '';
AIndex := AURL.IndexOf('//');
if AIndex > 0 then
begin
Protocol := AURL.Substring(0, AIndex + 1); // has /
AURL := AURL.Substring(AIndex + 1); // has /
end;
Result := Protocol + TNetEncoding.URL.EncodePath(AURL);
{$ELSE}
Result := TIdURI.URLEncode(Result);
{$ENDIF}
end;
function EncodeURLWithOutSchemeOrProtocol(const URL: string; Https: Boolean = True): string;
begin
Result := URL;
if FileExists(Result) then
begin
{$IFDEF MSWINDOWS}
{$ELSE}
if Result.Substring(0, 1) <> '/' then
begin
Result := '/' + Result;
end;
Result := 'file://' + Result;
{$ENDIF}
end
else
begin
if Https then
begin
Result := 'https://' + URL;
end
else
begin
Result := 'http://' + URL;
end;
Result := EncodeURLWithSchemeOrProtocol(Result);
end;
end;
function OpenFileOnExtApp(const FileName: string; Https: Boolean = True): Boolean;
{$IFDEF ANDROID}
var
Intent: JIntent;
FileExtension: string;
mime: JMimeTypeMap;
MIMEType: JString;
TempStr,
FileToOpen: string;
AJFile: JFile;
AJUri: Jnet_Uri;
begin
// There may be an issue with the geo: prefix and URLEncode.
// will need to research
Result := False;
if FileName = '' then
Exit;
FileExtension := AnsiLowerCase(ExtractFileExt(FileName));
if FileExtension = '' then
Exit;
mime := TJMimeTypeMap.JavaClass.getSingleton();
MIMEType := nil;
if mime <> nil then
begin
MIMEType := mime.getMimeTypeFromExtension(StringToJString(FileExtension.Substring(1)));
end;
if MIMEType <> nil then
begin
// 调用相应程序打开当前程序
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
//TempStr := IncludeTrailingPathDelimiter(TPath.GetDocumentsPath)
TempStr := IncludeTrailingPathDelimiter(TPath.GetHomePath);
{$IF CompilerVersion >= 33.0} // RAD10.3
if FileExists(FileName) then
begin
FileToOpen := FileName;
end
else
begin
FileToOpen := EncodeURLWithOutSchemeOrProtocol(FileName, Https);
end;
if Pos(TempStr, FileToOpen) = 1 then
begin
AJFile := TJFile.JavaClass.init(StringToJString(FileToOpen));
AJUri := TAndroidHelper.JFileToJURI(AJFile);
end
else
begin
{$ELSE}
begin
{$ENDIF}
FileToOpen := EncodeURLWithOutSchemeOrProtocol(FileName, Https);
AJUri := StrToJURI(FileToOpen);
end;
Intent.setDataAndType(AJUri, MIMEType);
try
SharedActivity.startActivity(Intent);
Result := True;
except
end;
end;
end;
{$ELSE}
{$IFDEF IOS}
var
NSU: NSUrl;
AURL: string;
begin
Result := False;
AURL := EncodeURLWithOutSchemeOrProtocol(FileName, Https);
// iOS doesn't like spaces, so URL encode is important.
NSU := StrToNSUrl(AURL);
if TOSVersion.Check(9) or SharedApplication.canOpenURL(NSU) then
try
Result := SharedApplication.openUrl(NSU);
except
end;
end;
{$ELSE}
{$IFDEF MSWINDOWS}
var
AURL: string;
begin
Result := False;
AURL := EncodeURLWithOutSchemeOrProtocol(FileName, Https);
try
ShellExecute(GetActiveWindow, 'open', PChar(AURL), '', '', SW_MAXIMIZE);
Result := True;
except
end;
end;
{$ELSE}
var
M:TMarshaller;
AURL: string;
begin
Result := False;
//AURL := 'open -a Safari ' + EncodeURLWithOutSchemeOrProtocol(AURL);
AURL := 'open ' + EncodeURLWithOutSchemeOrProtocol(FileName, Https);
try
_system(M.AsAnsi(AURL, CP_UTF8).ToPointer);
Result := True;
except
end;
// raise Exception.Create('Not supported!');
end;
{$ENDIF MSWINDOWS}
{$ENDIF IOS}
{$ENDIF ANDROID}
function IsPadOrPC: Boolean;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
{$IFDEF IOS}
begin
Result := IsPad;
end;
{$ELSE IOS}
begin
Result := True;
end;
{$ENDIF IOS}
{$ENDIF IOS or MACOS}
{$IFDEF MSWINDOWS}
begin
Result := True;
end;
{$ENDIF}
{$IFDEF ANDROID}
//var
// ScreenInches2,
// ScreenInches1,
// x,y: Double;
// dm: JDisplayMetrics;
//begin
// Result := False;
// dm := GetJDisplayMetrics;
// if dm = nil then exit;
// x := dm.widthPixels;
// y := dm.heightPixels;
// try
// ScreenInches1 := Sqrt((x * x / dm.xdpi / dm.xdpi) + (y * y / dm.ydpi / dm.ydpi));
// ScreenInches2 := Sqr(x * x + y * Y ) / dm.densityDpi;
// except
// exit;
// end;
// Result := ScreenInches1 >= MiniScreenInches;
// if Result then
// Result := ScreenInches2 >= MiniScreenInches;
//end;
var
IsTablet: Boolean;
begin
Result := False;
IsTablet := False;
// CallInUIThreadAndWaitFinishing(
// procedure
// begin
IsTablet := SharedActivity.getResources.getConfiguration.screenLayout and
TJConfiguration.JavaClass.SCREENLAYOUT_SIZE_MASK >= TJConfiguration.JavaClass.SCREENLAYOUT_SIZE_LARGE;
// end);
Result := IsTablet;
end;
{$ENDIF}
function IsPad: Boolean;
begin
{$IFDEF MSWINDOWS}
begin
//code by [龟山]Aone(1467948783)
Result := TOSVersion.Check(6, 1) and
(GetSystemMetrics(SM_TABLETPC) <> 0) and
((GetSystemMetrics(SM_DIGITIZER) and NID_MULTI_INPUT) = NID_MULTI_INPUT);
end;
{$ELSE MSWINDOWS}
{$IF DEFINED(IOS) or DEFINED(MACOS)}
{$IFDEF IOS}
begin
Result := IsPad;
end;
{$ELSE IOS}
begin
Result := False;
end;
{$ENDIF IOS}
{$ELSE} //IF DEFINED(IOS) or DEFINED(MACOS)
Result := IsPadOrPC;
{$ENDIF IOS or MACOS}
{$ENDIF MSWINDOWS}
end;
function GetTotalMemorySize: UInt64;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
begin
// Result := TUIDevice2.Wrap(TUIDevice2.OCClass.currentDevice).totalMemory;
Result := NSRealMemoryAvailable;
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
var
lpBuffer : TMEMORYSTATUSEX;
begin
Result := 0;
ZeroMemory(@lpBuffer, Sizeof(TMEMORYSTATUSEX));
lpBuffer.dwLength := Sizeof(TMEMORYSTATUSEX);
GlobalMemoryStatusEx(lpBuffer);
Result := lpBuffer.ullTotalPhys;
end;
{$ENDIF}
{$IFDEF ANDROID}
var
Mgr: JActivityManager;
MgrNative: JObject;
MemInfo: JActivityManager_MemoryInfo;
AStrings: TStringList;
TempValue: string;
AReader: JReader;
ABufferedReader: JBufferedReader;
begin
Result := 0;
MgrNative :=
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Context
{$ELSE}
SharedActivityContext
{$ENDIF}
.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
if MgrNative <> nil then
begin
Mgr := TJActivityManager.Wrap((MgrNative as ILocalObject).GetObjectID);
MemInfo := TJActivityManager_MemoryInfo.JavaClass.init;
Mgr.getMemoryInfo(MemInfo);
try
Result := UInt64(MemInfo.totalMem);
except
//API level < 16
try
Result := UInt64(MemInfo.availMem);
except
Result := 0;
end;
if FileExists('/proc/meminfo') then
begin
AStrings := TStringList.Create;
try
AStrings.LineBreak := sLineBreak;
AStrings.NameValueSeparator := ':';
AStrings.Clear;
AReader := TJFileReader.JavaClass.init(StringToJString('/proc/meminfo')) as JReader;
ABufferedReader := TJBufferedReader.JavaClass.init(AReader);
repeat
TempValue := JStringToString(ABufferedReader.readLine);
if TempValue <> '' then
begin
AStrings.Add(TempValue);
end;
until (not ABufferedReader.ready);
ABufferedReader.close;
TempValue := AStrings.Values['MemTotal'].Trim;
AStrings.Clear;
AStrings.NameValueSeparator := ' ';
AStrings.Add(TempValue.Trim);
TempValue := AStrings.Names[0];
// ShowMessage(TempValue);
Result := StrToInt64Def(TempValue, Result div 1024) * 1024;
finally
FreeAndNil(AStrings);
end;
end;
end;
end;
end;
{$ENDIF}
function GetFreeMemorySize: UInt64;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
begin
//Result := Max(0, GetTotalMemorySize -
// TUIDevice2.Wrap(TUIDevice2.OCClass.currentDevice).userMemory);
Result := NSRealMemoryAvailable;
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
var
lpBuffer : TMEMORYSTATUSEX;
begin
Result := 0;
ZeroMemory(@lpBuffer, Sizeof(TMEMORYSTATUSEX));
lpBuffer.dwLength := Sizeof(TMEMORYSTATUSEX);
GlobalMemoryStatusEx(lpBuffer);
Result := lpBuffer.ullAvailPhys;
end;
{$ENDIF}
{$IFDEF ANDROID}
var
Mgr: JActivityManager;
MgrNative: JObject;
MemInfo: JActivityManager_MemoryInfo;
begin
Result := 0;
MgrNative :=
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Context
{$ELSE}
SharedActivityContext
{$ENDIF}
.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
if MgrNative <> nil then
begin
Mgr := TJActivityManager.Wrap((MgrNative as ILocalObject).GetObjectID);
MemInfo := TJActivityManager_MemoryInfo.JavaClass.init;
Mgr.getMemoryInfo(MemInfo);
try
Result := UInt64(MemInfo.availMem);
except
Result := 0;
end;
end;
end;
{$ENDIF}
function GetTotalSpaceSize(Path: string = PathDelim): UInt64;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
var
Dict: NSDictionary;
P: Pointer;
const
FoundationFwk: string = '/System/Library/Frameworks/Foundation.framework/Foundation';
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
Dict := TNSFileManager.Wrap(TNSFileManager.OCClass.defaultManager).attributesOfFileSystemForPath(NSStr(Path), nil);
if Dict = nil then
Exit;
P := Dict.objectForKey((CocoaNSStringConst(FoundationFwk, 'NSFileSystemSize') as ILocalObject).GetObjectID);
if P <> nil then
Result := TNSNumber.Wrap(P).unsignedLongLongValue;
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
var
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes,
lpTotalNumberOfFreeBytes: Int64;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
lpTotalNumberOfBytes := MaxInt;
if GetDiskFreeSpaceEx(pchar(ExtractFileDrive(Path)),
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes, @lpTotalNumberOfFreeBytes) then
begin
Result := UInt64(lpTotalNumberOfBytes);
end;
end;
{$ENDIF}
{$IFDEF ANDROID}
var
AJFile: JFile;
AJStatFs: JStatFs;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
AJFile := TJFile.JavaClass.init(StringToJString(Path));
if AJFile = nil then exit;
AJStatFs := TJStatFs.JavaClass.init(AJFile.getPath);
if AJStatFs = nil then exit;
Result := UInt64(AJStatFs.getBlockSize) * UInt64(AJStatFs.getBlockCount);
end;
{$ENDIF}
function GetFreeSpaceSize(Path: string = PathDelim): UInt64;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
var
Dict: NSDictionary;
P: Pointer;
const
FoundationFwk: string = '/System/Library/Frameworks/Foundation.framework/Foundation';
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
Dict := TNSFileManager.Wrap(TNSFileManager.OCClass.defaultManager).attributesOfFileSystemForPath(NSStr(Path), nil);
if Dict = nil then
Exit;
P := Dict.objectForKey((CocoaNSStringConst(FoundationFwk, 'NSFileSystemFreeSize') as ILocalObject).GetObjectID);
if P <> nil then
Result := TNSNumber.Wrap(P).unsignedLongLongValue;
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
var
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes,
lpTotalNumberOfFreeBytes: Int64;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
lpTotalNumberOfFreeBytes := MaxInt;
if GetDiskFreeSpaceEx(pchar(ExtractFileDrive(Path)),
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes, @lpTotalNumberOfFreeBytes) then
begin
Result := UInt64(lpTotalNumberOfFreeBytes);
end;
end;
{$ENDIF}
{$IFDEF ANDROID}
var
AJFile: JFile;
AJStatFs: JStatFs;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
AJFile := TJFile.JavaClass.init(StringToJString(Path));
if AJFile = nil then exit;
AJStatFs := TJStatFs.JavaClass.init(AJFile.getPath);
if AJStatFs = nil then exit;
Result := UInt64(AJStatFs.getBlockSize) * UInt64(AJStatFs.getFreeBlocks);
end;
{$ENDIF}
function GetAvailableSpaceSize(Path: string = PathDelim): UInt64;
{$IF DEFINED(IOS) or DEFINED(MACOS)}
begin
Result := GetFreeSpaceSize(Path);
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
var
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes,
lpTotalNumberOfFreeBytes: Int64;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
lpFreeBytesAvailableToCaller := MaxInt;
if GetDiskFreeSpaceEx(pchar(ExtractFileDrive(Path)),
lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes, @lpTotalNumberOfFreeBytes) then
begin
Result := UInt64(lpFreeBytesAvailableToCaller);
end;
end;
{$ENDIF}
{$IFDEF ANDROID}
var
AJFile: JFile;
AJStatFs: JStatFs;
begin
Result := 0;
if DirectoryExists(Path) or FileExists(Path) then
begin
end
else
begin
raise Exception.Create('Path " ' + Path + '" not found.');
end;
AJFile := TJFile.JavaClass.init(StringToJString(Path));
if AJFile = nil then exit;
AJStatFs := TJStatFs.JavaClass.init(AJFile.getPath);
if AJStatFs = nil then exit;
Result := UInt64(AJStatFs.getBlockSize) * UInt64(AJStatFs.getAvailableBlocks);
end;
{$ENDIF}
{$IFDEF ANDROID}
function CanWriteExterStorage: Boolean;
begin
Result := False;
Result := CheckPermission(C_android_permission_EXTERNAL_STORAGE);
// if Result and TOSVersion.Check(4) then
// begin
// // 没有效果,而且返回 总是 False
// Result := CheckPermission(C_android_permission_WRITE_MEDIA);
// end;
end;
//copy form FMX.AddressBook.Android;
function CheckPermission(const APermissionName: string): Boolean;
var
PackageName: JString;
begin
PackageName := TAndroidHelper.Context.getPackageName;
// if TOSVersion.Check(6) then
// begin
// Result := TAndroidHelper.Context.checkSelfPermission(StringToJString(APermissionName)) =
// TJPackageManager.JavaClass.PERMISSION_GRANTED;
// exit;
// end;
Result := TAndroidHelper.Context.getPackageManager.checkPermission(StringToJString(APermissionName), PackageName) =
TJPackageManager.JavaClass.PERMISSION_GRANTED;
end;
function isExternalStorageDocument(URI: Jnet_Uri): Boolean;
begin
Result := False;
if URI = nil then exit;
Result := URI.getAuthority.equals(StringToJString('com.android.externalstorage.documents'));
end;
function isDownloadsDocument(URI: Jnet_Uri): Boolean;
begin
Result := False;
if URI = nil then exit;
Result := URI.getAuthority.equals(StringToJString('com.android.providers.downloads.documents'));
end;
function isMediaDocument(URI: Jnet_Uri): Boolean;
begin
Result := False;
if URI = nil then exit;
Result := URI.getAuthority.equals(StringToJString('com.android.providers.media.documents'));
end;
const
FILE_SELECT_CODE = 0;
var
FMessageChooserID: Integer = 0;
FGetFileNameCallBack1: TGetFileNameListener;
FGetFileNameCallBack2: TGetFileNameLIsternerMethod;
procedure HandleActivityMessageforChooser(const Sender: TObject; const M: TMessage);
var
MediaDocument,
ExternalStorageDocument,
IsOK: Boolean;
DocType,
DocIDs,
FileScheme,
FileName:string;
URI: Jnet_Uri;
cursor: JCursor;
projection: TJavaObjectArray;
column_index: Integer;
DocIDInfos: TArray;
TempDocIDStr: JString;
selection: JString;
selectionArgs: TJavaObjectArray;
begin
IsOK := False;
FileName := '';
IsOK := M is TMessageResultNotification;
if IsOK and (TMessageResultNotification(M).RequestCode = FILE_SELECT_CODE) then
begin
IsOK := (TMessageResultNotification(M).ResultCode = TJActivity.JavaClass.RESULT_OK);
end;
if IsOK then
begin
IsOK := TMessageResultNotification(M).Value <> nil;
end;
if IsOK then
begin
URI := TMessageResultNotification(M).Value.getData;
IsOK := URI <> nil;
end;
FileName := '';
if IsOK then
begin
FileScheme := JStringToString(URI.getScheme);
if TOSVersion.Check(4, 4) and TJDocumentsContract.JavaClass.isDocumentUri(
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Context
{$ELSE}
SharedActivityContext
{$ENDIF}
, URI) then
begin
//http://blog.csdn.net/u011200844/article/details/43703593
ExternalStorageDocument := False;
MediaDocument := False;
selection := nil;
selectionArgs := nil;
IsOK := False;
if isDownloadsDocument(URI) then
begin
TempDocIDStr := TJDocumentsContract.JavaClass.getDocumentId(URI);
URI := TJContentUris.JavaClass.withAppendedId(
TJnet_Uri.JavaClass.parse(StringToJString('content://downloads/public_downloads')),
TJLong.JavaClass.valueOf(TempDocIDStr).longValue);
//后面会继续处理。
end
else
begin
ExternalStorageDocument := isExternalStorageDocument(URI);
if not ExternalStorageDocument then
MediaDocument := isMediaDocument(URI);
end;
if ExternalStorageDocument or MediaDocument then
begin
DocIDs := JStringToString(TJDocumentsContract.JavaClass.getDocumentId(URI));
DocIDInfos := DocIDs.Split([':']);
if Length(DocIDInfos) > 1 then
begin
DocType := DocIDInfos[0];
if ExternalStorageDocument then
begin
if SameText(DocType, 'primary') then
begin
FileName := GetSDCardPath(0) + DocIDInfos[1];
IsOK := True;
end
else
begin
//后面会继续处理。
end;
end
else if MediaDocument then
begin
if SameText(DocType, 'image') then
begin
URI := TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI;
IsOK := True;
end
else if SameText(DocType, 'video') then
begin
URI := TJVideo_Media.JavaClass.EXTERNAL_CONTENT_URI;
IsOK := True;
end
else if SameText(DocType, 'audio') then
begin
URI := TJAudio_Media.JavaClass.EXTERNAL_CONTENT_URI;
IsOK := True;
end;
if IsOK then
begin
selection := StringToJString('_id=?');
selectionArgs := TJavaObjectArray.Create(1);
selectionArgs.Items[0] := StringToJString(DocIDInfos[1]);
IsOK := False;
//后面会继续处理。
end;
end;
end;
end;
end
else
IsOK := SameText('file', FileScheme);
end
else exit;
if IsOK and FileName.IsEmpty then
begin
FileName := JStringToString(URI.getPath);
end
else if not IsOK then
begin
IsOK := SameText('content', FileScheme);
if IsOK then
begin
IsOK := False;
projection := TJavaObjectArray.Create(1);
projection.Items[0] := StringToJString('_data');
try
cursor :=
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Context
{$ELSE}
SharedActivityContext
{$ENDIF}
.getContentResolver().query(URI, projection, selection, selectionArgs, nil);
if (cursor <> nil) then
begin
column_index := cursor.getColumnIndexOrThrow(StringToJString('_data'));
if cursor.moveToFirst then
begin
FileName := JStringToString(cursor.getString(column_index));
IsOK := True;
end;
end;
except
IsOK := False;
end;
end;
// if not IsOK then
// begin
// FileName := JStringToString(URI.getPath);
// FileName := FileName.Trim;
// IsOK := not FileName.IsEmpty;
// if not IsOK then
// begin
// IsOK := FileName.IndexOf(PathDelim) <> -1;
// end;
// end;
// end;
end;
if IsOK then
begin
FileName := FileName.Trim;
IsOK := not FileName.IsEmpty;
end;
// if IsOK then
// begin
//// IsOK := FileExists(FileName);
// IsOK := FileName.IndexOf(PathDelim) <> -1;
// end;
if Assigned(FGetFileNameCallBack1) then
FGetFileNameCallBack1(IsOK, FileName);
if Assigned(FGetFileNameCallBack2) then
FGetFileNameCallBack2(IsOK, FileName);
end;
function InternalOpenFileDialog(Title, FileExtension:string;
GetFileNameCallBack1: TGetFileNameListener;
GetFileNameCallBack2: TGetFileNameLIsternerMethod): Boolean;
var
MIMEType: JString;
mime: JMimeTypeMap;
Intent: JIntent;
IntentChooser: JIntent;
sMIMEType: string;
begin
Result := False;
FGetFileNameCallBack1 := GetFileNameCallBack1;
FGetFileNameCallBack2 := GetFileNameCallBack2;
if FileExtension.Substring(0,1) = '.' then
begin
FileExtension := FileExtension.Substring(1);
end;
if FileExtension = '*.*' then
begin
FileExtension := '*/*';
end;
MIMEType := nil;
sMIMEType := '';
if FileExtension = 'file/*' then
begin
sMIMEType := 'file/*';
end
else if FileExtension = '*' then
begin
if TOSVersion.Check(4, 4) then
begin
sMIMEType := '*/*';
end
else
begin
sMIMEType := 'file/*';
end;
end
else if FileExtension = '*/*' then
begin
sMIMEType := '*/*';
end
else if FileExtension = '' then
begin
sMIMEType := '*/*';
end
else
begin
FileExtension := AnsiLowerCase(FileExtension);
//http://www.oschina.net/code/snippet_1269559_25060
mime := TJMimeTypeMap.JavaClass.getSingleton;
if mime <> nil then
begin
MIMEType := mime.getMimeTypeFromExtension(StringToJString(FileExtension));
end;
end;
if MIMEType <> nil then
begin
sMIMEType := JStringToString(MIMEType).Trim;
end;
if sMIMEType.IsEmpty then
sMIMEType := '*/*'; // File/* 有时候打不开
//http://www.cnblogs.com/linlf03/archive/2013/08/19/3267732.html
// if TOSVersion.Check(4, 4) then
// begin
// Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT);
// end
// else
begin
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_GET_CONTENT);
end;
Intent.setType(StringToJString(sMIMEType));
Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
IntentChooser := TJIntent.JavaClass.createChooser(Intent, StrToJCharSequence(Title));
if FMessageChooserID <> 0 then
TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, FMessageChooserID);
FMessageChooserID := 0;
FMessageChooserID := TMessageManager.DefaultManager.SubscribeToMessage(
TMessageResultNotification, HandleActivityMessageforChooser);
try
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Activity
{$ELSE}
SharedActivity
{$ENDIF}
.startActivityForResult(IntentChooser, FILE_SELECT_CODE);
Result := True;
except
raise Exception.Create(Error_NotFoundFileManager_Str);
end;
end;
function OpenFileDialog(Title, FileExtension:string; GetFileNameCallBack: TGetFileNameListener): Boolean;
begin
Result := InternalOpenFileDialog(Title, FileExtension, GetFileNameCallBack, nil);
end;
function OpenFileDialog(Title, FileExtension:string; GetFileNameCallBack: TGetFileNameLIsternerMethod): Boolean;
begin
Result := InternalOpenFileDialog(Title, FileExtension, nil, GetFileNameCallBack);
end;
var
FExterStoragePath: string = '';
FInnerStoragePath: string = '';
FVolumePaths: string = '';
FSDRegisteredReceiver: Boolean = False;
//type
// TSDBroadcastListener = class(TJavaLocal, JFMXBroadcastReceiverListener)
// private
// //[Weak] FTestObj: TForm1; //TForm1 是拿来测试,不是必须的。
// public
// constructor Create;
// //看上去,下面的函数属于线程中
// procedure onReceive(context: JContext; intent: JIntent); cdecl;
// end;
//
//var
// //安卓下消息3元素。
// FSDBroadcastReceiver: JFMXBroadcastReceiver;
// FSDIntentFilter: JIntentFilter;
// FSDBroadcastListener: TSDBroadcastListener;
// //你的 APP 安卓对象。
// FActivity: JNativeActivity;
//
//
//{ TSDBroadcastListener }
//
//constructor TSDBroadcastListener.Create;
//begin
// inherited Create;
// InnerStoragePath := GetSDCardPath(0);
// ExterStoragePath := GetSDCardPath(1);
// ExterStoragePathCanRead := False;
// ExterStoragePathCanWrite := False;
// SDMountedMessageReceived := False;
//end;
//
//procedure TSDBroadcastListener.onReceive(context: JContext; intent: JIntent);
//var
// Action,
// ExternalPath,
// TempStr1, TempStr2, TempStr3,
// TempStr: string;
// AJFile: JFile;
//begin
// //这里就是处理消息的地方。
// Action := JStringToString(intent.getAction);
// TempStr1 := JStringToString(TJIntent.JavaClass.ACTION_MEDIA_MOUNTED);
// TempStr2 := JStringToString(TJIntent.JavaClass.ACTION_MEDIA_UNMOUNTED);
// ExternalPath := IncludeTrailingPathDelimiter(GetExternalStoragePath);
// if SameText(Action, TempStr1) or SameText(Action, TempStr2) then
// begin
// TempStr := JStringToString(intent.getData.getPath);// 外置设备路径
// TempStr := IncludeTrailingPathDelimiter(TempStr);
// if TOSVersion.Check(5) and SameFileName(TempStr, ExternalPath) then
// begin
// //说明内外卡交换了。这是 5.0 的特点。
// InnerStoragePath := ExterStoragePath;
// ExterStoragePath := TempStr;
// end;
// if SameFileName(TempStr, ExterStoragePath) then
// begin
// if SameText(Action, TempStr1) then
// begin
// ExterStoragePathCanRead := True;
// AJFile := TJFile.JavaClass.init(
// StringToJString(ExcludeTrailingPathDelimiter(TempStr)));
// if AJFile <> nil then
// begin
// ExterStoragePathCanRead := AJFile.canRead;
// ExterStoragePathCanWrite := AJFile.canWrite;
// end;
// SDMountedMessageReceived := True;
// end;
// end;
// end;
// TempStr1 := JStringToString(TJIntent.JavaClass.ACTION_MEDIA_REMOVED);
// TempStr2 := JStringToString(TJIntent.JavaClass.ACTION_MEDIA_SHARED);
// TempStr3 := JStringToString(TJIntent.JavaClass.ACTION_MEDIA_EJECT);
// if SameText(Action, TempStr1) or
// SameText(Action, TempStr2) or SameText(Action, TempStr3) then
// begin
// TempStr := JStringToString(intent.getData.getPath);// 外置设备路径
// TempStr := IncludeTrailingPathDelimiter(TempStr);
// if SameFileName(TempStr, ExterStoragePath) then
// begin
// ExterStoragePathCanRead := False;
// ExterStoragePathCanWrite := False;
// end;
// end;
//end;
//
//procedure RegisterSDReceiver;
//begin
// if FSDRegisteredReceiver then exit;
// FActivity := TJNativeActivity.Wrap(PANativeActivity(System.DelphiActivity)^.clazz);
// //FActivity :=
//{$IF CompilerVersion >= 30.0} // >=RAD10
// TAndroidHelper.Activity
//{$ELSE}
// SharedActivity
//{$ENDIF};
// FSDBroadcastListener := TSDBroadcastListener.Create;
// FSDBroadcastReceiver := TJFMXBroadcastReceiver.JavaClass.init(FSDBroadcastListener);
//
// //http://blog.csdn.net/yigelangmandeshiren/article/details/8145059
// FSDIntentFilter := TJIntentFilter.JavaClass.init(TJIntent.JavaClass.ACTION_MEDIA_MOUNTED);
// FSDIntentFilter.setPriority(1000);// 设置最高优先级
// FSDIntentFilter.addAction(TJIntent.JavaClass.ACTION_MEDIA_EJECT);
// FSDIntentFilter.addAction(TJIntent.JavaClass.ACTION_MEDIA_REMOVED);
// FSDIntentFilter.addAction(TJIntent.JavaClass.ACTION_MEDIA_SHARED);
// FSDIntentFilter.addAction(TJIntent.JavaClass.ACTION_MEDIA_UNMOUNTED);
// FSDIntentFilter.addDataScheme(StringToJString('file'));
//
// //注册消息接收器。别忘了 unregisterReceiver(FBroadcastReceiver);
// FActivity.registerReceiver(FSDBroadcastReceiver, FSDIntentFilter);
//end;
//
//procedure UnRegisterSDReceiver;
//begin
// if not FSDRegisteredReceiver then exit;
// FActivity.unregisterReceiver(FSDBroadcastReceiver);
// FreeAndNil(FSDBroadcastListener);
// FSDBroadcastReceiver := nil;
// FSDIntentFilter := nil;
// FActivity := nil;
//end;
function GetIsExternalStorageRemovable: Boolean;
begin
Result := True;
try
Result := TJEnvironment.JavaClass.isExternalStorageRemovable;
except
//低版本可能发生错误。
end;
end;
function GetExterStoragePath: string;
//var
// AJstr: JString;
begin
if FExterStoragePath.Trim = '' then
begin
FExterStoragePath := GetSDCardPath(1);
if GetIsExternalStorageRemovable then
begin
FExterStoragePath := GetSDCardPath(0);
end;
end;
Result := FExterStoragePath.Trim;
if not DirectoryExists(ExcludeTrailingPathDelimiter(Result)) then
Result := '';
// AJstr := TJSystem.JavaClass.getenv(StringToJString('SECONDARY_STORAGE'));
// if AJstr <> nil then
// Result := IncludeTrailingPathDelimiter(JStringToString(AJstr));
end;
function GetInnerStoragePath: string;
//var
// AJstr: JString;
begin
if FInnerStoragePath.Trim = '' then
begin
FInnerStoragePath := GetSDCardPath(0);
if GetIsExternalStorageRemovable then
begin
FInnerStoragePath := GetSDCardPath(1);
end;
end;
Result := FInnerStoragePath.Trim;
if not DirectoryExists(ExcludeTrailingPathDelimiter(Result)) then
Result := '';
// ExterStoragePath := GetSDCardPath(1);
// AJstr := TJSystem.JavaClass.getenv(StringToJString('EXTERNAL_STORAGE'));
// if AJstr <> nil then
// Result := IncludeTrailingPathDelimiter(JStringToString(AJstr));
end;
function GetVolumePaths: string;
var
Sm: JStorageManager;
SmNative: JObject;
AList: TStrings;
VolumePaths: TJavaObjectArray;
I: Integer;
begin
Result := '';
if not IsCanFindJavaMethod('getVolumePaths',
'()[Ljava/lang/String;', 'android/os/storage/StorageManager') then
begin
//可以返回点别的。
exit;
end;
Sm := nil;
SmNative :=
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Context
{$ELSE}
SharedActivityContext
{$ENDIF}
.getSystemService(TJContext.JavaClass.STORAGE_SERVICE);
if SmNative <> nil then
begin
Sm := TJStorageManager.Wrap((SmNative as ILocalObject).GetObjectID);
end;
//不打算兼容 2.X 了。
if (Sm <> nil) and TOSVersion.Check(4) then
begin
AList := TStringList.Create;
try
try
VolumePaths := Sm.getVolumePaths;
for I := 0 to VolumePaths.Length - 1 do
begin
AList.Add(JStringToString(VolumePaths.Items[I]));
end;
except
end;
VolumePaths := nil;
Result := AList.Text;
FVolumePaths := Result;
finally
FreeAndNil(AList);
end;
end;
end;
//function GetActiveMemorySize: UInt64;
//var
// AStringVs,
// AStrings: TStringList;
// TempValue: string;
// ABufferedReader: JBufferedReader;
// V1, V2: UInt64;
//begin
// Result := 0;
// if FileExists('/proc/meminfo') then
// begin
// AStrings := TStringList.Create;
// AStringVs := TStringList.Create;
// try
// AStrings.LineBreak := sLineBreak;
// AStrings.NameValueSeparator := ':';
// AStrings.Clear;
// ABufferedReader := TJBufferedReader.JavaClass.init(
// JReader(TJFileReader.JavaClass.init(StringToJString('/proc/meminfo'))));
// repeat
// TempValue := JStringToString(ABufferedReader.readLine);
// if TempValue <> '' then
// begin
// AStrings.Add(TempValue);
// end;
// until (not ABufferedReader.ready);
// ABufferedReader.close;
// V1 := 0;
// TempValue := AStrings.Values['Mapped'].Trim;
// AStringVs.Clear;
// AStringVs.NameValueSeparator := ' ';
// AStringVs.Add(TempValue.Trim);
// TempValue := AStringVs.Names[0];
// V2 := StrToInt64Def(TempValue, 0);
// V1 := V1 + V2;
// TempValue := AStrings.Values['Inactive'].Trim;
// AStringVs.Clear;
// AStringVs.NameValueSeparator := ' ';
// AStringVs.Add(TempValue.Trim);
// TempValue := AStringVs.Names[0];
// V2 := StrToInt64Def(TempValue, 0);
// V1 := V1 + V2;
// Result := V1 * 1024;
// finally
// FreeAndNil(AStringVs);
// FreeAndNil(AStrings);
// end;
// end;
// if Result = 0 then
// begin
// Result := GetFreeMemorySize;
// end;
//end;
function GetScreenClientInches: Single;
var
x,y: Double;
dm: JDisplayMetrics;
begin
Result := 3;
dm := GetJDisplayMetrics;
if dm = nil then exit;
x := dm.widthPixels;
y := dm.heightPixels;
// x := System.Math.Power(x / dm.xdpi, 2);
// y := System.Math.Power(y / dm.ydpi, 2);
// Result := Sqrt(x + y);
if (dm.densityDpi > dm.xdpi) and (dm.densityDpi > dm.ydpi) then
begin
Result := Sqrt(x * x + y * y ) / dm.densityDpi;
end
else
begin
Result := Sqrt((x * x / dm.xdpi / dm.xdpi) + (y * y / dm.ydpi / dm.ydpi));
end;
end;
function IsCanFindJavaClass(const NamePath: string): Boolean;
var
PEnv: PJNIEnv;
{$IF CompilerVersion < 30.0} // < RAD10
PActivity: PANativeActivity;
{$ELSE}
ContextClass: JNIClass;
{$ENDIF}
TempClass: JNIClass;
AMS: MarshaledAString;
begin
try
TempClass := nil;
{$IF CompilerVersion < 30.0} // < RAD10
PActivity := PANativeActivity(System.DelphiActivity);
PActivity^.vm^.AttachCurrentThread(PActivity^.vm, @PEnv, nil);
{$ELSE}
PJavaVM(System.JavaMachine)^.AttachCurrentThread(System.JavaMachine, @PEnv, nil);
ContextClass := nil;
ContextClass := PEnv^.GetObjectClass(PEnv, System.JavaContext);
{$ENDIF}
AMS := MarshaledAString(Utf8Encode(NamePath.Trim.Replace('.', '/', [rfReplaceAll])));
try
TempClass := PEnv^.FindClass(PEnv, AMS);
PEnv^.ExceptionClear(PEnv);
Result := TempClass <> nil;
finally
if TempClass <> nil then
PEnv^.DeleteLocalRef(PEnv, TempClass);
{$IF CompilerVersion < 30.0} // < RAD10
{$ELSE}
if ContextClass <> nil then
PEnv^.DeleteLocalRef(PEnv, ContextClass);
{$ENDIF}
end;
except
Result := False;
end;
end;
function IsCanFindJavaMethod(const MethodName, Signature: string; const CalssNamePath: string = ''): Boolean;
var
PEnv: PJNIEnv;
{$IF CompilerVersion < 30.0} // < RAD10
PActivity: PANativeActivity;
{$ELSE}
ContextClass: JNIClass;
{$ENDIF}
ActivityClass: JNIClass;
GetMethod: JNIMethodID;
ASignature: string;
AMSMethodName, AMSSignature: MarshaledAString;
begin
try
AMSMethodName := MarshaledAString(Utf8Encode(MethodName.Trim));
{$IF CompilerVersion < 30.0} // < RAD10
PActivity := PANativeActivity(System.DelphiActivity);
PActivity^.vm^.AttachCurrentThread(PActivity^.vm, @PEnv, nil);
{$ELSE}
PJavaVM(System.JavaMachine)^.AttachCurrentThread(System.JavaMachine, @PEnv, nil);
ContextClass := nil;
ContextClass := PEnv^.GetObjectClass(PEnv, System.JavaContext);
{$ENDIF}
ActivityClass := nil;
try
if CalssNamePath.Trim <> '' then
begin
ASignature := CalssNamePath.Trim.Replace('.', '/', [rfReplaceAll]);
AMSSignature := MarshaledAString(Utf8Encode(ASignature));
ActivityClass := PEnv^.FindClass(PEnv, AMSSignature);
PEnv^.ExceptionClear(PEnv);
end
else
begin
{$IF CompilerVersion < 30.0} // < RAD10
ActivityClass := PEnv^.GetObjectClass(PEnv, PActivity^.clazz);
{$ELSE}
ActivityClass := PEnv^.GetObjectClass(PEnv, System.JavaContext);
{$ENDIF}
end;
if ActivityClass <> nil then
begin
ASignature := Signature.Trim;
AMSSignature := MarshaledAString(Utf8Encode(ASignature));
GetMethod := PEnv^.GetMethodID(PEnv, ActivityClass, AMSMethodName, AMSSignature);
PEnv^.ExceptionClear(PEnv);
Result := GetMethod <> nil;
end;
finally
if ActivityClass <> nil then
PEnv^.DeleteLocalRef(PEnv, ActivityClass);
{$IF CompilerVersion < 30.0} // < RAD10
{$ELSE}
if ContextClass <> nil then
PEnv^.DeleteLocalRef(PEnv, ContextClass);
{$ENDIF}
end;
except
Result := False;
end;
end;
function IsCanFindJavaStaticMethod(const MethodName, Signature: string; const CalssNamePath: string = ''): Boolean;
var
PEnv: PJNIEnv;
{$IF CompilerVersion < 30.0} // < RAD10
PActivity: PANativeActivity;
{$ELSE}
ContextClass: JNIClass;
{$ENDIF}
ActivityClass: JNIClass;
GetMethod: JNIMethodID;
ASignature: string;
AMSMethodName, AMSSignature: MarshaledAString;
begin
try
AMSMethodName := MarshaledAString(Utf8Encode(MethodName.Trim));
{$IF CompilerVersion < 30.0} // < RAD10
PActivity := PANativeActivity(System.DelphiActivity);
PActivity^.vm^.AttachCurrentThread(PActivity^.vm, @PEnv, nil);
{$ELSE}
PJavaVM(System.JavaMachine)^.AttachCurrentThread(System.JavaMachine, @PEnv, nil);
ContextClass := nil;
ContextClass := PEnv^.GetObjectClass(PEnv, System.JavaContext);
{$ENDIF}
ActivityClass := nil;
try
if CalssNamePath.Trim <> '' then
begin
ASignature := CalssNamePath.Trim.Replace('.', '/', [rfReplaceAll]);
AMSSignature := MarshaledAString(Utf8Encode(ASignature));
ActivityClass := PEnv^.FindClass(PEnv, AMSSignature);
PEnv^.ExceptionClear(PEnv);
end
else
begin
{$IF CompilerVersion < 30.0} // < RAD10
ActivityClass := PEnv^.GetObjectClass(PEnv, PActivity^.clazz);
{$ELSE}
ActivityClass := PEnv^.GetObjectClass(PEnv, System.JavaContext);
{$ENDIF}
end;
if ActivityClass <> nil then
begin
ASignature := Signature.Trim;
AMSSignature := MarshaledAString(Utf8Encode(ASignature));
GetMethod := PEnv^.GetStaticMethodID(PEnv, ActivityClass, AMSMethodName, AMSSignature);
PEnv^.ExceptionClear(PEnv);
Result := GetMethod <> nil;
end;
finally
if ActivityClass <> nil then
PEnv^.DeleteLocalRef(PEnv, ActivityClass);
{$IF CompilerVersion < 30.0} // < RAD10
{$ELSE}
if ContextClass <> nil then
PEnv^.DeleteLocalRef(PEnv, ContextClass);
{$ENDIF}
end;
except
Result := False;
end;
end;
procedure UpdateAlbum(FileNames: string);
var
AJStrList: TJavaObjectArray;
I: Integer;
begin
With TStringList.Create do
begin
try
Text := FileNames;
AJStrList := TJavaObjectArray.Create(Count);
for I := 0 to Count - 1 do
begin
AJStrList.Items[I] := StringToJString(Strings[I]);
end;
finally
Free;
end;
end;
TJMediaScannerConnection.JavaClass.scanFile(TAndroidHelper.Context, AJStrList,
nil, nil);
end;
function ReadFileToString(const AFileName: string): string;
var
AReader: JReader;
ABufferedReader: JBufferedReader;
TempValue: string;
begin
Result := '';
if not FileExists(AFileName) then exit;
AReader := TJFileReader.JavaClass.init(StringToJString(AFileName)) as JReader;
ABufferedReader := TJBufferedReader.JavaClass.init(AReader);
repeat
TempValue := JStringToString(ABufferedReader.readLine);
if TempValue <> '' then
begin
Result := Result + sLineBreak + TempValue;
end;
until (not ABufferedReader.ready);
ABufferedReader.close;
end;
function ReadNoSizeFileToString(const AFileName: string): string;
begin
Result := '';
if not FileExists(AFileName) then exit;
With TFileStream.Create(AFileName, fmOpenRead) do
try
if Size = -1 then exit;
finally
Free;
end;
Result := ReadFileToString(AFileName);
end;
{$ENDIF}
{$IFDEF ANDROID}
function GetJniPath(MethodName, Signature: MarshaledAString): string;
var
PEnv: PJniEnv;
ActivityClass: JNIClass;
FileClass: JNIClass;
GetMethod: JNIMethodID;
GetPathMethod: JNIMethodID;
PActivity: PANativeActivity;
StrPathObject: JNIObject;
FileObject: JNIObject;
begin
PActivity := PANativeActivity(System.DelphiActivity);
PActivity^.vm^.AttachCurrentThread(PActivity^.vm, @PEnv, nil);
ActivityClass := PEnv^.GetObjectClass(PEnv, PActivity^.clazz);
GetMethod := PEnv^.GetMethodID(PEnv, ActivityClass, MethodName, Signature);
FileObject := PEnv^.CallObjectMethodA(PEnv, PActivity^.clazz, GetMethod, PJNIValue(ArgsToJNIValues([nil])));
if FileObject = nil then
Exit('');
FileClass := PEnv^.GetObjectClass(PEnv, FileObject);
GetPathMethod := PEnv^.GetMethodID(PEnv, FileClass, 'getPath', '()Ljava/lang/String;');
StrPathObject := PEnv^.CallObjectMethodA(PEnv, FileObject, GetPathMethod, PJNIValue(ArgsToJNIValues([])));
Result := JNIStringToString(PEnv, StrPathObject);
PEnv^.DeleteLocalRef(PEnv, StrPathObject);
PEnv^.DeleteLocalRef(PEnv, FileClass);
PEnv^.DeleteLocalRef(PEnv, FileObject);
PEnv^.DeleteLocalRef(PEnv, ActivityClass);
end;
function JNIgetExternalStorageDirectory(SubPath: string): string;
begin
// if SubPath <> '' then
// begin
// Result := IncludeTrailingPathDelimiter(
// GetJniPath('getExternalStorageDirectory', '()Landroid/os/Environment;')
// ) + SubPath;
// end
// else
// begin
// Result := GetJniPath('getExternalStorageDirectory', '()Landroid/os/Environment;');
// end;
try
if SubPath <> '' then
begin
Result := IncludeTrailingPathDelimiter(JStringToString(
TJEnvironment.JavaClass.getExternalStorageDirectory.getPath)) + SubPath;
end
else
begin
Result := JStringToString(TJEnvironment.JavaClass.getExternalStorageDirectory.getPath);
end;
except
//低版本可能发生错误。
end;
end;
function GetExternalStoragePath: string;
begin
Result := IncludeTrailingPathDelimiter(JNIgetExternalStorageDirectory(''));
end;
//function isPathCanWrite(const PathOrDir: string; const Default: Boolean = True): Boolean;
//var
// ADir: string;
//begin
// Result := False;
// ADir := ExcludeTrailingPathDelimiter(PathOrDir);
// if not DirectoryExists(ADir) then exit;
// Result := True;
// try
////明明无权限写入,却返回 canWrite 为 True
// if not TJFile.JavaClass.init(StringToJString(ADir)).canWrite then
// begin
// Result := False;
// end;
// except
// Result := False;
// end;
//end;
function isPathCanUseNow(const PathOrDir: string; const Default: Boolean = True): Boolean;
var
ADir: string;
begin
Result := False;
ADir := ExcludeTrailingPathDelimiter(PathOrDir);
if not DirectoryExists(ADir) then exit;
try
// //下面的代码不能正确的区分内外 SD。
// Result := TJEnvironment.JavaClass.getExternalStorageState.equals(TJEnvironment.JavaClass.MEDIA_MOUNTED);
// if GetExterStoragePath.Trim = '' then exit;
Result := True;
// if FindJavaMethod('getStorageState',
// '(Ljava/io/File;)Ljava/lang/String;', 'android/os/Environment') then
// begin
// // 这个接口 5.0 会死锁。还是不用了。
// if not TJEnvironment.JavaClass.getStorageState(
// TJFile.JavaClass.init(StringToJString(ADir))).equals(
// TJEnvironment.JavaClass.MEDIA_MOUNTED) then
// begin
// Result := False;
// end;
// end
// else
begin
if not TJFile.JavaClass.init(StringToJString(ADir)).canRead then
begin
Result := False;
end;
end;
except
end;
end;
{$ELSE}
function isPathCanUseNow(const PathOrDir: string; const Default: Boolean = True): Boolean;
begin
Result := False;
if not DirectoryExists(ExcludeTrailingPathDelimiter(PathOrDir)) then exit;
Result := Default;
end;
{$ENDIF ANDROID}
function TestPathCanWrite(const PathOrDir: string): Boolean;
var
ATempFile,
ADir: string;
AHandle: THandle;
begin
Result := False;
ADir := ExcludeTrailingPathDelimiter(PathOrDir);
if not DirectoryExists(ADir) then exit;
repeat
ATempFile := System.IOUtils.TPath.GetTempFileName;
try
if FileExists(ATempFile) then
System.IOUtils.TFile.Delete(ATempFile);
except
end;
ATempFile := IncludeTrailingPathDelimiter(ADir) + ExtractFileName(ATempFile);
until not FileExists(ATempFile);
// ATempFile := GetTempFileName(ADir);
// if FileExists(ATempFile) then
// begin
// System.IOUtils.TFile.Delete(ATempFile);
// end;
try
AHandle := INVALID_HANDLE_VALUE;
{$IF Defined(MSWINDOWS)}
AHandle := FileCreate(ATempFile, 0);
{$ELSEIF Defined(POSIX)}
AHandle := FileCreate(ATempFile, FileAccessRights);
{$ENDIF POSIX}
if AHandle = INVALID_HANDLE_VALUE then
begin
Result := False;
end
else
begin
Result := True;
FileClose(AHandle);
end;
except
Result := False;
end;
if FileExists(ATempFile) then
begin
System.IOUtils.TFile.Delete(ATempFile);
Result := True;
end;
end;
//function GetTempFileName(const ATempPath: string): string;
//{$IFDEF MSWINDOWS}
//var
// ErrCode: UINT;
//begin
// SetLength(Result, MAX_PATH);
//
// SetLastError(ERROR_SUCCESS);
// ErrCode := Winapi.Windows.GetTempFileName(PChar(
// IncludeTrailingPathDelimiter(ATempPath)
// ), 'tmp', 0, PChar(Result)); // DO NOT LOCALIZE
// if ErrCode = 0 then
// raise EInOutError.Create(SysErrorMessage(GetLastError));
//
// SetLength(Result, StrLen(PChar(Result)));
// if FileExists(Result) then
// begin
// System.IOUtils.TFile.Delete(Result);
// end;
//end;
//{$ENDIF}
//{$IFDEF POSIX}
//var
// LTempPath: TBytes;
// M: TMarshaller;
// LRet: MarshaledAString;
//begin
//// char * tempnam(const char *dir, const char *pfx);
//
// { Obtain a temporary file name }
// // This code changed from getting the temp name from the temp path via system
// // to get the temp name inside the specified temp path. We get the system temp path.
//// LTempPath := TEncoding.UTF8.GetBytes(string(tmpnam(nil)));
// LRet := tempnam(MarshaledAString(M.AsUTF8(
// //返回的路径没有包含 ATempPath
// IncludeTrailingPathDelimiter(ATempPath)
// ).ToPointer),nil);
// LTempPath := TEncoding.UTF8.GetBytes(string(LRet));
// free(LRet);
//
// { Convert to UTF16 or leave blank on possible error }
// if LTempPath <> nil then
// Result := TEncoding.UTF8.GetString(LTempPath)
// else
// Result := '';
//end;
//{$ENDIF POSIX}
{$IFDEF ANDROID}
function FileSystemAttributes(const Path: string): TFileSystemAttributes;
begin
Result := [fsSymLink, fsCaseSensitive];
// For android platform we can use the function PathConf on the same way
// that is used on IOS, but the problem is that for android we only can check
// _PC_2_SYMLINKS name, and that call is failing on version 2.3.3.
end;
{$ENDIF ANDROID}
function ExpandFileNameCase2(const FileName, RootPath: string; out MatchFound: TFilenameCaseMatch): string;
var
SR: System.SysUtils.TSearchRec;
FullPath, Name: string;
Status: Integer;
{$IFDEF POSIX}
FoundOne: Boolean;
Scans: Byte;
FirstLetter, TestLetter: string;
{$ENDIF POSIX}
begin
Result := ExpandFileName(FileName);
MatchFound := mkNone;
if FileName = '' then // Stop for empty strings, otherwise we risk to get info infinite loop.
Exit;
FullPath := ExtractFilePath(Result);
Name := ExtractFileName(Result);
// if FullPath is not the root directory (portable)
if not SameFileName(FullPath, IncludeTrailingPathDelimiter(ExtractFileDrive(FullPath))) then
begin // Does the path need case-sensitive work?
Status := FindFirst(ExcludeTrailingPathDelimiter(FullPath), faAnyFile, SR);
System.SysUtils.FindClose(SR); // close search before going recursive
if Status <> 0 then
begin
if SameFileName(IncludeTrailingPathDelimiter(FullPath), IncludeTrailingPathDelimiter(RootPath)) then
begin
end
else
begin
FullPath := ExcludeTrailingPathDelimiter(FullPath);
FullPath := ExpandFileNameCase2(FullPath, RootPath, MatchFound);
if MatchFound = mkNone then
Exit; // if we can't find the path, we certainly can't find the file!
end;
FullPath := IncludeTrailingPathDelimiter(FullPath);
end;
end;
// Path is validated / adjusted. Now for the file itself
try
if System.SysUtils.FindFirst(FullPath + Name, faAnyFile, SR)= 0 then // exact match on filename
begin
if not (MatchFound in [mkSingleMatch, mkAmbiguous]) then // path might have been inexact
begin
if Name = SR.Name then
MatchFound := mkExactMatch
else
MatchFound := mkSingleMatch;
end;
Exit(FullPath + SR.Name);
end;
finally
System.SysUtils.FindClose(SR);
end;
{$IFDEF POSIX}
{ Scan the directory.
To minimize the number of filenames tested, scan the directory
using upper/lowercase first letter + wildcard.
This results in two scans of the directory (particularly on Linux) but
vastly reduces the number of times we have to perform an expensive
locale-charset case-insensitive string compare. }
FoundOne := False;
if (fsCaseSensitive in FileSystemAttributes(FullPath)) or
(fsCasePreserving in FileSystemAttributes(FullPath)) then
begin
// First, scan for lowercase first letter
FirstLetter := AnsiLowerCase(Name[Low(string)]);
for Scans := 0 to 1 do
begin
Status := FindFirst(FullPath + FirstLetter + '*', faAnyFile, SR);
while Status = 0 do
begin
if AnsiSameText(SR.Name, Name) then
begin
if FoundOne then
begin // this is the second match
MatchFound := mkAmbiguous;
Exit;
end
else
begin
FoundOne := True;
Result := FullPath + SR.Name;
end;
end;
Status := FindNext(SR);
end;
FindClose(SR);
TestLetter := AnsiUpperCase(Name[Low(string)]);
if TestLetter = FirstLetter then
Break;
FirstLetter := TestLetter;
end;
if MatchFound <> mkAmbiguous then
begin
if FoundOne then
MatchFound := mkSingleMatch
else
MatchFound := mkNone;
end;
end;
{$ENDIF POSIX}
end;
function BuildFileListInAPath(const Path: string; const Attr: Integer;
JustFile: Boolean = False): string;
var
AList: TStringList;
begin
Result := '';
AList := TStringList.Create;
try
BuildFileListInAPath(Path, Attr, AList, JustFile);
Result := AList.Text;
finally
FreeAndNil(AList);
end;
end;
function BuildFileListInAPath(const Path: string; const Attr: Integer; const List: TStrings;
JustFile: Boolean = False): Boolean;
var
SearchRec: TSearchRec;
R: Integer;
begin
Assert(List <> nil);
R := System.SysUtils.FindFirst(ExcludeTrailingPathDelimiter(Path), Attr, SearchRec);
Result := R = 0;
try
if Result then
begin
while R = 0 do
begin
if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
begin
if ((SearchRec.Attr and faDirectory) = faDirectory) then
begin
if not JustFile then
List.Add(SearchRec.Name);
end
else
List.Add(SearchRec.Name);
end;
R := System.SysUtils.FindNext(SearchRec);
end;
Result := R = 0;
end;
finally
System.SysUtils.FindClose(SearchRec);
end;
end;
function faAttrToFileAttributes(
const Attributes: Integer): TFileAttributes;
begin
{$IFDEF MSWINDOWS}
Result := [];
if Attributes and faReadOnly <> 0 then
Include(Result, TFileAttribute.faReadOnly);
if Attributes and faHidden <> 0 then
Include(Result, TFileAttribute.faHidden);
if Attributes and faSysFile <> 0 then
Include(Result, TFileAttribute.faSystem);
if Attributes and faDirectory <> 0 then
Include(Result, TFileAttribute.faDirectory);
if Attributes and faArchive <> 0 then
Include(Result, TFileAttribute.faArchive);
if Attributes and faSymLink <> 0 then
Include(Result, TFileAttribute.faSymLink);
if Attributes and faNormal <> 0 then
Include(Result, TFileAttribute.faNormal);
if Attributes and faTemporary <> 0 then
Include(Result, TFileAttribute.faTemporary);
if Attributes and faCompressed <> 0 then
Include(Result, TFileAttribute.faCompressed);
if Attributes and faEncrypted <> 0 then
Include(Result, TFileAttribute.faEncrypted);
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
Result := [];
if Attributes and faDirectory <> 0 then
Include(Result, TFileAttribute.faDirectory);
if Attributes and faArchive <> 0 then
Include(Result, TFileAttribute.faNormal);
if Attributes and faSymLink <> 0 then
Include(Result, TFileAttribute.faSymLink);
if Attributes and faNormal <> 0 then
Include(Result, TFileAttribute.faNormal);
{$ENDIF POSIX}
end;
{$IFDEF MSWINDOWS}
{$ELSE}
//好像不一定有效果。
function FileSetAttr(const FileName: string; Attr: Integer; FollowLink: Boolean = True): Integer;
var
AFileAttributes: TFileAttributes;
begin
// Result := faInvalid;
// AFileAttributes := faAttrToFileAttributes(Attr);
// TFile.SetAttributes(FileName, AFileAttributes);
Result := 0;
end;
{$ENDIF MSWINDOWS}
function DeleteTreeByEcho(const Source: string; AbortOnFailure: Boolean = False;
YesToAll: Boolean = True;
WaitMinSecond: Integer = DeleteDirectories_WaitMinSecond): Boolean;
var
Files: TStringList;
LPath: string; // writable copy of Path
FileName: string;
I: Integer;
PartialResult: Boolean;
Attr: Integer;
isDir: Boolean;
ToDeleteDir: string;
T: TDateTime;
FWaitSecond, FWaitMinSecond: Integer;
begin
Result := False;
if not DirectoryExists(ExtractFileDir(Source)) then
begin
exit;
end;
if Trim(Source) = PathDelim then
begin
exit;
end;
isDir := DirectoryExists(Source);
Result := True;
Files := TStringList.Create;
try
if isDir then
begin
LPath := IncludeTrailingPathDelimiter(Source);
ToDeleteDir := LPath + '\*.*';
end
else
begin
LPath := IncludeTrailingPathDelimiter(ExtractFilePath(Source));
ToDeleteDir := Source;
end;
BuildFileListInAPath(ToDeleteDir, faAnyFile, Files);
for I := 0 to Files.Count - 1 do
begin
FileName := LPath + PathDelim + Files[I];
PartialResult := True;
// If the current file is itself a directory then recursively delete it
Attr := FileGetAttr(FileName);
if (Attr <> faInvalid) and ((Attr and faDirectory) <> 0) then
begin
PartialResult := DeleteTreeByEcho(FileName, AbortOnFailure, YesToAll,
WaitMinSecond);
end
else
begin
if YesToAll then
begin
// Set attributes to normal in case it's a readonly file
PartialResult := FileSetAttr(FileName, faNormal) = 0;
end
else
begin
if ((Attr and faSysFile) <> 0) or ((Attr and faReadOnly) <> 0) or (Attr = faInvalid)
then
begin
PartialResult := False;
end;
end;
if PartialResult then
PartialResult := System.SysUtils.DeleteFile(FileName); //TFile.Delete()
end;
if not PartialResult then
begin
Result := False;
if AbortOnFailure then
begin
Break;
end;
end;
end;
finally
FreeAndNil(Files);
end;
if Result and isDir then
begin
if YesToAll then
begin
// Finally remove the directory itself
Result := FileSetAttr(LPath, faNormal or faDirectory) = 0;
end
else
begin
Attr := FileGetAttr(LPath);
if ((Attr and faSysFile) <> 0) or ((Attr and faReadOnly) <> 0) or (Attr = faInvalid) then
begin
Result := False;
end;
end;
if Result then
begin
{$IOCHECKS OFF}
RmDir(LPath);
T := Now;
FWaitSecond := WaitMinSecond div 1000;
FWaitMinSecond := WaitMinSecond mod 1000;
while DirectoryExists(LPath) do
begin
if T + EncodeTime(0, 0, FWaitSecond, FWaitMinSecond) < now then
begin
break;
end;
Sleep(1);
end;
{$IFDEF IOCHECKS_ON}
{$IOCHECKS ON}
{$ENDIF IOCHECKS_ON}
// Result := IOResult = 0;
Result := not DirectoryExists(LPath);
// if not Result then
// begin
// ShowMessage(LPath);
// end;
end;
end;
end;
function DeleteDirectoryByEcho(const Source: string;
AbortOnFailure: Boolean = False; YesToAll: Boolean = True;
WaitMinSecond: Integer = DeleteDirectories_WaitMinSecond): Boolean;
var
T: TDateTime;
FWaitSecond, FWaitMinSecond: Integer;
begin
Result := False;
if not DirectoryExists(ExtractFileDir(Source)) then
begin
exit;
end;
if Trim(Source) = PathDelim then
begin
exit;
end;
Result := DeleteTreeByEcho(Source, AbortOnFailure, YesToAll, WaitMinSecond);
if Result then
begin
T := Now;
FWaitSecond := WaitMinSecond div 1000;
FWaitMinSecond := WaitMinSecond mod 1000;
while DirectoryExists(Source) do
begin
if T + EncodeTime(0, 0, FWaitSecond, FWaitMinSecond) < now then
begin
break;
end;
Sleep(1);
end;
end;
end;
function GetFileNamesFromDirectory(const DirName: string; const SearchFilter: string = '*';
const FileAttribs: Integer = faAnyFile; const isIncludeSubDirName: Boolean = False; const Recursion: Boolean = False;
const FullName: Boolean = False): string;
var
SubList, FileNameList: TStrings;
DirPath: string;
SearchRec: TSearchRec;
i: Integer;
begin
Result := '';
if Trim(SearchFilter) = '' then
begin
exit;
end;
if DirectoryExists(DirName) then
begin
FileNameList := TStringList.Create;
try
try
DirPath := IncludeTrailingPathDelimiter(DirName);
// 得到该目录下指定类型文件的文件名
if System.SysUtils.FindFirst(DirPath + Trim(SearchFilter), FileAttribs, SearchRec) = 0 then
try
repeat
if isIncludeSubDirName or ((SearchRec.Attr and faDirectory) <> faDirectory) then
begin
if (SearchRec.Attr and faDirectory) = faDirectory then
begin
if (ExtractFileName(SearchRec.Name) = '.') or (ExtractFileName(SearchRec.Name) = '..') then
begin
Continue;
end;
end;
if FullName then
begin
FileNameList.Add(ExpandFileName(SearchRec.Name));
end
else
begin
FileNameList.Add(ExtractFileName(SearchRec.Name));
end;
end;
until System.SysUtils.FindNext(SearchRec) <> 0;
finally
System.SysUtils.FindClose(SearchRec);
end;
// 递归该目录下所有子目录下的指定文件。
if Recursion and (System.SysUtils.FindFirst(DirPath + '*', faDirectory, SearchRec) = 0) then
try
repeat
if Recursion and ((SearchRec.Attr and faDirectory) = faDirectory) and
(SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
begin
Result := GetFileNamesFromDirectory(DirPath + ExtractFileName(SearchRec.Name), SearchFilter,
FileAttribs, isIncludeSubDirName, Recursion, FullName);
SubList := TStringList.Create;
try
SubList.Clear;
SubList.Text := Result;
if not FullName then
begin
for i := 0 to SubList.Count - 1 do
begin
SubList.Strings[i] := ExtractFileName(SearchRec.Name) + PathDelim + SubList.Strings[i];
end;
end;
Result := '';
FileNameList.AddStrings(SubList);
finally
FreeAndNil(SubList);
end;
end;
until System.SysUtils.FindNext(SearchRec) <> 0;
finally
System.SysUtils.FindClose(SearchRec);
end;
Result := FileNameList.Text;
except
Result := '';
end;
finally
if FileNameList <> nil then
begin
FreeAndNil(FileNameList);
end;
end;
end;
end;
function GetCaseSensitiveFileName(const FileName: string; RootPath: string = ''): string;
var
MatchFound: TFilenameCaseMatch;
begin
Result := ExpandFileNameCase2(FileName, RootPath, MatchFound);
end;
function GetSDCardPath(Index: Integer = 0): string;
begin
Result := FindSDCardSubPath('', Index);
end;
function FindSDCardSubPath(SubPath: string; Index: Integer = 0): string;
var
PathDelimedSubPath: string;
UnPathDelimedSubPath: string;
{$IFDEF ANDROID}
VolumePathList: TStrings;
UsbIndex,
CdRomIndex,
TempVolumeIndex,
I: Integer;
IsFoundDir0: Boolean;
{$ENDIF ANDROID}
begin
PathDelimedSubPath := '';
UnPathDelimedSubPath := SubPath;
if UnPathDelimedSubPath <> '' then
begin
if UnPathDelimedSubPath.Chars[0] = PathDelim then
begin
UnPathDelimedSubPath := UnPathDelimedSubPath.Substring(1);
end;
PathDelimedSubPath := PathDelim + UnPathDelimedSubPath;
end;
{$IFDEF ANDROID}
Result := '/storage/emulated/' + IntToStr(Index) + PathDelimedSubPath;
if FVolumePaths.Trim = '' then GetVolumePaths;
VolumePathList := TStringList.Create;
try
VolumePathList.Text := FVolumePaths;
if Index = 0 then
begin
Result := JNIgetExternalStorageDirectory(UnPathDelimedSubPath);
if not DirectoryExists(Result) and (VolumePathList.Count > 0) then
begin
Result := VolumePathList[0];
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/emulated/' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard_ext0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard_ext' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard_ext0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard_ext' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/emmc' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/nand' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/flash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/emmc' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/flash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/flash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/D' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/D' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/flash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/flash' + PathDelimedSubPath;
end;
//last
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard0' + PathDelimedSubPath;
end;
end
else if (Index < UsbDiskStartIndex) then
begin
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard' + IntToStr(Index + 1) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'sdcard' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'sdcard' + IntToStr(Index + 1) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard_ext' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard_ext' + IntToStr(Index + 1) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard' + IntToStr(Index + 1) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard_ext' + IntToStr(Index) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard_ext' + IntToStr(Index + 1) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) and (VolumePathList.Count > Index) then
begin
Result := VolumePathList[Index];
if (Result.IndexOf('usbcd') >= 0) or
(Result.IndexOf('usb') >= 0) or
(Result.IndexOf('otg') >= 0) or
(Result.IndexOf('-rw') >= 0) or
(Result.IndexOf('_rw') >= 0) or
(Result.IndexOf('cdrom') >= 0) or
(Result.IndexOf('cd_rom') >= 0) or
(Result.IndexOf('cd-rom') >= 0) then
begin
Result := '';
end;
end;
if Index = 1 then
begin
if not DirectoryExists(Result) and (VolumePathList.Count = 2) then
begin
Result := VolumePathList[1];
if (Result.IndexOf('/storage/emulated/') < 0) and
(Result.IndexOf('/storage/sdcard') < 0) and
(Result.IndexOf('/mnt/sdcard') < 0) then
begin
Result := '';
end;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/extSdCard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/external1' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard-ext' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/ext_card' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/extsd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/_ExternalSD' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/external_sd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/removable_sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/extSdCard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/external1' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/sdcard-ext' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/ext_card' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/extsd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/_ExternalSD' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/external_sd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/removable_sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'tflash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'removable_sdcard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'tflash' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'extSdCard' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'external1' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'sdcard-ext' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'ext_card' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'extsd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + '_ExternalSD' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'external_sd' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'removable_sdcard' + PathDelimedSubPath;
end;
end;
//last
if not DirectoryExists(Result) then
begin
Result := '/storage/sdcard' + IntToStr(Index) + PathDelimedSubPath;
end;
end
else if (Index >= UsbDiskStartIndex) and (Index < UsbDiskStartIndex + OTGDeivceCount) then //UsbDiskStartIndex
begin
UsbIndex := Index - UsbDiskStartIndex;
TempVolumeIndex := UsbIndex;
Result := '';
for I := 1 to VolumePathList.Count - 1 do
begin
Result := ExcludeTrailingPathDelimiter(VolumePathList[I]);
if (Result.IndexOf('/storage/usb') < 0) and
(Result.IndexOf('/storage/otg') < 0) and
(Result.IndexOf('/mnt/otg') < 0) and
(Result.IndexOf('/mnt/usb') < 0) then
begin
Result := '';
end
else if DirectoryExists(Result) then
begin
if (Result.IndexOf('usbcd') >= 0) then
begin
Result := '';
end
else if (Result.IndexOf('otgcd') >= 0) then
begin
Result := '';
end
else if (TempVolumeIndex = 0) and (Result.Chars[Result.Length] in ['2'..'9']) then
begin
Result := '';
end
else if (TempVolumeIndex <> 0) and (Result.Chars[Result.Length] <> IntToStr(TempVolumeIndex)) then
begin
Result := '';
end
else
begin
Result := Result + PathDelimedSubPath;
break;
end;
end;
end;
if (Result = '') or (not DirectoryExists(Result)) then
begin
if UsbIndex = 0 then
begin
Result := '/storage/udisk' + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/udisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'udisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbotg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbotg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbotg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdrive' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdrive' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdrive' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usb' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usb' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usb' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/otg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/otg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'otg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'udisk' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbotg' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usb' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'otg' + PathDelimedSubPath;
end;
//index = 0
if not DirectoryExists(Result) then
begin
Result := '/storage/udisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/udisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'udisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbotg0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbotg0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbotg0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdrive0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdrive0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdrive0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usb0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usb0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usb0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'udisk0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbotg0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usb0' + PathDelimedSubPath;
end;
end
else
begin
//index > 0
IsFoundDir0 := False;
Result := GetSDCardPath(UsbDiskStartIndex);
IsFoundDir0 := Result.Chars[Result.Length] = '0';
if not IsFoundDir0 then
begin
inc(TempVolumeIndex);
end;
Result := '/storage/udisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/udisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'udisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbotg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbotg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbotg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbdrive' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbdrive' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbdrive' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usb' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usb' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usb' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/otg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/otg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'otg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'udisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbotg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usb' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'otg' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
end;
end;
//last
if (Result = '') or (not DirectoryExists(Result)) then
begin
if UsbIndex = 0 then
begin
Result := '/storage/udisk' + PathDelimedSubPath;
end
else
begin
Result := '/storage/udisk' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
end;
end
else if (Index >= CDROMStartIndex) and (Index < CDROMStartIndex + OTGDeivceCount) then //CDROMStartIndex
begin
CdRomIndex := Index - CDROMStartIndex;
TempVolumeIndex := CdRomIndex;
for I := 1 to VolumePathList.Count - 1 do
begin
Result := ExcludeTrailingPathDelimiter(VolumePathList[I]);
if (Result.IndexOf('/storage/cd') < 0) and
(Result.IndexOf('/mnt/cd') < 0) and
(Result.IndexOf('/storage/usbcd') < 0) and
(Result.IndexOf('/mnt/usbcd') < 0) then
begin
Result := '';
end
else if DirectoryExists(Result) then
begin
if (TempVolumeIndex = 0) and (Result.Chars[Result.Length] in ['2'..'9']) then
begin
Result := '';
end
else if (TempVolumeIndex <> 0) and (Result.Chars[Result.Length] <> IntToStr(TempVolumeIndex)) then
begin
Result := '';
end
else
begin
Result := Result + PathDelimedSubPath;
break;
end;
end;
end;
if (Result = '') or (not DirectoryExists(Result)) then
begin
if CdRomIndex = 0 then
begin
Result := '/storage/cd-rom' + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cd-rom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cd-rom' + PathDelimedSubPath;
end;
Result := '/storage/media_rw' + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/media_rw' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'media_rw' + PathDelimedSubPath;
end;
Result := '/storage/cd_rom' + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cd_rom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cd_rom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbcdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbcdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbcdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/cdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cd-rom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbcdrom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cdrom' + PathDelimedSubPath;
end;
Result := '/storage/cd-rom' + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cd-rom' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cd-rom' + PathDelimedSubPath;
end;
//index = 0
if not DirectoryExists(Result) then
begin
Result := '/storage/cd_rom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cd_rom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cd_rom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbcdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbcdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbcdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/cdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cd-rom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbcdrom0' + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cdrom0' + PathDelimedSubPath;
end;
end
else
begin
//index > 0
IsFoundDir0 := False;
Result := GetSDCardPath(CDROMStartIndex);
IsFoundDir0 := Result.Chars[Result.Length] = '0';
if not IsFoundDir0 then
begin
inc(TempVolumeIndex);
end;
Result := '/storage/cd_rom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cd_rom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cd_rom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/usbcdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/usbcdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'usbcdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/storage/cdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := '/mnt/cdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := 'cdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cd-rom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'usbcdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
if not DirectoryExists(Result) then
begin
Result := GetSDCardPath(0) + 'cdrom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
end;
end;
//last
if (Result = '') or (not DirectoryExists(Result)) then
begin
if CdRomIndex = 0 then
begin
Result := '/storage/cd-rom' + PathDelimedSubPath;
end
else
begin
Result := '/storage/cd-rom' + IntToStr(TempVolumeIndex) + PathDelimedSubPath;
end;
end;
end
else
begin
Result := JNIgetExternalStorageDirectory(UnPathDelimedSubPath);
end;
finally
FreeAndNil(VolumePathList);
end;
{$ELSE}
if UnPathDelimedSubPath <> '' then
begin
Result := IncludeTrailingPathDelimiter(
System.IOUtils.TPath.GetSharedDocumentsPath) + UnPathDelimedSubPath;
end
else
begin
Result := System.IOUtils.TPath.GetSharedDocumentsPath;
end;
{$ENDIF ANDROID}
Result := IncludeTrailingPathDelimiter(Result);
end;
function GetAppPath: string;
begin
{$IF Defined(ANDROID)}
Result := ExtractFilePath(
ExcludeTrailingPathDelimiter(
System.IOUtils.TPath.GetHomePath));
{$ELSE}
Result := ExtractFilePath(ParamStr(0));
{$ENDIF ANDROID}
Result := IncludeTrailingPathDelimiter(Result);
end;
//initialization
//
//finalization
// UnRegisterSDReceiver;
end.