xamarin - Android change theme programmatically - xamarin

I have a multi-tenant app, and have to switch the theme after the user logs in.
What I have so far is that when the User is already logged in and starts the app, I call setTheme() on the MainActivity OnCreate Method.
This works fine and the app is shown in the correct theme.
But when the user is not logged in, the app will start with the default theme. After login i can recognize the user and have to change the theme. But how can I do this. The login process is not in the android project, and from there I have no access to the MainActivity.
How can I set the new theme and recreate the app?
I am very grateful for help.
UPDATE
The log in method is at the moment in the MC.Core (Shared) (.NET Standard 2.0) library and the MainActivity is in the MC.Android library. So there is an Plugin which you can access the actual activity over projects, but I can't use it, becouse it does support .Net Standard.
And I'm not shure, if I can subscribe an event over two project. If this would be possible, how can I to this?
My very last solution is to move the login method form my core project to the Android project. But in this case I have to implement this for every platform.

To call a method in your MainActivity from the net standard project, you need to pass a reference to your MainActivity to the standard project. The best way to do this is to pass a reference to the shared net standard project App constructor, which is invoked from MainActivity. Of course, you cannot declare a parameter in your App constructor of type MainActivity, because your net standard project cannot reference the Android project, and because, if you will in the future implement iOS and / or UWP version of your app, you need a common type between all these different project.
So, you have to define an interface in your net standard project:
public interface IThemeChanger
{
void ApplyTheme(string newTheme);
}
Then, in the Android project, make your MainActivity implement this interface:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IThemeChanger
{
public void ApplyTheme(string newTheme)
{
if (newTheme?.ToLower() == "dark")
{
SetTheme(Resource.Style.Base_Theme_AppCompat);
}
else
{
SetTheme(Resource.Style.Base_Theme_AppCompat_Light);
}
}
and make it pass a reference to itself in the constructor of the app class:
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App(this));
}
Then, in the net standard App class, change the constructor so that it accepts an argument of type IThemeChanger, and store a reference to it in a private or public field (depending if you need or not to access it from outside App class):
public readonly IThemeChanger ThemeChanger;
public App(IThemeChanger themeChanger)
{
InitializeComponent();
this.ThemeChanger = themeChanger;
MainPage = new MainPage();
}
Then, in your login page, after the user login successfully, change the theme accordingly, for example:
((App.Current) as App).ThemeChanger.ApplyTheme("Dark");
If you do not instantiate your App class directly but use a dependency injection container, then register the current instance of MainActivity as an implementer of the IThemeChanger interface with your container, and simply request an IThemeChanger instance in the constructor of your ViewModel. Of course syntax vary depending on which DI Container you use, here is an example with Caliburn.Micro SimpleContainer:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IThemeChanger
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
var container = IoC.Get<SimpleContainer>();
if (container.HasHandler<IThemeChanger>())
{
container.UnregisterHandler<IThemeChanger>();
}
container.Instance<IThemeChanger>(this);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(container.GetInstance<App>());
}
In this case, please note that a new instance of MainActivity can be created during app resume, so you need to check for a potential previous registration of the interface and unregister it.
In your ViewModel:
public class LoginViewmModel{
private readonly IThemeChanger themeChanger;
public LoginViewModel(IThemeChanger themeChanger){
this.themeChanger = themChanger;
}
private void ApplyTheme{
themeChanger.ApplyTheme("Dark");
}
}

Related

How to Cluster Xamarin.Forms.GoogleMaps Pin in PCL Project

Since I am doing a map based cross-platform project on Xamarin, And I'm stuck to the point where I need to cluster markers when zooming out.
things I had tried:
GoogleMapUtility Project.
And tried to use ClusterManager of GoogleMapUtility through dependency. so that I can add a pin to cluster manager From PCL project. But to initialize cluster manager I need a Native Map instance i.e. Android.Gms.Maps.GoogleMap.(don't know how to get)
I know I can achieve it with that with custom map renderer, but I cant do that coz. a lot of code related to map is already written, I don't want to rewrite a Code. So Is there any possibility that I can get a native instance of the map that I can use on dependency service.
I created CustomRenderer For Map without creating a customMap class at PCL (inherit Map class) the code is below. but it's not get triggered. What wrong I am doing here...
custom renderer code:
[assembly: ExportRenderer(typeof(Map), typeof(MarkerClusterRenderer))]
namespace SamplingApp.Droid.CustomRenderers
{
public class MarkerClusterRenderer : MapRenderer
{
ClusterManager _clusterManager;
protected override void OnMarkerCreated(Pin outerItem, Marker innerItem)
{
base.OnMarkerCreated(outerItem, innerItem);
AddToMarkerCluster();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
}
public void AddToMarkerCluster()
{
_clusterManager = new ClusterManager(Android.App.Application.Context, NativeMap);
//[do other things here]
}
}
}
since I am is new to xamarin. little guidance is appreciated.
am I in the right direction?
Thank you in advance. and sorry for my English.
I created CustomRenderer For Map without creating a customMap class at PCL (inherit Map class) the code is below. but it's not get triggered. What wrong I am doing here...
If you want to customize the map, you need to create a custom class for map control in PCL:
public class CustomMap:Map
{
}
And use it in xaml:
<local:CustomMap WidthRequest="320" HeightRequest="200"
x:Name="MyMap"
IsShowingUser="true"
MapType="Hybrid"/>
And renderer:
[assembly:ExportRenderer(typeof(SamplingApp.CustomMap),
typeof(MarkerClusterRenderer))]
namespace SamplingApp.Droid.CustomRenderers
{
public class MarkerClusterRenderer:MapRenderer
{
...
Then, your renderer's codes will be triggered correctly.

How can I launch a Android Activity from Xamarin Forms Page?

I know that i can launch a Xamarin Forms page from an Native Android Activity, but how do i start a Native Android Activity from Xamarin Forms ContentPage. I tried with DependencyService but without success.
I figured it out.
First i needed to create a Interface like this in Xamarin Project
public interface IRecordVideoPage
{
void StartNativeIntentOrActivity();
}
then i launched the Native Android Activity from my Xamarin Forms Content Page using Dependency Service like this:
DependencyService.Register<IRecordVideoPage>();
DependencyService.Get<IRecordVideoPage>().StartNativeIntentOrActivity();
next I created a RecordActivity class in my Android Project that loads the Test Activity
[assembly: Xamarin.Forms.Dependency(typeof(RecordActivity))]
namespace StreamTest.Droid
{
[Activity(Label = "RecordActivity")]
public class RecordActivity : IRecordVideoPage
{
public void StartNativeIntentOrActivity()
{
var intent = new Intent(Forms.Context, typeof(Test));
Forms.Context.StartActivity(intent);
}
}
finally I set the content view in the Test class:
public class Test : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public Test() { }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Record);
}
}
}
DependencyService is your best option. The concept behind PCL (Portable Class Library) is exactly to be platform independet.
Maybe we can help if you show the error you are getting with Dependency Service

Custom PageRenderer or NavigationRenderer not working (virtual missing?)

Currently I'm working on a Win Phone 8.1 project and need some Custom Renderer for ContentPage and NavigationPages.
But when I tried to implement it i saw there is no possibility for overwrite the OnElementChanged Method. After some minutes with DotPeek I saw that those methods not are virtual like on other plattforms. So does anybody have an idea or a workaround for implementing custom rederers on Windows RT?
Tried on Xamarin.Forms: 2.0.0.6490 & 2.1.0.6529
Sincerely
You can use the Event ElementChanged.
public class MyRender: NavigationPageRenderer
{
public MyRender()
{
ElementChanged += OnElementChanged;
}
private void OnElementChanged(object sender, VisualElementChangedEventArgs visualElementChangedEventArgs)
{
// do some stuff here
}
}

POJO as a BootStrapNotifier instead of Application

I am using a POJO as a BootStrapNotifier instead of Application class. The POJO has reference to the context. Will background detection start in this way?
I am also using this class as MonitorNotifier when the app is in foreground.
Is it mandatory to use BootStrapNotifier and RangeNotifier in the same class like shown in the Reference Application?
Is this a correct approach? If the app is killed, will the beacon detection start only when power is connected or disconnected or re booted?
Yes, it is possible to use a POJO to receive the callbacks from a RegionBootsrap, but you must still use the onCreate method of an Android Application class to construct this POJO and set it up.
The Application class is needed because its onCreate method is the first user-executable code that executes when an Android application starts up. The Android Beacon Library’s RegionBootstrap works because the library sets up a broadcast receiver that looks for BOOT_COMPLETED, ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED events. This broadcast receiver doesn’t do much, but if the app is not running when one of these events happens, it causes the Application class’ onCreate method to get executed. It is the creation of a RegionBootstrap at this time that causes beacon scanning to start in the background and then notify user code when beacons of interest are located.
The code below shows how you set up a POJO called MyPojo to receive the callbacks from the RegionBootstrap, and register that POJO in the Application’s onCreate method. The first parameter of the RegionBootstrap is the class that will receive the callbacks when beacons are detected.
You can use a POJO like this to set up ranging or do anything else you want -- there is no reason that such code has to reside in an Android Application class.
public void onCreate() {
super.onCreate();
MyPojo myPojo = new MyPojo(this);
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(myPojo, region);
}
...
public class MyPojo implements BootstrapNotifier {
private Context mContext;
public MyPojo(Context context) {
mContext = context;
}
public Context getApplicationContext() {
return mContext;
}
public void didEnterRegion(Region region) {
...
}
public void didExitRegion(Region region) {
...
}
public void didDetermineStateForRegion(int state, Region region) {
...
}
}

VAADIN 7: Navigating sub views

How would I navigate between sub-views in Vaadin UI. I want my header on the website to stay static like a master page and the content view to change using navigation. How can I do that in Vaadin.
Well you could simply design your UI with a header (and other stuff if needed) and a component that will act as a placeholder for the changing content.
Then I add a method that receives the new content to display and puts it inside the place holder.
Here is some code:
public class MyUI extends UI implements ErrorHandler {
// I usually use a layout as a place holder
private VerticalLayout content;
...
#Override
public void init(VaadinRequest request) {
...
content = new VerticalLayout();
content.setSizeFull();
final VerticalLayout layout = new VerticalLayout(header, menu, content, footer);
layout.setMargin(true);
setContent(layout);
}
public void changeContent(Component view) {
content.removeAllComponents();
content.addComponent(view);
content.setComponentAlignment(view, Alignment.MIDDLE_CENTER);
}
}
If in you application you need to support browser's bookmarks and navigation (backward and forward buttons) you'll need to use URIFragments, described on the book of vaadin.
A common way of using it is with a Navigator:
public class NavigatorUI extends UI {
Navigator navigator;
protected static final String MAINVIEW = "main";
#Override
protected void init(VaadinRequest request) {
getPage().setTitle("Navigation Example");
// Create a navigator to control the views
navigator = new Navigator(this, this);
// Create and register the views
navigator.addView("", new StartView());
navigator.addView(MAINVIEW, new MainView());
} }
Your views will need to implemente the View interface as described in the book of vaadin
And if you use Spring, you could also use an addon, like SpringVaadinIntegration or vaadin4spring to make this easier and each of them have several other advantages.

Resources