implement android beam in xamarin forms - xamarin.forms

I'm develop cross-platform mobile application that use NFC. I already check the xamarin android beam here. Now i'm trying implement the same sample using xamarin forms so i'm using dependency service to call the function from android project. I try to make simple message that pass from device to another but it didn't work. The problem is the implementation of NfcAdapter.IOnNdefPushCompleteCallback
PhoneBeam.cs
using System;
using System.Text;
using Android.App;
using MyApp.Droid;
using Android.Nfc;
using Xamarin.Forms;
[assembly: Dependency(typeof(PhoneBeam))]
namespace MyApp.Droid
{
public class PhoneBeam : Activity, NfcAdapter.ICreateNdefMessageCallback, NfcAdapter.IOnNdefPushCompleteCallback, iBeam
{
private NfcAdapter nfcAdapter;
public void Beam()
{
nfcAdapter = NfcAdapter.GetDefaultAdapter(MainActivity.Instance);
nfcAdapter.SetNdefPushMessageCallback(this, MainActivity.Instance);
nfcAdapter.SetOnNdefPushCompleteCallback(this, MainActivity.Instance);
}
public NdefMessage CreateNdefMessage(NfcEvent evt)
{
DateTime time = DateTime.Now;
var text = ("Beam me up!\n\n" + "Beam : " +
time.ToString("HH:mm:ss"));
NdefMessage msg = new NdefMessage(
new NdefRecord[]{ CreateMimeRecord (
"application/com.example.android.beam",
Encoding.UTF8.GetBytes (text)) });
return msg;
}
public NdefRecord CreateMimeRecord(String mimeType, byte[] payload)
{
byte[] mimeBytes = Encoding.UTF8.GetBytes(mimeType);
NdefRecord mimeRecord = new NdefRecord(
NdefRecord.TnfMimeMedia, mimeBytes, new byte[0], payload);
return mimeRecord;
}
}
}
It says 'PhoneBeam does not implement interface member NfcAdapter.IOnNdefPushCompleteCallback.OnNdefPushComplete(NfcEvent)'. Am i missing something?

Your class implements IOnNdefPushCompleteCallback, which has a public method OnNdefPushComplete that must be implemented in order to satisfy the Interface.

Related

How to send message from Unity to SignalR Self Hosted server using asp.net signal R and not asp.netcore

I am trying to create a chat application in a Unity game. So basically in one instance of the game if someone sends a message , all the other open instances of the game should get the message.
I successfully created a self-hosted SignalR 2 server using this tutorial
The code for the console app is as follows:
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
using Microsoft.Owin.Cors;
namespace SignalRSelfHost
{
class Program
{
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8080";//a web application of type Startup is started at the specified URL (http://localhost:8080).
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{// the class containing the configuration for the SignalR server ,which creates routes for any Hub objects in the project.
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{//the SignalR Hub class that the application will provide to clients.
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);//clients will call to broadcast a message to all other connected clients.
}
}
}
I created a dummy Unity game as of now. There are the username input field and chat input field. Next to the chat input field there is a send button. So the person enters his/her name and enters something into the chat and all running instances of the game should receive the message , but I am not able to achieve that thing
The script for this is attached to a empty GameObject SignalRManager. The code is as follows:
using System.Collections.Generic;
using UnityEngine;
using Microsoft.AspNet.SignalR.Client;
using UnityEngine.UI;
using System;
using TMPro;
public class SignalRManager : MonoBehaviour
{
string url = "http://localhost:8080";
HubConnection connection;
[SerializeField] private GameObject ChatMessage;
[SerializeField] private GameObject UserName;
// Start is called before the first frame update
void Start()
{
connection = new HubConnection(url);
connection.Start();
connection.StateChanged += connection_StateChanged;
}
private void connection_StateChanged(StateChange state)
{
if(state.NewState== ConnectionState.Connected)
{
Debug.Log("Connected to Server");
}
if(state.NewState == ConnectionState.Disconnected)
{
Debug.Log("Disconnected from Server");
}
}
public void OnClickSendChatButton()
{
string message= ChatMessage.GetComponent<TMP_InputField>().text;
string userName= UserName.GetComponent<TMP_InputField>().text;
}
private void OnDisable()
{
connection.StateChanged -= connection_StateChanged;
}
}
I am able to connect to the server as the log message appears , but dont know what to write to send message to the server. Also how do I receive the message from the server as well
I tried to use a function called connection.Send(), but it is not accepting two arguments. I tried to look into many tutorials ,but many are for asp.netcore Signal r , but I need it for asp.net signalR because thats my requirement.
<<<<--------edit------>>>
I was able to send data to the server but I am not able to receive data from the server on my Unity game. The updated code is as follows:
using System.Collections.Generic;
using UnityEngine;
using Microsoft.AspNet.SignalR.Client;
using UnityEngine.UI;
using System;
using TMPro;
public class SignalRManager : MonoBehaviour
{
string url = "http://localhost:8080";
HubConnection connection;
IHubProxy hubProxy;
[SerializeField] private GameObject ChatMessage;
[SerializeField] private GameObject UserName;
[SerializeField] private TextMeshProUGUI OutputText;
// Start is called before the first frame update
void Start()
{
connection = new HubConnection(url);//Create a connection for the SignalR server
hubProxy = connection.CreateHubProxy("MyHub");//Get a proxy object that will be used to interact with the specific hub on the server.There may be many hubs hosted on the server, so provide the type name for the hub
connection.Start();
connection.StateChanged += connection_StateChanged;
OutputText.text = "";
}
private void OnReceivedMessageFromServer(string name, string message)
{
OutputText.text = OutputText.text+name + ":" + message + "\n";
Debug.Log(OutputText.text + name + ":" + message + "\n");
}
private void connection_StateChanged(StateChange state)
{
if(state.NewState== ConnectionState.Connected)
{
Debug.Log("Connected to Server");
}
if(state.NewState == ConnectionState.Disconnected)
{
Debug.Log("Disconnected from Server");
}
}
public void OnClickSendChatButton()
{
string message= ChatMessage.GetComponent<TMP_InputField>().text;
string userName= UserName.GetComponent<TMP_InputField>().text;
hubProxy.On<string, string>("addMessage", OnReceivedMessageFromServer);//register hub events (methods invoked by the hub). The following code registers a handler method for ChatMessage event.
hubProxy.Invoke("Send", userName, message);
}
private void OnDisable()
{
connection.StateChanged -= connection_StateChanged;
}
}
The callBack OnReceivedMessageFromServer is not invoked. Can anyone help me here?

NServicebus not unable to resolve service for interface in handler

I'm trying to build my first NserviceBus application, but I cannot get it to work. The message arrives on the "backend" but the handler uses DI and NServicebus claims it cannot find the service for the interface. But the code was lifted directly from the examples. I'm missing something and I cannot figure it out.
The Error:
dbug: NServiceBus.LoadHandlersConnector[0]
Processing message type: Messages.JobStartCommand
Message headers:
NServiceBus.MessageId : 0044f261-e3b3-4287-b6f0-ad7400ef43cb
NServiceBus.MessageIntent : Send
NServiceBus.ConversationId : bb4f276f-63fe-450b-b234-ad7400ef43cd
NServiceBus.CorrelationId : 0044f261-e3b3-4287-b6f0-ad7400ef43cb
NServiceBus.ReplyToAddress : ClientUI
NServiceBus.OriginatingMachine : L19002992
NServiceBus.OriginatingEndpoint : ClientUI
$.diagnostics.originating.hostid : e1fecb2b72b8185e47341bb4dfb37dd7
NServiceBus.ContentType : text/xml
NServiceBus.EnclosedMessageTypes : Messages.JobStartCommand, Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
NServiceBus.Version : 7.5.0
NServiceBus.TimeSent : 2021-07-29 14:31:08:228497 Z
NServiceBus.Retries : 3
NServiceBus.Retries.Timestamp : 2021-07-29 14:31:40:397277 Z
Handlers to invoke:
Extractor.JobStartHandler
Extractor.JobStartHandler
info: NServiceBus.RecoverabilityExecutor[0]
Immediate Retry is going to retry message '0044f261-e3b3-4287-b6f0-ad7400ef43cb' because of an exception:
System.InvalidOperationException: Unable to resolve service for type 'IExtract' while attempting to activate 'Extractor.JobStartHandler'.
at Microsoft.Extensi
My backend program.cs
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NServiceBus;
using System.Threading.Tasks;
using Messages;
using Extractor;
namespace ExtractorOsiris
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.UseWindowsService();
//builder.UseMicrosoftLogFactoryLogging();
builder.ConfigureLogging((ctx, logging) =>
{
logging.AddConfiguration(ctx.Configuration.GetSection("Logging"));
//logging.AddEventLog();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
});
#region back-end-use-nservicebus
builder.UseNServiceBus(ctx =>
{
var endpointConfiguration = new EndpointConfiguration("Sample.Extractor");
endpointConfiguration.UseTransport<LearningTransport>();
var transport = endpointConfiguration.UseTransport<LearningTransport>();
transport.Routing().RouteToEndpoint(typeof(ProcessObjectCommand), "Sample.Processor");
endpointConfiguration.DefineCriticalErrorAction(OnCriticalError);
return endpointConfiguration;
});
#endregion
#region back-end-register-service
builder.ConfigureServices(services =>
{
services.AddSingleton<IExtract, ExtractOsiris>();
});
#endregion
return builder;
}
private static Task OnCriticalError(ICriticalErrorContext arg)
{
throw new NotImplementedException();
}
}
}
The Interface
using Newtonsoft.Json.Linq;
using NServiceBus;
using System;
using System.Threading.Tasks;
namespace Extractor
{
public interface IExtract
{
Task<JArray> Extract(string #object, DateTime deltaTime);
}
}
The handler
using System.Threading.Tasks;
using Messages;
using Newtonsoft.Json.Linq;
using NServiceBus;
namespace Extractor
{
#region back-end-handler
public class JobStartHandler : IHandleMessages<JobStartCommand>
{
private readonly IExtract extractor;
public JobStartHandler(IExtract extractor)
{
this.extractor = extractor;
}
public async Task Handle(JobStartCommand message, IMessageHandlerContext context)
{
Task<JArray> result = extractor.Extract("Medewerkers", message.DeltaTime);
await result;
JArray test = result.Result;
foreach (JObject x in test)
{
// send the object to be processed.
//await context.Send(new ProcessObjectCommand(x.ToString()));
}
}
}
#endregion
}
The implementation of the interface
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Extractor;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NServiceBus;
namespace ExtractorOsiris
{
public class ExtractOsiris : IExtract
{
private readonly ILogger logger;
public ExtractOsiris(ILogger<ExtractOsiris> logger)
{
this.logger = logger;
}
public Task<JArray> Extract(string #object, DateTime deltaTime)
{
logger.LogInformation($"getting {#object} for delta time {deltaTime}");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://xx.yy");
WebResponse response = request.GetResponse();
JObject temp = JObject.Load(new JsonTextReader(new StreamReader(response.GetResponseStream())));
return Task<JArray>.FromResult(temp["items"] as JArray);
}
}
}
I know the code ain't pretty, but it's pure quick and dirty testing code.
I cleaned out the offending interface from the code and still got the error on an interface that was not even there anymore. I removed the bin folder and build everything back up step by step. Stupid thing is, source control says I am back where I started, without changes. And it just works....
So must have bin something stupid with VS2019. Sorry to anyone who's time I wasted

Is it possible to integrate AdMob in an Android app using Uno Platform

I have a few UWP apps I would like to migrate to Android.
I already migrated some using Xamarin.Forms
I have discovered Uno Platform that seems to be great. But I didn't find any information about integration AdMob advertisement in an Android project using Uno Platform.
Has anyone done it already?
Yes, it is possible and I have been able to get it working in my Uno Platform app on Android and iOS. I am planning to write a blogpost about getting AdMob and AdSense running on Android, iOS and WASM, and publish a Uno Platform library on NuGet that will do all the heavy lifting for you, so stay tuned :-) .
For now, here is a unedited, raw version of the control I am using currently. It requires that you install the Google Play Services Ads NuGet packages in the Android project and in the iOS project.
Android
#if __ANDROID__
using Android.Gms.Ads;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Text;
using Uno.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace SmsTicket.Core.Controls
{
public partial class AdControl : ContentControl
{
public AdControl()
{
var adView = new AdView(ContextHelper.Current);
adView.AdSize = AdSize.SmartBanner;
adView.AdUnitId = "YOUR AD UNIT ID";
HorizontalContentAlignment = HorizontalAlignment.Stretch;
VerticalContentAlignment = VerticalAlignment.Stretch;
var adParams = new LinearLayout.LayoutParams(
LayoutParams.WrapContent, LayoutParams.WrapContent);
adView.LayoutParameters = adParams;
adView.LoadAd(new AdRequest.Builder().AddTestDevice("YOUR TEST DEVICE ID").Build());
Content = adView;
}
}
}
#endif
iOS
#if __IOS__
using Google.MobileAds;
using System;
using System.Collections.Generic;
using System.Text;
using UIKit;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using CoreGraphics;
namespace SmsTicket.Core.Controls
{
public partial class AdControl : ContentControl
{
public AdControl()
{
HorizontalContentAlignment = HorizontalAlignment.Stretch;
VerticalContentAlignment = VerticalAlignment.Stretch;
Background = SolidColorBrushHelper.Red;
Width = AdSizeCons.LargeBanner.Size.Width;
Height = AdSizeCons.LargeBanner.Size.Height;
Windows.UI.Xaml.Window.Current.Activated += Current_Activated;
}
private void LoadAd()
{
if (!(Content is BannerView))
{
var adView = new BannerView(AdSizeCons.LargeBanner)
{
AdUnitID = "YOUR AD UNIT ID",
RootViewController = GetVisibleViewController()
};
adView.LoadRequest(GetRequest());
Content = adView;
}
}
Request GetRequest()
{
var request = Request.GetDefaultRequest();
// Requests test ads on devices you specify. Your test device ID is printed to the console when
// an ad request is made. GADBannerView automatically returns test ads when running on a
// simulator. After you get your device ID, add it here
request.TestDevices = new[] { Request.SimulatorId.ToString(), "YOUR TEST DEVICE ID" };
return request;
}
UIViewController GetVisibleViewController()
{
UIViewController rootController;
if (UIApplication.SharedApplication.KeyWindow == null)
{
return null;
}
else
{
rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
}
if (rootController.PresentedViewController == null)
return rootController;
if (rootController.PresentedViewController is UINavigationController)
{
return ((UINavigationController)rootController.PresentedViewController).VisibleViewController;
}
if (rootController.PresentedViewController is UITabBarController)
{
return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
}
return rootController.PresentedViewController;
}
private void Current_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
{
LoadAd();
}
}
}
#endif
Also make sure to include the Ad control only conditionally (as I have provided only Android and iOS version here).
Uno Platform won't block you from using any third party (especially not on mobile). If there is a xamarin binding you can use it as-is in your code, just like you would use it in a Xamarin.Forms app. If it's a library affecting the view you will most likely create a custom control and interact with the third-party classes via C#.
If there's no xamarin binding for a library, you can create one following microsoft documentation.
Good news! For admob there is a microsoft-supported binding nuget and it has been used in an uno application in the past.

Xamarin iOS reference in Android project in Xamarin Forms

I created a cross platform application using Xamarin. I need to call native functions of iOS and Android platform in my project. Here is the code:
private static Func<IDownloadFile, string> _downloadPath = new Func<IDownloadFile, string>(file =>
{
if (Device.RuntimePlatform == Device.iOS)
{
string fileName = (new NSUrl(file.Url, false)).LastPathComponent;
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);
}
else if (Device.RuntimePlatform == Device.Android)
{
string fileName = Android.Net.Uri.Parse(file.Url).Path.Split('/').Last();
return Path.Combine(Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDownloads).AbsolutePath, fileName);
}
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "");
});
This is the code from notification plugin https://github.com/thudugala/Plugin.LocalNotification.
The problem is when I use that code the Mono.Android and Xamarin.iOS references are being added to my shared project in Dependencies/Assemblies and then when I try to run application in the release mode there is a reference error - I noticed that in my Android project in bin/Release there is Xamarin.iOS reference but there is no reference in Android project. When I remove that reference from Dependencies/Assemblies and comment native calls in my code everything compiles correctly. I am confused because of this. Is my above code correctly or I need to call native functions in another way?
When using .net Standard the approach taken is using an interface that defines the functionality you want to expose then implement in each platform.
In Shared:
public interface IMyInterface
{
string GetUrlPath(string fileUrl);
}
iOS Implementation:
public class MyClass : IMyInterface
{
public string GetUrlPath(string fileUrl)
{
string fileName = (new NSUrl(file.Url, false)).LastPathComponent;
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);
}
}
Android Implementation:
public class MyClass : IMyInterface
{
public string GetUrlPath(string fileUrl)
{
string fileName = (new NSUrl(file.Url, false)).LastPathComponent;
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);
}
}
Then using either Xamarin.Forms DependencyService or any other IoC container you can match the Interface with the correct implementation.
In your shared code you will use the Interface and the implementation picked will be transparent.
This post shows a very complete example of how to do it.

Xamarin forms 4 shell navigation with complex parameters

I´m migratting a xamarin forms 3.x app with Prism to forms 4 with shell navigation.
Do I have to create my custom solution to pass complex parameters to the new page or Xamarin has some buildin feature to receive other than string parameters?
Thanks.
As far as I know, and, reading the docs, the only samples regard passing simple data, like string when navigating.
However, I was able to find an Issue (and Pull Request), for passing objects/ Models, for the next version (I assume this is the case you are referring to).
You can track it here.
I've run some tests which seems to be working. I'm relatively new to Xamarin, hence recommend caution and welcome any feedback for any potential issues I may be overlooking.
I wrote an extension to Shell to accept a data object parameter 'navigationData' as follows:-
await Shell.Current.GoToAsync(state, navigationData, animate);
The extension ...
namespace Xamarin.Forms
{
public static class ShellExtensions
{
public static async Task GoToAsync(this Shell shell, ShellNavigationState state, object navigationData, bool animate=false)
{
shell.Navigated += async (sender, e) =>
{
if ((Shell.Current?.CurrentItem?.CurrentItem as IShellSectionController)?.PresentedPage is MyContentPage
p) await p.InitializeAsync(navigationData).ConfigureAwait(false);
};
await shell.GoToAsync(state, animate);
}
}
}
As shown above the extension:-
hooks to the Shell 'Navigated' event,
retrieves the 'current view (page)' as 'MyContentPage' i.e. subclassed ContentPage,
calls an InitializeAsync method on the view passing in the
navigationData parameter
the view then calls an InitializeAsync
method on the binding context (view model) passing the
navigationData parameter onto the viewModel.
In the extension method above, 'MyContentPage' is a custom abstract subclass of ContentPage with an InitializeAsync(navigationData) method that simply calls a similar method on the viewModel (binding context of the view).
Similarily, ViewModels subclass a custom ViewModelBase class that has a virtual InitializeAsync(navigationData). This can be overridden in the viewModel with the desired implementation and handling of the navigation data.
Simplified sample of Views, ViewModels and related base classes shown below
using System.Threading.Tasks;
using MyXamarinApp.ViewModels;
using Xamarin.Forms;
namespace MyXamarinApp.Views
{
public ItemDetailPage : MyContent<ItemDetailViewModel>{}
public ItemPage : MyContentPage<ItemViewModel>{}
public abstract class MyContentPage<T> : MyContentPage where T : ViewModelBase
{
protected T Vm;
protected override ViewModelBase VmBase => Vm as ViewModelBase;
protected MyContentPage()
{
BindingContext = Vm = ViewModelLocator.Resolve<T>();
}
private Comand _showDetailCommand;
public Command ShowDetailCommand
{
get { return _showDetailCommand ??= new Command(async () =>
await Shell.Current.GoToAsync("itemDetail", new NavigationDataObject())); }
}
}
public abstract class MyContentPage : ContentPage
{
protected abstract ViewModelBase VmBase { get; }
public virtual async Task InitializeAsync(object navigationData)
{
await VmBase.InitializeAsync(navigationData);
}
}
}
public class NavigationDataObject
{
'Properties' etc.
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace MyXamarinApp.ViewModels
{
public ItemViewModel : ViewModelBase{}
public ItemDetailViewModel : ViewModelBase
{
private NavigationDataObject _navData;
public override async Task InitializeAsync(object navigationData)
{
if (navigationData is NavigationDataObject navData)
{
_navData = navData;
}
await base.InitializeAsync(navigationData);
}
}
public abstract class ViewModelBase
{
public virtual Task InitializeAsync(object navigationData)
{
return Task.FromResult(false);
}
}
}
You can always serialize the model to a JSON string and un-serializes it on the other side?
async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
return;
DailyPnL PnLClicked = (DailyPnL)e.Item;
string jason = await Task.Run(() => JsonConvert.SerializeObject(PnLClicked));
await Shell.Current.GoToAsync($"viewdailypnl?pnlmodel={jason}");
//Deselect Item
((ListView)sender).SelectedItem = null;
}
Then in your code behind:
public string pnlmodel
{
set
{
string derulo = Uri.UnescapeDataString(value);
viewModel.PnL = Task.Run(() => JsonConvert.DeserializeObject<DailyPnL>(derulo)).Result;
}
}
There is a framework called Xamarin.Zero https://github.com/markjackmilian/Xam.Zero
It lets you use shell while giving you convenient ViewModel to ViewModel navigation, IOC.
You can user stored preferences to store complex data like:
private async void OnItemSelected(Item item)
{
if (item == null)
return;
var jsonstr = JsonConvert.SerializeObject(item);
//Clear the shared preferences in case there is any
Preferences.Clear();
//Store your complex json on a shared preference
Preferences.Set("Data", jsonstr);
await Shell.Current.GoToAsync(nameof(DetailsPage));
}
Retrieve it on the details page like:
bool hasKey = Preferences.ContainsKey("Data");
var content = Preferences.Get("Data", string.Empty);
Details details = hasKey ? JsonConvert.DeserializeObject<Model>(content) : null;

Resources