{*******************************************************} { } { Helpers for Android } { } { Copyright (C) 2020 KngStr } { } { Some Code from } { Kastri Free of DelphiWorlds } { QDAC of swish } { Thanks } { } {*******************************************************} unit ksAndroid.Helpers; interface uses System.SysUtils, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.App, Androidapi.JNI.JavaTypes, Androidapi.JNI.Net; type TAndroidHelperEx = record private class function GetJActivity: JActivity; static; class function GetJContext: JContext; static; class function GetJActivityManager: JActivityManager; static; public const ICE_CREAM_SANDWICH = 14; ICE_CREAM_SANDWICH_MR1 = 15; JELLY_BEAN = 16; JELLY_BEAN_MR1 = 17; JELLY_BEAN_MR2 = 18; KITKAT = 19; KITKAT_MR1 = 20; LOLLIPOP = 21; LOLLIPOP_MR1 = 22; MARSHMALLOW = 23; NOUGAT = 24; NOUGAT_MR1 = 25; OREO = 26; OREO_MR1 = 27; PIE = 28; Q = 29; /// /// Checks if both build and target are greater or equal to the tested value /// class function CheckBuildAndTarget(const AValue: Integer): Boolean; static; /// /// Returns the equivalent of "AndroidClass.class" /// class function GetClass(const APackageClassName: string): Jlang_Class; static; /// /// Returns the application default icon ID /// class function GetDefaultIconID: Integer; static; /// /// Returns a URI to the notification sound /// class function GetDefaultNotificationSound: Jnet_Uri; static; /// /// Returns target Sdk version /// class function GetTargetSdkVersion: Integer; static; /// /// Returns installed Sdk version /// class function GetBuildSdkVersion: Integer; static; /// /// Returns whether the activity is running foreground /// /// /// Useful from within a service to determine whether or not the service needs to run in foreground mode /// class function IsActivityForeground: Boolean; static; /// /// Converts file to uri, using FileProvider if target API >= 24 /// /// /// Use this only when accessing files with an "external" URI /// class function UriFromFile(const AFile: JFile; const AAuthority: string = ''): Jnet_Uri; overload; static; /// /// Converts filename to uri, using FileProvider if target API >= 24 /// /// /// Use this only when accessing files with an "external" URI /// class function UriFromFile(const AFileName: string; const AAuthority: string = ''): Jnet_Uri; overload; static; /// Returns Java Application Context class property Context: JContext read GetJContext; /// Returns Java Application Activity /// An exception will be launched if there is no activity, for example a Service class property Activity: JActivity read GetJActivity; /// Returns Java Application Activity Manager class property ActivityManager: JActivityManager read GetJActivityManager; /// Need reorder tasks permission class procedure BringAppToFront; static; /// Need reorder tasks permission class procedure SendAppToBack; static; /// Call a Java Activity class function StartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static; /// Returns Application package name class function GetPackageName: string; static; /// Returns MimeType from filename class function GetMimeType(AFileName: string): JString; static; /// Checks if there is at least one application capable of receiving the intent. class function HasAssocApp(const URI: string): Boolean; overload; static; /// Checks if there is at least one application capable of receiving the intent. class function HasAssocApp(const Intent: JIntent): Boolean; overload; static; /// Install an android package: xxx.apk /// /// /// class function InstallPackage(const AFileName: string; const AAuthority: string = ''): Boolean; static; /// Check if an application is installed class function IsAppInstalled(const APackage: string): Boolean; static; /// Add/Cear FLAG_KEEP_SCREEN_ON class function KeepScreen(AOn: Boolean): Boolean; static; end; implementation uses Androidapi.JNIBridge, Androidapi.JNI.Os, Androidapi.JNI.Support, Androidapi.JNI.Media, Androidapi.JNI.Provider, Androidapi.JNI.Webkit, FMX.Helpers.Android, Androidapi.Helpers; { TAndroidHelperEx } class procedure TAndroidHelperEx.BringAppToFront; begin ActivityManager.moveTaskToFront(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); end; class function TAndroidHelperEx.CheckBuildAndTarget(const AValue: Integer): Boolean; begin Result := (GetBuildSdkVersion >= AValue) and (GetTargetSdkVersion >= AValue); end; class function TAndroidHelperEx.GetBuildSdkVersion: Integer; begin Result := TJBuild_VERSION.JavaClass.SDK_INT; end; class function TAndroidHelperEx.GetClass(const APackageClassName: string): Jlang_Class; begin Result := TJLang_Class.JavaClass.forName(StringToJString(APackageClassName), True, Context.getClassLoader); end; class function TAndroidHelperEx.GetDefaultIconID: Integer; begin Result := Context.getApplicationInfo.icon; end; class function TAndroidHelperEx.GetDefaultNotificationSound: Jnet_Uri; begin Result := TJRingtoneManager.JavaClass.getDefaultUri(TJRingtoneManager.JavaClass.TYPE_NOTIFICATION); end; class function TAndroidHelperEx.GetJActivity: JActivity; begin Result := {$IF CompilerVersion > 27} TAndroidHelper.Activity {$ELSE} SharedActivity {$ENDIF} end; class function TAndroidHelperEx.GetJActivityManager: JActivityManager; var AService: JObject; begin AService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE); Result := TJActivityManager.Wrap((AService as ILocalObject).GetObjectID); end; class function TAndroidHelperEx.GetJContext: JContext; begin Result := {$IF CompilerVersion > 27} TAndroidHelper.Context; {$ELSE} SharedActivityContext; {$ENDIF} end; class function TAndroidHelperEx.GetMimeType(AFileName: string): JString; var LExt: string; LTypeMap: JMimeTypeMap; begin Result := nil; if AFileName = '' then Exit; LExt := LowerCase(ExtractFileExt(AFileName)); if Length(LExt) < 2 then Exit; LExt := Copy(LExt, 1); LTypeMap := TJMimeTypeMap.JavaClass.getSingleton(); if LTypeMap = nil then Exit; Result := LTypeMap.getMimeTypeFromExtension(StringToJString(LExt)); end; class function TAndroidHelperEx.GetPackageName: string; begin Result := JStringToString(Context.getPackageName); end; class function TAndroidHelperEx.GetTargetSdkVersion: Integer; var LApplicationInfo: JApplicationInfo; begin LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0); Result := LApplicationInfo.targetSdkVersion; end; class function TAndroidHelperEx.HasAssocApp(const Intent: JIntent): Boolean; var LList: JList; begin // Android 6+ APP LINK closed Will cause list is null. // Activity with LList := Activity.getPackageManager.queryIntentActivities(Intent, TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY); Result := (LList <> nil) and (LList.size > 0); end; class function TAndroidHelperEx.HasAssocApp(const URI: string): Boolean; var Intent: JIntent; begin Result := False; Intent := TJIntent.Create; Intent.setData(TJnet_Uri.JavaClass.parse(StringToJString(URI))); Intent.setAction(StringToJString('android.intent.action.VIEW')); Result := HasAssocApp(Intent); end; class function TAndroidHelperEx.InstallPackage(const AFileName, AAuthority: string): Boolean; var LIntent: JIntent; begin Result := False; if Trim(AFileName) = '' then Exit; LIntent := TJIntent.Create; if CheckBuildAndTarget(OREO) then LIntent.setAction(TJIntent.JavaClass.ACTION_INSTALL_PACKAGE) else LIntent.setAction(TJIntent.JavaClass.ACTION_VIEW); // 没有这个也可以安装成功,但是安装成功后的成功页面,也就是,完成/打开 会无法显示 LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); LIntent.setDataAndType(UriFromFile(AFileName, AAuthority), StringToJString('application/vnd.android.package-archive')); LIntent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION); Result := StartActivity(LIntent); end; class function TAndroidHelperEx.IsActivityForeground: Boolean; var LService: JObject; LRunningApps: JList; LAppInfo: JActivityManager_RunningAppProcessInfo; I: Integer; begin Result := False; LService := Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE); LRunningApps := TJActivityManager.Wrap(TAndroidHelper.JObjectToID(LService)).getRunningAppProcesses; for I := 0 to LRunningApps.size - 1 do begin LAppInfo := TJActivityManager_RunningAppProcessInfo.Wrap(TAndroidHelper.JObjectToID(LRunningApps.get(I))); if LAppInfo.importance = 100 then begin if LAppInfo.importanceReasonComponent <> nil then begin if LAppInfo.importanceReasonComponent.getPackageName.equals(Context.getPackageName) then Exit(True); end else if LRunningApps.size = 1 then Exit(True); end; end; end; class function TAndroidHelperEx.IsAppInstalled(const APackage: string): Boolean; begin Result := False; try //只有异常是可靠的,返回值判定不对 Result := Context.getPackageManager.getPackageInfo(StringToJString(APackage), TJPackageManager.JavaClass.GET_ACTIVITIES) = nil; Result := True; except end; end; class function TAndroidHelperEx.KeepScreen(AOn: Boolean): Boolean; begin CallInUIThreadAndWaitFinishing( procedure begin if AOn then SharedActivity.getWindow.addFlags( TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON) else SharedActivity.getWindow.clearFlags( TJWindowManager_LayoutParams.JavaClass.FLAG_KEEP_SCREEN_ON); end); end; class procedure TAndroidHelperEx.SendAppToBack; begin //SharedActivityManager.moveTaskToBack(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); Activity.moveTaskToBack(True); end; class function TAndroidHelperEx.StartActivity(Intent: JIntent; const Code: Integer): Boolean; begin Result := False; if HasAssocApp(Intent) then begin if Code = -1 then Activity.startActivity(Intent) else Activity.startActivityForResult(Intent, Code); Result := True; end; end; class function TAndroidHelperEx.UriFromFile(const AFile: JFile; const AAuthority: string): Jnet_Uri; var LAuthority: JString; begin if CheckBuildAndTarget(NOUGAT) then begin if AAuthority <> '' then LAuthority := StringToJString(AAuthority) else LAuthority := Context.getApplicationContext.getPackageName.concat(StringToJString('.fileprovider')); Result := TJFileProvider.JavaClass.getUriForFile(Context, LAuthority, AFile); end else Result := TJnet_uri.JavaClass.fromFile(AFile); end; class function TAndroidHelperEx.UriFromFile(const AFileName, AAuthority: string): Jnet_Uri; begin Result := UriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)), AAuthority); end; end.