I'm making a very simple software with a TCP Listener that (until now) only receives a message from a TCP Client codified in ASCII and I'll have to do something with the UI that I still don't know, but by now, I'm just trying to show an AlertDialog with this message on Samsung Galaxy Tab.
The problem is that, I believe that for some reason the setContentView is not working. I have one .axml (layout) file with an AbsoluteLayout, and I'm calling this AbsoluteLayout on code, changing its color, and trying to show this AbsoluteLayout (with its color changed) on the screen, but the problem is that I just see the regular black screen.
I started debugging the code, and I can see the all the Console.Writeline commands on the output of the MS VS 2010, even the message sent from the client. But I can't see the layout and the AlertDialog.
Can anybody help me? Thanks in advance.
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Android.Graphics.Drawables;
using System.Drawing;
namespace Gafisa.Automacao.VideoWall.Listener
{
[Activity(Label = "Listener", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
AbsoluteLayout abs = FindViewById<AbsoluteLayout>(Resource.Id.abslayout);
abs.SetBackgroundColor(new Android.Graphics.Color(125,125,125,125));
//ImageButton btn = new ImageButton(this);
//var lp = new AbsoluteLayout.LayoutParams(50, 50, 200, 200);
//btn.LayoutParameters = lp;
//BitmapDrawable dd = new BitmapDrawable("/mnt/sdcard/1.png");
//btn.SetBackgroundDrawable(dd);
//abs.AddView(btn);
System.Net.Sockets.TcpListener listener = null;
byte[] rcvBuffer = new byte[40];
int bytesRcvd;
try
{
listener = new System.Net.Sockets.TcpListener(IPAddress.Any, 13000);
listener.Start();
Console.WriteLine("Listener iniciado");
}
catch (SocketException se)
{
Console.WriteLine("Erro ao iniciar o listener: " + se.Message);
}
for (;;)
{
TcpClient client = null;
NetworkStream netStream = null;
try
{
client = listener.AcceptTcpClient();
netStream = client.GetStream();
int totalBytesEchoed = 0;
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
{
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
string recebido = System.Text.Encoding.ASCII.GetString(rcvBuffer);
Console.WriteLine(recebido);
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetMessage(recebido);
alert.SetTitle("Mensagem Recebida");
alert.Show();
Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine("Erro no LOOP");
}
finally
{
netStream.Close();
client.Close();
}
}
}
}
}
By running an infinite loop in this OnCreate function, you prevents the UI framework from finishing rendering. That's why what you see is only the black screen.
You are supposed to run non-UI code asynchronously (in a separate thread).
Change it to
Task.Factory.StartNew(() =>
{
for (;;)
{
TcpClient client = null;
NetworkStream netStream = null;
try
{
client = listener.AcceptTcpClient();
netStream = client.GetStream();
int totalBytesEchoed = 0;
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
{
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
string recebido = System.Text.Encoding.ASCII.GetString(rcvBuffer);
Console.WriteLine(recebido);
RunOnUiThread(() =>
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetMessage(recebido);
alert.SetTitle("Mensagem Recebida");
alert.Show();
}
Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine("Erro no LOOP");
}
finally
{
netStream.Close();
client.Close();
}
}
}
Also with Mono for Android (Xamarin.Android) you should use Log.Info(string tag, string message) where tag is the name of calling class. Don't use Console.WriteLine(string). You can also use Log.Warn(string, string) and Log.Error(string, string)
Related
I am fairly new at working with the Android code in Visual Studio 2019. The ultimate result desired here was to be able to display the various wifi Access Points near the phone. I have gotten the wifi scan to work and I can see the results of the scan in the "WifiScans" collection but the listWifiScan ListView does not show the results on the display. I can see lines on the display for the number of items that should be displayed. If I touch one of the items I can see it turn to a solid color (Orange) but the information is not displayed. Can anyone tell me what I am doing wrong? Thanks for any help you can give.
using Android;
using Android.Content.PM;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using Xamarin.Forms;
namespace MobileWifi
{
public partial class MainPage : ContentPage
{
ObservableCollection<WifiScan> WifiScans { get; set; }
Label lblTitle;
ListView listWifiScan;
readonly Button btnWifiScan;
public static bool response = false;
public MainPage()
{
// add code to handle the components here
WifiScans = new ObservableCollection<WifiScan>();
this.Padding = new Thickness(20,20,20,20); // wall padding for page
// Listview data template
var scanDataTemplate = new DataTemplate(() =>
{
var grid = new Grid();
var bssidLabel = new Label { FontAttributes = FontAttributes.Bold };
var ssidLabel = new Label();
var levelLabel = new Label { HorizontalTextAlignment = TextAlignment.End };
bssidLabel.SetBinding(Label.TextProperty, "BSSID");
ssidLabel.SetBinding(Label.TextProperty, "SSID");
levelLabel.SetBinding(Label.TextProperty, "Level");
grid.Children.Add(bssidLabel);
grid.Children.Add(ssidLabel, 1, 0);
grid.Children.Add(levelLabel, 2, 0);
return new ViewCell { View = grid };
});
// setup StackLayout for controls and set spacing of controls within the layout
StackLayout panel = new StackLayout
{
Spacing = 15,
Margin = new Thickness(20),
};
panel.Children.Add(lblTitle = new Label
{
Text = "Mobile Wifi Scanner",
HorizontalTextAlignment = TextAlignment.Center,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
FontAttributes = FontAttributes.Bold,
});
panel.Children.Add(btnWifiScan = new Button
{
Text = "Start Scan"
});
panel.Children.Add(listWifiScan = new ListView
{
ItemsSource = WifiScans,
HasUnevenRows = true,
ItemTemplate = scanDataTemplate,
Margin = new Thickness(0, 20, 0, 0),
});
btnWifiScan.Clicked += OnBtnWifiScanClicked;
this.Content = panel;
}
private void OnBtnWifiScanClicked(object sender, EventArgs e)
{
btnWifiScan.Text = "Scanning...";
WifiScans.Clear(); // clear out results from previous scan
try
{
IWifiScan service = DependencyService.Get<IWifiScan>().GetObj();
service.Start();
service.Finished += WifiScanDone;
}
catch (Exception ex)
{
btnWifiScan.Text = "Start Scan";
DisplayAlert("Alert", ex.Message, "OK");
}
}
public void WifiScanDone(object sender,EventArgs e)
{
IWifiScan service = DependencyService.Get<IWifiScan>();
List<string> WifiBSSID = service.GetBSSID();
List<string> WifiSSID = service.GetSSID();
List<int> WifiLevel = service.GetLevel();
int count = WifiBSSID.Count;
// add logic here to display the data from the scan
// add results of the scan
if(count > 0)
{
for (int i = 0; i < count; i++)
{
WifiScans.Add(new WifiScan { WifiBSSID = WifiBSSID[i], WifiSSID = WifiSSID[i], WifiLevel = WifiLevel[i].ToString()});
}
}else
{
WifiScans.Add(new WifiScan { WifiBSSID = "None Found", WifiSSID = "", WifiLevel = "0"});
}
btnWifiScan.Text = "Start Scan";
// finalize the scan, etc.
service.Done();
}
}
}
I finally found the problem. It turns out I did not specify the correct property in the binding. The correct code for the binding in the data template should have been:
bssidLabel.SetBinding(Label.TextProperty, "WifiBSSID");
ssidLabel.SetBinding(Label.TextProperty, "WifiSSID");
levelLabel.SetBinding(Label.TextProperty, "WifiLevel");
I'm not sure where I got the original values but after studying the data binding properties I realized they were wrong. Also I did not include the source in the original question that would have helped identify the problem so I am adding it here in case it is helpful to someone else that may have a similar problem.
public class WifiScan
{
public string WifiSSID { get; set; }
public string WifiBSSID { get; set; }
public string WifiLevel { get; set; }
}
Is there any way to send a push notification from server when a user complete a task? For example: A todo app will notify on that date with push notification. I want to use firebase and firestore for storing user tokens.
Alarm manager can be a solution that I have found but I don't wanna use it.
Yes, you can use scheduler to send notification from server to your app:
You may follow my working code:
Emplement IJob:
public class SendNotificationViaFcm: IJob
{
public void Execute(IJobExecutionContext context)
{
bool isNotificationSent=false;
try
{
var taskToSendNotification = FirebaseCloudMessaging.SendMessage();
Task.WaitAll(taskToSendNotification);
isNotificationSent = taskToSendNotification.Result;
}
catch (Exception exception)
when (
exception is ObjectDisposedException || exception is ArgumentNullException ||
exception is AggregateException)
{
}
catch (Exception exception) when (exception is InvalidOperationException)
{
}
catch (Exception exception)
{
// ignored
}
}
}
Call FCM Api from your server:
public class FirebaseCloudMessaging
{
private static readonly Uri FcmUri = new Uri(
uriString: #"https://fcm.googleapis.com",
uriKind: UriKind.Absolute);
private const string FcmApiKey = "Your Legacy Server Key";
public static async Task<bool> SendMessage()
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = FcmUri;
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization",
"key=" + FcmApiKey);
var response = await httpClient.PostAsJsonAsync(#"/fcm/send", new
{
to = "/topics/global",
priority = "high",
data = new
{
title = "Warning",
message = "Please start app to track movemoent!"
}
//to = "/topics/global",
//priority = "high",
//notification = new
//{
// title = "Warning!",
// body = "Please start app to track movemoent!"
//}
});
Debug.Write(response.Content.ReadAsStringAsync());
var ck = response.IsSuccessStatusCode;
return response.IsSuccessStatusCode;
}
}
}
Implement schedular for your time interval:
public class Scheduler
{
public static void Start()
{
try
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();
// scheduler.Shutdown();
var sentCloudNotification = JobBuilder.Create<SendNotificationViaFcm>().Build();
var cloudNotificationTrigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInMinutes(1).RepeatForever()).Build();
scheduler.ScheduleJob(sentCloudNotification, cloudNotificationTrigger);
}
catch (SchedulerException exception)
{
Debug.Write(exception.Message);
}
catch (Exception exception)
{
Debug.Write(exception.Message);
}
}
}
Finally Run in from your Global.asax.cs
protected void Application_Start()
{
Scheduler.Start();
}
It sounds like you're looking for a tool that allows you to schedule transactional notifications. What sort of server technology are you using?
From a high level you could do something like this:
1) user adds a task in the Android application
2) android application sends request to server to save the task
3) you have some code that runs in some sort of on task save callback that schedules a block of code to run in the future using crontab, celery or something similar.
4) the block of code that runs in the future is an api call to twilio to send a push notification
relevant links: https://www.twilio.com, https://firebase.google.com/docs/cloud-messaging/, http://www.celeryproject.org/, https://en.wikipedia.org/wiki/Cron
I have looked many source and question but I am still not clear to how this code of sending email through unity is not working in android build. It works fine in windows build but doesn't work in android. Can someone help me. Here is the code -
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using UnityEngine;
public class EmailUnity
{
public static string SenderEmail;
public static string SenderPassword;
public static string SmtpClient;
public static int SmtpPort;
public static void SendEmail(string to, string subject, string body, bool isHtml, string[] attachmentPaths,
Action<object, AsyncCompletedEventArgs> callback = null)
{
try
{
SmtpClient emailServer = new SmtpClient(SmtpClient, SmtpPort);
emailServer.EnableSsl = true;
emailServer.Credentials = (ICredentialsByHost) new NetworkCredential(SenderEmail, SenderPassword);
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
MailMessage message = new MailMessage(SenderEmail, to);
message.Subject = subject;
message.Body = body;
message.IsBodyHtml = isHtml;
foreach (string path in attachmentPaths)
{
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
message.Attachments.Add(new Attachment(path));
}
}
if (callback == null)
{
callback = SampleCallback;
}
emailServer.SendCompleted += new SendCompletedEventHandler(callback);
emailServer.SendAsync(message, "");
Debug.Log("Email sending");
}
catch (Exception ex)
{
Debug.Log("Error: " + ex.Message);
callback("", new AsyncCompletedEventArgs(ex, true, "Exception occured"));
}
}
private static void SampleCallback(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled || e.Error != null)
{
Debug.Log("Error: " + e.Error.Message);
}
else
{
Debug.Log("Email sent");
}
}
}
These are the things to try if SmtpClient is not working on Android.
Go to File --> Build Settings... --> Select Android. Now, click on Player Settings.
1.On the Internet Access, change it from Auto to Require.
2.Make sure that API Compatible Level is set to .NET 2.0 not .NET 2.0 Subset.
3.Make sure that Stripping Level is set to Disabled.
4.Go to Player settings --> Android and change Internet Access from Auto to Require
I am trying to record a video and save it to a file but I keep getting an error when it comes to the recording. The code is
using System;
using Android.App;
using Android.OS;
using Android.Widget;
using Android.Media;
using System.Timers;
using Android.Content;
using System.IO;
namespace ShowHouseDemo2._1
{
[Activity(Label = "VideoPage", Icon = "#drawable/icon")]
//Intent intent = GetInTent();
class VideoPage : Activity
{
//Intent string Vid = Intent.GetStringExtra("VidSent");
MediaRecorder recorder;
Timer StartTimer;
int StartCount = 0;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.VidTakerPage);
var video = FindViewById<VideoView>(Resource.Id.UserVid);
//string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath. "/UserVid4.mp4";
// string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + ;
var documents = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file!");
video.StopPlayback();
recorder = new MediaRecorder();
recorder.SetVideoSource(VideoSource.Camera);
recorder.SetAudioSource(AudioSource.Mic);
recorder.SetOutputFormat(OutputFormat.Default);
recorder.SetVideoEncoder(VideoEncoder.Default);
recorder.SetAudioEncoder(AudioEncoder.Default);
recorder.SetOutputFile(filename);
recorder.SetPreviewDisplay(video.Holder.Surface);
recorder.Prepare();
recorder.Start();
timerStarted();
}
private void timerStarted()
{
base.OnResume();
StartTimer = new Timer();
StartTimer.Interval = 1000;
StartTimer.Elapsed += delegate
{
while (StartCount < 10)
{
if (StartCount == 9)
{
RunOnUiThread(() =>
{
Intent k = new Intent(this, typeof(TakeVideo));
this.StartActivity(k);
});
}
StartCount++;
}
};
}
}
}
the Error that I get is :
Unhandled Exception:
Java.IO.IOException: invalid preview surface.
I love any bit of help you can give me. Any questions that you have please ask
You need to implement the surface callback interface (Android.Views.ISurfaceHolderCallback) and only start using the ISurfaceHolder.Holder when it is available and valid.
You can do this on your VideoPage Activity, i.e.
[Activity(Label = "VideoPage", Icon = "#drawable/icon")]
public class VideoPage : Activity, Android.Views.ISurfaceHolderCallback
Implement the three methods of the interface (SurfaceDestroyed, SurfaceCreated, SurfaceChanged) and then you can assign the callback via the VideoView.Holder.AddCallback:
var video = FindViewById<VideoView>(Resource.Id.UserVid);
video.Holder.AddCallback((this));
Alrighty, so I understand that this general question has been asked numerous times here, but I have yet to find an answer that makes sense to me. Almost every answer I've seen just says some blurb like, "hey, just throw this in your method and you're good", but I'm not seeing full examples, and what I've tried is not working either.
Here's the error I receive:
[mono] android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
So, simply put, I have an activity that grabs some information from a web service and then throws the web service results into a couple of TextViews. Could someone please help me figure out where and how I need to use the RunOnUiThread()? Here's the code:
using Android.App;
using Android.OS;
using System;
using System.Web;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Android.Widget;
namespace DispatchIntranet
{
[Activity (Label = "#string/Summary")]
public class SummaryActivity : Activity
{
private static readonly Log LOG = new Log(typeof(SummaryActivity));
private TextView summaryTotalRegularLabel;
private TextView summaryTotalRollover;
private TextView summaryScheduledLabel;
private TextView summaryRemainingRegular;
private string url;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// SET THE LAYOUT TO BE THE SUMMARY LAYOUT
SetContentView(Resource.Layout.Summary);
// INITIALIZE CLASS MEMBERS
init();
if (LOG.isInfoEnabled())
{
LOG.info("Making call to rest endpoint . . .");
if (LOG.isDebugEnabled())
{
LOG.debug("url: " + this.url);
}
}
try
{
// BUILD REQUEST FROM URL
HttpWebRequest httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(this.url));
// SET METHOD TO 'GET'
httpReq.Method = GetString(Resource.String.web_service_method_get);
// ASK FOR JSON RESPONSE
httpReq.Accept = GetString(Resource.String.web_service_method_accept);
// INVOKE ASYNCHRONOUS WEB SERVICE
httpReq.BeginGetResponse((ar) => {
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse (ar))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
// PUT RESPONSE INTO STRING
string content = reader.ReadToEnd();
// CONVERT STRING TO DYNAMIC JSON OBJECT
var json = JsonConvert.DeserializeObject<dynamic>(content);
if (LOG.isDebugEnabled())
{
LOG.debug("content: " + content);
LOG.debug("json: " + json);
LOG.debug("TOTAL_REGULAR_PTO_HOURS: " + json.d[0].TOTAL_REGULAR_PTO_HOURS);
}
// ** THIS IS WHAT WILL NOT WORK **
this.summaryTotalRegularLabel.Text = json.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = json.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = json.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = json.d[0].TOTAL_REMAINING_PTO_HOURS;
}
}
}, httpReq);
}
catch (Exception e)
{
LOG.error("An exception occurred while attempting to call REST web service!", e);
}
}
private void init()
{
// GET GUID FROM PREVIOUS INTENT AND DETERMINE CURRENT YEAR
string guid = Intent.GetStringExtra("guid");
int year = DateTime.Now.Year;
// BUILD URL
this.url = GetString(Resource.String.web_service_url)
+ GetString(Resource.String.ws_get_pto_summary)
+ "?" + "guid='" + HttpUtility.UrlEncode(guid) + "'"
+ "&" + "year=" + HttpUtility.UrlEncode(year.ToString());
// GET THE SUMMARY LABELS
this.summaryTotalRegularLabel = FindViewById<TextView>(Resource.Id.SummaryTotalRegular);
this.summaryTotalRollover = FindViewById<TextView>(Resource.Id.summaryTotalRollover);
this.summaryScheduledLabel = FindViewById<TextView>(Resource.Id.summaryScheduledLabel);
this.summaryRemainingRegular = FindViewById<TextView>(Resource.Id.SummaryRemainingRegular);
}
}
}
When you make a web service call, HttpWebRequest creates a new thread to run the operation on. This is done to keep your user interface from locking up or skip frames. Once your web service call is complete, you need to go back to the UI Thread to update the UI components that live on that thread. You can do that a couple of different ways.
First, you can wrap your code in an anonymous function call like so:
RunOnUiThread(()=>{
this.summaryTotalRegularLabel.Text = json.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = json.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = json.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = json.d[0].TOTAL_REMAINING_PTO_HOURS;
});
Or you can call a function via RunOnUiThread (jsonPayload is a field on the class):
jsonPayload = json;
RunOnUiThread(UpdateTextViews);
...
void UpdateTextViews()
{
this.summaryTotalRegularLabel.Text = jsonPayload.d[0].TOTAL_REGULAR_PTO_HOURS;
this.summaryTotalRollover.Text = jsonPayload.d[0].TOTAL_ROLLOVER_PTO_HOURS;
this.summaryScheduledLabel.Text = jsonPayload.d[0].TOTAL_USED_PTO_HOURS;
this.summaryRemainingRegular.Text = jsonPayload.d[0].TOTAL_REMAINING_PTO_HOURS;
}