-
Content Count
1424 -
Joined
-
Last visited
-
Days Won
32
Everything posted by Dave Nottage
-
D11, Android new App Billing Service
Dave Nottage replied to John van de Waeter's topic in Cross-platform
It says the officially supported versions are Android 8 and above. That does not mean apps will not run on lower versions - I have apps built with Delphi 11 running on Android 4.4. -
I wish to use the App Store Connect REST API to retrieve some information, and I have created a key as per these instructions. Next thing is to create a JWT for passing in the Authorization as per these instructions. I have chosen Paolo Rossi's excellent JOSE JWT library for Delphi (especially since it now includes an ES256 signing algorithm), and have come up with the following code for generating the JWT and sending via HTTP: const cTokenExpirySeconds = 60; cIssuerID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'; cKeyID = 'yyyyyyyy'; cAppStoreConnectAPIURL = 'https://api.appstoreconnect.apple.com/v1'; cAppStoreConnectAPIGetProfiles = cAppStoreConnectAPIURL + '/profiles'; // AIssuer = id obtained from https://appstoreconnect.apple.com/access/api // AKeyID = id associated with the key // ASecret = text read from the .p8 file function CreateJWT(const AIssuer, AKeyID, ASecret: string): string; var LJWT: TJWT; LSigner: TJWS; LKey: TJWK; // LScope: TJSONArray; begin LJWT := TJWT.Create; try LJWT.Header.Algorithm := 'ES256'; LJWT.Header.KeyID := AKeyID; LJWT.Claims.Audience := 'appstoreconnect-v1'; LJWT.Claims.Issuer := AIssuer; LJWT.Claims.IssuedAt := Now; LJWT.Claims.Expiration := IncSecond(Now, cTokenExpirySeconds); // LScope := TJSONArray.Create; // LScope.Add('GET /v1/profiles') ; // LJWT.Claims.JSON.AddPair('scope', LScope); LSigner := TJWS.Create(LJWT); try LSigner.SkipKeyValidation := False; LKey := TJWK.Create(ASecret); try LSigner.Sign(LKey, TJOSEAlgorithmId.ES256); finally LKey.Free; end; Result := LSigner.Payload + '.' + LSigner.Signature; finally LSigner.Free; end; finally LJWT.Free; end; end; procedure TForm1.Button1Click(Sender: TObject); var LHTTP: THTTPClient; LResponse: IHTTPResponse; LToken: string; begin LToken := CreateJWT(cIssuerID, cKeyID, TFile.ReadAllText('Z:\Config\AppStoreConnectAPI\AuthKey.p8')); LHTTP := THTTPClient.Create; try LHTTP.CustomHeaders['Authorization'] := 'Bearer ' + LToken; LResponse := LHTTP.Get(cAppStoreConnectAPIGetProfiles); Memo1.Lines.Add(LResponse.ContentAsString); finally LHTTP.Free; end; end; The response is: { "errors": [{ "status": "401", "code": "NOT_AUTHORIZED", "title": "Authentication credentials are missing or invalid.", "detail": "Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens" }] } So I assume I am missing something. According to the documentation, adding the scope (the code that is commented out) can be optional depending on the request. Adding the scope yields the same result in this case. Any clues as to what the problem may be?
-
App Store Connect REST API problem
Dave Nottage replied to Dave Nottage's topic in Network, Cloud and Web
D'Oh. Should be: Result := LSigner.CompactToken; Bit of a head-slap moment, there.. -
App Store Connect REST API problem
Dave Nottage replied to Dave Nottage's topic in Network, Cloud and Web
-
App Store Connect REST API problem
Dave Nottage replied to Dave Nottage's topic in Network, Cloud and Web
Mine looks exactly like that, but I've obfuscated the value in the code, since I don't want to give out my private info -
With the direct download method I used, it took around 30-40 minutes total to update I'm using Xcode 13 also with Delphi 10.4.2 and it appears to be working OK, aside from the issue mentioned earlier about deploying for App Store when using Debug config.
-
After I updated my phone to iOS 15.0, the apps that were already on it, compiled with Delphi 10.4.2 using iOS 14.5 SDK, ran without any warnings. Xcode 13 is now installed, and I was able to import the iOS 15.0 SDK. EDIT: As per this report on Facebook: https://www.facebook.com/groups/137012246341854/posts/4389107827798920 App Store builds using Xcode 13 FAIL. Dev builds are OK. I'm looking into why the App Store build fails EDIT 2: App Store builds work if Release config is selected. I'm pretty sure that in earlier versions of Xcode, it did not matter if Debug config was used. A word of warning about installing Xcode 13: When I used the App Store app on the Mac to update Xcode, it spent a couple of hours "stuck" at around the 95% mark. I ended up restarting my machine, and it went back to around 75%: I waited another hour or so, and it did not move, so I gave up, and downloaded Xcode 13 from the developer site: https://developer.apple.com/download/release/ The link to the released version of Xcode 13 seems to have disappeared from that link at the moment - hopefully it will re-appear. Anyway, I made a backup of Xcode 12.5.1 in my /Applications folder first, and after downloading Xcode 13, opened a command-line window and executed this: cd /Applications sudo xip -x [path_to_download]/Xcode_13.xip Where [path_to_download] is the path that I downloaded Xcode 13 to. This unarchives Xcode 13 into the /Applications folder. sudo is required for permissions to unarchive to /Applications
-
Define "cannot run", as in - are there any errors? Does the app start? Incidentally, I'm updating my device today (backing it up with iOS 14.8 first) as well as checking out Xcode 13
-
Sorry.. I've only seen this just now. I assume you've made the changes?
-
If you can't wait for @dummzeuch to provide an updated GExperts, I have a DLL compiled for Delphi 11, here.
-
How know interface GUID from generic anonymous function using RTTI
Dave Nottage replied to jairgza's topic in Algorithms, Data Structures and Class Design
Can you show how you're querying MyField? -
Steps to reproduce: (Delphi 10.4.2) 1. Start new VCL app 2. Put a TStatusBar on the form 3. Right-click the TStatusBar, click Panels Editor.. 4. Click the "Add" button in the top left, 15 times (panels will be numbered 0 - 14) 5. Save the project 6. Close the form 7. Re-open the form 8. Check the appearance of the TStatusBar On the PCs I have tried this on, the status bar fails to draw correctly when first shown. If the status bar needs to repaint, all is OK. This happens both at design time and runtime. Might anyone know why this occurs, and is there a solution?
-
OK.. it may be a problem at runtime only in some circumstances, which includes running on a remote machine. Needs further investigation in that regard
-
I'm interested, thanks!
-
Is it possible to create on Delphi my own accessibility service?
Dave Nottage replied to Ranja AZ's topic in Cross-platform
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 -
It's not correct. It needs to be: keyResponse := TAVContentKeyResponse.Wrap(TAVContentKeyResponse.OCClass.contentKeyResponseWithFairPlayStreamingKeyResponseData(ckcData)); 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 assetIDData := assetIDString.dataUsingEncoding(NSUTF8StringEncoding);
-
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.
-
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.
-
You might want to read this:
-
FMX on ios shows annoying messages about pasting in editable controls.
Dave Nottage replied to darnocian's topic in FMX
Do you have a minimal reproducible example? -
A user friendly way of showing location permissions settings on Android
Dave Nottage posted a topic in General Help
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: Scroll down to the "Denied" section and tap the app in question: Then select whichever option is appropriate (for this particular app it's "Allow all the time") 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? -
A user friendly way of showing location permissions settings on Android
Dave Nottage replied to Dave Nottage's topic in General Help
Understood, however there will always be someone, and they may still contact support to ask why the app is not working 😉 -
A user friendly way of showing location permissions settings on Android
Dave Nottage replied to Dave Nottage's topic in General Help
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. -
A user friendly way of showing location permissions settings on Android
Dave Nottage replied to Dave Nottage's topic in General Help
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 🙂 -
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