{*******************************************************}
{                                                       }
{       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.JNIBridge,
  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;
    /// <summary>
    ///   Checks if both build and target are greater or equal to the tested value
    /// </summary>
    class function CheckBuildAndTarget(const AValue: Integer): Boolean; static;
    /// <summary>
    ///   Returns the equivalent of "AndroidClass.class"
    /// </summary>
    class function GetClass(const APackageClassName: string): Jlang_Class; static;
    /// <summary>
    ///   Returns the application default icon ID
    /// </summary>
    class function GetDefaultIconID: Integer; static;
    /// <summary>
    ///   Returns a URI to the notification sound
    /// </summary>
    class function GetDefaultNotificationSound: Jnet_Uri; static;
    /// <summary>
    ///   Returns target Sdk version
    /// </summary>
    class function GetTargetSdkVersion: Integer; static;
    /// <summary>
    ///   Returns installed Sdk version
    /// </summary>
    class function GetBuildSdkVersion: Integer; static;
    /// <summary>
    ///   Returns whether the activity is running foreground
    /// </summary>
    /// <remarks>
    ///   Useful from within a service to determine whether or not the service needs to run in foreground mode
    /// </remarks>
    class function IsActivityForeground: Boolean; static;

    /// <summary>
    ///   Get column data from uri
    /// </summary>
    class function GetColumnAsString(const AUri: Jnet_Uri; const AColumn: JString; ASelection: JString = nil;
      ASelectionArgs: TJavaObjectArray<JString> = nil; ASortOrder: JString = nil): string; static;
    /// <summary>
    ///   Get data column data from uri
    /// </summary>
    class function GetDataColumnAsString(const AUri: Jnet_Uri; ASelection: JString = nil;
      ASelectionArgs: TJavaObjectArray<JString> = nil; ASortOrder: JString = nil): string; static;

    /// <summary>
    ///   Converts uri to file path
    /// </summary>
    class function FileFromUri(const AUri: Jnet_Uri): string; overload; static;

    /// <summary>
    ///   Converts file to uri, without FileProvider
    /// </summary>
    class function UriFromFile(const AFile: JFile): Jnet_Uri; overload; static;
    /// <summary>
    ///   Converts file to uri, without FileProvider
    /// </summary>
    class function UriFromFile(const AFileName: string): Jnet_Uri; overload; static;
    /// <summary>
    ///   Converts file to uri, using FileProvider if target API >= 24
    /// </summary>
    /// <remarks>
    ///   Use this only when accessing files with an "external" URI
    /// </remarks>
    class function SharedUriFromFile(const AFile: JFile; const AAuthority: string = ''): Jnet_Uri; overload; static;
    /// <summary>
    ///   Converts filename to uri, using FileProvider if target API >= 24
    /// </summary>
    /// <remarks>
    ///   Use this only when accessing files with an "external" URI
    /// </remarks>
    class function SharedUriFromFile(const AFileName: string; const AAuthority: string = ''): Jnet_Uri; overload; static;

    /// <summary>
    ///   TJnet_Uri.JavaClass.parse
    /// </summary>
    class function UriParse(const S: string): Jnet_Uri; overload; static;
    /// <summary>
    ///   TJnet_Uri.JavaClass.parse
    /// </summary>
    class function UriParse(const S: JString): Jnet_Uri; overload; static;

    /// <summary>Returns Java Application Context</summary>
    class property Context: JContext read GetJContext;
    /// <summary>Returns Java Application Activity</summary>
    /// <remarks>An exception will be launched if there is no activity, for example a Service</remarks>
    class property Activity: JActivity read GetJActivity;
    /// <summary>Returns Java Application Activity Manager</summary>
    class property ActivityManager: JActivityManager read GetJActivityManager;

    /// <remarks>Need reorder tasks permission</remarks>
    class procedure BringAppToFront; static;
    /// <remarks>Need reorder tasks permission</remarks>
    class procedure SendAppToBack; static;
    /// <summary>Call a Java Activity</summary>
    class function StartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static;
    /// <summary>Check HasAssocApp and Call a Java Activity</summary>
    class function CheckAndStartActivity(Intent: JIntent; const Code: Integer = -1): Boolean; static;

    /// <summary>Returns Application package name</summary>
    class function GetPackageName: string; static;
    /// <summary>Returns Application package label</summary>
    class function GetPackageLabel: string; static;
    /// <summary>Returns MimeType from filename</summary>
    class function GetMimeType(AFileName: string): JString; static;
    /// <summary>Returns Primary SDCard path</summary>
    class function GetSDCardPath: string; static;

    /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
    class function HasAssocApp(const URI: string): Boolean; overload; static;
    /// <summary>Checks if there is at least one application capable of receiving the intent.</summary>
    class function HasAssocApp(const Intent: JIntent): Boolean; overload; static;

    /// <summary>Install an android package: xxx.apk</summary>
    /// <remarks>
    /// <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    /// </remarks>
    class function InstallPackage(const AFileName: string; const AAuthority: string = ''): Boolean; static;
    /// <summary>Check if an application is installed</summary>
    class function IsAppInstalled(const APackage: string): Boolean; static;

    /// <summary>Add/Cear FLAG_KEEP_SCREEN_ON</summary>
    class function KeepScreen(AOn: Boolean): Boolean; static;
  end;

implementation

uses
  {$IFDEF DEBUG}FMX.Types,{$ENDIF}
  Androidapi.JNI.Os, Androidapi.JNI.Support,
  Androidapi.JNI.Media, Androidapi.JNI.Provider, Androidapi.JNI.Webkit,
  Androidapi.Helpers, FMX.Helpers.Android, System.IOUtils;

{ TAndroidHelperEx }

class procedure TAndroidHelperEx.BringAppToFront;
begin
  ActivityManager.moveTaskToFront(Activity.getTaskId, TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
end;

class function TAndroidHelperEx.CheckAndStartActivity(Intent: JIntent;
  const Code: Integer): Boolean;
begin
  Result := (Intent <> nil) and HasAssocApp(Intent);
  if not Result then
    Exit;
  Result := TAndroidHelperEx.StartActivity(Intent, Code);
end;

class function TAndroidHelperEx.CheckBuildAndTarget(const AValue: Integer): Boolean;
begin
  Result := (GetBuildSdkVersion >= AValue) and (GetTargetSdkVersion >= AValue);
end;

(*
 * >=4.4
 * uri=content://com.android.providers.media.documents/document/image%3A293502
 * uri=file:///storage/emulated/0/temp_photo.jpg
 * uri=content://media/external/images/media/193968
 * <4.4
 * uri=content://media/external/images/media/13
 * third party
 * content://com.speedsoftware.explorer.fileprovider/root/storage/emulated/0/Android/data/com.lifan.qspsy/files/cache/thumb/F6AB021A6BBCEFC3B942625FBA2E6ADE/7.jpg
 * content://com.tencent.mtt.fileprovider/QQBrowser/Movies/BVR_2019_10_14_10_52_29_trimq.mp4
 * 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
 *
 * �����
 * https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java
 * https://github.com/DB-BOY/FileChoose/blob/master/app/src/main/java/cn/dbboy/filechoose/FileUtil.java
 * https://www.jianshu.com/p/c5f207f8cce6
 * https://blog.csdn.net/chengfu116/article/details/74923161
 * https://www.cnblogs.com/epmouse/p/5421048.html
 *
 * ��δʵ��
 * ��׿10������ȫû�л�ȡ��ʵ·���Ļ���
 * ��׿7/8/9ò����Щ���ͻ��Dz��У�ֻ���ǻ�ȡ���ݺ����Լ��ɶ�д�������һ�ݶ�
 * ����ǻ�ȡ���ݶ�����path�Ļ����Ͳ���Ҫ�������ˣ��������api����
 *)
class function TAndroidHelperEx.FileFromUri(const AUri: Jnet_Uri): string;

  function GetUriByType(S: string): Jnet_Uri;
  begin
    if SameText(S, 'image') then
      Result := TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI
    else if SameText(S, 'video') then
      Result := TJVideo_Media.JavaClass.EXTERNAL_CONTENT_URI
    else if SameText(S, 'audio') then
      Result := TJAudio_Media.JavaClass.EXTERNAL_CONTENT_URI
    else
      Result := nil;
  end;

  function GetDocId(var ADocId, AType, AId: string): Boolean;
  var
    LJString: JString;
    LArr: TArray<string>;
  begin
    Result := False;
    LJString := TJDocumentsContract.JavaClass.getDocumentId(AUri);
    if LJString = nil then
      Exit;
    ADocId := JStringToString(LJString);
    if ADocId = '' then
      Exit;
    LArr := ADocId.Split([':']);
    if Length(LArr) < 2 then begin
      AType := '';
      AId := ADocId;
    end
    else begin
      AType := LArr[0];
      AId := LArr[1];
    end;
    Result := AId <> '';
  end;

var
  LSdCard, LPath: string;
  LAuthority, LScheme, LDocId, LType, LId: string;
  LSelection: JString;
  LUri: Jnet_Uri;
  LArr: TArray<string>;
  I: Integer;
  iId: Int64;
begin
  Result := '';
  if AUri = nil then
    Exit;

  LSdCard := GetSDCardPath;
  LAuthority := JStringToString(AUri.getAuthority().toString);
  LScheme := JStringToString(AUri.getScheme());
  {$IFDEF DEBUG}
  Log.d('---TAndroidHelperEx.FileFromUri-AUri:%s', [JStringToString(AUri.toString)]);
  Log.d('---TAndroidHelperEx.FileFromUri-AUri.Authority:%s-LScheme:%s', [LAuthority, LScheme]);
  {$ENDIF}
  if CheckBuildAndTarget(KITKAT) and TJDocumentsContract.JavaClass.isDocumentUri(Context, AUri) then begin
    if not GetDocId(LDocId, LType, LId) then
      Exit;
    {$IFDEF DEBUG}
    Log.d('---TAndroidHelperEx.FileFromUri-LDocId:%s-LType:%s-LId:%s', [LDocId, LType, LId]);
    {$ENDIF}
    if SameText(LAuthority, 'com.android.externalstorage.documents') then begin
      if SameText(LType, 'primary') then
        Result := TPath.Combine(LSdCard, LId)
      else if SameText(LType, 'home') then
        Result := TPath.Combine(TPath.GetSharedDocumentsPath, LId)
      {$IFDEF DEBUG}
      else
        Log.d('---TAndroidHelperEx.FileFromUri-Unkown externalstorage-LDocId:%s-LType:%s-LId:%s', [LDocId, LType, LId]);
      {$ENDIF}
    end
    else if SameText(LAuthority, 'com.android.providers.media.documents') then begin
      LSelection := StringToJString('_id=' + LId);
      LUri := GetUriByType(LType);
      if LUri <> nil then
        Result := GetDataColumnAsString(LUri, LSelection);
    end else if SameText(LAuthority, 'com.android.providers.downloads.documents') then begin
      if SameText(LType, 'raw') then
        Result := Copy(LDocId, 5)
      //else if GetBuildSdkVersion < OREO then begin // ����˵O��ʱ����Ҫ�Լ������������Դ����ֻ�7.1������
      else begin
        SetLength(LArr, 2);
        LArr[0] := 'content://downloads/public_downloads';
        LArr[1] := 'content://downloads/my_downloads';
        //LArr[2] := 'content://downloads/all_downloads'; // ���ò��ûȨ��

        iId := StrToInt64Def(LId, -1);
        for I := 0 to Length(LArr) - 1 do begin
          LUri := TJContentUris.JavaClass.withAppendedId(UriParse(LArr[I]), iId);
          if LUri <> nil then
            try
              Result := GetDataColumnAsString(LUri);
              if Result <> '' then
                Break;
            except
              on E: Exception do begin
                {$IFDEF DEBUG}
                Log.d('---TAndroidHelperEx.FileFromUri Error: [%s]%s', [E.ClassName, E.Message]);
                {$ENDIF}
              end;
            end;
        end;
      end;
      //else
      if Result = '' then
        Result := GetDataColumnAsString(AUri);
    end
    {$IFDEF DEBUG}
    else
      Log.d('---TAndroidHelperEx.FileFromUri-Unkown DocumentUri-');
    {$ENDIF}
  end else if SameText(LScheme, 'content') then begin
    // Return the remote address
    if SameText(LAuthority, 'com.google.android.apps.photos.content') then
      Result := JStringToString(AUri.getLastPathSegment())
    else if SameText(LAuthority, 'com.tencent.mtt.fileprovider') then begin
      LPath := JStringToString(AUri.getPath());
      if Pos('/QQBrowser/', LPath) = 1 then begin  // /QQBrowser/XXX
        LPath := TPath.Combine(LSdCard, Copy(LPath, 12));
        if FileExists(LPath) then
          Result := LPath;
      end;
    end
    else if SameText(LAuthority, 'com.speedsoftware.explorer.fileprovider') then begin
      LPath := JStringToString(AUri.getPath());
      if Pos('/root/', LPath) = 1 then
        Result := Copy(LPath, 6);
    end
    else if SameText(LAuthority, 'com.estrongs.files') then
      Result := JStringToString(AUri.getPath())
    else
      Result := GetDataColumnAsString(AUri);
  end
  else if SameText(LScheme, 'file') then
    Result := JStringToString(AUri.getPath())
  {$IFDEF DEBUG}
  else
    Log.d('---TAndroidHelperEx.FileFromUri-Unkown Uri-');
  {$ENDIF}
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.GetColumnAsString(const AUri: Jnet_Uri; const AColumn: JString;
  ASelection: JString; ASelectionArgs: TJavaObjectArray<JString>; ASortOrder: JString): string;
var
  LCursor: JCursor;
  LIndex: Integer;
  LPojection: TJavaObjectArray<JString>;
begin
  Result := '';
  if AUri = nil then
    Exit;

  LPojection := TJavaObjectArray<JString>.Create(1);
  LPojection.Items[0] := AColumn;
  LCursor := Context.getContentResolver().query(AUri, LPojection, ASelection, ASelectionArgs, ASortOrder);
  if LCursor = nil then begin
    {$IFDEF DEBUG}
    Log.d('---TAndroidHelperEx.GetColumnAsString-LCursor = nil-');
    {$ENDIF}
    Exit;
  end;
  try
    LIndex := LCursor.getColumnIndex(AColumn);
    if (LIndex > -1) and LCursor.moveToFirst then
      Result := JStringToString(LCursor.getString(LIndex))
    {$IFDEF DEBUG}
    else
      Log.d('---TAndroidHelperEx.GetColumnAsString-LIndex:%d-', [LIndex]);
    {$ENDIF}
  finally
    LCursor.close;
  end;
end;

class function TAndroidHelperEx.GetDataColumnAsString(const AUri: Jnet_Uri;
  ASelection: JString; ASelectionArgs: TJavaObjectArray<JString>;
  ASortOrder: JString): string;
begin
  Result := GetColumnAsString(AUri, StringToJString('_data'), ASelection, ASelectionArgs, ASortOrder);
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));
  if Result <> nil then
    Result := Result.trim();
end;

class function TAndroidHelperEx.GetPackageLabel: string;
var
  LApplicationInfo: JApplicationInfo;
  LLabel: JCharSequence;
begin
  Result := '';
  try
    LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0);
    if LApplicationInfo <> nil then begin
      LLabel := Context.getPackageManager.getApplicationLabel(LApplicationInfo);
      if LLabel <> nil then
        Result := JCharSequenceToStr(LLabel);
    end;
  except
  end;
end;

class function TAndroidHelperEx.GetPackageName: string;
begin
  Result := JStringToString(Context.getPackageName);
end;

class function TAndroidHelperEx.GetSDCardPath: string;
var
  sPath: string;
begin
  Result := '';
  sPath := System.IOUtils.TPath.GetSharedDocumentsPath;
  if Pos(PathDelim, sPath) = 0 then
    Exit;
  Result := ExtractFilePath(ExcludeTrailingPathDelimiter(sPath));
end;

class function TAndroidHelperEx.GetTargetSdkVersion: Integer;
var
  LApplicationInfo: JApplicationInfo;
begin
  try
    LApplicationInfo := Context.getPackageManager.getApplicationInfo(Context.getPackageName, 0);
    if LApplicationInfo <> nil then
      Result := LApplicationInfo.targetSdkVersion
    else
      Result := -1;
  except
    Result := -1;
  end;
end;

class function TAndroidHelperEx.HasAssocApp(const Intent: JIntent): Boolean;
var
  LList: JList;
begin
  Result := False;
  if Intent = nil then
    Exit;

  Result := Intent.resolveActivityInfo(Activity.getPackageManager(),
    TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY) <> nil;

  // Android 6+ APP LINK closed Will cause list is null.
  // Activity with <action android:name="android.intent.action.VIEW" />
  // ���������������õ�ʱ�򣬱���Ӧ�����ش�ʦ���ᵼ������size����ȷ
//  LList := Activity.getPackageManager.queryIntentActivities(Intent,
//    TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY);
//  Result := (LList = nil) or (LList.size > 0);
end;

class function TAndroidHelperEx.HasAssocApp(const URI: string): Boolean;
var
  Intent: JIntent;
begin
  Result := False;
  if URI = '' then
    Exit;

  Intent := TJIntent.Create;
  Intent.setData(UriParse(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);
  // û�����Ҳ���԰�װ�ɹ������ǰ�װ�ɹ���ijɹ�ҳ�棬Ҳ���ǣ����/�� ���޷���ʾ
  LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  LIntent.setDataAndType(SharedUriFromFile(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 Code = -1 then
    Activity.startActivity(Intent)
  else
    Activity.startActivityForResult(Intent, Code);
  Result := True;
end;

class function TAndroidHelperEx.UriParse(const S: JString): Jnet_Uri;
begin
  Result := TJnet_Uri.JavaClass.parse(S);
end;

class function TAndroidHelperEx.UriParse(const S: string): Jnet_Uri;
begin
  Result := UriParse(StringToJString(S));
end;

class function TAndroidHelperEx.UriFromFile(const AFileName: string): Jnet_Uri;
begin
  Result := UriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)));
end;

class function TAndroidHelperEx.UriFromFile(const AFile: JFile): Jnet_Uri;
begin
  Result := TJnet_uri.JavaClass.fromFile(AFile);
end;

class function TAndroidHelperEx.SharedUriFromFile(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.SharedUriFromFile(const AFileName, AAuthority: string): Jnet_Uri;
begin
  Result := SharedUriFromFile(TJFile.JavaClass.init(StringToJString(AFileName)), AAuthority);
end;

end.