Passing values between tabs - xamarin

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);

Related

How to create a app where that app services always runs in background even if the app is closed in Xamarin (Android)?

I am creating an application in Xamarin using visual studio,where that app services always should be running in background even if the app is closed and that service should send notification in a interval.
If that app is closed by the system(in the condition when it is taking more memory space) then it should restart the services by showing the status as "0 process and 0 service Restarting.." under running services list in Settings.
Can anyone help me how to achieve that having some reference of sample project.
In BackgroundService.cs
[Service]
public class BackgroundService : Service
{
const int SERVICE_RUNNING_NOTIFICATION_ID = 123;
const string NOTIFICATION_CHANNEL_ID = "com.company.app.channel";
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug("service", "Service Started");
// Check if device is running Android 8.0 or higher and call StartForeground() if so
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText(Resources.GetString(Resource.String.notification_text))
.SetSmallIcon(Resource.Drawable.notification_icon_background)
.SetOngoing(true)
.Build();
var notificationManager =
GetSystemService(NotificationService) as NotificationManager;
var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "On-going Notification", NotificationImportance.Min);
notificationManager.CreateNotificationChannel(chan);
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
}
return StartCommandResult.NotSticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartServiceIntent = new Intent(this, this.Class);
restartServiceIntent.SetPackage(this.PackageName);
Log.Debug("service", "Service Restarted");
PendingIntent restartServicePendingIntent = PendingIntent.GetService(this, 1, restartServiceIntent, PendingIntentFlags.OneShot);
AlarmManager alarmService = (AlarmManager)this.GetSystemService(Context.AlarmService);
alarmService.Set(
AlarmType.ElapsedRealtime,
SystemClock.ElapsedRealtime() + 1000,
restartServicePendingIntent);
base.OnTaskRemoved(rootIntent);
}
}
Updtaed MainActivity.cs
public class MainActivity : AppCompatActivity
{
String alarm = Android.Content.Context.AlarmService;
const string NOTIFICATION_CHANNEL_ID = "com.companyname.ServiceSample3";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.abc_activity_chooser_view);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
//var alarmIntent = new Intent(this, typeof(AlarmReceiver));
//alarmIntent.PutExtra("title", "Hello");
//alarmIntent.PutExtra("message", "World!");
Intent intent = new Intent(NOTIFICATION_CHANNEL_ID);
intent.SetClass(this, typeof(AlarmReceiver));
var pending = PendingIntent.GetBroadcast(this, 0, intent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetSystemService(AlarmService).JavaCast<AlarmManager>();
alarmManager.SetRepeating(AlarmType.ElapsedRealtime, DateTime.Now.Millisecond, 5 * 100, pending);
}
}
Thanks in advance.
Please refer the below sample.You can use alarm manager for restarting the service.
Please follow the steps.
Step 1 :
Create BroadcastReceiver
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace AlarmManagerApp
{
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
class AlarmReceiver : BroadcastReceiver
{
Context context;
public override void OnReceive(Context context, Intent intent)
{
this.context = context;
Toast.MakeText(context,"Recieved",ToastLength.Long).Show();
Intent background = new Intent(context, typeof(BackgroundService));
context.StartService(background);
}
}
}
Step 2: Add AlarmManager in the MainActivity where you want to call the service
namespace AlarmManagerApp
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
String alarm = Context.AlarmService;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
var alarmIntent = new Intent(this, typeof(AlarmReceiver));
alarmIntent.PutExtra("title", "Hello");
alarmIntent.PutExtra("message", "World!");
var pending = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetSystemService(AlarmService).JavaCast<AlarmManager>();
alarmManager.SetRepeating(AlarmType.ElapsedRealtime,DateTime.Now.Millisecond, 5 * 100, pending);
}
}
Step 3: Add you Service
namespace AlarmManagerApp
{
[Service]
public class BackgroundService : Service
{
public override void OnCreate()
{
base.OnCreate();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug("service","Service Started");
//init the handler in oncreate
System.Timers.Timer Timer1 = new System.Timers.Timer();
Timer1.Start();
Timer1.Interval = 3000;
int a = 0;
Timer1.Enabled = true;
//Timer1.Elapsed += OnTimedEvent;
Timer1.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) =>
{
Timer1.Stop();
Timer1.Start();
a++;
//Delete time since it will no longer be used.
Timer1.Dispose();
};
Timer1.Start();
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartServiceIntent = new Intent(this, this.Class);
restartServiceIntent.SetPackage(this.PackageName);
Log.Debug("service", "Service Restarted");
PendingIntent restartServicePendingIntent = PendingIntent.GetService(this, 1, restartServiceIntent, PendingIntentFlags.OneShot);
AlarmManager alarmService = (AlarmManager)this.GetSystemService(Context.AlarmService);
alarmService.SetRepeating(AlarmType.RtcWakeup, SystemClock.CurrentThreadTimeMillis(), 30 * 1000, restartServicePendingIntent);
NotificationManager notificationManager =
(NotificationManager)GetSystemService(NotificationService);
Notification.Builder builder = new Notification.Builder(this);
Intent notificationIntent = new Intent(this, typeof(MainActivity));
PendingIntent contentIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
//set
builder.SetContentIntent(contentIntent);
builder.SetSmallIcon(Resource.Mipmap.ic_launcher);
builder.SetContentText("Contents");
builder.SetContentTitle("title");
builder.SetAutoCancel(true);
builder.SetDefaults(NotificationDefaults.All);
notificationManager.Notify(1, builder.Build());
base.OnTaskRemoved(rootIntent);
}
}
}
This works also if app is closed and device restarted.

Xamarin, how to clear intentExtra after use

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.

android media picker from Xamarin forms

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);

DisplayAlert With changing Text xamarin forms

I have a requirement where i have to show the status of the download on a DisplayAlert. But with changing text on it asynchronously.
How to achieve this?
DisplayAlert("Download Info", "Downloading.....", "Ok");
I want to show status like...
Connected to server
Downloading
Download Complete
Here is a simple "Dynamic Alert" for Forms and iOS using UIAlertController and Android using a DialogFragment and a Xamarin.Forms dependency service:
Dependency Interface:
public interface IDynamicAlert
{
void Show(string title, string message);
void Update(string message);
void Dismiss();
}
iOS IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
UIAlertController alert;
public void Show(string title, string message)
{
if (alert != null) throw new Exception("DynamicAlert already showing");
alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(alert, true, () =>
{
});
}
public void Update(string message)
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.Message = message;
}
public void Dismiss()
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.DismissViewController(true, () =>
{
alert.Dispose();
alert = null;
});
}
}
Example Usage:
var alert = DependencyService.Get<IDynamicAlert>();
if (alert != null)
{
alert.Show("StackOverflow", "Starting your request...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is processing...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is complete...");
await Task.Delay(750);
alert.Dismiss();
}
else
{
throw new Exception("IDynamicAlert Dependency not found");
}
Output:
Android Version:
The android version consists of a couple of parts, a DialogFragment subclass and the IDynamicAlert implementation that uses the custom DialogFragment.
Android DialogFragment Subclass:
public class DynamicAlertDialogFragment : DialogFragment
{
AlertDialog alertDialog;
readonly Context context;
public static DynamicAlertDialogFragment Instance(Context context, string title, string message)
{
var fragment = new DynamicAlertDialogFragment(context);
Bundle bundle = new Bundle();
bundle.PutString("title", title);
bundle.PutString("message", message);
fragment.Arguments = bundle;
return fragment;
}
public DynamicAlertDialogFragment(Context context)
{
this.context = context;
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
var title = Arguments.GetString("title");
var message = Arguments.GetString("message");
alertDialog = new AlertDialog.Builder(context)
.SetIcon(Android.Resource.Drawable.IcDialogInfo)
.SetTitle(title)
.SetMessage(message)
.Create();
return alertDialog;
}
public void SetMessage(string message)
{
(context as Activity).RunOnUiThread(() => { alertDialog.SetMessage(message);});
}
}
Android IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
const string FRAGMENT_TAG = "DynamicAlert_Fragment";
DynamicAlertDialogFragment fragment;
static FormsAppCompatActivity currentActivity;
public static FormsAppCompatActivity CurrentActivity { set { currentActivity = value; } }
public void Show(string title, string message)
{
if (currentActivity == null) throw new Exception("DynamicAlert.CurrentActivity needs assigned");
var fragMgr = currentActivity.FragmentManager;
var fragTransaction = fragMgr.BeginTransaction();
var previous = fragMgr.FindFragmentByTag(FRAGMENT_TAG);
if (previous != null)
{
fragTransaction.Remove(previous);
}
fragTransaction.DisallowAddToBackStack();
fragment = DynamicAlertDialogFragment.Instance(currentActivity, title, message);
fragment.Show(fragMgr, FRAGMENT_TAG);
}
public void Update(string message)
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.SetMessage(message);
}
public void Dismiss()
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.Dismiss();
fragment.Dispose();
fragment = null;
}
}
Android Init / Usage:
When creating the AlertDialog in the DialogFragment we need access to the current Activity and when using Xamarin.Forms, that is normally the MainActivity that is a FormsAppCompatActivity subclass. Thus you will need to initialize the DynamicAlert.CurrentActivity static property with this Activity in your MainActivity.OnCreate subclass:
Example:
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
////////////
DynamicAlert.CurrentActivity = this;
////////////
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
Android Output:

Why can't I access the Assets folder of my Xamarin.Android project this way?

I want to set the Typeface of the TextView to a font in the Assets folder. The problem-code is "var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");," not the first use, but the second one towards the end of my code (the red squiggly line appears under "Assets").
namespace UndergroundSports.Android
{
[Activity]
public class CityPage : Activity
{
Sport[] sports = Sport.Sports;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
this.SetContentView(Resource.Layout.CityPage);
var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");
Button bttJoin = FindViewById<Button>(Resource.Id.bttJoin);
bttJoin.Click += (sender, e) =>
{
gotoJoinPage();
};
bttJoin.Typeface = font;
ListView lstSports = FindViewById<ListView>(Resource.Id.lstSport);
lstSports.Adapter = new SportsAdapter(this, sports);
lstSports.ItemClick += (object sender, AdapterView.ItemClickEventArgs e) =>
{
Sport selectedFromList = sports[e.Position];
Global.Instance.CurrentSport = selectedFromList;
gotoMembersPage();
};
}
private void gotoJoinPage()
{
var intent = new Intent(this, typeof(JoinPage));
StartActivity(intent);
}
private void gotoMembersPage()
{
var intent = new Intent(this, typeof(MembersPage));
StartActivity(intent);
}
public class SportsAdapter : BaseAdapter<Sport>
{
Sport[] items;
Activity context;
public SportsAdapter(Activity context, Sport[] items) : base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override Sport this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Length; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(global::Android.Resource.Layout.SimpleListItem1, null);
TextView txtView = view.FindViewById<TextView>(global::Android.Resource.Id.Text1);
var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");
txtView.Text = items[position].Name;
txtView.Gravity = GravityFlags.Center;
txtView.Typeface = font;
return view;
}
}
}
}
But when I tried to create a variable containing the font I got an error telling me:
Cannot access a nonstatic member of outer type Android.Content.Context' via nested typeUndergroundSports.Android.CityPage.SportsAdapter' (CS0038) (UndergroundSportsAndroid)"
From looking at related questions I think I need to either create an instance of the Assets object or make it static.
I'm pretty new to C# and don't really understand what's going on. I would appreciate it if someone could explain why I'm unable to access Assets in this part of my code. The part that confuses me the most is that I use the exact same line of code to access the font earlier within the same file without getting that error.
var font = Typeface.CreateFromAsset(context.Assets, "Enter-The-Grid.ttf");
Pass your activity's instance to your adapter via constructor, and use it to access Assests
public class SportsAdapter : BaseAdapter<Sport>
{
Sport[] items;
Activity context;
public SportsAdapter(Activity context, Sport[] items) : base()
{
this.context = context;
this.items = items;
}
....
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(global::Android.Resource.Layout.SimpleListItem1, null);
TextView txtView = view.FindViewById<TextView>(global::Android.Resource.Id.Text1);
var font = Typeface.CreateFromAsset(context.Assets, "Enter-The-Grid.ttf");
txtView.Text = items[position].Name;
txtView.Gravity = GravityFlags.Center;
txtView.Typeface = font;
return view;
}
}
Also, make sure your .ttf file's build action is set to AndroidAssests. Right the .tff file > Build Action > AndroidAsset

Resources