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.