I am having quite an interesting issue. Let me emphasize that I haven't been programming much for android and it seems I might have gone off track somewhere.
My problem is the triggering of the event onActivityResult(). I have read the Xamarin receipies but somehow it does not work. The routine does not get called in the MainActivity.
I even tried to do a similar example on java, and there it worked like a charm.
So, now to business. I have a MainActivity, which can start the Configuration Activity. This activity is supposed to collect the IP from the server (user manually puts it in), and return the String back to MainActivity. Fair and simple.
MainActivity code snippet:
[Activity(Label = "Test", MainLauncher = true, Icon = "#drawable/ax")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
setBindigs();
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch (resultCode)
{
case Result.Ok:
//do something
break;
}
}
private void setBindigs()
{
ImageView ax = FindViewById<ImageView>(Resource.Id.ax);
ax.Click += HandleImageClick;
}
private void HandleImageClick(object sender, EventArgs e)
{
var conf = new Intent(this, typeof(Configuration));
StartActivityForResult(conf, Convert.ToInt32(Result.Ok));
}
And the Configuration snippet:
[Activity(Label = "Configuration")]
public class Configuration : Activity
{
private Button[] Connects;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Configuration);
setBindigs();
}
private void setBindigs()
{
const int KeyboardLength = 2;
Connects = new Button[KeyboardLength];
for (int I = 0; I < KeyboardLength; ++I)
{
Connects[I] = FindViewById<Button>(Resource.Id.ConnectButton + I);
Connects[I].Click += HandleKeyboardClick;
}
}
private void HandleKeyboardClick(object sender, EventArgs e)
{
Button Clicked = sender as Button;
Result ActivityResult = Result.Ok;
String IpText = String.Empty;
switch(Clicked.Id)
{
case Resource.Id.ConnectButton:
EditText IP = FindViewById<EditText>(Resource.Id.ServerIP);
IpText = IP.Text;
break;
case Resource.Id.DisconnectButton:
ActivityResult = Result.Canceled;
break;
}
Intent myIntent = new Intent (this, typeof(MainActivity));
myIntent.PutExtra ("IP", IpText);
SetResult (Result.Ok, myIntent);
Finish();
}
Would you have any ideas? In java I used the option getIntent(); instead of Intent myIntent = new Intent (this, typeof(MainActivity));
I have just found the problem in my code. For future reference, here is what seems to be the problem:
When calling StartActivityForResult() I used the arguments StartActivityForResult(conf, Convert.ToInt32(Result.Ok));. It seems that the expected result should not be passed as Convert.ToInt32(Result.Ok) but rather as a 0.
This solved my problem
Related
I am using Xamarin Android to build an app which should allow the app to keep sending a driver's location every 15 minutes so that I can keep track of his movement. I used JobScheduler to get this done. My project is very simple now and only contains the following 3 files:
MainActivity.cs
AttendancePage.cs (Content page, interact with UI button to start the service)
ServiceClass.cs
Methods in Main Activity.cs
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
scheduler = (JobScheduler)GetSystemService(JobSchedulerService);
LoadApplication(new App()); //This line will then jump to AttendancePage.cs
}
public void ScheduleJob()
{
ComponentName componentName = new ComponentName(this, Java.Lang.Class.FromType(typeof(ServiceClass)));
JobInfo info = new JobInfo.Builder(123, componentName)
.SetPersisted(true)
.SetPeriodic(60000)
.Build();
int resultCode = scheduler.Schedule(info); //The error show when hit this line.
if (resultCode == JobScheduler.ResultSuccess)
{
Log.Info("Message", "Job Schedule!");
}
else
{
Log.Info("Message", "Job shceduling failed");
}
}
public void CancelJob()
{
scheduler.Cancel(123);
}
AttendancePage.cs
public partial class AttendancePage : ContentPage
{
MainActivity main = new MainActivity();
public AttendancePage()
{
InitializeComponent();
Title = "Attendance App";
}
//Button OnClickEvent
async void ScheduleJob(object s, EventArgs e)
{
main.ScheduleJob();
}
//Button OnClickEvent
async void CancelJob(object s, EventArgs e)
{
main.CancelJob();
}
}
ServiceClass.cs
[Service(Name = "com.SampleApp.AttendanceApp.ServiceClass", Permission = "android.permission.BIND_JOB_SERVICE")]
public class ServiceClass : JobService
{
public ServiceClass()
{
}
public override bool OnStartJob(JobParameters jobParamsOnStart)
{
doBackgroundWork(jobParamsOnStart);
return true;
}
private void doBackgroundWork(JobParameters jobParam)
{
//My code to send driver's location
TestingPage.GetGPS();
JobFinished(jobParam, false);
}
public override bool OnStopJob(JobParameters jobParamsOnStop)
{
return false;
}
}
I have added the service tag inside AndroidManifest.xml as well.
<service android:name=".ServiceClass"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true" />
I have no idea why the error is still there. The error is from the line scheduler.Schedule(JobInfo). Anyone has another possible solution? I am frustrated on solving this. Will the reason be I can't debug on the service but only can straight away run in release mode? Please help.
Froms shared code it works in Xamarin.Android project , however here is a Xamarin.Forms project . It can not work.
In AttendancePage.cs , you create a new MainActivity to invoke the native method ScheduleJob and CancelJob . This will not find the JobScheduler in native ,then it will return null .
No such service ComponentInfo{/com.SampleApp.AttendanceApp.ServiceClass}
If you want to invoke native method , you can have a try with DependencyService in Xamarin Forms .
At least need to create a Interface in Forms to invoke :
public interface IJobSchedulerService
{
//Start JobSchedule
void StartJobSchedule();
//Cancel JobSchedule
void CancelJobSchedule();
}
Then can invoke in Xamarin Forms as follow :
async void ScheduleJob(object s, EventArgs e)
{
DependencyService.Get<IJobSchedulerService>().StartJobSchedule();
}
//Button OnClickEvent
async void CancelJob(object s, EventArgs e)
{
DependencyService.Get<IJobSchedulerService>().CancelJobSchedule();
}
Now you need to implement the IJobSchedulerService in native android .Create the JobSchedulerDependcenyService:
public class JobSchedulerDependcenyService : IJobSchedulerService
{
JobScheduler jobScheduler;
public JobSchedulerDependcenyService()
{
jobScheduler = (JobScheduler)MainActivity.Instance.GetSystemService(Android.Content.Context.JobSchedulerService);
}
public void StartJobSchedule()
{
ComponentName componentName = new ComponentName(MainActivity.Instance, Java.Lang.Class.FromType(typeof(DownloadJob)));
JobInfo jobInfo = new JobInfo.Builder(1, componentName)
.SetOverrideDeadline(0)
.Build();
//var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
var scheduleResult = jobScheduler.Schedule(jobInfo);
if (JobScheduler.ResultSuccess == scheduleResult)
{
var snackBar = Snackbar.Make(MainActivity.Instance.FindViewById(Android.Resource.Id.Content), "jobscheduled_success", Snackbar.LengthShort);
snackBar.Show();
}
else
{
var snackBar = Snackbar.Make(MainActivity.Instance.FindViewById(Android.Resource.Id.Content), "jobscheduled_failure", Snackbar.LengthShort);
snackBar.Show();
}
}
public void CancelJobSchedule()
{
jobScheduler.CancelAll();
}
}
Here you will find the MainActivity.Instance inside it , that's a static instance defined by self in MainActivity.
public static MainActivity Instance { set; get; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Instance = this;
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
I'm creating an Android App in C# Xamarin.
Is there a way to "listen" for volume up/down key presses when an App goes into "background" mode, i.e. when a user "locks" their phone?
I've created several Service objects and made them "resident" by issuing the command 'StartCommandResult.Sticky'.
Any sample C# Xamarin code would be much appreciated.
You do not need to create a background service, just start a another task to listen the volume control. If the application do not be killed the task will run on the background.
public class MainActivity : Activity
{
private int currentVolume;
public AudioManager mAudioManager;
private int maxVolume;
private bool isDestory;
Android.Media.MediaPlayer player;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
player = Android.Media.MediaPlayer.Create(this, Resource.Raw.SampleAudio);
SetContentView (Resource.Layout.Main);
mAudioManager = (AudioManager)GetSystemService(Context.AudioService);
maxVolume = mAudioManager.GetStreamMaxVolume(Stream.Music);
onVolumeChangeListener();
player.Start();
}
protected override void OnDestroy()
{
base.OnDestroy();
isDestory = true;
}
private Task voluemChangeTask;
public void onVolumeChangeListener()
{
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
voluemChangeTask = new Task(ChangeVolume);
voluemChangeTask.Start();
}
public void ChangeVolume()
{
while (!isDestory)
{
int count = 0;
try
{
Thread.Sleep(20);
}
catch (Exception e)
{
}
if (currentVolume < mAudioManager.GetStreamVolume(Stream.Music))
{
System.Console.WriteLine("volunm+");
count++;
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
mAudioManager.SetStreamVolume(Stream.Music, currentVolume, VolumeNotificationFlags.RemoveSoundAndVibrate);
}
if (currentVolume > mAudioManager.GetStreamVolume(Stream.Music))
{
System.Console.WriteLine("volunm-");
count++;
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
mAudioManager.SetStreamVolume(Stream.Music, currentVolume, VolumeNotificationFlags.RemoveSoundAndVibrate);
}
}
}
}
I have tested it in the real device with screen lock and got the log:
My BroadcastReceiver does not receive anything. Most likely its my setup that is wrong, because I was not able to find any good examples on this. I need my receiver to receive something in my MainActivity, and change a View. I have almost the same code in an Android project, and here it is working, however BroadcastReceivers seems to be implemented a tiny bit differently in Xamarin (in Android, I can make a new BroadcastReceiver almost like an object, but in Xamarin, or C#, it seems I must make my own class and thus do not have the same possibilities to directly reference the views). If I get this to work, I will post a full working example for everyone too.
Here is how I have tried to set it up:
[Activity(Label = "GetLocation.Droid", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
Button button;
protected override void OnCreate(Bundle bundle)
{
// ... various OnCreate() code
LocationBroadcastReciever lbr = new LocationBroadcastReciever();
RegisterReceiver(lbr, new IntentFilter("test"));
}
public void SetButtonText(string text)
{
button.Text = text;
}
}
[BroadcastReceiver]
public class LocationBroadcastReciever : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
/* My program never get this far, so I have not been able
to confirm if the bellow code works or not (its from
another example I saw). */
//EDIT: It does NOT work. See my answer for a working example
string text = intent.GetStringExtra("title");
((MainActivity)context).SetButtonText(text);
InvokeAbortBroadcast();
}
}
And in my IntentService I have this method that actually runs, but never arrives at my receiver.
private void SendBroadcast(double lat, double lng, string activity)
{
Intent intent = new Intent("test");
intent.PutExtra("title", "Updated");
LocalBroadcastManager.GetInstance(this).SendBroadcast(intent);
}
This is pretty much the same code as I have in my working Android (only tweaked the BroadcastReceiver and minor adjustments to make it compile).
Can anyone see whats wrong??
EDIT
Finally got this whole thing to work. You can see my answer for a full, clean example.
Local
You register receiver as global, but send intents via LocalBroadcastManager. If you want to use this manager you should register your receiver like this:
LocalBroadcastManager.GetInstance(this).RegisterReceiver(lbr, filter);
You can find more about LocalBroadcastManager here.
Global
Or if you want to use global broadcasts, you should create intent by type:
var intent = new Intent(this, typeof(LocationBroadcastReciever));
and send it via android Context (in your service):
this.SendBroadcast(intent);
Also you can use intent with action, but it requires IntentFilter attribute on your receiver:
[IntentFilter(new []{ "test" })]
[BroadcastReceiver]
public class LocationBroadcastReciever : BroadcastReceiver { ... }
For the sake of future search results, here is a clean example of my code with a working BroadcastReceiver
// ** MainActivity
namespace GetLocation.Droid
{
[Activity(Label = "GetLocation.Droid", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
//I initialize my view(s) here to access them from outside of OnCreate().
Button button;
//I found this in an Android BroadcastReceiver example of how to access the MainActivity from the BroadcastReceiver.
private static MainActivity ins;
public static MainActivity getInstace()
{
return ins;
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
button = FindViewById<Button>(Resource.Id.myButton);
ins = this;
button.Click += delegate
{
Intent intent = new Intent(this, typeof(MyIntentService));
StartService(intent);
};
LocationBroadcastReciever lbr = new LocationBroadcastReciever();
LocalBroadcastManager.GetInstance(this).RegisterReceiver(lbr, new IntentFilter("test"));
}
public void SetButtonText(string text)
{
button.Text = text;
}
}
[BroadcastReceiver]
[IntentFilter(new[] { "test" })]
public class LocationBroadcastReciever : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
string text = intent.GetStringExtra("title");
MainActivity.getInstace().SetButtonText(text);
}
}
}
And in my IntentService
namespace GetLocation.Droid
{
[Service]
[IntentFilter(new String[] { "com.mytos.MyIntentService" })]
public class MyIntentService : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
SendBroadcast("My message");
}
private void SendBroadcast(string message)
{
//Here you can of course send whatever variable you want. Mine is a string
Intent intent = new Intent("test");
intent.PutExtra("title", message);
LocalBroadcastManager.GetInstance(this).SendBroadcast(intent);
}
}
}
A couple of things:
First, you need the [IntentFilter] on the receiver. So it should look like....
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new [] { "test" })]
public class LocationBroadcastReciever : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
/* My program never get this far, so I have not been able
to confirm if the bellow code works or not (its from
another example I saw). */
string text = intent.GetStringExtra("title");
((MainActivity)context).SetButtonText(text);
InvokeAbortBroadcast();
}
}
That should get you past your issue.
Second, you should register and unregister the reciever. So you should register in the OnResume and unregister in OnPause.
[Activity(Label = "GetLocation.Droid", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
LocationBroadcastReciever _lbr;
Button button;
protected override void OnCreate(Bundle bundle)
{
// ... various OnCreate() code
}
protected override void OnResume()
{
base.OnResume();
_lbr = new LocationBroadcastReciever();
RegisterReceiver(lbr, new IntentFilter("test"));
}
protected override void OnPause()
{
UnregisterReceiver(_lbr);
base.OnPause();
}
public void SetButtonText(string text)
{
button.Text = text;
}
}
Note: these changes are what I saw different between your code and my code for a working broadcast receiver. Whether my changes are necessary or not, I'm not entirely sure.
Hello, I want to build an app, in which you can start a service, which runs intependenly and creates a notification, and this service should constantly proof, if the DateTime.Now.Date is bigger than a spezific Date.
When I execute the code below, the notification gets displayed, but when I am closing the app, a few secondes later I get two times an information that the app crashed and I dont know why.
I cant even debug the code because this anly happens when the application is closed....
I hope you can help me thanks!
Here is my code:
namespace App
{
[Activity(Label = "App", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate {
button.Text = string.Format("{0} clicks!", count++);
StartService(new Intent(this, typeof(backgroudservice)));
};
}
}
public class backgroudservice : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
newnotification("Title", "Text: ", 0);
new Task(() => {
DoWork();
Thread.Sleep(1000);
}).Start();
return StartCommandResult.Sticky;
}
public void DoWork()
{
if (DateTime.Now.Date > Convert.ToDateTime("2016-03-29").Date)
{
cancelnotification(0);
StopSelf();
}
}
public override void OnDestroy()
{
base.OnDestroy();
cancelnotification(0);
}
private void newnotification(string titel, string text, int id)
{
Notification.Builder builder = new Notification.Builder(this)
.SetContentTitle(titel)
.SetContentText(text)
.SetSmallIcon(Resource.Drawable.droidlogo_small)
.SetAutoCancel(false)
.SetVisibility(NotificationVisibility.Public)
.SetContentIntent(PendingIntent.GetActivity(this, 0, new Intent(this, typeof(MainActivity)), PendingIntentFlags.OneShot));
// Build the notification:
Notification notification = builder.Build();
notification.Flags = NotificationFlags.NoClear;
//notification.ContentIntent = new Intent(this,typeof(login));
// Get the notification manager:
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
notificationManager.Notify(id, notification);
}
private void cancelnotification(int id)
{
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
notificationManager.Cancel(id);
}
}
}
I solved it, I forgot the [Service] above my class, now it works!
[Service]
public class backgroudservice : Service
{
...
}
You might try moving the call to cancelnotification in your service's OnDestroy to before the call to the base method, i.e.:
public override void OnDestroy()
{
cancelnotification(0);
base.OnDestroy();
}
I am building a native android app using xamarin. The issue is, that the application collects and displays the coordinates perfectly on the emulator but when I put it on a smartphone (tried 2 samsung phones) it comes up with can't determine the current address. Extra information data and locations are turned on so I am not sure where the issue is. Thanks for your help. here is the xammarin recipe encase that helps https://developer.xamarin.com/recipes/android/os_device_resources/gps/get_current_device_location/
[Activity(Label = "NewRoute")]
public class NewRouteActivity : Activity, ILocationListener
{
static readonly string TAG = "X:" + typeof(NewRouteActivity).Name;
TextView _addressText;
Location _currentLocation;
LocationManager _locationManager;
string _locationProvider;
TextView _locationText;
public async void OnLocationChanged(Location location) {
_currentLocation = location;
if (_currentLocation == null)
{
_locationText.Text = "Unable to determine your location. Try again in a short while.";
}
else
{
_locationText.Text = string.Format("{0:f6},{1:f6}", _currentLocation.Latitude, _currentLocation.Longitude);
Address address = await ReverseGeocodeCurrentLocation();
DisplayAddress(address);
}
}
public void OnProviderDisabled(string provider) { }
public void OnProviderEnabled(string provider) { }
public void OnStatusChanged(string provider, Availability status, Bundle extras) { }
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.CreatetRoute);
_addressText = FindViewById<TextView>(Resource.Id.address_text);
_locationText = FindViewById<TextView>(Resource.Id.location_text);
FindViewById<TextView>(Resource.Id.get_address_button).Click += AddressButton_OnClick;
InitializeLocationManager();
Button btnEndPoint = FindViewById<Button>(Resource.Id.btnEndPoint);
btnEndPoint.Click += new EventHandler(AfterPointsCollected);
}
//Location Stuff
void InitializeLocationManager()
{
_locationManager = (LocationManager)GetSystemService(LocationService);
Criteria criteriaForLocationService = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
if (acceptableLocationProviders.Any())
{
_locationProvider = acceptableLocationProviders.First();
}
else
{
_locationProvider = string.Empty;
}
Log.Debug(TAG, "Using " + _locationProvider + ".");
}
//Override OnResume so that Activity1 will begin listening to the LocationManager when the activity comes into the foreground:
protected override void OnResume()
{
base.OnResume();
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
}
async void AddressButton_OnClick(object sender, EventArgs eventArgs)
{
if (_currentLocation == null)
{
_addressText.Text = "Can't determine the current address. Try again in a few minutes.";
return;
}
Address address = await ReverseGeocodeCurrentLocation();
DisplayAddress(address);
}
async Task<Address> ReverseGeocodeCurrentLocation()
{
Geocoder geocoder = new Geocoder(this);
IList<Address> addressList =
await geocoder.GetFromLocationAsync(_currentLocation.Latitude, _currentLocation.Longitude, 10);
Address address = addressList.FirstOrDefault();
return address;
}
void DisplayAddress(Address address)
{
if (address != null)
{
StringBuilder deviceAddress = new StringBuilder();
for (int i = 0; i < address.MaxAddressLineIndex; i++)
{
deviceAddress.AppendLine(address.GetAddressLine(i));
}
// Remove the last comma from the end of the address.
_addressText.Text = deviceAddress.ToString();
}
else
{
_addressText.Text = "Unable to determine the address. Try again in a few minutes.";
}
}
//Override OnPause and unsubscribe Activity1 from the LocationManager when the activity goes into the background:
protected override void OnPause()
{
base.OnPause();
_locationManager.RemoveUpdates(this);
}
//Changing Activity
void AfterPointsCollected(object sender, EventArgs e)
{
//context //activity
Intent intent = new Intent(this, typeof(AfterPointsCollectedActivity));
//starts the activity with the intent above
this.StartActivity(intent);
}
Your phones are probably running MarshMallow which now require that you request permission for location services.
You can read more about it here https://blog.xamarin.com/requesting-runtime-permissions-in-android-marshmallow/. You might want to use this Nuget package that handles all that for you. https://github.com/jamesmontemagno/Xamarin.Plugins/tree/master/Geolocator