Hello everyone I am working on the project related to the WebView and everything is fine so far. When I use WKNavigationDelegate and build with simulator it is working but when I try to build with the real device it gives me an error and I cannot publish my app.
Any help would be appreciate thank you.
Error MT4136: The registrar cannot marshal the parameter type 'System.Action`2<WebKit.WKNavigationActionPolicy,WebKit.WKWebpagePreferences>' of the parameter '?' in the method 'WebKit.IWKNavigationDelegate.' (MT4136) (midssamIOSXamarin)
Error MT4136: The registrar cannot marshal the parameter type 'System.Action`2<Foundation.NSUrlSessionAuthChallengeDisposition,Foundation.NSUrlCredential>' of the parameter '?' in the method 'WebKit.IWKNavigationDelegate.' (MT4136) (midssamIOSXamarin)
namespace midssamIOSXamarin
{
public partial class ViewController : UIViewController
{
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var configuration = new WKWebViewConfiguration();
var controller = new WKUserContentController();
configuration.UserContentController = controller;
var webView = new WKWebView(View.Frame, configuration);
webView.UIDelegate = new JavascriptUIDelegate();
webView.NavigationDelegate = new JavascriptNavigationDelegate();
this.View.AddSubview(webView);
webView.LoadRequest(new NSUrlRequest("test url"));
}
}
public class JavascriptUIDelegate : WKUIDelegate
{
public JavascriptUIDelegate()
{
}
public override void RunJavaScriptAlertPanel(WKWebView webView, string message, WKFrameInfo frame, Action completionHandler)
{
var alert = UIAlertController.Create("", message, UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("확인", UIAlertActionStyle.Default, null));
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(alert, true, completionHandler);
}
public override void RunJavaScriptConfirmPanel(WKWebView webView, string message, WKFrameInfo frame, Action<bool> completionHandler)
{
var alert = UIAlertController.Create("", message, UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("확인", UIAlertActionStyle.Default, action => completionHandler(true)));
alert.AddAction(UIAlertAction.Create("취소", UIAlertActionStyle.Cancel, action => completionHandler(false)));
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(alert, true, null);
}
}
public class JavascriptNavigationDelegate : WKNavigationDelegate
{
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
decisionHandler(WKNavigationActionPolicy.Allow);
}
public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error)
{
}
public override void DidFailProvisionalNavigation(WKWebView webView, WKNavigation navigation, NSError error)
{
}
public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation)
{
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
}
public override void DidCommitNavigation(WKWebView webView, WKNavigation navigation)
{
}
}
}
I am also a developer using xamarin.
I had the same error when I tried to implement the sample WebView app.
The most effective way to deal with it was to enable the Mono interpreter.
Click on the project name in the solution explorer to open the
project options
Open the "iOS build" tab
Check "Enable Mono interpreter"
In my case, the above method solved the problem.
i have a Xamarin-Android app in which i used fragments .i want to access public Method of MainActivity in my fragment class.
webview_download+=mWebViewDownload
but this method mWebViewDownload is defined in MainActivity.i cannot access this method from fragment.
i tried to make this method static but this method uses services which can't be accessed without instance.
i tried to access through this.mWebViewDownload but the error is mWebViewDownload is not defined in this scope like that.
i searched stackoverflow for it most of question suggest getActivity() but this is java related solution but i need c# related solution.
i tried to access it through MainActivity.mWebViewDownload but it also gives error that cann't access non-static without object reference like that.please help.fragment class is as follows:
internal class WebviewFragment : Fragment
{
public const string ARG_NUMBER = "number";
public WebviewFragment()
{
// Empty constructor required for fragment subclasses
}
public static Fragment NewInstance(int position)
{
Fragment fragment = new WebviewFragment();
Bundle args = new Bundle();
args.PutInt(WebviewFragment.ARG_NUMBER, position);
fragment.Arguments = args;
return fragment;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.Inflate(Resource.Layout.fragment_content2, container, false);
var i = this.Arguments.GetInt(ARG_NUMBER);
var url = this.Resources.GetStringArray(Resource.Array.weburls_array)[i];
var title = this.Resources.GetStringArray(Resource.Array.contents_array)[i];
// show progress bar
progressBar = (ProgressBar)rootView.FindViewById<ProgressBar>(Resource.Id.progressBar1);
var web_view = rootView.FindViewById<WebView>(Resource.Id.webview);
web_view.SetWebViewClient(new HelloWebViewClient());
web_view.Settings.JavaScriptCanOpenWindowsAutomatically = true;
web_view.Settings.JavaScriptEnabled = true;
web_view.Download += Mwebview_Download;// here is error
//set the custom web client
web_view.SetWebViewClient(new JavaScriptWebViewClient());
web_view.LoadUrl(url);
this.Activity.Title = title;
return rootView;
}
}
here is the mWebView_Download Method in MainActivity class
// Download
public void Mwebview_Download(object sender, DownloadEventArgs e)
{
var listPermissions = new System.Collections.Generic.List<string>();
if (CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
Log.Warn(LOG_TAG, "CheckSelfPermission(WriteExternalStorage) not yet granted - will prompt user for permission");
listPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
// Make the request with the permissions needed...and then check OnRequestPermissionsResult() for the results
RequestPermissions(listPermissions.ToArray(), PERMISSION_Write_External_Storage);
}
else
{
var url = e.Url;
DownloadManager.Request request = new DownloadManager.Request(Android.Net.Uri.Parse(url));
request.AllowScanningByMediaScanner();
string filename = System.IO.Path.GetFileName(url);
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
// request.SetNotificationVisibility(DownloadManager.Request.VisibilityVisibleNotifyCompleted); //Notify client once download is completed!
request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, filename);
DownloadManager dm = (DownloadManager)GetSystemService("download");
dm.Enqueue(request);
Toast.MakeText(ApplicationContext, "Downloading File", ToastLength.Long//To notify the Client that the file is being downloaded
).Show();
}
}
```
Solutions:
1.Define a static property in your MainActivity and use it in Fragment, for example:
public class MainActivity : AppCompatActivity
{
public static MainActivity Instance;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Instance = this;
}
public void test()
{
}
}
And then in your fragment, you can access the method by:
MainActivity.Instance.test();
2.getActivity() method in C# is ((ActivityType)Activity).yourPublicMethod();
:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
((MainActivity)Activity).test();
return base.OnCreateView(inflater, container, savedInstanceState);
}
I'm using VS 17 for Xamarin Forms. I've set up Prism in my Xamarin.Forms app and I just added a reference to my Api interface (in ViewModel Constructor) and it makes the app stop navigation to the second page. I need to do this in order to pass parameters etc. I followed this guide:
https://blog.qmatteoq.com/prism-for-xamarin-forms-basic-navigation-and-dependency-injection-part-2/
This is what I did to make the navigation stop working:
private readonly IService _Service;
private ObservableCollection<TodoItem> _topSeries;
public ObservableCollection<TodoItem> TopSeries
{
get { return _topSeries; }
set { SetProperty(ref _topSeries, value); }
}
This is the constructor:
public SecondPageViewModel(IService Service, INavigationService navigationService)
{
_Service = Service;
_navigationService = navigationService;
}
So I cant even reach the above viewmodel because of the above code that I added. I tried to put break points on the DelegateCommand (on first ViewModel) but it just stops after InitializeComponent(); and then nothing happens. No error messages! Thanks!
Update:
My Service class that fetches data:
public class Service : IService
{
public List<TodoItem> TodoList { get; private set; }
HttpClient client;
Service()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<TodoItem>> DataAsync()
{
TodoList = new List<TodoItem>();
var uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
TodoList = JsonConvert.DeserializeObject<List<TodoItem>>(content);
Debug.WriteLine(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return TodoList;
}
}
This is my App.Xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<View.MainPage, MainPageViewModel>();
containerRegistry.RegisterForNavigation<View.SecondPage, SecondPageViewModel>();
containerRegistry.Register<IService, Service>();
}
My Interface:
public interface IService
{
Task<List<TodoItem>> DataAsync();
}
This is how I navigate (click from listview):
private EventItem _selectedEvent { get; set; }
public EventItem SelectedEvent
{
get { return _selectedEvent; }
set
{
if (_selectedEvent != value)
{
if (Device.RuntimePlatform == Device.iOS)
{
_selectedEvent = null;
}
else
{
_selectedEvent = value;
}
NavigationParameters navParams = new NavigationParameters();
navParams.Add("PassedValue", _todoItem.name);
_navigationService.NavigateAsync("SecondPage", navParams);
}
}
}
Edit:
When I debug without the ApiService code the command is taking me to new new constructor in the new viewmodel. With the code it does not reach the contructor.
According to your code you have declared constructor like this:
Service()
{
// ...
}
You didn't set access modifier, therefore the default one is internal. Here is the definition:
Internal types or members are accessible only within files in the same
assembly.
Most likely you have your Service.cs declared in another Assembly and Prism can't access its constructor.
Your navigation doesn't work because dependency injection fails. To fix it, just change your access modifier to public:
public Service()
{
// ...
}
I'm trying to use the ShouldOverrideUrlLoading() method but the app crashes when I call it.
Below is my code:
private class HybridWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView webView, string url)
{
var tel = "tel:";
if (url.StartsWith(tel))
{
var uri = Android.Net.Uri.Parse(url);
var intent = new Intent(Intent.ActionDial, uri);
var act = new Activity();
act.StartActivity(intent);
}
}
}
Thanks in Advance!
The problem lies in the following codes snippet:
var act = new Activity();
act.StartActivity(intent);
The method StartActivity should be called from current context instead of a new Activity. So you need to pass the current context to HybridWebViewClient:
public class HybridWebViewClient : WebViewClient
{
Context context;
public HybridWebViewClient(Context context)
{
this.context = context;
}
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
var tel = "tel:";
if (url != null)
{
if (url.StartsWith(tel))
{
var uri = Android.Net.Uri.Parse(url);
var intent = new Intent(Intent.ActionDial, uri);
context.StartActivity(intent);
}
}
return true;
}
}
And in the OnCreate method:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
...
webview.SetWebViewClient(new HybridWebViewClient(this));
webview.LoadUrl("http://example.com");
...
}
What is in the craash dump? Is this related?
shouldOverrideUrlLoading(WebView view, String url)
This method was deprecated in API level 24. Use shouldOverrideUrlLoading(WebView, WebResourceRequest) instead.
I am trying to integrate signalR in android app but no luck. I've been looking at various links but none of them provide proper information about implementation.
I've the following questions.
SignalR integration has to be done inside Service/Intent Service?
If we want to receive response via same calling method then how to get?
I've added three libraries i.e signalr android,signalr client and gson but unable to understand how code works, no proper documentation is available to understand the code.
Some of the questions asked but not much information
SignalR in Android Studio
Unable to implement p2p chat using SignalR in Android
If anyone experienced in signal for native apps, it would be very helpful for me.
Update
public class SignalRService extends Service {
private static final String TAG = "Service";
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder();
private SharedPreferences sp;
#Override
public void onCreate() {
super.onCreate();
Utility.showLog(TAG, "Service Created");
sp = getSharedPreferences(Utility.SHARED_PREFS, MODE_PRIVATE);
mHandler = new Handler(Looper.myLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public IBinder onBind(Intent intent) {
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage() {
String SERVER_METHOD_SEND = "iAmAvailable";
final String string = new String();
mHubProxy.invoke(new String(), SERVER_METHOD_SEND, sp.getString("user_id", null), sp.getString("pass", null), "TransMedic").done(new Action() {
#Override
public void run(Object o) throws Exception {
Utility.showLog(TAG, o.toString());
}
}).onError(new ErrorCallback() {
#Override
public void onError(Throwable throwable) {
}
});
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
String serverUrl = "http://transit.alwaysaware.org/signalr";
mHubConnection = new HubConnection(serverUrl);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
sendMessage();
}
#Override
public void onDestroy() {
mHubConnection.stop();
super.onDestroy();
}
}
UPDATE 2018:
If you are using SignalR.net Core use this library otherwise you will get error on connection.
SERVER SIDE:
The following is my sample server-side code, you can pay attention to public void Send(string message) and public void SendChatMessage(string to, string message).
Server-side app: public void SendChatMessage(string to, string message)
Android client app: mHubProxy.invoke("SendChatMessage", receiverName, message);
Server-side app: public void Send(string message)
Android client app: mHubProxy.invoke("Send", message);
namespace SignalRDemo
{
public class ChatHub : Hub
{
private static ConcurrentDictionary<string, string> FromUsers = new ConcurrentDictionary<string, string>(); // <connectionId, userName>
private static ConcurrentDictionary<string, string> ToUsers = new ConcurrentDictionary<string, string>(); // <userName, connectionId>
private string userName = "";
public override Task OnConnected()
{
DoConnect();
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online" });
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
if (stopCalled) // Client explicitly closed the connection
{
string id = Context.ConnectionId;
FromUsers.TryRemove(id, out userName);
ToUsers.TryRemove(userName, out id);
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline" });
}
else // Client timed out
{
// Do nothing here...
// FromUsers.TryGetValue(Context.ConnectionId, out userName);
// Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline By TimeOut"});
}
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
DoConnect();
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online Again" });
return base.OnReconnected();
}
private void DoConnect()
{
userName = Context.Request.Headers["User-Name"];
if (userName == null || userName.Length == 0)
{
userName = Context.QueryString["User-Name"]; // for javascript clients
}
FromUsers.TryAdd(Context.ConnectionId, userName);
String oldId; // for case: disconnected from Client
ToUsers.TryRemove(userName, out oldId);
ToUsers.TryAdd(userName, Context.ConnectionId);
}
public void Send(string message)
{
// Call the broadcastMessage method to update clients.
string fromUser;
FromUsers.TryGetValue(Context.ConnectionId, out fromUser);
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = fromUser, Message = message });
}
public void SendChatMessage(string to, string message)
{
FromUsers.TryGetValue(Context.ConnectionId, out userName);
string receiver_ConnectionId;
ToUsers.TryGetValue(to, out receiver_ConnectionId);
if (receiver_ConnectionId != null && receiver_ConnectionId.Length > 0)
{
Clients.Client(receiver_ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = message });
}
}
}
public class ChatMessage
{
public string UserName { get; set; }
public string Message { get; set; }
}
}
CLIENT SIDE:
If you have not read my answer at the following question:
SignalR integration in android studio
Then, here is my working basic code:
public class SignalRService extends Service {
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder(); // Binder given to clients
public SignalRService() {
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public void onDestroy() {
mHubConnection.stop();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Return the communication channel to the service.
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage(String message) {
String SERVER_METHOD_SEND = "Send";
mHubProxy.invoke(SERVER_METHOD_SEND, message);
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader("User-Name", "BNK");
}
};
String serverUrl = "http://192.168.1.100";
mHubConnection = new HubConnection(serverUrl);
mHubConnection.setCredentials(credentials);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
String HELLO_MSG = "Hello from Android!";
sendMessage(HELLO_MSG);
String CLIENT_METHOD_BROADAST_MESSAGE = "broadcastMessage";
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler1<CustomMessage>() {
#Override
public void run(final CustomMessage msg) {
final String finalMsg = msg.UserName + " says " + msg.Message;
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, CustomMessage.class);
}
}
Activity:
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private SignalRService mService;
private boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setClass(mContext, SignalRService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
public void sendMessage(View view) {
if (mBound) {
// Call a method from the SignalRService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
EditText editText = (EditText) findViewById(R.id.edit_message);
if (editText != null && editText.getText().length() > 0) {
String message = editText.getText().toString();
mService.sendMessage(message);
}
}
}
/**
* Defines callbacks for service binding, passed to bindService()
*/
private final ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to SignalRService, cast the IBinder and get SignalRService instance
SignalRService.LocalBinder binder = (SignalRService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
CustomMessage Class:
public class CustomMessage {
public String UserName;
public String Message;
}
You can also see my sample client project at this GitHub link
UPDATE FOR RESPONSE FROM INVOKE:
I have just added new sample methods:
Server side:
public string iAmAvailable(string username, string password, string message)
{
return "BNK Response for testing Android INVOKE";
}
Client side:
mHubProxy.invoke(String.class, "iAmAvailable", "username", "password", "TransMedic").done(new Action<String>() {
#Override
public void run(String s) throws Exception {
Log.w("SimpleSignalR", s);
}
}).onError(new ErrorCallback() {
#Override
public void onError(Throwable throwable) {
Log.e("SimpleSignalR", throwable.toString());
}
});
And here is the screenshot:
This work for me : Full source Android (Client) & Server GitHub
Server Slide If one argument must use this interface SubscriptionHandler1 if two argument must use this interfaceSubscriptionHandler2 ,...
Sample for two argument like :
Server slide :
using Microsoft.AspNet.SignalR;
namespace SignalRChat
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Two argument must use this interfaceSubscriptionHandler2 .
Clients.All.broadcastMessage(name, message);
}
}
}
Client slide :
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler2<String, String>() {
#Override
public void run(final String name,final String msg) {
final String finalMsg = msg.toString();
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, String.class,String.class);
For catch all message can use this :
mHubConnection.received(new MessageReceivedHandler() {
#Override
public void onMessageReceived(final JsonElement json) {
Log.e("onMessageReceived ", json.toString());
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), json.toString(), Toast.LENGTH_SHORT).show();
}
});
}
});
The SignalR team recently released a Java client for ASP.NET Core SignalR. Here is a link to getting started docs https://learn.microsoft.com/en-us/aspnet/core/signalr/java-client?view=aspnetcore-2.2
do this tutorial step by step :
https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?tabs=visual-studio-mac&view=aspnetcore-5.0
1.According above tutorial publish your chat server to favorite host
2.add this dependency to your android sample:
implementation 'com.microsoft.signalr:signalr:3.0.0'
3.add these permission to manifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4.below code is MainActivity.class:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HubConnection hubConnection =
HubConnectionBuilder.create("https://your_chat_server_url/chatHub").build();
TextView textView = (TextView)findViewById(R.id.tvMain);
ListView listView = (ListView)findViewById(R.id.lvMessages);
Button sendButton = (Button)findViewById(R.id.bSend);
EditText editText = (EditText)findViewById(R.id.etMessageText);
List<String> messageList = new ArrayList<String>();
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, messageList);
listView.setAdapter(arrayAdapter);
hubConnection.on("ReceiveMessage", (user, message)-> {
runOnUiThread(new Runnable() {
#Override
public void run() {
arrayAdapter.add( user + " : " + message);
arrayAdapter.notifyDataSetChanged();
}
});
}, String.class,String.class);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String message = editText.getText().toString();
String user = "SAEID";
editText.setText("");
try {
hubConnection.send("SendMessage", user,message);
} catch (Exception e) {
e.printStackTrace();
}
}
});
new HubConnectionTask().execute(hubConnection);
}
static class HubConnectionTask extends AsyncTask<HubConnection, Void, Void>{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(HubConnection... hubConnections) {
HubConnection hubConnection = hubConnections[0];
hubConnection.start().blockingAwait();
return null;
}
}
}
5.below code is activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tvMain" />
<ListView
android:layout_height="0dp"
android:layout_weight="1"
android:layout_width="fill_parent"
android:id="#+id/lvMessages"
android:transcriptMode="alwaysScroll">
</ListView>
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="#+id/etMessageText"
android:hint="Enter Message" />
<Button
android:text="Send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/bSend" />
</LinearLayout>
For those who are implementing signalR client in android and the given answer here doesn't help in receiving the messages can check out this answer by rejnev.
The answer implements a different method connection.received() which is able to receive message callbacks from the server in my case.