How do I know when an iOS App Open Ad is dismissed in Xamarin/MAUI? - ios

I am using Xamarin.Google.iOS.MobileAds package to show Google App Open Ad in my MAUI app. The code below has error handling code removed for brevity.
this.ad = await AppOpenAd.LoadAsync(
o.IosUnitId,
null,
o.Orientation switch
{
AppOpenAdOrientation.Landscape => UIInterfaceOrientation.LandscapeLeft,
_ => UIInterfaceOrientation.Portrait,
}
);
var oldHandler = ad.PaidEventHandler;
var openTcs = new TaskCompletionSource();
// The handler below is actually called when the ad show up
ad.PaidEventHandler = v =>
{
oldHandler?.Invoke(v);
openTcs.TrySetResult();
};
var root = Platform.GetCurrentUIViewController();
this.ad.PresentFromRootViewController(root);
In the Google's doc on Handle presentation callbacks, a GADFullScreenContentDelegate is needed but I have no idea how I can put it in C# and there is no event/delegate corresponding to this self.appOpenAd.fullScreenContentDelegate = self;.
Please advice on the event of ad dismissal. I could do it for Android like this but I can't see the equivalent in Xamarin.iOS:
this.currAd.FullScreenContentCallback = callback;

Related

Google ads scripts: newVideoAd This is not implemented yet. For currently supported API

8/3/2022 13:40:32 NotImplementedError: This is not implemented yet. For currently supported API, see: https://developers.google.com/google-ads/scripts-beta/docs/reference/adsapp/adsapp
at Ha (adsapp_compiled:298:11)
at new $y (adsapp_compiled:13027:5)
at qC.newVideoAd (adsapp_compiled:15250:12)
at Object.<anonymous> (adsapp_compiled:18396:54)
var youtubeVideoId = "youtubeVideoId";
var adGroupName = "adGroupName" // name of my target video ad group
var displayUrl = "display-url.com";
var finalUrl = "https://final-url.com";
var assetOperation = AdsApp.adAssets().newYouTubeVideoAssetBuilder()
.withName(adName)
.withYouTubeVideoId(youtubeVideoId)
.build();
var videoAsset = assetOperation.getResult();
var videoAdGroup = AdsApp.videoAdGroups()
.withCondition(`Name = '${adGroupName}'`)
.withLimit(1)
.get()
.next();
var videoAdOperation = videoAdGroup.newVideoAd().inStreamAdBuilder()
.withVideo(videoAsset)
.withAdName(adName)
.withDisplayUrl(displayUrl)
.withFinalUrl(finalUrl)
.build()
.getResult();
// Code crash before next statement
if(videoAdOperation.isSuccessful()) {
var videoAd = videoAdOperation.getResult();
Logger.log("VideoAd " + videoAd.getName() + " created.");
} else {
Logger.log(videoAdOperation.getErrors());
}
Code breaks after videoAdGroup.newVideoAd().inStreamAdBuilder().
Following the google ads script docs everything should works nice.
But when I compile the script, I always get the next error:
I have just found in a Google ads scripts forum somebody who has the same problem here but no solution.
The problem is that beta mode which is active by default. Must be switched off for this action.
As a negative point, switch off Beta mode will not allow you to use javascript arrow functions, neither js classes.

How to use SimpleProvider with my own MSAL C# code

I'm trying to use my own MSAL code to work together. Developed with .NET Core 5 MVC.
I have similar problem as I found in below link. But I just don't know how to make it work with the proposed answer. Or in other words, I'm still confuse how this integration is done.
[It is mandatory to use the login component in order to use the other components]It is mandatory to use the login component in order to use the other components
[Quickstart for MSAL JS]https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/main/samples/examples/simple-provider.html
I also have read following article too:
[Simple Provider Example]https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/main/samples/examples/simple-provider.html
[A lap around microsoft graph toolkit day 7]https://developer.microsoft.com/en-us/office/blogs/a-lap-around-microsoft-graph-toolkit-day-7-microsoft-graph-toolkit-providers/
is there someone can pointing to me more details explanation about how to archive this.
Can someone explains further below response further. How to do it. Where should I place the code and how to return AccessToken to SimpleProvider?
Edited:
Update my question to be more precise to what I want besides on top of the question. Below is the code I used in Startup.cs to automatically trigger pop up screen when user using the web app. When using the sample provided, it is always cannot get access token received or userid data. Question 2: How to save or store token received in memory or cache or cookies for later use by ProxyController and its classes.
//Sign in link under _layouts.aspx
<a class="nav-link" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in</a>
// Use OpenId authentication in Startup.cs
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
// Specify this is a web app and needs auth code flow
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAd", options);
options.Prompt = "select_account";
options.Events.OnTokenValidated = async context =>
{
var tokenAcquisition = context.HttpContext.RequestServices
.GetRequiredService<ITokenAcquisition>();
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) =>
{
var token = await tokenAcquisition
.GetAccessTokenForUserAsync(GraphConstants.Scopes, user: context.Principal);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
})
);
// Get user information from Graph
try
{
var user = await graphClient.Me.Request()
.Select(u => new
{
u.DisplayName,
u.Mail,
u.UserPrincipalName,
u.MailboxSettings
})
.GetAsync();
context.Principal.AddUserGraphInfo(user);
}
catch (ServiceException)
{
}
// Get the user's photo
// If the user doesn't have a photo, this throws
try
{
var photo = await graphClient.Me
.Photos["48x48"]
.Content
.Request()
.GetAsync();
context.Principal.AddUserGraphPhoto(photo);
}
catch (ServiceException ex)
{
if (ex.IsMatch("ErrorItemNotFound") ||
ex.IsMatch("ConsumerPhotoIsNotSupported"))
{
context.Principal.AddUserGraphPhoto(null);
}
}
};
options.Events.OnAuthenticationFailed = context =>
{
var error = WebUtility.UrlEncode(context.Exception.Message);
context.Response
.Redirect($"/Home/ErrorWithMessage?message=Authentication+error&debug={error}");
context.HandleResponse();
return Task.FromResult(0);
};
options.Events.OnRemoteFailure = context =>
{
if (context.Failure is OpenIdConnectProtocolException)
{
var error = WebUtility.UrlEncode(context.Failure.Message);
context.Response
.Redirect($"/Home/ErrorWithMessage?message=Sign+in+error&debug={error}");
context.HandleResponse();
}
return Task.FromResult(0);
};
})
// Add ability to call web API (Graph)
// and get access tokens
.EnableTokenAcquisitionToCallDownstreamApi(options =>
{
Configuration.Bind("AzureAd", options);
}, GraphConstants.Scopes)
// Add a GraphServiceClient via dependency injection
.AddMicrosoftGraph(options =>
{
options.Scopes = string.Join(' ', GraphConstants.Scopes);
})
// Use in-memory token cache
// See https://github.com/AzureAD/microsoft-identity-web/wiki/token-cache-serialization
.AddInMemoryTokenCaches();
Since you are using MVC, I recommend using the ProxyProvider over the Simple Provider.
SimpleProvider - useful when you have existing authentication on the client side (such as Msal.js)
ProxyProvider - useful when you are authenticating on the backend and all graph calls are proxied from the client to your backend.
This .NET core MVC sample might help - it is using the ProxyProvider with the components
Finally, I have discovered how to do my last mile bridging for these two technology.
Following are the lines of the code that I have made the changes. Since I'm using new development method as oppose by MSAL.NET, a lot of implementation has been simplified, so many of examples or article out there, may not really able to use it directly.
Besides using links shared by #Nikola and me above, you also can try to use below
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/
to consolidate to become your very own solution. Below are the changes I have made to make it worked.
Change in Startup.cs class
// Add application services. services.AddSingleton<IGraphAuthProvider, GraphAuthProvider>(); //services.AddSingleton<IGraphServiceClientFactory, GraphServiceClientFactory>();
Change in ProxyController.cs class
private readonly GraphServiceClient _graphClient;
public ProxyController(IWebHostEnvironment hostingEnvironment, GraphServiceClient graphclient)
{
_env = hostingEnvironment;
//_graphServiceClientFactory = graphServiceClientFactory;
_graphClient = graphclient;
}
Change in ProcessRequestAsync method under ProxyController.cs
//var graphClient = _graphServiceClientFactory.GetAuthenticatedGraphClient((ClaimsIdentity)User.Identity);
var qs = HttpContext.Request.QueryString;
var url = $"{GetBaseUrlWithoutVersion(_graphClient)}/{all}{qs.ToUriComponent()}";
var request = new BaseRequest(url, _graphClient, null)
{
Method = method,
ContentType = HttpContext.Request.ContentType,
};

Microsoft graph calling/communication SDK not working with MediaSession

I am making calls to teams user using Graph Communication SDK with local machine.I am using ngork for making local machine endpoints public. I can make call to teams user without using MediaSession but when I use Media Session call is not reaching to teams user and it is not giving any error.Need help to find out issue.I am referring examples from this doc- https://github.com/microsoftgraph/microsoft-graph-comms-samples/tree/master/Samples/V1.0Samples/LocalMediaSamples
Working Call:
var mediaToPrefetch = new List<MediaInfo>();
var call = new Call()
{
Targets = new[] { target },
MediaConfig = new ServiceHostedMediaConfig { PreFetchMedia = mediaToPrefetch },
RequestedModalities = new List<Modality> { Modality.Audio, Modality.Video, Modality.VideoBasedScreenSharing },
TenantId = joinCallBody.TenantId,
};
var statefulCall = await this.Client.Calls().AddAsync(call, scenarioId: scenarioId).ConfigureAwait(false);
Non Working Call:
var mediaSession=this.CreateMediaSession();
var mediaToPrefetch = new List<MediaInfo>();
var call = new Call()
{
Targets = new[] { target },
MediaConfig = new ServiceHostedMediaConfig { PreFetchMedia = mediaToPrefetch },
RequestedModalities = new List<Modality> { Modality.Audio, Modality.Video, Modality.VideoBasedScreenSharing },
TenantId = joinCallBody.TenantId,
};
var statefulCall = await this.Client.Calls().AddAsync(call, scenarioId: scenarioId,mediaSession:mediaSession).ConfigureAwait(false);
Those samples refer to application-hosted media bots: https://learn.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/requirements-considerations-application-hosted-media-bots
Did you follow all the steps to register the bot app? https://github.com/microsoftgraph/microsoft-graph-comms-samples/tree/master/Samples/V1.0Samples/LocalMediaSamples/PolicyRecordingBot#bot-registration
You need to explicitly add permissions to access media, and later to grant by an administrator of the azure account.
If you did all that, could you please share what kind of exception/error are you getting?

Notification at DART lang - html / browser / webkit / Desktop

I want to ask about the normal notification in DART, which is different than the Chrome Packaged App discussed here
I created the below code, the browser [DARTIUM] asked for permission as expected, but the response in both cases (Allow/Deny) appeared as "default" in the console, and the notification did not appear.
{
void main() {
Notification.requestPermission().then((String permission) {
print(permission); // ==> This is always = "default" for both Allow and Deny
if (permission == "default") {
print('permission granted');
var notification = new Notification('hello');
}
else print('sorry no permission!');
});
}
any thought! thanks
The code as it's currently written would never show a permission either way -- "default" means that permission hasn't been allowed or denied. You should be checking for permission == "granted" before displaying the notification.
Keep in mind, there is an outstanding bug with notifications and checking the current permissions, see https://code.google.com/p/dart/issues/detail?id=20585, although this may not be affecting you.
thanks Brian and Gunter, I made my own notification, inspired by this
I made used the same, CSS file, and made a custom element, notification.dart
part of myApp;
class NotificationElement extends HtmlElement {
// Define the custom element tag
static final tag = 'x-Notification';
factory NotificationElement() => new Element.tag(tag);
// Create the element and define its stylesheet
NotificationElement.created() : super.created(){
LinkElement styleSheet = new LinkElement()..rel = "stylesheet"..type="text/css"..href="./style/toastr.css";
document.head.append(styleSheet);
}
var notificationContainer = new Element.html('<div></div>')
..id='notification-container';
var notificationBody = new Element.html('<div></div>')
..classes.add('notification');
var notificationButton = new ButtonElement()
..classes.add('notification-close-button')
..text='x';
var notificationTitle = new Element.html('<div></div>')..classes.add('notification-title');
var notificationMsg = new Element.html('<div></div>')..classes.add('notification-message');
var notificationMsgLabel = new LabelElement();
Element launchElement(type,location,Title,Msg){
notificationButton.onClick.listen((e) => notificationContainer.nodes.remove(notificationBody));
notificationContainer..classes.add('notification-'+location);
notificationBody..classes.add('notification-'+type);
notificationTitle.text=Title;
notificationMsg.innerHtml='<label>'+Msg+'</label>';
notificationBody.nodes..add(notificationButton)..add(notificationTitle)..add(notificationMsg);
notificationContainer.nodes..add(notificationBody);
return (notificationContainer);
}
}
Element mynotification = querySelector('#notification-element');
void CreatefonixNotification(type,location,Title,Msg){
var notifyMe = new Element.tag('x-notification');
notifyMe = notifyMe.launchElement(type,location,Title,Msg);
mynotification.nodes.add(notifyMe);
}
in the index.dart, I registered the element:
document.registerElement(NotificationElement.tag, NotificationElement);
and in index.html, I added this div:
<div id='notification-element'></div>
and any place in the application, I can call it like:
CreateNotification('error','top-left','Error:','sorry we have some issue!!.')

Go to App Store Reviews in iOS 6

It appears that using the itms-apps//.... URL scheme doesn't work in iOS 6 with the new App Store to show the product reviews area. Now I'm using the code below, but it just shows you the product. How do you get to the review area to ask for a review and take the user to the right tab of the product displayed?
void DoReview()
{
var spp = new StoreProductParameters(appId);
var productViewController = new SKStoreProductViewController();
// must set the Finished handler before displaying the view controller
productViewController.Finished += (sender2, err) => {
// Apple's docs says to use this method to close the view controller
this.navigationController.DismissViewController(true, null);
MySettings.AskedForReview = true;
};
productViewController.LoadProduct(spp, (ok, err) => { // ASYNC !!!
if (ok)
{
this.navigationController.PresentViewController(productViewController, true, null);
}
else
{
Console.WriteLine(" failed ");
if (err != null)
Console.WriteLine(" with error " + err);
}
});
}
Hello you could try iRate from Nick Lockwood and see if that fits your needs, you can find the MonoTouch bindings of iRate here.
btw it uses the following URL to open AppStore in review mode:
itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id= your-AppID-here
Alex

Resources