ksAndroid.Helpers.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. {*******************************************************}
  2. { }
  3. { Helpers for Android }
  4. { }
  5. { Copyright (C) 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,
  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. /// Converts file to uri, without FileProvider
  76. /// </summary>
  77. class function UriFromFile(const AFile: JFile): Jnet_Uri; overload; static;
  78. /// <summary>
  79. /// Converts file to uri, without FileProvider
  80. /// </summary>
  81. class function UriFromFile(const AFileName: string): Jnet_Uri; overload; static;
  82. /// <summary>
  83. /// Converts file to uri, using FileProvider if target API >= 24
  84. /// </summary>
  85. /// <remarks>
  86. /// Use this only when accessing files with an "external" URI
  87. /// </remarks>
  88. class function SharedUriFromFile(const AFile: JFile; const AAuthority: string = ''): Jnet_Uri; overload; static;
  89. /// <summary>
  90. /// Converts filename to uri, using FileProvider if target API >= 24
  91. /// </summary>
  92. /// <remarks>
  93. /// Use this only when accessing files with an "external" URI
  94. /// </remarks>
  95. class function SharedUriFromFile(const AFileName: string; const AAuthority: string = ''): Jnet_Uri; overload; static;
  96. /// <summary>Returns Java Application Context</summary>
  97. class property Context: JContext read GetJContext;
  98. /// <summary>Returns Java Application Activity</summary>
  99. /// <remarks>An exception will be launched if there is no activity, for example a Service</remarks>
  100. class property Activity: JActivity read GetJActivity;
  101. /// <summary>Returns Java Application Activity Manager</summary>
  102. class property ActivityManager: JActivityManager read GetJActivityManager;
  103. /// <remarks>Need reorder tasks permission</remarks>
  104. class procedure BringAppToFront; static;
  105. /// <remarks>Need reorder tasks permission</remarks>
  106. class procedure SendAppToBack; static;
  107. /// <summary>Call a Java Activity</summary>
  108. class function StartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static;
  109. /// <summary>Returns Application package name</summary>
  110. class function GetPackageName: string; static;
  111. /// <summary>Returns MimeType from filename</summary>
  112. class function GetMimeType(AFileName: string): JString; static;
  113. /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
  114. class function HasAssocApp(const URI: string): Boolean; overload; static;
  115. /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
  116. class function HasAssocApp(const Intent: JIntent): Boolean; overload; static;
  117. /// <summary>Install an android package: xxx.apk</summary>
  118. /// <remarks>
  119. /// <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
  120. /// </remarks>
  121. class function InstallPackage(const AFileName: string; const AAuthority: string = ''): Boolean; static;
  122. /// <summary>Check if an application is installed</summary>
  123. class function IsAppInstalled(const APackage: string): Boolean; static;
  124. /// <summary>Add/Cear FLAG_KEEP_SCREEN_ON</summary>
  125. class function KeepScreen(AOn: Boolean): Boolean; static;
  126. end;
  127. implementation
  128. uses
  129. Androidapi.JNIBridge, Androidapi.JNI.Os, Androidapi.JNI.Support,
  130. Androidapi.JNI.Media, Androidapi.JNI.Provider, Androidapi.JNI.Webkit,
  131. FMX.Helpers.Android, Androidapi.Helpers;
  132. { TAndroidHelperEx }
  133. class procedure TAndroidHelperEx.BringAppToFront;
  134. begin
  135. ActivityManager.moveTaskToFront(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  136. end;
  137. class function TAndroidHelperEx.CheckBuildAndTarget(const AValue: Integer): Boolean;
  138. begin
  139. Result := (GetBuildSdkVersion >= AValue) and (GetTargetSdkVersion >= AValue);
  140. end;
  141. class function TAndroidHelperEx.GetBuildSdkVersion: Integer;
  142. begin
  143. Result := TJBuild_VERSION.JavaClass.SDK_INT;
  144. end;
  145. class function TAndroidHelperEx.GetClass(const APackageClassName: string): Jlang_Class;
  146. begin
  147. Result := TJLang_Class.JavaClass.forName(StringToJString(APackageClassName), True, Context.getClassLoader);
  148. end;
  149. class function TAndroidHelperEx.GetDefaultIconID: Integer;
  150. begin
  151. Result := Context.getApplicationInfo.icon;
  152. end;
  153. class function TAndroidHelperEx.GetDefaultNotificationSound: Jnet_Uri;
  154. begin
  155. Result := TJRingtoneManager.JavaClass.getDefaultUri(TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
  156. end;
  157. class function TAndroidHelperEx.GetJActivity: JActivity;
  158. begin
  159. Result :=
  160. {$IF CompilerVersion > 27}
  161. TAndroidHelper.Activity
  162. {$ELSE}
  163. SharedActivity
  164. {$ENDIF}
  165. end;
  166. class function TAndroidHelperEx.GetJActivityManager: JActivityManager;
  167. var
  168. AService: JObject;
  169. begin
  170. AService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
  171. Result := TJActivityManager.Wrap((AService as ILocalObject).GetObjectID);
  172. end;
  173. class function TAndroidHelperEx.GetJContext: JContext;
  174. begin
  175. Result :=
  176. {$IF CompilerVersion > 27}
  177. TAndroidHelper.Context;
  178. {$ELSE}
  179. SharedActivityContext;
  180. {$ENDIF}
  181. end;
  182. class function TAndroidHelperEx.GetMimeType(AFileName: string): JString;
  183. var
  184. LExt: string;
  185. LTypeMap: JMimeTypeMap;
  186. begin
  187. Result := nil;
  188. if AFileName = '' then
  189. Exit;
  190. LExt := LowerCase(ExtractFileExt(AFileName));
  191. if Length(LExt) < 2 then
  192. Exit;
  193. LExt := Copy(LExt, 1);
  194. LTypeMap := TJMimeTypeMap.JavaClass.getSingleton();
  195. if LTypeMap = nil then
  196. Exit;
  197. Result := LTypeMap.getMimeTypeFromExtension(StringToJString(LExt));
  198. if Result <> nil then
  199. Result := Result.trim();
  200. end;
  201. class function TAndroidHelperEx.GetPackageName: string;
  202. begin
  203. Result := JStringToString(Context.getPackageName);
  204. end;
  205. class function TAndroidHelperEx.GetTargetSdkVersion: Integer;
  206. var
  207. LApplicationInfo: JApplicationInfo;
  208. begin
  209. LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0);
  210. Result := LApplicationInfo.targetSdkVersion;
  211. end;
  212. class function TAndroidHelperEx.HasAssocApp(const Intent: JIntent): Boolean;
  213. var
  214. LList: JList;
  215. begin
  216. // Android 6+ APP LINK closed Will cause list is null.
  217. // Activity with <action android:name="android.intent.action.VIEW" />
  218. LList := Activity.getPackageManager.queryIntentActivities(Intent,
  219. TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY);
  220. Result := (LList <> nil) and (LList.size > 0);
  221. end;
  222. class function TAndroidHelperEx.HasAssocApp(const URI: string): Boolean;
  223. var
  224. Intent: JIntent;
  225. begin
  226. Result := False;
  227. Intent := TJIntent.Create;
  228. Intent.setData(TJnet_Uri.JavaClass.parse(StringToJString(URI)));
  229. Intent.setAction(StringToJString('android.intent.action.VIEW'));
  230. Result := HasAssocApp(Intent);
  231. end;
  232. class function TAndroidHelperEx.InstallPackage(const AFileName, AAuthority: string): Boolean;
  233. var
  234. LIntent: JIntent;
  235. begin
  236. Result := False;
  237. if Trim(AFileName) = '' then
  238. Exit;
  239. LIntent := TJIntent.Create;
  240. if CheckBuildAndTarget(OREO) then
  241. LIntent.setAction(TJIntent.JavaClass.ACTION_INSTALL_PACKAGE)
  242. else
  243. LIntent.setAction(TJIntent.JavaClass.ACTION_VIEW);
  244. // 没有这个也可以安装成功,但是安装成功后的成功页面,也就是,完成/打开 会无法显示
  245. LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  246. LIntent.setDataAndType(SharedUriFromFile(AFileName, AAuthority),
  247. StringToJString('application/vnd.android.package-archive'));
  248. LIntent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
  249. Result := StartActivity(LIntent);
  250. end;
  251. class function TAndroidHelperEx.IsActivityForeground: Boolean;
  252. var
  253. LService: JObject;
  254. LRunningApps: JList;
  255. LAppInfo: JActivityManager_RunningAppProcessInfo;
  256. I: Integer;
  257. begin
  258. Result := False;
  259. LService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
  260. LRunningApps := TJActivityManager.Wrap(TAndroidHelper.JObjectToID(LService)).getRunningAppProcesses;
  261. for I := 0 to LRunningApps.size - 1 do begin
  262. LAppInfo := TJActivityManager_RunningAppProcessInfo.Wrap(TAndroidHelper.JObjectToID(LRunningApps.get(I)));
  263. if LAppInfo.importance = 100 then begin
  264. if LAppInfo.importanceReasonComponent <> nil then begin
  265. if LAppInfo.importanceReasonComponent.getPackageName.equals(Context.getPackageName) then
  266. Exit(True);
  267. end
  268. else if LRunningApps.size = 1 then
  269. Exit(True);
  270. end;
  271. end;
  272. end;
  273. class function TAndroidHelperEx.IsAppInstalled(const APackage: string): Boolean;
  274. begin
  275. Result := False;
  276. try
  277. //只有异常是可靠的,返回值判定不对
  278. Result := Context.getPackageManager.getPackageInfo(StringToJString(APackage),
  279. TJPackageManager.JavaClass.GET_ACTIVITIES) = nil;
  280. Result := True;
  281. except
  282. end;
  283. end;
  284. class function TAndroidHelperEx.KeepScreen(AOn: Boolean): Boolean;
  285. begin
  286. CallInUIThreadAndWaitFinishing(
  287. procedure begin
  288. if AOn then
  289. SharedActivity.getWindow.addFlags(
  290. TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON)
  291. else
  292. SharedActivity.getWindow.clearFlags(
  293. TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON);
  294. end);
  295. end;
  296. class procedure TAndroidHelperEx.SendAppToBack;
  297. begin
  298. //SharedActivityManager.moveTaskToBack(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  299. Activity.moveTaskToBack(True);
  300. end;
  301. class function TAndroidHelperEx.StartActivity(Intent: JIntent; const Code: Integer): Boolean;
  302. begin
  303. Result := False;
  304. if HasAssocApp(Intent) then begin
  305. if Code = -1 then
  306. Activity.startActivity(Intent)
  307. else
  308. Activity.startActivityForResult(Intent, Code);
  309. Result := True;
  310. end;
  311. end;
  312. class function TAndroidHelperEx.UriFromFile(const AFileName: string): Jnet_Uri;
  313. begin
  314. Result := UriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)));
  315. end;
  316. class function TAndroidHelperEx.UriFromFile(const AFile: JFile): Jnet_Uri;
  317. begin
  318. Result := TJnet_uri.JavaClass.fromFile(AFile);
  319. end;
  320. class function TAndroidHelperEx.SharedUriFromFile(const AFile: JFile; const AAuthority: string): Jnet_Uri;
  321. var
  322. LAuthority: JString;
  323. begin
  324. if CheckBuildAndTarget(NOUGAT) then begin
  325. if AAuthority <> '' then
  326. LAuthority := StringToJString(AAuthority)
  327. else
  328. LAuthority := Context.getApplicationContext.getPackageName.concat(StringToJString('.fileprovider'));
  329. Result := TJFileProvider.JavaClass.getUriForFile(Context, LAuthority, AFile);
  330. end
  331. else
  332. Result := TJnet_uri.JavaClass.fromFile(AFile);
  333. end;
  334. class function TAndroidHelperEx.SharedUriFromFile(const AFileName, AAuthority: string): Jnet_Uri;
  335. begin
  336. Result := SharedUriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)), AAuthority);
  337. end;
  338. end.