ksAndroid.Helpers.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. {*******************************************************}
  2. { }
  3. { Helpers for Android }
  4. { }
  5. { CopyRight (C) 2018-2020 KngStr }
  6. { }
  7. { Some Code from }
  8. { Kastri Free of DelphiWorlds }
  9. { QDAC of swish }
  10. { Thanks }
  11. { }
  12. {*******************************************************}
  13. unit ksAndroid.Helpers;
  14. interface
  15. uses
  16. System.SysUtils, Androidapi.JNIBridge,
  17. Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.App,
  18. Androidapi.JNI.JavaTypes, Androidapi.JNI.Net;
  19. type
  20. TAndroidHelperEx = record
  21. private
  22. class function GetJActivity: JActivity; static;
  23. class function GetJContext: JContext; static;
  24. class function GetJActivityManager: JActivityManager; static;
  25. public
  26. const
  27. ICE_CREAM_SANDWICH = 14;
  28. ICE_CREAM_SANDWICH_MR1 = 15;
  29. JELLY_BEAN = 16;
  30. JELLY_BEAN_MR1 = 17;
  31. JELLY_BEAN_MR2 = 18;
  32. KITKAT = 19;
  33. KITKAT_MR1 = 20;
  34. LOLLIPOP = 21;
  35. LOLLIPOP_MR1 = 22;
  36. MARSHMALLOW = 23;
  37. NOUGAT = 24;
  38. NOUGAT_MR1 = 25;
  39. OREO = 26;
  40. OREO_MR1 = 27;
  41. PIE = 28;
  42. Q = 29;
  43. /// <summary>
  44. /// Checks if both build and target are greater or equal to the tested value
  45. /// </summary>
  46. class function CheckBuildAndTarget(const AValue: Integer): Boolean; static;
  47. /// <summary>
  48. /// Returns the equivalent of "AndroidClass.class"
  49. /// </summary>
  50. class function GetClass(const APackageClassName: string): Jlang_Class; static;
  51. /// <summary>
  52. /// Returns the application default icon ID
  53. /// </summary>
  54. class function GetDefaultIconID: Integer; static;
  55. /// <summary>
  56. /// Returns a URI to the notification sound
  57. /// </summary>
  58. class function GetDefaultNotificationSound: Jnet_Uri; static;
  59. /// <summary>
  60. /// Returns target Sdk version
  61. /// </summary>
  62. class function GetTargetSdkVersion: Integer; static;
  63. /// <summary>
  64. /// Returns installed Sdk version
  65. /// </summary>
  66. class function GetBuildSdkVersion: Integer; static;
  67. /// <summary>
  68. /// Returns whether the activity is running foreground
  69. /// </summary>
  70. /// <remarks>
  71. /// Useful from within a service to determine whether or not the service needs to run in foreground mode
  72. /// </remarks>
  73. class function IsActivityForeground: Boolean; static;
  74. /// <summary>
  75. /// Get column data from uri
  76. /// </summary>
  77. class function GetColumnAsString(const AUri: Jnet_Uri; const AColumn: JString; ASelection: JString = nil;
  78. ASelectionArgs: TJavaObjectArray<JString> = nil; ASortOrder: JString = nil): string; static;
  79. /// <summary>
  80. /// Get data column data from uri
  81. /// </summary>
  82. class function GetDataColumnAsString(const AUri: Jnet_Uri; ASelection: JString = nil;
  83. ASelectionArgs: TJavaObjectArray<JString> = nil; ASortOrder: JString = nil): string; static;
  84. /// <summary>
  85. /// Converts uri to file path
  86. /// </summary>
  87. class function FileFromUri(const AUri: Jnet_Uri): string; overload; static;
  88. /// <summary>
  89. /// Converts file to uri, without FileProvider
  90. /// </summary>
  91. class function UriFromFile(const AFile: JFile): Jnet_Uri; overload; static;
  92. /// <summary>
  93. /// Converts file to uri, without FileProvider
  94. /// </summary>
  95. class function UriFromFile(const AFileName: string): Jnet_Uri; overload; static;
  96. /// <summary>
  97. /// Converts file to uri, using FileProvider if target API >= 24
  98. /// </summary>
  99. /// <remarks>
  100. /// Use this only when accessing files with an "external" URI
  101. /// </remarks>
  102. class function SharedUriFromFile(const AFile: JFile; const AAuthority: string = ''): Jnet_Uri; overload; static;
  103. /// <summary>
  104. /// Converts filename to uri, using FileProvider if target API >= 24
  105. /// </summary>
  106. /// <remarks>
  107. /// Use this only when accessing files with an "external" URI
  108. /// </remarks>
  109. class function SharedUriFromFile(const AFileName: string; const AAuthority: string = ''): Jnet_Uri; overload; static;
  110. /// <summary>
  111. /// TJnet_Uri.JavaClass.parse
  112. /// </summary>
  113. class function UriParse(const S: string): Jnet_Uri; overload; static;
  114. /// <summary>
  115. /// TJnet_Uri.JavaClass.parse
  116. /// </summary>
  117. class function UriParse(const S: JString): Jnet_Uri; overload; static;
  118. /// <summary>Returns Java Application Context</summary>
  119. class property Context: JContext read GetJContext;
  120. /// <summary>Returns Java Application Activity</summary>
  121. /// <remarks>An exception will be launched if there is no activity, for example a Service</remarks>
  122. class property Activity: JActivity read GetJActivity;
  123. /// <summary>Returns Java Application Activity Manager</summary>
  124. class property ActivityManager: JActivityManager read GetJActivityManager;
  125. /// <remarks>Need reorder tasks permission</remarks>
  126. class procedure BringAppToFront; static;
  127. /// <remarks>Need reorder tasks permission</remarks>
  128. class procedure SendAppToBack; static;
  129. /// <summary>Call a Java Activity</summary>
  130. class function StartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static;
  131. /// <summary>Check HasAssocApp and Call a Java Activity</summary>
  132. class function CheckAndStartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static;
  133. /// <summary>Returns Application package name</summary>
  134. class function GetPackageName: string; static;
  135. /// <summary>Returns Application package label</summary>
  136. class function GetPackageLabel: string; static;
  137. /// <summary>Returns MimeType from filename</summary>
  138. class function GetMimeType(AFileName: string): JString; static;
  139. /// <summary>Returns Primary SDCard path</summary>
  140. class function GetSDCardPath: string; static;
  141. /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
  142. class function HasAssocApp(const URI: string): Boolean; overload; static;
  143. /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
  144. class function HasAssocApp(const Intent: JIntent): Boolean; overload; static;
  145. /// <summary>Install an android package: xxx.apk</summary>
  146. /// <remarks>
  147. /// <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
  148. /// </remarks>
  149. class function InstallPackage(const AFileName: string; const AAuthority: string = ''): Boolean; static;
  150. /// <summary>Check if an application is installed</summary>
  151. class function IsAppInstalled(const APackage: string): Boolean; static;
  152. /// <summary>Add/Cear FLAG_KEEP_SCREEN_ON</summary>
  153. class function KeepScreen(AOn: Boolean): Boolean; static;
  154. end;
  155. implementation
  156. uses
  157. {$IFDEF DEBUG}FMX.Types,{$ENDIF}
  158. Androidapi.JNI.Os, Androidapi.JNI.Support,
  159. Androidapi.JNI.Media, Androidapi.JNI.Provider, Androidapi.JNI.Webkit,
  160. Androidapi.Helpers, FMX.Helpers.Android, System.IOUtils;
  161. { TAndroidHelperEx }
  162. class procedure TAndroidHelperEx.BringAppToFront;
  163. begin
  164. ActivityManager.moveTaskToFront(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  165. end;
  166. class function TAndroidHelperEx.CheckAndStartActivity(Intent: JIntent;
  167. const Code: Integer): Boolean;
  168. begin
  169. Result := (Intent <> nil) and HasAssocApp(Intent);
  170. if not Result then
  171. Exit;
  172. Result := TAndroidHelperEx.StartActivity(Intent, Code);
  173. end;
  174. class function TAndroidHelperEx.CheckBuildAndTarget(const AValue: Integer): Boolean;
  175. begin
  176. Result := (GetBuildSdkVersion >= AValue) and (GetTargetSdkVersion >= AValue);
  177. end;
  178. (*
  179. * >=4.4
  180. * uri=content://com.android.providers.media.documents/document/image%3A293502
  181. * uri=file:///storage/emulated/0/temp_photo.jpg
  182. * uri=content://media/external/images/media/193968
  183. * <4.4
  184. * uri=content://media/external/images/media/13
  185. * third party
  186. * content://com.speedsoftware.explorer.fileprovider/root/storage/emulated/0/Android/data/com.lifan.qspsy/files/cache/thumb/F6AB021A6BBCEFC3B942625FBA2E6ADE/7.jpg
  187. * content://com.tencent.mtt.fileprovider/QQBrowser/Movies/BVR_2019_10_14_10_52_29_trimq.mp4
  188. * content://com.estrongs.files/storage/emulated/0/DCIM/360%E8%A1%8C%E8%BD%A6%E8%AE%B0%E5%BD%95%E4%BB%AA/2018_02_11_14_16_40_ABAC1BB6.mp4
  189. *
  190. * 参考文献
  191. * https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java
  192. * https://github.com/DB-BOY/FileChoose/blob/master/app/src/main/java/cn/dbboy/filechoose/FileUtil.java
  193. * https://www.jianshu.com/p/c5f207f8cce6
  194. * https://blog.csdn.net/chengfu116/article/details/74923161
  195. * https://www.cnblogs.com/epmouse/p/5421048.html
  196. *
  197. * 暂未实现
  198. * 安卓10好像完全没有获取真实路径的机会
  199. * 安卓7/8/9貌似有些机型还是不行,只能是获取内容后在自己可读写的区域存一份儿
  200. * 如果是获取内容而不是path的话,就不需要用这里了,单独搞个api更好
  201. *)
  202. class function TAndroidHelperEx.FileFromUri(const AUri: Jnet_Uri): string;
  203. function GetUriByType(S: string): Jnet_Uri;
  204. begin
  205. if SameText(S, 'image') then
  206. Result := TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI
  207. else if SameText(S, 'video') then
  208. Result := TJVideo_Media.JavaClass.EXTERNAL_CONTENT_URI
  209. else if SameText(S, 'audio') then
  210. Result := TJAudio_Media.JavaClass.EXTERNAL_CONTENT_URI
  211. else
  212. Result := nil;
  213. end;
  214. function GetDocId(var ADocId, AType, AId: string): Boolean;
  215. var
  216. LJString: JString;
  217. LArr: TArray<string>;
  218. begin
  219. Result := False;
  220. LJString := TJDocumentsContract.JavaClass.getDocumentId(AUri);
  221. if LJString = nil then
  222. Exit;
  223. ADocId := JStringToString(LJString);
  224. if ADocId = '' then
  225. Exit;
  226. LArr := ADocId.Split([':']);
  227. if Length(LArr) < 2 then begin
  228. AType := '';
  229. AId := ADocId;
  230. end
  231. else begin
  232. AType := LArr[0];
  233. AId := LArr[1];
  234. end;
  235. Result := AId <> '';
  236. end;
  237. var
  238. LSdCard, LPath: string;
  239. LAuthority, LScheme, LDocId, LType, LId: string;
  240. LSelection: JString;
  241. LUri: Jnet_Uri;
  242. LArr: TArray<string>;
  243. I: Integer;
  244. iId: Int64;
  245. begin
  246. Result := '';
  247. if AUri = nil then
  248. Exit;
  249. LSdCard := GetSDCardPath;
  250. LAuthority := JStringToString(AUri.getAuthority().toString);
  251. LScheme := JStringToString(AUri.getScheme());
  252. {$IFDEF DEBUG}
  253. Log.d('---TAndroidHelperEx.FileFromUri-AUri:%s', [JStringToString(AUri.toString)]);
  254. Log.d('---TAndroidHelperEx.FileFromUri-AUri.Authority:%s-LScheme:%s', [LAuthority, LScheme]);
  255. {$ENDIF}
  256. if CheckBuildAndTarget(KITKAT) and TJDocumentsContract.JavaClass.isDocumentUri(Context, AUri) then begin
  257. if not GetDocId(LDocId, LType, LId) then
  258. Exit;
  259. {$IFDEF DEBUG}
  260. Log.d('---TAndroidHelperEx.FileFromUri-LDocId:%s-LType:%s-LId:%s', [LDocId, LType, LId]);
  261. {$ENDIF}
  262. if SameText(LAuthority, 'com.android.externalstorage.documents') then begin
  263. if SameText(LType, 'primary') then
  264. Result := TPath.Combine(LSdCard, LId)
  265. else if SameText(LType, 'home') then
  266. Result := TPath.Combine(TPath.GetSharedDocumentsPath, LId)
  267. {$IFDEF DEBUG}
  268. else
  269. Log.d('---TAndroidHelperEx.FileFromUri-Unkown externalstorage-LDocId:%s-LType:%s-LId:%s', [LDocId, LType, LId]);
  270. {$ENDIF}
  271. end
  272. else if SameText(LAuthority, 'com.android.providers.media.documents') then begin
  273. LSelection := StringToJString('_id=' + LId);
  274. LUri := GetUriByType(LType);
  275. if LUri <> nil then
  276. Result := GetDataColumnAsString(LUri, LSelection);
  277. end else if SameText(LAuthority, 'com.android.providers.downloads.documents') then begin
  278. if SameText(LType, 'raw') then
  279. Result := Copy(LDocId, 5)
  280. //else if GetBuildSdkVersion < OREO then begin // 有人说O的时候不需要自己解析,但测试锤子手机7.1,不行
  281. else begin
  282. SetLength(LArr, 2);
  283. LArr[0] := 'content://downloads/public_downloads';
  284. LArr[1] := 'content://downloads/my_downloads';
  285. //LArr[2] := 'content://downloads/all_downloads'; // 这个貌似没权限
  286. iId := StrToInt64Def(LId, -1);
  287. for I := 0 to Length(LArr) - 1 do begin
  288. LUri := TJContentUris.JavaClass.withAppendedId(UriParse(LArr[I]), iId);
  289. if LUri <> nil then
  290. try
  291. Result := GetDataColumnAsString(LUri);
  292. if Result <> '' then
  293. Break;
  294. except
  295. on E: Exception do begin
  296. {$IFDEF DEBUG}
  297. Log.d('---TAndroidHelperEx.FileFromUri Error: [%s]%s', [E.ClassName, E.Message]);
  298. {$ENDIF}
  299. end;
  300. end;
  301. end;
  302. end;
  303. //else
  304. if Result = '' then
  305. Result := GetDataColumnAsString(AUri);
  306. end
  307. {$IFDEF DEBUG}
  308. else
  309. Log.d('---TAndroidHelperEx.FileFromUri-Unkown DocumentUri-');
  310. {$ENDIF}
  311. end else if SameText(LScheme, 'content') then begin
  312. // Return the remote address
  313. if SameText(LAuthority, 'com.google.android.apps.photos.content') then
  314. Result := JStringToString(AUri.getLastPathSegment())
  315. else if SameText(LAuthority, 'com.tencent.mtt.fileprovider') then begin
  316. LPath := JStringToString(AUri.getPath());
  317. if Pos('/QQBrowser/', LPath) = 1 then begin // /QQBrowser/XXX
  318. LPath := TPath.Combine(LSdCard, Copy(LPath, 12));
  319. if FileExists(LPath) then
  320. Result := LPath;
  321. end;
  322. end
  323. else if SameText(LAuthority, 'com.speedsoftware.explorer.fileprovider') then begin
  324. LPath := JStringToString(AUri.getPath());
  325. if Pos('/root/', LPath) = 1 then
  326. Result := Copy(LPath, 6);
  327. end
  328. else if SameText(LAuthority, 'com.estrongs.files') then
  329. Result := JStringToString(AUri.getPath())
  330. else
  331. Result := GetDataColumnAsString(AUri);
  332. end
  333. else if SameText(LScheme, 'file') then
  334. Result := JStringToString(AUri.getPath())
  335. {$IFDEF DEBUG}
  336. else
  337. Log.d('---TAndroidHelperEx.FileFromUri-Unkown Uri-');
  338. {$ENDIF}
  339. end;
  340. class function TAndroidHelperEx.GetBuildSdkVersion: Integer;
  341. begin
  342. Result := TJBuild_VERSION.JavaClass.SDK_INT;
  343. end;
  344. class function TAndroidHelperEx.GetClass(const APackageClassName: string): Jlang_Class;
  345. begin
  346. Result := TJLang_Class.JavaClass.forName(StringToJString(APackageClassName), True, Context.getClassLoader);
  347. end;
  348. class function TAndroidHelperEx.GetColumnAsString(const AUri: Jnet_Uri; const AColumn: JString;
  349. ASelection: JString; ASelectionArgs: TJavaObjectArray<JString>; ASortOrder: JString): string;
  350. var
  351. LCursor: JCursor;
  352. LIndex: Integer;
  353. LPojection: TJavaObjectArray<JString>;
  354. begin
  355. Result := '';
  356. if AUri = nil then
  357. Exit;
  358. LPojection := TJavaObjectArray<JString>.Create(1);
  359. LPojection.Items[0] := AColumn;
  360. LCursor := Context.getContentResolver().query(AUri, LPojection, ASelection, ASelectionArgs, ASortOrder);
  361. if LCursor = nil then begin
  362. {$IFDEF DEBUG}
  363. Log.d('---TAndroidHelperEx.GetColumnAsString-LCursor = nil-');
  364. {$ENDIF}
  365. Exit;
  366. end;
  367. try
  368. LIndex := LCursor.getColumnIndex(AColumn);
  369. if (LIndex > -1) and LCursor.moveToFirst then
  370. Result := JStringToString(LCursor.getString(LIndex))
  371. {$IFDEF DEBUG}
  372. else
  373. Log.d('---TAndroidHelperEx.GetColumnAsString-LIndex:%d-', [LIndex]);
  374. {$ENDIF}
  375. finally
  376. LCursor.close;
  377. end;
  378. end;
  379. class function TAndroidHelperEx.GetDataColumnAsString(const AUri: Jnet_Uri;
  380. ASelection: JString; ASelectionArgs: TJavaObjectArray<JString>;
  381. ASortOrder: JString): string;
  382. begin
  383. Result := GetColumnAsString(AUri, StringToJString('_data'), ASelection, ASelectionArgs, ASortOrder);
  384. end;
  385. class function TAndroidHelperEx.GetDefaultIconID: Integer;
  386. begin
  387. Result := Context.getApplicationInfo.icon;
  388. end;
  389. class function TAndroidHelperEx.GetDefaultNotificationSound: Jnet_Uri;
  390. begin
  391. Result := TJRingtoneManager.JavaClass.getDefaultUri(TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
  392. end;
  393. class function TAndroidHelperEx.GetJActivity: JActivity;
  394. begin
  395. Result :=
  396. {$IF CompilerVersion > 27}
  397. TAndroidHelper.Activity
  398. {$ELSE}
  399. SharedActivity
  400. {$ENDIF}
  401. end;
  402. class function TAndroidHelperEx.GetJActivityManager: JActivityManager;
  403. var
  404. AService: JObject;
  405. begin
  406. AService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
  407. if AService <> nil then
  408. Result := TJActivityManager.Wrap((AService as ILocalObject).GetObjectID)
  409. else
  410. Result := nil;
  411. end;
  412. class function TAndroidHelperEx.GetJContext: JContext;
  413. begin
  414. Result :=
  415. {$IF CompilerVersion > 27}
  416. TAndroidHelper.Context;
  417. {$ELSE}
  418. SharedActivityContext;
  419. {$ENDIF}
  420. end;
  421. class function TAndroidHelperEx.GetMimeType(AFileName: string): JString;
  422. var
  423. LExt: string;
  424. LTypeMap: JMimeTypeMap;
  425. begin
  426. Result := nil;
  427. if AFileName = '' then
  428. Exit;
  429. LExt := LowerCase(ExtractFileExt(AFileName));
  430. if Length(LExt) < 2 then
  431. Exit;
  432. LExt := Copy(LExt, 1);
  433. LTypeMap := TJMimeTypeMap.JavaClass.getSingleton();
  434. if LTypeMap = nil then
  435. Exit;
  436. Result := LTypeMap.getMimeTypeFromExtension(StringToJString(LExt));
  437. if Result <> nil then
  438. Result := Result.trim();
  439. end;
  440. class function TAndroidHelperEx.GetPackageLabel: string;
  441. var
  442. LApplicationInfo: JApplicationInfo;
  443. LLabel: JCharSequence;
  444. begin
  445. Result := '';
  446. try
  447. LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0);
  448. if LApplicationInfo <> nil then begin
  449. LLabel := Context.getPackageManager.getApplicationLabel(LApplicationInfo);
  450. if LLabel <> nil then
  451. Result := JCharSequenceToStr(LLabel);
  452. end;
  453. except
  454. end;
  455. end;
  456. class function TAndroidHelperEx.GetPackageName: string;
  457. begin
  458. Result := JStringToString(Context.getPackageName);
  459. end;
  460. class function TAndroidHelperEx.GetSDCardPath: string;
  461. var
  462. sPath: string;
  463. begin
  464. Result := '';
  465. sPath := System.IOUtils.TPath.GetSharedDocumentsPath;
  466. if Pos(PathDelim, sPath) = 0 then
  467. Exit;
  468. Result := ExtractFilePath(ExcludeTrailingPathDelimiter(sPath));
  469. end;
  470. class function TAndroidHelperEx.GetTargetSdkVersion: Integer;
  471. var
  472. LApplicationInfo: JApplicationInfo;
  473. begin
  474. try
  475. LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0);
  476. if LApplicationInfo <> nil then
  477. Result := LApplicationInfo.targetSdkVersion
  478. else
  479. Result := -1;
  480. except
  481. Result := -1;
  482. end;
  483. end;
  484. class function TAndroidHelperEx.HasAssocApp(const Intent: JIntent): Boolean;
  485. var
  486. LList: JList;
  487. begin
  488. Result := False;
  489. if Intent = nil then
  490. Exit;
  491. Result := Intent.resolveActivityInfo(Activity.getPackageManager(),
  492. TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY) <> nil;
  493. // Android 6+ APP LINK closed Will cause list is null.
  494. // Activity with <action android:name="android.intent.action.VIEW" />
  495. // 被第三方软件套用的时候,比如应用隐藏大师,会导致这里size不正确
  496. // LList := Activity.getPackageManager.queryIntentActivities(Intent,
  497. // TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY);
  498. // Result := (LList = nil) or (LList.size > 0);
  499. end;
  500. class function TAndroidHelperEx.HasAssocApp(const URI: string): Boolean;
  501. var
  502. Intent: JIntent;
  503. begin
  504. Result := False;
  505. if URI = '' then
  506. Exit;
  507. Intent := TJIntent.Create;
  508. Intent.setData(UriParse(URI));
  509. Intent.setAction(StringToJString('android.intent.action.VIEW'));
  510. Result := HasAssocApp(Intent);
  511. end;
  512. class function TAndroidHelperEx.InstallPackage(const AFileName, AAuthority: string): Boolean;
  513. var
  514. LIntent: JIntent;
  515. begin
  516. Result := False;
  517. if Trim(AFileName) = '' then
  518. Exit;
  519. LIntent := TJIntent.Create;
  520. if CheckBuildAndTarget(OREO) then
  521. LIntent.setAction(TJIntent.JavaClass.ACTION_INSTALL_PACKAGE)
  522. else
  523. LIntent.setAction(TJIntent.JavaClass.ACTION_VIEW);
  524. // 没有这个也可以安装成功,但是安装成功后的成功页面,也就是,完成/打开 会无法显示
  525. LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  526. LIntent.setDataAndType(SharedUriFromFile(AFileName, AAuthority),
  527. StringToJString('application/vnd.android.package-archive'));
  528. LIntent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
  529. Result := StartActivity(LIntent);
  530. end;
  531. class function TAndroidHelperEx.IsActivityForeground: Boolean;
  532. var
  533. LService: JObject;
  534. LRunningApps: JList;
  535. LAppInfo: JActivityManager_RunningAppProcessInfo;
  536. I: Integer;
  537. begin
  538. Result := False;
  539. LService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
  540. LRunningApps := TJActivityManager.Wrap(TAndroidHelper.JObjectToID(LService)).getRunningAppProcesses;
  541. for I := 0 to LRunningApps.size - 1 do begin
  542. LAppInfo := TJActivityManager_RunningAppProcessInfo.Wrap(TAndroidHelper.JObjectToID(LRunningApps.get(I)));
  543. if LAppInfo.importance = 100 then begin
  544. if LAppInfo.importanceReasonComponent <> nil then begin
  545. if LAppInfo.importanceReasonComponent.getPackageName.equals(Context.getPackageName) then
  546. Exit(True);
  547. end
  548. else if LRunningApps.size = 1 then
  549. Exit(True);
  550. end;
  551. end;
  552. end;
  553. class function TAndroidHelperEx.IsAppInstalled(const APackage: string): Boolean;
  554. begin
  555. Result := False;
  556. try
  557. //只有异常是可靠的,返回值判定不对
  558. Result := Context.getPackageManager.getPackageInfo(StringToJString(APackage),
  559. TJPackageManager.JavaClass.GET_ACTIVITIES) = nil;
  560. Result := True;
  561. except
  562. end;
  563. end;
  564. class function TAndroidHelperEx.KeepScreen(AOn: Boolean): Boolean;
  565. begin
  566. CallInUIThreadAndWaitFinishing(
  567. procedure begin
  568. if AOn then
  569. SharedActivity.getWindow.addFlags(
  570. TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON)
  571. else
  572. SharedActivity.getWindow.clearFlags(
  573. TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON);
  574. end);
  575. end;
  576. class procedure TAndroidHelperEx.SendAppToBack;
  577. begin
  578. //SharedActivityManager.moveTaskToBack(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  579. Activity.moveTaskToBack(True);
  580. end;
  581. class function TAndroidHelperEx.StartActivity(Intent: JIntent; const Code: Integer): Boolean;
  582. begin
  583. Result := False;
  584. if Code = -1 then
  585. Activity.startActivity(Intent)
  586. else
  587. Activity.startActivityForResult(Intent, Code);
  588. Result := True;
  589. end;
  590. class function TAndroidHelperEx.UriParse(const S: JString): Jnet_Uri;
  591. begin
  592. Result := TJnet_Uri.JavaClass.parse(S);
  593. end;
  594. class function TAndroidHelperEx.UriParse(const S: string): Jnet_Uri;
  595. begin
  596. Result := UriParse(StringToJString(S));
  597. end;
  598. class function TAndroidHelperEx.UriFromFile(const AFileName: string): Jnet_Uri;
  599. begin
  600. Result := UriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)));
  601. end;
  602. class function TAndroidHelperEx.UriFromFile(const AFile: JFile): Jnet_Uri;
  603. begin
  604. Result := TJnet_uri.JavaClass.fromFile(AFile);
  605. end;
  606. class function TAndroidHelperEx.SharedUriFromFile(const AFile: JFile; const AAuthority: string): Jnet_Uri;
  607. var
  608. LAuthority: JString;
  609. begin
  610. if CheckBuildAndTarget(NOUGAT) then begin
  611. if AAuthority <> '' then
  612. LAuthority := StringToJString(AAuthority)
  613. else
  614. LAuthority := Context.getApplicationContext.getPackageName.concat(StringToJString('.fileprovider'));
  615. Result := TJFileProvider.JavaClass.getUriForFile(Context, LAuthority, AFile);
  616. end
  617. else
  618. Result := TJnet_uri.JavaClass.fromFile(AFile);
  619. end;
  620. class function TAndroidHelperEx.SharedUriFromFile(const AFileName, AAuthority: string): Jnet_Uri;
  621. begin
  622. Result := SharedUriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)), AAuthority);
  623. end;
  624. end.