Jump to content

Dave Nottage

Members
  • Content Count

    1613
  • Joined

  • Last visited

  • Days Won

    37

Posts posted by Dave Nottage


  1. This looks like two questions. The first part is not currently possible using Delphi code, since there's no option to use AccessibilityService (there is for a plain Service and JobIntentService)

    In order to use UssdResponseCallback you need to write Java code, since you need to create a descendant of it (this is otherwise not currently possible in Delphi), and override its methods. You could define a Java interface that the descendant takes as a parameter in its constructor, and uses to redirect the overridden methods, much like I have in the code here: https://github.com/DelphiWorlds/Kastri/tree/master/Java/Base/Connectivity
     

    This Java code would need to be compiled into a jar which the Delphi app can consume. You would need to import the Java code into Delphi code. Using the same example above, this is the corresponding Delphi import: https://github.com/DelphiWorlds/Kastri/blob/master/API/DW.Androidapi.JNI.DWNetworkCallback.pas

    The next step is to construct a class that implements the interface defined earlier. Following the same example, that is TNetworkCallbackDelegate in this unit: https://github.com/DelphiWorlds/Kastri/blob/master/Features/Connectivity/DW.Connectivity.Android.pas

    You would then create an instance of the "delegate", and pass a reference to that when creating an instance of the descendant. Again following the above example, it would be similar to the code here: https://github.com/DelphiWorlds/Kastri/blob/82da3db3d0a526f6e93a30f3eb1a6c14779399bb/Features/Connectivity/DW.Connectivity.Android.pas#L98

     

    • Like 1

  2. 2 hours ago, CHackbart said:

    I suppose something like keyResponse := TAVContentKeyResponse.Wrap(crcData) is not correct, right?

    It's not correct. It needs to be:

    keyResponse := TAVContentKeyResponse.Wrap(TAVContentKeyResponse.OCClass.contentKeyResponseWithFairPlayStreamingKeyResponseData(ckcData));

     

    2 hours ago, CHackbart said:

    And how do I fill a NSDictionary like options: [AVContentKeyRequestProtocolVersionsKey: [1]], 

    If it's an NSDictionary with only one object, this is an example using a typical pattern for that scenario:

    dict := TNSDictionary.Wrap(TNSDictionary.OCClass.dictionaryWithObject(TNSNumber.OCClass.numberWithInt(1), NSObjectToID(AVContentKeyRequestProtocolVersionsKey));

    When there's more than one value to add, one way is to create an instance of NSMutableDictionary, and use the setValue method. There's a couple of examples in FMX.AddressBook.iOS

    2 hours ago, CHackbart said:

    assetIDData = assetIDString.data(using: .utf8)

     

    assetIDData := assetIDString.dataUsingEncoding(NSUTF8StringEncoding);

     


  3. 1 hour ago, CHackbart said:

    FContentKeySession.addContentKeyRecipient(FAsset)

    Should be:

    FContentKeySession.addContentKeyRecipient(NSObjectToID(FAsset))

    You shouldn't need to use a TTask in the code there, either. I had issues compiling your test project, so I started a new one, and just added the form from the original project to it.


  4. 5 hours ago, Rollo62 said:

    Stupid question, is it possible that Win and Macos have different format setting ?

    Well, they're obtained differently, since they're different operating systems. The problem here is either in the call to CFLocaleCopyCurrent or CFLocalGetValue, which is what Delphi currently uses to obtain the decimal separator. The decimalSeparator method of NSLocale gives the correct result.

    • Like 1

  5. 56 minutes ago, Kas Ob. said:

    Is it possible to clear app data and cache to force resetting user permissions setting ?

    As far as I can work out, it's not possible to revoke permissions from within an application, however it is possible external to the device, using adb:

     

    https://stackoverflow.com/a/32683390/3164070

     

    I'm not sure what the use case would be for being able to do it from within the app other than for testing purposes, and that's covered by using adb as per the link above.


  6. I've improved the experience somewhat, by opening the App Info screen instead using this code:

    var
      LIntent: JIntent;
      LUri: Jnet_Uri;
    begin
      LUri := TJnet_Uri.JavaClass.fromParts(StringToJString('package'), TAndroidHelper.Context.getPackageName, nil);
      LIntent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_APPLICATION_DETAILS_SETTINGS, LUri);
      LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
      TAndroidHelper.Context.startActivity(LIntent);
    end;

    Then the user only has to:

    • Tap "Permissions"
    • Tap "Location"
    • Select the appropriate option (e.g. "Allow All The Time")
    • Use the app switcher to switch back to the app, or use the back arrow/button 3 times

    Still not great, but better 🙂

     


  7. I have code that requests location permissions at runtime, however of course the user might deny that permission. In case they made a mistake, or change their mind, I want to be able to give the user the opportunity to grant the permission because attempting to request the permission in code will result in the user not being prompted, and the permission being denied.

     

    Unfortunately, there does not seem to be an easy way (at least on Android 11) of providing this. The following code will show the Location Permissions settings for the device:

    var
      LIntent: JIntent;
    begin
      LIntent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_LOCATION_SOURCE_SETTINGS);
      TAndroidHelper.Activity.startActivityForResult(LIntent, 0);
    end;

    However the user then has to - Tap the "App access to location" item:
    image.png

     

    Scroll down to the "Denied" section and tap the app in question:

     

    image.thumb.png.877bbaf1b7a80979e2811eb8dc47d525.png

     

    Then select whichever option is appropriate (for this particular app it's "Allow all the time")

    image.thumb.png.110d49b5f1df86b1e0f6c262302d61c4.png

     

    Then the user needs to either tap the back arrow until the settings screens disappear (Cannot use the app switcher to switch back to the app)

     

    This is obviously a horrible user experience. Does anyone know of a better way?

     


  8. 10 hours ago, David Schwartz said:

    This looks great, except I can't get the compiler to find the Tidy method:

    Tidy is a method of TJson (via a class helper), not TJsonObject. It's also a class method, so you don't need an instance of anything to call it


  9. 13 minutes ago, corneliusdavid said:

    my target SDK is only at 25.2.25

    I guess I did not explain it well enough. The value you quoted is not the targetSdkVersion that I mentioned - it is the version of the installed SDK, which has absolutely nothing to do with targetSdkVersion, platform API level, or pretty much any other value that is important to Android development.

    • Like 1

  10. 8 hours ago, MikeMon said:

    But the official Embarcadero docwiki 10.4 Sydney - Release 2 - RAD Studio (embarcadero.com) states that it does. 

    To clarify: Android 11 is supported. The distinction is in the value for targetSdkVersion that ends up in AndroidManifest.xml (currently, the value is set to 29, as opposed to 30). A targetSdkVersion of 30 is yet to be fully supported, as it requires changes to the support of accessing external storage (and possibly other aspects of API 30). This is what is being referred to with a statement of:

    8 hours ago, MikeMon said:

    API 30 is not officially supported with 10.4.2

    They really should make the distinction if they are saying it like that.

    Also, targeting an API level (via targetSdkVersion) and building against an API level are 2 completely different things, so downloading platform API level 30 and changing the SDK settings to suit is not the same as changing the target.

     

    • Like 3

  11. On 5/29/2021 at 8:07 PM, Fabian1648 said:

    Is there a solution with FMX to have an events listener that detects all the events that are triggered in an app?

    By "events", if you mean things like whether the app became active, entered the background etc, you can have the app subscribe to TApplicationEventMessage:

     

    http://docwiki.embarcadero.com/Libraries/Sydney/en/FMX.Platform.TApplicationEventMessage

     

    Use TMessageManager to subscribe to the event, e.g:

      TMessageManager.DefaultManager.SubscribeToMessage(TApplicationEventMessage, ApplicationEventMessageHandler);

    ..and to unsubscribe when you no longer wish to receive the messages:

      TMessageManager.DefaultManager.Unsubscribe(TApplicationEventMessage, ApplicationEventMessageHandler);

    Example handler:

    procedure TMyClass.ApplicationEventMessageHandler(const Sender: TObject; const M: TMessage);
    var
      LMessage: TApplicationEventMessage;
    begin
      LMessage := TApplicationEventMessage(M);
      case LMessage.Value.Event of
        TApplicationEvent.BecameActive:
          DoAppBecameActive;
        TApplicationEvent.EnteredBackground:
          DoAppEnteredBackground;
        // etc
      end;
    end;

     

    • Like 1
    • Thanks 2

  12. 10 hours ago, Rollo62 said:

    I wonder if it would be possible to intercept the texture stream

    I have been working on that. I needed to make a substantial change to using an OpenGL surface - a problem I am having is doing a transform when the device is not oriented in the "normal" position. 

    10 hours ago, Rollo62 said:

    I find the (for Android)

        FSurfaceTexture: JSurfaceTexture;
        FSurfaceTextureListener: JTextureView_SurfaceTextureListener;


    which leads to the assumption that this could be tapped somehow.

    That already is "tapped", however the SurfaceTextureListener is not the right place. As per my comment above, I've switched to using GLSurfaceView: https://developer.android.com/reference/android/opengl/GLSurfaceView

     

    Which uses: https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer

     

    The GLSurfaceView calls onDrawFrame of the Renderer when a new frame is available, so the capture/filtering can be done there. This part is all done in Java, however the Delphi code could "hook" into the filtering, and can already pass a Java bitmap of the resulting frame to Delphi. Again as per above, I want to resolve the transform issue which is currently eluding me.

     

    I'm willing to share the work in progress with others if they think they can help. Best to join my Slack workspace to do so: https://slack.delphiworlds.com

    • Like 1

  13. 22 hours ago, Ranja AZ said:

    I want to convert java code to delphi code.

    It appears you have already asked a similar question, here: https://stackoverflow.com/questions/65345853/is-it-possible-to-write-the-following-java-program-in-delphi

    The principal for your present issue is identical, i.e. if OnPrintListener is a class, then you. cannot do it in Delphi. If it's an interface (which, given your other question, it's likely), then you already have an example of how to implement a Java interface, in the StackOverflow answer. A possible example for this case:

    uses
      Androidapi.JNIBridge, Androidapi.JNI.JavaTypes, Androidapi.Helpers;
    
    type
      JOnPrintListener = interface;
    
      JOnPrintListenerClass = interface(IJavaClass)
        ['{076904DD-77D9-497D-BA21-992A9B8FED3E}']
      end;
    
      [JavaSignature('com.nexgo.oaf.apiv3.device.printer.OnPrintListener')]
      JOnPrintListener = interface(IJavaInstance)
        ['{F688775D-067F-4521-A045-9106599F4C4A}']
        procedure onPrintResult(retCode: Integer); cdecl;
        // *** NOTE *** There may be other methods for this interface
      end;
      TJOnPrintListener = class(TJavaGenericImport<JOnPrintListenerClass, JOnPrintListener>) end;
    
      TRunnable = class(TJavaLocal, JRunnable)
      private
        FCallback: TProc;
      public
        { JRunnable }
        procedure run; cdecl;
      public
        constructor Create(const ACallback: TProc);
      end;
    
      TPrintListener = class(TJavaLocal, JOnPrintListener)
      private
        FRetCode: Integer;
        FRunnable: JRunnable;
        procedure DoRun;
      public
        { JOnPrintListener }
        procedure onPrintResult(retCode: Integer); cdecl;
      public
        constructor Create;
      end;
    
    { TRunnable }
    
    constructor TRunnable.Create(const ACallback: TProc);
    begin
      inherited Create;
      FCallback := ACallback;
    end;
    
    procedure TRunnable.run;
    begin
      FCallback;
    end;
    
    { TPrintListener }
    
    constructor TPrintListener.Create;
    begin
      inherited;
      FRunnable := TRunnable.Create(DoRun);
    end;
    
    procedure TPrintListener.DoRun;
    begin
      // Do the toast thing here, using FRetCode
    end;
    
    procedure TPrintListener.onPrintResult(retCode: Integer);
    begin
      FRetCode := retCode;
      TAndroidHelper.Activity.runOnUiThread(FRunnable);
    end;
    
    { TForm1 }
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      // Assuming FPrintListener is defined as: FPrintListener: JOnPrintListener
      FPrintListener := TPrintListener.Create;
      // Assuming you have created an instance of Printer (imported as JPrinter) called FPrinter
      FPrinter.startPrint(False, FPrintListener);
    end;

    Note all of the assumptions being made - the biggest one being that OnPrintListener is an interface. Also I don't have the .jar, nor the printer, so of course it is untested

     

    • Like 3
×