I'm working on a Xamarin application, which I will at first have working on iOS, but plan to later expand to Android and other mobile platforms.
As such, I'm trying to keep as much common code in PCLs as possible.
My question: what is the best practise - in Xamarin.iOS for now - to initialize any dependent PCL code?
For now I have it in the RootViewController inside ViewDidLoad()
public override void ViewDidLoad()
{
base.ViewDidLoad();
_engine = new MyEngine();
View = new MainView(_engine);
}
Is this the right spot? I'd considered putting it in the ctor for the RootViewController, but there's a fair bit going on in the initialization code, which thus ran against "don't put heavy duty init code into a constructor".
Things that happen are:
Load app settings
If app is run for first time ever, load basic defaults
Initialise other PCL libraries, such as a TextToSpeech module, a state engine (hence the name of the class above), etc
Prepare a data grid based on XML or JSON input
Alternately, I though it should possibly go into the AppDelegate section, but that didn't sound right.
I'm still fairly new to mobile app dev in general and Xamarin in specific, though I've done C# native code for Windows for years. I just want to make sure I follow best practises, but there doesn't seem to be a "thou shalt" in this case.
Edit: I've extracted the solution based on #wishmaster's suggestions.
For iOS the Appdelegate method is the best place for initialization code. The appdelegate also provides multiple delegate methods to give you feedback on application lifecyle events such as the method "DidFinishLauchingWithOptions"
. if you have a lot of data to download or long running tasks that your app depends on I would suggest you take a look backgrounding for iOS.
A technique I have also used is for my first viewcontroller on IOS (or activity on Android) to display a splash screen and a loading indicator while i run some code to refresh the cache.
Using #wishmaster's pointers, this solution works like a charm:
In AppDelegate.cs
// in the global section put any data you may make available elsewhere
private var _engine;
public Engine => _engine;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
/*
* Do whatever init needs to happen here, if you need to make this
* available elsewhere, ensure you have properties or accessors,
* as above.
*/
_engine = new MyEngine();
return true;
}
Then in RootViewController.cs using a similar approach to these examples in Obc-C or Swift you can access the information through a property pointing at the AppDelegate.
var myappdelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
var engine = myappdelegate.Engine;
View = new MainView(engine);
The result resulted in a snappier start up of the application, because the initialisation now happens during the splash screen and no longer between splash screen and appearance of the UI.
Related
I am currently trying to draw into a GLArea of Gtk#. Gtk# sadly doesn't come with the GL functions and only provides context creation.
I am now trying for days to get the latest prerelease of OpenTK (4.0.0-pre.10) work with Gtk#'s GLArea.
using Gtk;
using OpenToolkit.Graphics.OpenGL4;
using OpenToolkit.Windowing.GraphicsLibraryFramework;
namespace Test
{
class Viewport : GLArea
{
protected override bool OnRender(Gdk.GLContext context)
{
GL.ClearColor(0.3f, 0.3f, 0.3f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
return true;
}
}
class MainWindow : Gtk.Window
{
public MainWindow() : base(WindowType.Toplevel)
{
this.Add(new Viewport());
}
}
class Program
{
static void Main(string[] args)
{
GLFW.Init();
GL.LoadBindings(new GLFWBindingsContext());
Application.Init();
var window = new MainWindow();
window.ShowAll();
Application.Run();
}
}
}
The problem is that the GL functions seem to not get loaded, the first attempt to call one of them gives a System.AccessViolationException.
I couldn't really find any information on how to initialize the OpenTK 4.0 bindings. OpenTK 3 had OpenTK.Toolkit.Init() which seems to be exactly what i need.
Documentation for OpenTK.Toolkit.Init:
"You must call this method if you are combining OpenTK with a third-party windowing toolkit (e.g. GTK#). In this case, this should be the first method called by your application"
I can't find this function or something similar in version 4. It seems like it just got removed.
I can't use OpenTK 3 either because it lacks support for .Net Core. I get everything to work by creating a OpenTK dummy window and instantly throw it away. That initializes the GL.* functions but you see the second window opening and closing for a second. I really don't want to use workarounds like that.
Does anyone have any insight on how to properly initialize the OpenTK 4.0 bindings? I mean, there has to be a way, the bindings would essentially be useless for a lot of use cases otherwise.
First of all using the GLFW bindings requires an active Opengl Context created by it and seconds of all you might want to use some other bindings context, sadly they provide only the one for GLFW because that's what they chose to use for windowing.
I have made a bindings context that works for Linux and with the help of someone I made something that will work on both windows too. You can grab it from here https://gist.github.com/NepNet/e4e75defb3748f76f10302b585b9839b. I suggest loading the bindings in the OnRealized event of the GLArea and make it's context current if you use the NativeBindingsContext because OpenGL on windows requires an active context for it to return valid pointers the other 2 files are required by it but if you want to target only one platform you can use only the glx context for linux and wgl for windows
edited:
You can load the bindings before starting the gtk app because glx doesn't require an active context to return the function pointers but I sugest loading them in the OnRealized event of the GLArea with an optional static boolean to not load them more than once and If you want to do GL calls in the onrealized I suggest to make the context of the area curent and as for windows I don't have a solution yet, tried with 'opengl32.dll' and wglGetProcAddress and seems to return 0 most of the time even with an active context
I have this code to check if a service is active, but I would like to know if there is any way for an EditText to show the status of the service, without making this query per second, or in a separate thread, or linking it in some way? that it is possible to detect if the service stopped
private bool MiServicioEstaCorriendo(Class #class, Context contexto)
{
ActivityManager manager = (ActivityManager)contexto.GetSystemService(Context.ActivityService);
foreach (ActivityManager.RunningServiceInfo service in manager.GetRunningServices(Integer.MaxValue))
{
if (#class.Name.Equals(service.Service.ClassName))
{
Log.Info(typeof(BroadcastGps).Name, "MiServcicioEstaCorriendo: true");
return true;
}
}
Log.Info(typeof(BroadcastGps).Name, "MiServcicioEstaCorriendo: false");
return false;
}
You are basically in need of a way to pass events/messages among classes within your application. So this question probably goes down to Android & C# implementation of such a pattern. Xamarin.Forms has a MessagingCenter class, but since you are using Xamarin.Native, you would have to create something yourself
There's nothing actually already baked in to Android or C#, but so you can implement one of the most common ways to let a class spread an event/message using the "Listener" (term used in Android) or "Delegate" (term used in C#) technique.
There are frameworks too like PubNub that you can use as Nuget packages that simplify everything for you.
Some more resources to understand the concept: Wikipedia, IBM.
And Some Android resources: Handlers, AsyncTasks, Parcelables.
Don't forget that your event to update your EditText may not be fired on the Main UIThread so you won't be able to see the changes unless you force that update line to be Run on UI Thread.
Is it possible to keep audio from a WebView, in particular, an embedded Webtorrent client (which plays video), running in the background on android? I've seen conflicting answers on this, and I'm curious what you guys know about the topic. I've seen some confirmed answers on ways to do it in android studio but not seen any for Xamarin.
I've been told that the WebView is considered a UI element; therefore, this makes it impossible to keep the video/audio running while in the background. So if that's the case, do you think with some clever coding I could override the android OS to fool it into thinking that the WebView is still in the foreground?
I know that it's possible to keep the audio running using MediaPlayer, if for example say, you're playing an MP3.. So another possibility might be using a service to maintain audio focus; but then, would the video stop playing (seeing as how that doesn't fix WebView being a UI element)?
One other possibility would be porting the entire app into a service.. but I'm not sure if that's possible. If I get an answer that it is, I'll do the work to make it happen.
I'm not looking for you guys to do the coding; I'm just looking for guidance on which method (if any) would be possible/plausible/most effective.
and here is some sample code I'm currently using to construct my WebView (not sure if that matters)
//what's on
[Activity]
//this class should be an aggregate subscription feed
public class WhatsOnActivity : Activity
{
public override void OnBackPressed()
{
WebView whatsOnWebView = FindViewById<WebView>(Resource.Id.webViewWhatsOn);
whatsOnWebView.GoBack();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.whatsOn);
//declare webview and tell our code where to find the XAML resource
WebView whatsOnWebView = FindViewById<WebView>(Resource.Id.webViewWhatsOn);
whatsOnWebView.SetBackgroundColor(Android.Graphics.Color.Green);
//set the webview client
whatsOnWebView.SetWebViewClient(new WebViewClient());
//load the 'whats on' url, will need webscript for curated subscribed feed
whatsOnWebView.LoadUrl("https://www.bitchute.com/#listing-subscribed");
//enable javascript in our webview
whatsOnWebView.Settings.JavaScriptEnabled = true;
//zoom control on? This should perhaps be disabled for consistency?
//we will leave it on for now
whatsOnWebView.Settings.BuiltInZoomControls = true;
whatsOnWebView.Settings.SetSupportZoom(true);
//scrollbarsdisabled
// subWebView.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
whatsOnWebView.ScrollbarFadingEnabled = false;
}
}
EDIT: Also, my opensource project can be found here
https://github.com/hexag0d/bitchute_mobile_android_a2
Thanks, in advance. =]
I think it is quite late to answer this question, but the project I am working on is similar to this one.
I am currently working on this project by calling Activity with WebView through Foreground Service.
This is the code that calls Activity with Webview in Service.
Put this in the onStartCommand() of the Service.
Context context = getApplicationContext();
Intent dialogIntent = new Intent(context, WebViewActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
My question is: How do I add a windows context menu item for a specific application, not globally?
Quick Brief:
We use Access (groan) for our CRM system. We use a basic 'copy to local' process for multi-access. I have written a C# 'launcher' of which handles this much better than a .bat file (they click the launcher, the launcher downloads the db, launches the db and quits). I also currently use a C# console application to handle development, automating stuff like incrementing version number, moving files around etc.
My Question/Goal:
I want to combine the two programs into one but I don't want to hinder the launcher from it's main purpose by jarring the user asking if they want to develop or not. I use this launcher too as I am primarily an estimator, hence wanting to combine the two. I have read that you can add context menu items to Windows as a whole, but I want to be able to add a launch option into the context menu just for this application. i.e. right click on program, normal menu options but with the addition of "Development Mode", this opens the program with arguments that I can use to open the development window/console instead.
Things to note:
I have played around with holding a key on start but it can be vague when to press the key. Too early - you will end up typing "r" several times into the active window, too late - and it will miss the capture point.
I have also looked at having a button on the launcher that gives you the option to go into dev mode, but the launcher is only open for around a second so its really easy to miss.
Thanks in advance
EDIT: The launcher is made and run as a click-once app.
I dont really know about whether its possible to have a custom context for a specific program, as far as i know the context works with the extension. That being said, i think there are better ways to handle your problem. Have a look at this
static void Main(string[] args)
{
/* here normal flow of the launcher*/
if (args[0] == "-dev")
{
/*here de developer mode*/
Console.WriteLine("Developer mode activated");
}
}
The way to use it is simple, you make a shorcut, and where it says the shorcut target you will have something like this "C:\Users****\Documents\visual studio 2017\Projects\Test\Test\bin\Debug\Test.exe" and you should change it to something like this "C:\Users****\Documents\visual studio 2017\Projects\Test\Test\bin\Debug\Test.exe" -dev
Further to this I stumbled upon some code to make this work. Hopefully this will help someone in the future. I am doing this in WPF, but I am sure you could probably adapt this code to work elsewhere.
1) App.xaml - Adding the JumpList action
After (not inside) the Application.Resources property add the following and change to your liking (There are a lot of properties I haven't used for development sake, check out the link to learn more):
<Application.Resources>
...
</Application.Resources>
<JumpList.JumpList>
<JumpList ShowRecentCategory="False"
ShowFrequentCategory="False">
<JumpTask Title="Open Dev Mode"
Description="Use this to enter dev mode (admins only)"
Arguments="DevMode:true"/>
</JumpList>
</JumpList.JumpList>
This will create a "Task" in the jump list:
2) Create Global static class - this will allow you to store the variable for later use in other forms.
public static class Global
{
public static Boolean DevMode = false;
}
3) App.xaml.cs - Adding OnStartup handler
Inside the App class create an override method for OnStartup
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
Global.DevMode = Boolean.Parse(e.Args.FirstOrDefault().ToString().Split(':')[1]);
}
}
4) Read the variable in your form using Global.DevMode
private void Window_ContentRendered(object sender, EventArgs e)
{
if (Global.DevMode) RunYourDevScript();
}
As per a suggestion in a comment, that has gone for some reason. I am going to revert to keypress but use shift instead or "R".
Failing this, or if it causes problems it will look at creating a global context menu item for all programs, and just won't click it when I don't need to
I'm creating a Xamarin.Forms App and want to use UITest to test the correct behavior of my View. The View does not only react on user input like button click or the like. Instead there are background operations which should lead to changes in the View like hiding one element and showing another element at this point. Another example is filling a ListView with elements which would be produced by a background operation. These changes would be made on the ViewModel whose properties are bound to the View.
Now I want to simulate the background operation and test that my View behaves correctly. But I can not manipulate my ViewModel while in the UITest project because I cannot reference Xamarin.Forms in the Test class.
It seems like it isn't intended to test the application this way. The whole app is a black box for the UITest and you can only interact through mouse and keyboard input with it.
How can I access internals of my app, the corresponding ViewModel for example.
I already searched for this problem but found nothing. Maybe I'm searching in the wrong direction.
Any help would be highly appreciated.
You can use a back door to access a method in a platform project, and from there you should be able to access Forms code since your app project references the Forms core project. See: https://developer.xamarin.com/guides/testcloud/uitest/working-with/backdoors/
Create backdoor method in Android project:
In MainActivity:
[Export("MyBackdoorMethod")]
// Can optionally take a parameter of string, int, or bool.
// Can optionally return string or Java.Lang.String.
public void MyBackdoorMethod()
{
// In through the backdoor - do some work.
}
To call the Android backdoor method from a test:
app.Invoke("MyBackdoorMethod");
Create backdoor method in iOS project:
In AppDelegate:
[Export("myBackdoorMethod:")] // notice the colon at the end of the method name
// Must take an NSString and return an NSString.
public NSString MyBackdoorMethod(NSString value)
{
// In through the backdoor - do some work.
}
To call the iOS backdoor method from a test:
app.Invoke("myBackdoorMethod:", "the value");
More details at the link, but this should suffice to get one going.
On iOS, you already have a method to achieve it called SendAppToBackground. You can also pass the time to be in the background with a TimeSpan object (wiki info). However, you cannot achieve this on Android.
Here is a sample to use it on your UITest project:
public void SendAppToBackground(IApp app, TimeSpan timeSpan)
{
if (OnAndroid)
{
return;
}
((iOSApp)app).SendAppToBackground(timeSpan);
app.Screenshot("Return from background state.");
return this;
}