Using iframe for google maps embed api in multi device Delphi - android

Currently I have a code like this to display a google map with my current location in my TWebBrowser
procedure TForm1.LocationSensor1LocationChanged(Sender: TObject; const
OldLocation, NewLocation: TLocationCoord2D);
begin
var URLString := Format('https://maps.google.com/maps?q=%s,%s&output=embed', [Format('%2.6f', [NewLocation.Latitude]), Format('%2.6f', [NewLocation.Longitude])]);
WebBrowser1.Navigate(URLString);
end;
If I use my URL as https://maps.google.com/maps?q=%s,%s then it works properly but when I use my URL as https://maps.google.com/maps?q=%s,%s&output=embed then it will prompt an error "The Google Maps Embed API must be used in an iframe" as shown in the picture
Is there a way I could have an iframe in my delphi project?

As the error message says, Google's embedded map wants to be hosted in an HTML <iframe>. TWebBrowser has a LoadFromStrings() method that you can use for that purpose, eg:
procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
const OldLocation, NewLocation: TLocationCoord2D);
begin
var URL := Format('https://maps.google.com/maps?q=%2.6f,%2.6f&output=embed', [NewLocation.Latitude, NewLocation.Longitude]);
var HTML = Format('<iframe src="%s" width="%d" height="%d" style="border:0;" allowfullscreen="" loading="lazy"></iframe>', [URL, <DesiredWidth>, <DesiredHeight>]);
WebBrowser1.LoadFromStrings(HTML, URL);
end;

Related

Crossplatform audio support in Delphi

I'm running Delphi Tokyo and I'm looking for a way to play audio on Windows and Android (and maybe at some point iOS).
On Windows I can do with something like PlaySound(PChar(ResourceName), 0, SND_RESOURCE or SND_ASYNC), but I'm stuck on Android. I've tried TMediaPlayer, but it takes about a second before it starts playing, which is too long for a mouse click or screen tap.
Basically I've built a Minesweeper clone and I'm looking for sound support (if you want to know the background).
Suggestions?
There a few arcade game demos that have audio classes that you can use. See https://github.com/Embarcadero/DelphiArcadeGames
You can also see Is there an alternative to TMediaPlayer for multi platform rapid sound effects? for a description of some issues encountered on Android with the audio management class in these demos.
With the latest version of the audio manager provided in the games samples, the developer simply removes all notifications/checking that an audio file is actually loaded and ready to play. I personally did not like the idea of simply expecting the audio to be ready to play.
My audio management classes are a bit too complicated to simply post, but if you need this functionality hopefully this bit of pseudo code can offer some clues on how I addressed the short comings of the AudioManager provided in the game demos.
The idea is to create a callback in my main application that is invoked when an audio file is ready to play. With a bit of web search, I found the following links to be helpful references for my implementation:
https://developer.android.com/reference/android/media/SoundPool
https://www.101apps.co.za/articles/using-android-s-soundpool-class-a-tutorial.html
1 - Define a notification type that will provide sufficient info on the audio file that is ready to play. Mine looks like this:
TSoundLoadedEvent = procedure(Sender: TObject; ASoundID: integer; AStatus: Integer) of object;
2 - As per the documentation, define a class that handles JSoundPool_OnLoadCompleteListener. Note that the class utilizes our custom event defined as TSoundLoadedEvent, which means that the AudioManager will have to implement this callback:
TMyAudioLoadedListener = class(TJavaLocal, JSoundPool_OnLoadCompleteListener)
private
FSoundPool : JSoundPool;
FOnJLoadCompleted : TSoundLoadedEvent;
public
procedure onLoadComplete(soundPool: JSoundPool; sampleId,status: Integer); cdecl;
property OnLoadCompleted: TSoundLoadedEvent read FOnJLoadCompleted write FOnJLoadCompleted;
property SoundPool: JSoundPool read FSoundPool;
end;
...
procedure TMyAudioLoadedListener.onLoadComplete(soundPool: JSoundPool; sampleId, status: Integer);
begin
FSoundPool := soundPool;
if Assigned(FOnJLoadCompleted) then
FOnJLoadCompleted(Self, sampleID, status);
end;
3 - Modify the audio manager class to implement the listener:
TAudioManager = Class
Private
fAudioMgr : JAudioManager;
fSoundPool : JSoundPool;
fmyAudioLoadedListener : TMyAudioLoadedListener;
fOnPlatformLoadComplete : TSoundLoadedEvent;
Public
Constructor Create; override;
...
procedure DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
...
property OnLoadComplete: TSoundLoadedEvent read fOnPlatformLoadComplete write fOnPlatformLoadComplete;
4 - Implement the JSoundPool listener and wire the callback from the listener to our AudioManager:
constructor TAudioManger.Create;
begin
...
//create our listener
fmyAudioLoadedListener := TMyAudioLoadedListener.Create;
// set the listener callback
fmyAudioLoadedListener.OnLoadCompleted := DoOnLoadComplete;
// inform JSoundPool that we have a listener
fSoundPool.setOnLoadCompleteListener( fmyAudioLoadedListener );
...
And
procedure TAudioManager.DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
begin
if Succeeded(status) then //remove this if you want all notifications
begin
if Assigned(Self.fOnPlatformLoadComplete) then
fOnPlatformLoadComplete( self, sampleID, status );
end;
end;
5 - Last thing is to implement a callback in the main application:
TMainForm = class(TForm)
...
fAudioMgr : TAudioManager;
...
procedure OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
Then, where you create the AudioManager, assign the new TSoundLoadedEvent to a local procedure that I called OnSoundLoaded:
procedure TMainForm.FormCreate(Sender: TObject);
...
begin
...
fAudioMgr := TAudioManager.Create;
fAudioMgr.OnSoundLoaded := OnSoundLoaded;
Now, when an audio file is ready to play, you should get notified:
procedure TMainForm.OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
begin
// track IDs when loading sounds to identify which one is ready
// check status to confirm that the audio was loadded successfully
end;
This is definitely only bits and pieces, but hopefully can be helpful.

(Delphi 10.2) How can I open a URL in Android's web browser from my application?

Delphi 10.2.2 Firemonkey Android Question: How can I open a URL in Android's web browser from my application?
I tried to understand the concepts here: How can I open a URL in Android's web browser from my application? but the information didn't help me any. I am unable to wrap my brain around how they can get it to work compared to the code I currently have.
I've looked here too: http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FMX.Android_Intents_Sample
I've also looked here: https://developer.android.com/guide/components/intents-common.html?hl=en
I've tried this code:
function OpenURL(const URL: string; const DisplayError: Boolean = False): Boolean;
var
Intent: JIntent;
begin
// There may be an issue with the geo: prefix and URLEncode.
// will need to research
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
TJnet_Uri.JavaClass.parse(StringToJString(System.NetEncoding.TNetEncoding.URL.Encode(URL))));
try
TAndroidHelper.Activity.startActivity(Intent);
exit(true);
except
on e: Exception do
begin
if DisplayError then ShowMessage('Error: ' + e.Message);
exit(false);
end;
end;
end;
This is how I use that function:
OpenURL('https://www.patreon.com/phonelosers/overview/', true)
And it keeps giving me an error:
Error: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=https://www.patreon.com/phonelosers/overview/ }
I tried this code too:
function OpenURL(const URL: string; const DisplayError: Boolean = False): Boolean;
var
Intent: JIntent;
begin
// There may be an issue with the geo: prefix and URLEncode.
// will need to research
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
TJnet_Uri.JavaClass.parse(StringToJString(System.NetEncoding.TNetEncoding.URL.Encode(URL))));
if TAndroidHelper.Activity.getPackageManager.queryIntentActivities(Intent, TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size > 0 then
begin
try
TAndroidHelper.Activity.startActivity(Intent);
exit(true);
except
on e: Exception do
begin
if DisplayError then ShowMessage('Error: ' + e.Message);
exit(false);
end;
end;
end
else
begin
ShowMessage('Intent.resolveActivity <= 0');
end;
end;
which gives me "Intent.resolveActivity <= 0"
It doesn't matter which android phone I use. I have a Moto G Play, Samsung S8+, and a Moto Z2 Force which run Android versions 6.0.1, 7.0, 8.0. All the phones have chrome web browser installed and I can use it.
I look all over the web and the code below is what everyone is using to supposedly do what I need it to do. I looked at both Delphi and Android programming information.
Please help solve this Delphi 10.2.2 Firemonkey Android problem!
After using a different coding method:
procedure OpenURL(const URL: string);
var
LIntent: JIntent;
Data: Jnet_Uri;
begin
LIntent := TJIntent.Create;
Data := TJnet_Uri.JavaClass.parse(StringToJString(URL));
LIntent.setData(Data);
LIntent.setAction(StringToJString('android.intent.action.VIEW'));
TAndroidHelper.Activity.startActivity(LIntent);
end;
I figured out I forgot "System.NetEncoding.TNetEncoding.URL.Encode" and just removing that code from the source fixed the issue so that this code:
function OpenURL(const URL: string; const DisplayError: Boolean = False): Boolean;
var
Intent: JIntent;
begin
// There may be an issue with the geo: prefix and URLEncode.
// will need to research
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
TJnet_Uri.JavaClass.parse(StringToJString(URL)));
try
TAndroidHelper.Activity.startActivity(Intent);
exit(true);
except
on e: Exception do
begin
if DisplayError then ShowMessage('Error: ' + e.Message);
exit(false);
end;
end;
end;
now works!
Since this System.NetEncoding.TNetEncoding.URL.Encode has caused the issue, I'm wondering if I do need to encode my url special, what should I use?
use this code :
var
URL: string;
Intent: JIntent;
begin
URL := 'https://www.google.com';
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
TJnet_Uri.JavaClass.parse(StringToJString(URL)));
SharedActivity.startActivity(Intent);
end

Delphi XE7 Android - No mapping for unicode character exists in the target multi-byte code page

I am trying to fetch an image from a SOAP Web Service into a Delphi XE7 Android App using TSoapAttachment component.
procedure TTabbedwithNavigationForm.Button1Click(Sender: TObject);
Var img: TSOAPAttachment;
strm: TStream;
begin
img:= Service.GetImage('d:\pictures\pic.jpg'); <== Error on this line
if img <> nil then begin
strm:= TMemoryStream.Create;
img.SaveToStream(strm);
strm.Position:= 0;
Image1.Bitmap.LoadFromStream(strm);
strm.Free;
DeleteFile(img.CacheFile);
end;
end;
The Web Service is implemented as follows:
function TMyService.GetImage(const imgfile: AnsiString) : TSOAPAttachment; stdcall;
begin
Result:= TSOAPAttachment.Create;
Result.SetSourceFile(imgfile);
end;
This runs fine on Win32, but on Android gives error "No mapping for Unicode character exists in the target multi-byte code page" on the Service.GetImage line.

Can't get authentication code

I want to work with Google Api using Rest (Delphi XE7), do it this way:
uses
...
{$IF DEFINED(ANDROID)}
REST.Authenticator.OAuth.WebForm.FMX;
{$ENDIF}
{$IF DEFINED(MsWindows)}
REST.Authenticator.OAuth.WebForm.Win;
{$ENDIF}
That procedure is working on Windows, after that programm changes AuchCode to access token and everything working.
procedure TForm2.OAuth2_GoogleTasks_BrowserTitleChanged(const ATitle: string; var DoCloseWebView: boolean);
begin
if Pos('Success code', ATitle) > 0 then
begin
AuthCode := Copy(ATitle, 14, Length(ATitle));
if (AuthCode <> '') then
begin
editactoken.Text:= AuthCode;
DoCloseWebView := true;
webform.Release;
end;
end;
end;
procedure TForm2.Button59Click(Sender: TObject);
begin
WebForm:=Tfrm_OAuthWebForm.Create(nil);
WebForm.OnTitleChanged := self.OAuth2_GoogleTasks_BrowserTitleChanged;
WebForm.ShowWithURL(OAuth2Authenticator1.AuthorizationRequestURI);
end;
But I've met a trouble that I cant copy auth code on android:
Form2.OAuth2_GoogleTasks_BrowserTitleChanged doesn't work because title doesn't change;
There is no title at all in REST.Authenticator.OAuth.WebForm.FMX :
private
{ Private declarations }
FOnBeforeRedirect: TOAuth2WebFormRedirectEvent;
FOnAfterRedirect: TOAuth2WebFormRedirectEvent;
FOnBrowserTitleChanged : TOAuth2WebFormTitleChangedEvent;
FLastURL: string;
public
while in REST.Authenticator.OAuth.WebForm.Win it looks like this:
private
{ Private declarations }
FOnBeforeRedirect: TOAuth2WebFormRedirectEvent;
FOnAfterRedirect: TOAuth2WebFormRedirectEvent;
FOnBrowserTitleChanged : TOAuth2WebFormTitleChangedEvent;
FLastTitle: string; // <-------
FLastURL: string
there is no property webform.lasttitle in fmx variant (in win i could get it)
LastUrl property looks like that after redirecting : "......" but I can't understand how to use it to get auth code.
Try to use IFMXClipboardService but could not get this text from Twebbrowser.
Google not allowed to use Http get method for lastUrl adress to get response and parse this code (error 405).
I've read some article that there is no way to get html code from webbrowser fmx, is it so?
Any ideas how can I get this code in my programm?
Android (with Google Play Services installed) provides a native solution for authentication with other Google APIs, where no web browser is required:
See https://developers.google.com/android/guides/http-auth:
When you want your Android app to access Google APIs using the user's
Google account over HTTP, the GoogleAuthUtil class and related APIs
provide your users a secure and consistent experience for picking an
account and retrieving an OAuth 2.0 token for your app.
You can then use that token in your HTTP-based communications with
Google API services that are not included in the Google Play services
library, such as the Blogger or Translate APIs.
I suggest to do some research in this direction. The page linked above also mentions the GoogleApiClient for some (non HTTP) services.

Handling Custom URI in Delphi XE5 Android App

I have managed to register a custom protocol handler in my XE5 Android app by modifying the Androidmanifest.template.xml file. My application pops up properly whenever a myapp://myurl URL is clicked on.
The problem is, I need to get the URL that was clicked on when the app is launched in order to bring up the correct part of the app. Can anyone help me figure out how to get this?
Based on this example, try something like this:
uses
...,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Net,
FMX.Helpers.Android;
procedure TMainForm.FormCreate(Sender: TObject);
var
intent: JIntent;
uri: Jnet_Uri;
uriStr: String;
begin
intent := SharedActivity.getIntent;
if intent <> nil then
begin
if TJIntent.JavaClass.ACTION_VIEW.equals(intent.getAction) then
begin
uri := intent.getData;
uriStr := JStringToString(uri.toString);
// use uriStr as needed...
end;
end;
end;

Categories

Resources