Get current Activity - Xamarin Android - c#

I am developing an portable App for Android and iOS. My current function is taking a Screenshot and use that image in the code. Therefor I have an Interface in the portable library.
public interface IFileSystemService
{
string GetAppDataFolder();
}
I am taking the Screenshot also in the portable Library with the following code:
static public bool TakeScreenshot()
{
try
{
byte[] ScreenshotBytes = DependencyService.Get<Interface.IScreenshotManager>().TakeScreenshot();
return true;
}
catch (Exception ex)
{
}
return false;
}
This either calls the Android or the iOS version.
Android:
class ScreenshotManagerAndroid : IScreenshotManager
{
public static Activity Activity { get; set; }
public byte[] TakeScreenshot()
{
if (Activity == null)
{
throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
}
var view = Activity.Window.DecorView;
view.DrawingCacheEnabled = true;
Bitmap bitmap = view.GetDrawingCache(true);
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
The question now is to get the current Activity from my app.

A better way would be to use the Standalone Current Activity Plugin or the Current Activity Property in the Xamarin Essentials Plugin. Then you could just do:
Standalone: CrossCurrentActivity.Current.Activity
Xamarin Essentials: Platform.CurrentActivity
If you do not want to use a plugin and you only have 1 Activity in your app, you could get away with assigning a static variable in MainActivity and referencing that where ever you needed it like this:
public class MainActivity : FormsApplicationActivity {
public static Context Context;
public MainActivity () {
Context = this;
}
}
If you needed Context within a custom renderer, you would want to use the Context passed into the constructor, like this:
public class MyEntryRenderer : EntryRenderer {
private readonly Context _context;
public MyEntryRenderer(Context context) : base(context) {
_context = context;
}
// Now use _context or ((Activity)_context) any where you need to (just make sure you pass it into the base constructor)
}
The old deprecated way would be Context view = (Activity)Xamarin.Forms.Forms.Context
Xamarin automatically assigns the Activity to Forms.Context.

Since the release of Xamarin 2.5, Xamarin.Forms.Forms.Context is obsolete. The Context can now be obtained as follows:
var currentContext = Android.App.Application.Context;

var activity = (Activity)Forms.Context;
or if you are using MainActivity
var activity = (MainActivity)Forms.Context;

If you are using Xamarin Essentials 1.5 or higher, then you can use Platform.CurrentActivity. This is basically the equivalent of using the CurrentActivity plugin.
Ensure you initialise this correctly as per the instructions ie. in MainActivity OnCreate add the following line
Xamarin.Essentials.Platform.Init(this, savedInstanceState);

I was trying to do something similar in Xamarin 5 and am having some luck in both my Android and iOS versions using
Shell.Current.CurrentPage
So something happens like a screenshot or a login and that method (whatever it is) can fire a static event so that any interested activity can look for itself whether it's the active view or not, and if so consume the data (byte array etc.) transported by the event.
class FileChooserPage : ContentPage
{
public FileChooserPage()
{
InitializeComponent();
GoogleDriveService.Authenticated += GoogleDriveService_Authenticated;
}
private void GoogleDriveService_Authenticated(object sender, EventArgs e)
{
if (ReferenceEquals(this, Shell.Current.CurrentPage))
{
Populate(e);
}
}
}

Related

How to make possible to listen to another App song/audio

I am leaning Xamarin,
I have build a quick app that read audio effect file (mp3/wav).
My problem :
When I start the App while I was listened music ( spotify/deezer ) the musique stops because the app has started.
So, I would like to be able to listen to music ( spotify/deezer ) and use my app.
My audio C# class in Android looks like this :
public class MyAudioOutput : IAudioServiceOutput
{
public void GetAudioSetting()
{
var audioManager = (Android.Media.AudioManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.AudioService);
audioManager.Mode = Mode.Normal;
audioManager.SpeakerphoneOn = true;
}
}
My Audio C# classe in IOS looks like this :
public class MyAudioOutput : IAudioServiceOutput
{
public void GetAudioSetting()
{
var session = AVAudioSession.SharedInstance();
session.OverrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, out NSError error);
session.SetCategory(AVAudioSessionCategory.Playback);
session.SetActive(true);
}
}
My interface in Xamarin Forms shared :
public interface IAudioServiceOutput
{
void GetAudioSetting();
}
In my Xamarin forms cs file , I use the function this way :
ISimpleAudioPlayer AudioPlayerGood;
void InitMyAudio()
{
string filenamegood = "Question.Good_Answer.mp3";
using (Stream streamGood = GetType().GetTypeInfo().Assembly.GetManifestResourceStream(filenamegood))
{
AudioPlayerGood = CrossSimpleAudioPlayer.CreateSimpleAudioPlayer();
AudioPlayerGood.Load(streamGood);
}
}
void OnPlayAnswerSong(bool goodanswer)
{
if (goodanswer == true)
{
AudioPlayerGood.Play();
}
else if (goodanswer == false)
{
AudioPlayerWrong.Play();
}
}
Thanks for you help
For this, you can refer to the following thread:Pause background service in Xamarin.Forms
The implementation on android platform is as follows:
[assembly: Dependency(typeof(StopMusicService))]
namespace TTSDemo.Droid
{
public class StopMusicService : IControl
{
AudioManager audioMan;
AudioManager.IOnAudioFocusChangeListener listener;
public void StopBackgroundMusic()
{
audioMan = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
listener = new MyAudioListener(this);
var ret = audioMan.RequestAudioFocus(listener, Stream.Music, AudioFocus.Gain);
}
}
internal class MyAudioListener :Java.Lang.Object, AudioManager.IOnAudioFocusChangeListener
{
private StopMusicService stopMusicService;
public MyAudioListener(StopMusicService stopMusicService)
{
this.stopMusicService = stopMusicService;
}
public void OnAudioFocusChange([GeneratedEnum] AudioFocus focusChange)
{
// throw new NotImplementedException();
}
}
}
And you can also check the updated part code by Nieminen included in above link.

Is there any possibility to open Contacts App in Xamarin IOS

Is there any possibility to open Contacts App in Xamarin IOS,
For android the below code worked for me.
var activity = Forms.Context as Activity;
var intent = new Intent(Intent.ActionInsert);
intent.SetType(ContactsContract.Contacts.ContentType);
activity.StartActivity(intent);
Where as for IOS I have't find any code can any one have solution for this.
Apple has released two new frameworks, Contacts and ContactsUI, that replace the existing Address Book and Address Book UI frameworks used by iOS 8 and earlier.
You could use these two frameworks to deal with all situation which involve contacts.
To make it clear, I create a simple app to show how to display contact app in ios.
you can open the contacts app with code like:
public override void ViewDidLoad()
{
base.ViewDidLoad();
picker1.TouchDown += Picker1_TouchDown;
}
private void Picker1_TouchDown(object sender, EventArgs e)
{ //create a picker
var picker = new CNContactPickerViewController();
//set the delegate
picker.Delegate = new ContactPickerDelegate();
//display picker
PresentViewController(picker, true, null);
}
The ContactPickerDelegate is what you need to create and used to respond to the user's interaction with the picker.
The code is like:
public class ContactPickerDelegate: CNContactPickerDelegate
{
#region Constructors
public ContactPickerDelegate ()
{
}
public ContactPickerDelegate (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ContactPickerDidCancel (CNContactPickerViewController picker)
{
Console.WriteLine ("User canceled picker");
}
public override void DidSelectContact (CNContactPickerViewController picker, CNContact contact)
{
Console.WriteLine ("Selected: {0}", contact);
}
public override void DidSelectContactProperty (CNContactPickerViewController picker, CNContactProperty contactProperty)
{
Console.WriteLine ("Selected Property: {0}", contactProperty);
}
#endregion
}
Screenshots:
For more information, you can refer to https://learn.microsoft.com/en-us/xamarin/ios/platform/contacts

Change starting page in app created with Windows Template Studio

I created an app with Windows Template Studio on Visual Studio 2017.
The app is mainly a NavigationDrawer with different pages.
Everything was ok, until I wanted to add a login page.
So I created the XAML of the login page, etc. But now I want it to show before the NavigationDrawer page on app startup.
I seeked some documentation about the App.xaml.cs to know what to change to do that but, because of the use of Windows Template Studio, the code is not really vanilla anymore.
I tried a few things and the only thing I'm able to do right now is to change the shell page of the NavigationDrawer to my Login page.
That's not exactly what I want because my first intention was to make the app unavailable until you log in, and because the NavigationDrawer is still usable the user can still do what he wants to.
My app.xaml.cs looks like this :
using System;
using BasePosteMobilite.Services;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
namespace BasePosteMobilite
{
public sealed partial class App : Application
{
private Lazy<ActivationService> _activationService;
private ActivationService ActivationService
{
get { return _activationService.Value; }
}
public App()
{
InitializeComponent();
// Deferred execution until used. Check https://msdn.microsoft.com/library/dd642331(v=vs.110).aspx for further info on Lazy<T> class.
_activationService = new Lazy<ActivationService>(CreateActivationService);
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
if (!args.PrelaunchActivated)
{
await ActivationService.ActivateAsync(args);
}
}
protected override async void OnActivated(IActivatedEventArgs args)
{
await ActivationService.ActivateAsync(args);
}
private ActivationService CreateActivationService()
{
return new ActivationService(this, typeof(ViewModels.LoginViewModel), new Lazy<UIElement>(CreateShell));
}
private UIElement CreateShell()
{
return new Views.ShellPage();
}
}
}
ShellPage.xaml.cs :
using System;
using BasePosteMobilite.ViewModels;
using Windows.UI.Xaml.Controls;
namespace BasePosteMobilite.Views
{
// TODO WTS: Change the icons and titles for all NavigationViewItems in ShellPage.xaml.
public sealed partial class ShellPage : Page
{
private ShellViewModel ViewModel
{
get { return ViewModelLocator.Current.ShellViewModel; }
}
public ShellPage()
{
InitializeComponent();
DataContext = ViewModel;
ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators);
}
}
}
ViewModel.Initialize :
public void Initialize(Frame frame, WinUI.NavigationView navigationView, IList<KeyboardAccelerator> keyboardAccelerators)
{
_navigationView = navigationView;
_keyboardAccelerators = keyboardAccelerators;
NavigationService.Frame = frame;
NavigationService.NavigationFailed += Frame_NavigationFailed;
NavigationService.Navigated += Frame_Navigated;
_navigationView.BackRequested += OnBackRequested;
}
You can create a project with login required feature and you will see the following code from ActivateAsync method:
var silentLoginSuccess = await IdentityService.AcquireTokenSilentAsync();
if (!silentLoginSuccess || !IdentityService.IsAuthorized())
{
await RedirectLoginPageAsync();
}
That's it. If you want to redirect to your own page, write the detectation code under ActivationService.ActivateAsync(args) method. If you see the customer is not logged in. Call redirect login method. Here is the code from template studio about redirectlogin:
public async Task RedirectLoginPageAsync()
{
var frame = new Frame();
NavigationService.Frame = frame;
Window.Current.Content = frame;
await ThemeSelectorService.SetRequestedThemeAsync();
NavigationService.Navigate<Views.LogInPage>();
}

How can I debug a Xamarin watchOS Linker Error

I am developing an iOS App using Xamarin Forms for which I have created a core model around which all app functionality works.
I would like to include a simple watchOS app which allows the user to operate on a single instance of this model at any one time. I have implemented some code to update the model in the watchOS App using WCSession (via this WCSessionManager Class). I have also reused some code for implementing a timer from my Xamarin Forms project.
However I am encountering a Linker error when building my solution. I think it may be because I have referenced my Xamarin Forms project from my watchOS project, which may not be allowed. Here is the error:
/Users/luketimothy/Projects/TodoQ/TodoQ.Watch/TodoQ.Watch.WatchOSExtension/MTOUCH: Error MT2001: Could not link assemblies. Reason: Error while processing references of 'TodoQWatchWatchOSExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (MT2001) (TodoQ.Watch.WatchOSExtension)
The file the error references is MTOUCH. I am not sure what this is exactly, but the only place in my watchOS app I have referenced my Xamarin Forms code is this object:
using System;
using System.Collections.Generic;
using TodoQ.Models;
using TodoQ.Utilities;
using WatchConnectivity;
using WatchKit;
namespace TodoQ.Watch.WatchOSExtension
{
internal class TodoState
{
private TodoItem current;
private ISessionTimer timer;
public TodoItem Current { get => current; set { current = value; TaskUpdated(this, value); } }
public event TaskUpdatedEventHandler TaskUpdated;
public delegate void TaskUpdatedEventHandler(object sender, TodoItem current);
public event TimerElapsedEventHandler TimerElapsed;
public delegate void TimerElapsedEventHandler(object sender, TimerElapsedEventArgs current);
public TodoState()
{
WCSessionManager.SharedManager.ApplicationContextUpdated += DidReceiveApplicationContext;
timer = new PomodoroTimer();
timer.ProgressUpdate += (object sender, ProgressUpdateEventArgs e) =>
{
TimerElapsed(this, new TimerElapsedEventArgs() { Elapsed = e.Elapsed, EndTime = e.EndTime });
};
timer.MilestoneUpdate += (object sender, PomodoroStateID e) =>
{
var audio_file = WKAudioFilePlayerItem.Create(WKAudioFileAsset.Create(new Foundation.NSUrl("ShortBreak.wav")));
var audio_player = WKAudioFilePlayer.Create(audio_file);
audio_player.Play();
WKInterfaceDevice.CurrentDevice.PlayHaptic(WKHapticType.Notification);
};
}
public void DidReceiveApplicationContext(WCSession session, Dictionary<string, object> applicationContext)
{
var message = (TodoItem)applicationContext["FocusedItem"];
if (message != null)
{
Console.WriteLine($"Application context update received : {message.Heading}");
Current = message;
}
}
public void StartTimer()
{
timer.StartSession();
}
}
public class TimerElapsedEventArgs
{
public TimeSpan Elapsed;
public TimeSpan EndTime;
}
}
So, my question is. If this ought to be allowed, and the error is something else, could I get some help tracking down what this MTOUCH is and why it's throwing this error? If it is not allowed, what is the recommended solution for sharing this kind of code between my Phone App and my Watch App? Could I put it in a PCL? Should I copy the code between projects?
You should not reference your WatchOS project to the Forms project. It should be added in iOS project directly.
And if you want to define some common code for reuse. You could create a shared library:
Add some public classes there:
namespace UtiLibrary
{
public static class UtiClass
{
public static List<Model> datas { get => new List<Model> { new Model { Name = "name" } }; }
}
public class Model
{
public string Name { set; get; }
}
}
Then you could utilize it on each platform which has referenced this library.

Passing a string from an event to MainActivity in Android Xamarin Visual Studio

The project is done in Visual Studio with Xamarin and targeted for Android.
In my simple project I have 1 activity (MainActivity). Here I create an instance of a Scanner object that will sit in background and listen for iBeacons. Once the scanner has been created I call a Start method on the scanner object.
In the constructor of the Scanner object, there will be setup an instance of a Listener object. This instance is called when ever a beacon is detected.
Cut short – my main activity makes an instance of a scanner object. On creation of the scanner object a listener object is created. This listener object will be activated when a beacon is detected.
In my MainActivity I have a multiline TextView. I want it to show the beacons found in the listener object.
What is the best way to pass this beacon ID (string) to the TextView in the main activity?
I’m new to programming in Android, so all the different concepts with blocks, intents and what have you, are a bit confusing. I would have thought it was straight forward passing data from a listener event to the TextView but this has proven more difficult then expected.
I don’t mind going slow – so please feel free to elaborate and consider me the novice I am :-D
UPDATE:
I have edited the code to make it as short as possible, and pasted it below. Hope this gives an idea.
[Activity(Label = "DeviceScanSample", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity
{
KontaktScanner scanner;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
// Initialize Scanner
scanner = new KontaktScanner(this);
// Button actions
startScanButton.Click += delegate
{
if (CheckPermission(Manifest.Permission.AccessCoarseLocation))
{
// Start devices scan
scanner.Start();
}
else
{
// Ask for permissions if needed
...
}
};
}
}
public class KontaktScanner : Java.Lang.Object, IOnServiceReadyListener
{
IProximityManager proximityManager;
public KontaktScanner(Context context)
{
// Set Space listener
proximityManager.SetSpaceListener(new KontaktSimpleSpaceListener());
}
public void Start()
{
proximityManager.Connect(this);
}
}
class KontaktSimpleSpaceListener : SimpleSpaceListener
{
public void OnRegionEntered(IBeaconRegion beaconRegion)
{
Log.Info(TAG, string.Format("Entered {0} region", beaconRegion.Identifier));
}
public void OnRegionAbandoned(IBeaconRegion beaconRegion)
{
Log.Info(TAG, string.Format("Abandoned {0} region", beaconRegion.Identifier));
}
}
Try something like this:
Pass the context here:
public KontaktScanner(Activity activity)
{
// Set Space listener
proximityManager.SetSpaceListener(new KontaktSimpleSpaceListener(activity));
}
Then:
class KontaktSimpleSpaceListener : SimpleSpaceListener
{
Activity context
public KontaktSimpleSpaceListener(Activity activity)
{
this.context = activity;
}
public void OnRegionEntered(IBeaconRegion beaconRegion)
{
Log.Info(TAG, string.Format("Entered {0} region", beaconRegion.Identifier));
MainActivity myActivity = (MainActivity) context;
myActivity.updateTextView("My Data");// pass the string here.
}
public void OnRegionAbandoned(IBeaconRegion beaconRegion)
{
Log.Info(TAG, string.Format("Abandoned {0} region", beaconRegion.Identifier));
MainActivity myActivity = (MainActivity) context;
myActivity.updateTextView("My another data");// pass the string here.
}
}
Then create method in MainActivity:
public void updateTextView(string s)
{
RunOnUiThread(() =>
{
yourTextView.Text = s;//set your TextView here
});
}
I have not checked the syntax but something like this should work.

Categories

Resources