My app showing notification on specific time. Notification has got pending intent with note id. When user tapped on notification, this note id is use to launch app and load note with id that come from notification.
Basically it works fine but problem starts when user leave app using "back button". On next application start that note id is still in memory and app takes user to that note again.
How can I clear that intent from memory after first use.
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Context AndroidContext { get; private set; }
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//noteId is a id of Note thet notification was dispaly and tapped by user
//Id is pass to xamarin PCL and proper note basic on this id is displayed
int noteId = Intent.GetIntExtra("noteId", 0);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
CalligraphyConfig.InitDefault(new CalligraphyConfig.Builder()
.SetDefaultFontPath("Dosis-Regular.ttf")
.SetFontAttrId(Resource.Attribute.fontPath)
.Build()
);
AndroidContext = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.FormsGoogleMaps.Init(this, bundle);
LoadApplication(new App(noteId));
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(CalligraphyContextWrapper.Wrap(context));
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
BroadcastReciver
class AlamReciver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
int noteId = intent.GetIntExtra("id",0);
var noteTitle = intent.GetStringExtra("title");
var noteContent = intent.GetStringExtra("content");
var resultIntent = new Intent(context, typeof(MainActivity));
resultIntent.PutExtra("noteId", noteId);
resultIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.ClearTask);
var PI = PendingIntent.GetActivity(context, noteId, resultIntent, PendingIntentFlags.OneShot);
var builder = new Notification.Builder(context)
.SetContentTitle(noteTitle)
.SetContentText(noteContent)
.SetSmallIcon(Resource.Drawable.ic_gnote_logo)
.SetPriority(1)
.SetAutoCancel(true)
.SetDefaults(NotificationDefaults.All)
.SetContentIntent(PI);
var notification = builder.Build();
var manager = NotificationManager.FromContext(context);
manager.Notify(noteId, notification);
}
}
Thank you.
Related
I have a plan and want to periodically check a URL every 5 minutes(NOTIFY CENTER SERVER(Listener)).
My problem: Once the program closes, the process closes
Is it possible that the project will not be shut down if the original program is closed ?
My Code After Changed Worked with : Matcha.BackgroundService
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Matcha.BackgroundService.Droid;
using Matcha.BackgroundService;
using System.Threading.Tasks;
using Android.Util;
using System.Threading;
using AndroidApp = Android.App.Application;
using Android.Content;
using Android.Support.V4.App;
using Android.Graphics;
namespace Solution.Droid
{
[Activity(Label = "Solution", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private NotificationManager _manager;
private bool _channelInitialized = false;
public const int _pendingIntentId = 0;
public int _channelID = 10001;
private long _mssageID=0;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
BackgroundAggregator.Init(this);
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
protected override void OnStart()
{
base.OnStart();
//Register Periodic Tasks
var _notifyTASK = new DevinuxTaskPeriodic(10);
_notifyTASK.DoTask += () =>
{
SendNotify("salam", DateTime.Now.ToString());
};
BackgroundAggregatorService.Add(() => _notifyTASK);
BackgroundAggregatorService.StartBackgroundService();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
public int SendNotify(string title, string message)
{
_mssageID++;
if (!_channelInitialized)
{
CreateNotificationChannel();
}
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, _pendingIntentId, intent, PendingIntentFlags.OneShot);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, _channelID.ToString())
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.notification_template_icon_bg))
.SetSmallIcon(Resource.Drawable.notification_template_icon_bg)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
Notification notification = builder.Build();
_manager.Notify((int)_mssageID, notification);
return (int)_mssageID;
}
void CreateNotificationChannel()
{
_manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String("Solution");
var channel = new NotificationChannel(_channelID.ToString(), channelNameJava, NotificationImportance.Default)
{
Description = "My Company Notify Camp."
};
_manager.CreateNotificationChannel(channel);
}
_channelInitialized = true;
}
public class DevinuxTaskPeriodic : IPeriodicTask
{
public bool use { set; get; } = false;
public delegate void DoArgs();
public event DoArgs DoTask;
public DevinuxTaskPeriodic(int seconds)
{
Interval = TimeSpan.FromSeconds(seconds);
}
public TimeSpan Interval { get; set; }
public Task<bool> StartJob()
{
if (!use)
{
Timer tmr = new Timer((o) => {
if (DoTask != null)
{
DoTask();
}
}, null, 0, (int)Interval.TotalSeconds*1000);
}
use = true;
return new Task<bool>(() => true);
}
}
}
}
Yes, it is possible to run processes even when the original program/app is not in the foreground.
You are entering the territory of "backgrounding" which is less simple to do. There isn't an inbuilt/official way of performing backgrounding using Xamarin.Forms, so you will have to either create a dependency service (shown here), or try using Shiny.
If you follow the dependency services route, you just need to follow the official iOS & Android tutorials and implement them in your Native project. Note that if you only need a periodic alarm, Android provides a simpler Alarm/PowerManager that you can use.
This question already has answers here:
How to extend application class in xamarin android
(2 answers)
Closed 3 years ago.
I am trying to implement the very promising Shiny library (for background jobs) from Allan Ritchie. I have only tried this in a simple File/New Project for Android thus far (haven't implemented the code for iOS or UWP), but I am not able to get it to run.
I am following the article https://allancritchie.net/posts/shinyjobs. However, when I run I get the following exception...
And I never hit this breakpoint...
My code can be cloned from https://github.com/JohnLivermore/SampleXamarinApp.git
But here it is inline as well...
App.xaml.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
protected override async void OnStart()
{
var job = new JobInfo
{
Identifier = "YourFirstJob",
Type = typeof(YourFirstJob),
// these are criteria that must be met in order for your job to run
BatteryNotLow = false,
DeviceCharging = false,
RequiredInternetAccess = InternetAccess.Any,
Repeat = true //defaults to true, set to false to run once OR set it inside a job to cancel further execution
};
// lastly, schedule it to go - don't worry about scheduling something more than once, we just update if your job name matches an existing one
await ShinyHost.Resolve<Shiny.Jobs.IJobManager>().Schedule(job);
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
Startup.cs
public class Startup : ShinyStartup
{
public override void ConfigureServices(IServiceCollection services)
{
}
}
YourFirstJob.cs
public class YourFirstJob : IJob
{
public YourFirstJob()
{
}
public async Task<bool> Run(JobInfo jobInfo, CancellationToken cancelToken)
{
//await this.dependency.SomeAsyncMethod(id);
return true; // this is for iOS - try not to lie about this - return true when you actually do receive new data from the remote method
}
}
MainActivity.cs
[Activity(Label = "SampleApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
Shiny.AndroidShinyHost.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
MainApplication.cs
[Application]
public class MainApplication : Application
{
public override void OnCreate()
{
base.OnCreate();
Shiny.AndroidShinyHost.Init(this, new SampleApp.Startup());
}
}
Any help would be greatly appreciated!
You might want to add following constructor to your MainApplication class.
public MainApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
{
}
I am developing xamarin.Android app in MvvmCross. I want to call a service even when the App is backgrounded and a user is logged in. The problem is, I want to call this service within every say 2 hours whether the app is in foreground or background, just the user of the App needs to be logged in.
Intent loggedintent = new Intent(this,typeof(DeviceLoginHelper));
loggedintent.PutExtra("LoggedIn", true);
StartService(loggedintent);
I have written an android service:
[Service]
public class DeviceLoginHelper : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
try
{
if(intent.HasExtra("LoggedIn"))
{
}
}
catch(Exception ex) { }
}
}
But how can I implement a timer? Where do I initialise and handle event to the timer. And when timer is elapsed when should I call ?
public override void OnDestroy()
{
try
{
base.OnDestroy();
}
catch(Exception ex){}
}
and when a user loges out i want to stop this service. Where do I put the call StopService() in MvvmCross
I would not use a Timer. Instead you should configure the AlarmManager.
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
private static AlarmManager alarmMgr;
private static PendingIntent alarmIntent;
public const int NOTIFICATION_ID = 1;
public const int IDLE_TIME_MS = 30 * 1000; // 30-seconds (update here)
private NotificationManager mNotificationManager;
Notification.Builder builder;
public override void OnReceive(Context context, Intent intent)
{
// Do something when alarm triggers (here I'm building notification)
BuildNotification(context);
// reschedule alarm
ScheduleAlarm(IDLE_TIME_MS);
}
public static Context ApplicationContext { get; set; }
public static void ScheduleAlarm(int milliseconds)
{
if (milliseconds == 0) return;
alarmMgr = (AlarmManager)ApplicationContext.GetSystemService(Context.AlarmService);
var intent = new Intent(ApplicationContext, typeof(AlarmReceiver));
alarmIntent = PendingIntent.GetBroadcast(ApplicationContext, 0, intent, 0);
alarmMgr.Set(AlarmType.ElapsedRealtimeWakeup,
SystemClock.ElapsedRealtime() + milliseconds, alarmIntent);
}
private void BuildNotification(Context context)
{
mNotificationManager = (NotificationManager)context.GetSystemService(Context.NotificationService);
var contentIntent = PendingIntent.GetActivity(context, 0, new Intent(context, typeof(MainView)), 0);
var message = $"Time is up";
var mBuilder = new Notification.Builder(context)
.SetAutoCancel(true)
.SetPriority(NotificationCompat.PriorityMax)
.SetDefaults(NotificationDefaults.All)
.SetContentTitle("Time is up")
.SetStyle(new Notification.BigTextStyle()
.BigText(message))
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.ic_launcher);
mBuilder.SetContentIntent(contentIntent);
mNotificationManager.Notify(NOTIFICATION_ID, mBuilder.Build());
}
}
In your startup code, simply call:
AlarmReceiver.ApplicationContext = context;
AlarmReceiver.ScheduleAlarm(timeInMs);
I am writting an application with xamarin forms for iOS and Android.
I want to pick a photo from image gallery.
I have created an android specific static helper:
var i = new Intent();
i.SetType("*/*");
Forms.Context.StartActivity(Intent.CreateChooser(i, ""));
But i have no way to get the selected picture bytes.
I have seen on android tutorials i should implement onActivityResult, but i am not on an activity, this is a specific static call...
Thanks
Via a Form's dependency service:
Create your dependency interface (IMediaPicker)
Create a Activity subclass (MediaChooserActivityProxy) that will act as your Intent.ActionPick proxy
In your Xamarin.Android implementation of the IMediaPicker, use a AutoResetEvent to convert the Android StartActivityForResult / OnActivityResult callback to an await-able synchronous flow.
Dependency Service Interace:
public interface IMediaPicker
{
Task<string> ChooseAFileAsync();
}
Android Dependency Implementation:
public class MediaPicker : IMediaPicker
{
public static string filePickedPath;
public static AutoResetEvent waitHandle;
public async Task<string> ChooseAFileAsync()
{
waitHandle = new AutoResetEvent(false);
filePickedPath = "";
Forms.Context.StartActivity(typeof(MediaChooserActivityProxy));
await Task.Run(() => waitHandle.WaitOne());
return filePickedPath;
}
}
The Proxy/Pseudo Activity to capture OnActivityResult:
public class MediaChooserActivityProxy : Activity
{
const string mimeType = "image/*";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var intent = new Intent(Intent.ActionPick);
intent.SetType(mimeType);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
intent.PutExtra(Intent.ExtraMimeTypes, mimeType);
}
StartActivityForResult(Intent.CreateChooser(intent, "StackOverflow"), 73);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == 73)
if (resultCode == Result.Ok)
{
string[] filePathColumn = { MediaStore.Images.ImageColumns.Data };
var cursor = ContentResolver.Query(data.Data, filePathColumn, null, null, null);
cursor.MoveToFirst();
var colummIndex = cursor.GetColumnIndex(filePathColumn[0]);
MediaPicker.filePickedPath = cursor.GetString(colummIndex);
}
MediaPicker.waitHandle.Set();
Finish();
}
}
Note: This can be implemented on the MainActivity/FormsAppCompatActivity to avoid this additional Activity if desired...
Usage:
var filePath = await DependencyService.Get<IMediaPicker>().ChooseAFileAsync();
System.Diagnostics.Debug.WriteLine(filePath);
I want to pass a value from my main activity in both of my tab-activities. Here is my code:
Main Activity:
public class MainActivity : TabActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
CreateTab(typeof(test1), "Page1", "Page1");
CreateTab(typeof(test2), "Page2", "Page2");
var test = new Intent(this, typeof(test1));
test.PutExtra("MyData", "Data from MainActivity");
var test = new Intent(this, typeof(test2));
test.PutExtra("MyData", "Data from MainActivity");
}
private void CreateTab(Type activityType, string tag, string label)
{
var intent = new Intent(this, activityType);
intent.AddFlags(ActivityFlags.NewTask);
var spec = TabHost.NewTabSpec(tag);
spec.SetIndicator(label);
spec.SetContent(intent);
TabHost.AddTab(spec);
}
}
And in both of my activities i'm trying this:
TextView textview = new TextView(this);
textview.Text = Intent.GetStringExtra("MyData");
SetContentView(textview);
Unfortanetely i dont take any result.
I found a solution which works but i dont know if its the right way:
Main Activity:
public class MainActivity : TabActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
CreateTab(typeof(test1), "Page1", "Page1");
CreateTab(typeof(test2), "Page2", "Page2");
var prefs = Application.Context.GetSharedPreferences("MyApp", FileCreationMode.Private);
var prefEditor = prefs.Edit();
prefEditor.PutString("PrefName", "Some value");
prefEditor.Commit();
}
private void CreateTab(Type activityType, string tag, string label)
{
var intent = new Intent(this, activityType);
intent.AddFlags(ActivityFlags.NewTask);
var spec = TabHost.NewTabSpec(tag);
spec.SetIndicator(label);
spec.SetContent(intent);
TabHost.AddTab(spec);
}
}
And into my activities
// Function called from OnCreate
SetContentView(Resource.Layout.test1);
var prefs = Application.Context.GetSharedPreferences("MyApp", FileCreationMode.Private);
var somePref = prefs.GetString("PrefName", null);