Statusbar theme only changes after restarting app - xaml

ORIGINAL QUESTION:
My apps statusbar does not seem to react to android system theme changes while the app is loaded.
The statusbar only updates after a restart. Please see videos below:
This video shows the light theme correctly applied to the app's statusbar when loaded. But after loading the app, if the user changes the system theme to dark, the statusbar of the app does not update until the app is restarted.
Light to dark theme example video.
This video shows the dark theme correctly applied to the app's statusbar when loaded. But after loading the app, if the users changes the system theme to light, the statusbar of the app does not update until the app is restarted.
Dark to light theme example video.
Anyone know how to get the statusbar to react to system theme changes while having the app loaded?
UPDATE 1:
The following returns the value of the selected theme in the console, but as soon as I uncomment Application.Current.UserAppTheme = AppTheme.Dark; no values are returned in the console when I change the system theme.
private void ChangeTheme()
{
Application.Current.RequestedThemeChanged += (s, a) =>
{
AppTheme currentTheme = Application.Current.RequestedTheme;
if (currentTheme is AppTheme.Dark)
{
Console.WriteLine(currentTheme.ToString());
//Application.Current.UserAppTheme = AppTheme.Dark;
}
else if (currentTheme is AppTheme.Light)
{
Console.WriteLine(currentTheme.ToString());
}
};
}
UPDATE 2:
I am using 2 different styles.xml files in Platforms > Android > Resources > values and values-night to set the statusbarcolor.

In your MainActivity.cs you can override the OnConfigurationChanged() method and listen to UI configuration changes:
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
if (Build.VERSION.SdkInt < BuildVersionCodes.R)
{
return;
}
if (newConfig.IsNightModeActive)
{
Window?.SetStatusBarColor(Colors.Black.ToAndroid());
Window?.InsetsController?.SetSystemBarsAppearance(0, (int)WindowInsetsControllerAppearance.LightStatusBars);
}
else
{
Window?.SetStatusBarColor(Colors.White.ToAndroid());
Window?.InsetsController?.SetSystemBarsAppearance((int)WindowInsetsControllerAppearance.LightStatusBars, (int)WindowInsetsControllerAppearance.LightStatusBars);
}
}
You also need to include the ConfigChanges.UiMode flag in the ConfigurationChanges attribute (included by default in new MAUI projects):
[Activity(Theme = "#style/MyAppTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
UPDATE
About the RequestedThemeChanged event and the RequestedTheme property in your case: When you are setting Application.Current.UserAppTheme = AppTheme.Dark; you are effectively saying that you want the theme to be set to Dark permanently, so the event doesn't get raised anymore after that, since UserAppTheme is not set to Unspecified anymore. See more: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/system-theme-changes#set-the-current-user-theme

Related

Identify orientation with degrees on startup

Without a 3rd party lib, we can detect orientation changes with DeviceEventEmitter with this undocumented feature like this:
import { DeviceEventEmitter } from 'react-native'
function handleOrientationDidChange(data) {
console.log('orientation changed, data:', data)
}
DeviceEventEmitter.addListener('namedOrientationDidChange', handleOrientationDidChange);
This gives us data that looks like this:
{ rotationDegrees: -90, isLandscape: true, name: "landscape-primary" }
Note: I tested this only on Android. It would be nice to know if it works on iOS too.
However this only works ON CHANGE. Is there a way to get this info on startup?
Have you tried this library!
Here is example usage from the repo:
componentWillMount() {
// The getOrientation method is async. It happens sometimes that
// you need the orientation at the moment the JS runtime starts running on device.
// `getInitialOrientation` returns directly because its a constant set at the
// beginning of the JS runtime.
const initial = Orientation.getInitialOrientation();
if (initial === 'PORTRAIT') {
// do something
} else {
// do something else
}
}

Xam.Plugins.Notifier not work on IOS 11

I am using Xam.Plugins.Notifier package to implement Local Notification in Xamarin.Forms project.
Here is the code what I wrote in PCL project.
CrossLocalNotifications.Current.Show("Title", "Description");
It works good on Android but It doesn't work on IOS.
I am not sure if it works on lower IOS sdk.
Anyway it doesn't work on IOS 11.
Here is the code that I added in AppDelegate.cs
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// Ask the user for permission to get notifications on iOS 10.0+
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
(approved, error) => { });
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
// Ask the user for permission to get notifications on iOS 8.0+
var settings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
Can anybody help me to fix it?
I want to have this package working on IOS.
Thanks.
Which scenario does it not work ? Active or in background?
If it doesn't work when it is active , you may forget to handle the delegate (subclasses UNUserNotificationCenterDelegate)
Modify your code as below:
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// Ask the user for permission to get notifications on iOS 10.0+
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
(approved, error) => { });
// Watch for notifications while app is active
UNUserNotificationCenter.Current.Delegate = new UserNotificationCenterDelegate();
}
Create a subclass UserNotificationCenterDelegate
public class UserNotificationCenterDelegate : UNUserNotificationCenterDelegate
{
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Tell system to display the notification anyway or use
// `None` to say we have handled the display locally.
completionHandler(UNNotificationPresentationOptions.Alert);
}
}

Handling Windows 8 lifecycle

i have run into a problem, that my app sometimes Activates and sometimes Launches when i open something via:
var options = new Windows.System.LauncherOptions();
options.DisplayApplicationPicker = false;
bool success = await Windows.System.Launcher.LaunchFileAsync(sampleFile, options);
When app re-activates it shows the same window - when i went to an external app using LaunchFileAsync - this is nice.
But sometimes the app launches, i see a SplashPage and app is beginning from the MainPage. - how can i make this also to return to the page, that i left when used LaunchFileAsync?
Example:
I have a MainPage and a BlankPage1
So here is my page on suspend+shutdown (terminate) 8 buttons:
On Restore 0 buttons, I WANT TO SAVE MY VIEW XAML CODE when app gets killed by system:
It depends entirely on the conditions of your application shutdown. Was it suspended and terminated automatically by the OS ? or did you close it yourself ? (ex : ALT-F4)
You can see here the application lifecyle : http://msdn.microsoft.com/en-us/library/windows/apps/hh464925.aspx
If you want your application to restore its previous state on a user shutdown, I think you can enable it on your OnLaunched method in you App.xaml.cs :
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated
|| args.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
{
try
{
await SuspensionManager.RestoreAsync();
}
catch (SuspensionManagerException)
{
}
}
Then, if your Page extends LayoutAwarePage, you have two methods, SaveState and LoadState.
These methods are called automatically when navigating from or to the frame (including suspending/restoring/opening...).
If you save your data behind your buttons in your SaveState method, you can restore it in the LoadState method (and thus redraw your buttons). There is a detailled exemple here : http://msdn.microsoft.com/en-us/library/windows/apps/hh986968.aspx

Worklight initialization for Android always clears WebView history

I noticed cordovaInitCallback is called each time Worklight/Cordova is initialized in an Android app. In particular, it calls Cordova's "clearHistory" to wipe out the WebView history. This has been an issue when I try to make use of window.history in a multi-page app since the history is always reset during the initializtion from page to page.
Since the comment suggests that the purpose for this clearHistory call is to prevent going back to an old page in a direct update scenario, could the condition be strengthened over an Android environment check so that it is only called if a direct update has just taken place? One case, for example, I can think of is when connectOnStartup=false, then direct update would not occur.
wlclient.js:
var cordovaInitCallback = function(returnedData) {
onEnvInit(options);
if (WL.Client.getEnvironment() == WL.Env.ANDROID) {
if (returnedData !== null && returnedData !== "") {
WL.StaticAppProps.APP_VERSION = returnedData;
}
// In development mode, the application has a settings
// widget in which the user may alter
// the application's root url
// and here the application reads this url, and replaces the
// static prop
// WL.StaticAppProps.WORKLIGHT_ROOT_URL
// __setWLServerAddress for iOS is called within
// wlgap.ios.js's wlCheckReachability
// function because it is an asynchronous call.
// Only in Android we should clear the history of the
// WebView, otherwise when user will
// press the back button after upgrade he will return to the
// html page before the upgrade
if (**WL.Env.ANDROID == getEnv()**) {
cordova.exec(null, null, 'Utils', 'clearHistory', []);
}
}
I am currently using Worklight 5.0.5, and have checked this same condition exists in 5.0.5.1.
Thanks!
The architectural design of Worklight is SPA (Single Page Application).
cordovaInitCallback should be called only once in the life cycle of the application.
That said, you can, if you wish, override it.

React to every app activation in Windows Store app

I have a Windows Store app with Live Tile updates using Background Task. When I activate the app by any means (click on the live tile, switch back to the app, etc..) I want to clear the live tile (I have a number there that I want to change to zero).
To be more concerete, I run the app, I switch to another app or desktop, then I switch ti the star screen and I see a number on the Live Tile. I click the Live Tile, I am taken to the app and I want the Live Tile to clear. The same functionality as the Email app.
I tried the OnActivated method in App.xaml.cs but it does not seem to get called at any time (I put a throw new NotImplementeExeption there and the app never crashes).
You should put it in the OnLaunched method, you just need to determine where.
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
var rootFrame = new Frame();
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
//....
}
if (args.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
{
/....
}
if (!String.IsNullOrEmpty(args.Arguments))
{
//....
}
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//....
}
if (args.PreviousExecutionState == ApplicationExecutionState.NotRunning)
{
//.....
}
TileUpdateManager.CreateTileUpdaterForApplication().Clear();
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
SettingsPane.GetForCurrentView().CommandsRequested += OnCommandsRequested;
// Create a Frame to act navigation context and navigate to the first page
if (!rootFrame.Navigate(typeof(MainPage)))
{
throw new Exception("Failed to create initial page");
}
// Place the frame in the current Window and ensure that it is active
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
If you look at the code, there are several reasons of why your App is closed/suspended. So, determine in which cases you want to run de code for updating the number in the Live Tile, put it inside that if, and it should work.
I guess that the better place for such actions is the OnLaunched method. It called every time you appication start.
update: Hmm, seems you should react on both OnActivated and OnLaunched methods:
OnLaunched - Invoked when the application is launched. Override this
method to perform application initialization and to display initial
content in the associated Window.
On the application start OnLaunched will be called. But when you switch to another app and then go back OnActivated should be called:
OnActivated - Invoked when the application is activated by some means other than normal launching.