Xamarin.Android always getting previously selected Language and not current - xamarin

I am developing an app in Xamarin.Android and I am fetching the current system language using
CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
It returns the current system language but when I close the app and go to settings and change the language to some another and again open the app then it still returns the old language only. When I re install the app it then shows me the new language.

I created a simple xamarin.android project and as I explained in the comment, I put the CultureInfo.CurrentUICulture.TwoLetterISOLanguageName; in the OnResume() method.Then I start the app, go to the settings app to change the language, then open the app again, it returns the old language. But if I make the app stop by slide it out of the task stack, then open the app again, it returns the new language.
The code is like this:
public class MainActivity : AppCompatActivity
{
TextView tv;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
}
protected override void OnResume()
{
base.OnResume();
var lang = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
Log.Debug("OnResume", lang);
tv = FindViewById<TextView>(Resource.Id.textView1);
tv.Text = lang;
}
}

Related

Xamarin Android RequestPermissions( ) doesn't work 2nd time. No permissions popup Dialog 2nd time

I am writing a Xamarin (android flavor) app, OS 12, and am trying to call ActivityCompat.RequestPermissions. Maybe I'm expecting the wrong behavior, since I'm not familiar with this stuff. I expect no matter how many times I run the app, that calling ActivityCompat.RequestPermissions( xxx, yyy, 1 ) will popup a system OS dialog box and ask the user to permit or deny the permissions I specify...
In my MainActivity for my Xamarin Forms (android) app, I correctly init Xamarin Essentials...
I override OnRequestPermissionsResult( )....
Now, in my one LoginViewModel, I call back into MainActivity (via a callback) and inside THAT callback, in MainActivity, it calls
public void Foo()
{
foreach (string s in m_szNeededAppPermissions)
{
bool b = ActivityCompat.ShouldShowRequestPermissionRationale(this, s);
System.Diagnostics.Debug.WriteLine("should show request for " + s + ", = " + b);
}
ActivityCompat.RequestPermissions(this, m_szNeededAppPermissions.ToArray(), 1);
}
Not that big of a deal, right? First time I run the app, the OS dialog for allowing permissions work GREAT. Now if I press "Deny" in those permissions dialogs, I get called back with OnRequestPermissionsResult( ) and the grantResults are -1. Super, that's what I'd expect.
Now, if I stop the app debugging and run it AGAIN, the 2nd time it calls Foo( ) which calls ActivityCompat.RequestPermissions( ), I get a callback immediately with OnRequestPermissionsResult( ) with denied grantResults. No OS dialogs. If I go in and delete the app and re-start debugging, I DO get the OS permission dialogs again.
I think it's because when I say "No" to the 1st-run OS permissions dialogs, it's taking that answer as "never ask again". I think somehow the OS itself is remembering not to show the OS dialogs a 2nd time, even though i'm ASKING it to! How weird!
I think my expectations must be off, or I'm missing 1 key step. Can anybody explain why?
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
// OS dialog ran and told us which permissions user accepted vs which ones they denied.
// We look at them and then call back with whether they all succeeded or not.
bool bGotAllPermissions = true;
foreach( Permission p in grantResults )
{
if( p != Permission.Granted )
{
bGotAllPermissions = false;
break;
}
}
// ps.OnRequestPermissionsResult(bGotAllPermissions);
}
here is the main activity:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, ICallbackApp
{
PermissionsService ps;
string[] m_szNeededAppPermissions = { Manifest.Permission.AccessFineLocation, Manifest.Permission.BluetoothAdmin };
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
DependencyService.RegisterSingleton<ICallbackApp>(this);
ps = new PermissionsService(this, m_szNeededAppPermissions);
LoadApplication(new App());
}
and in the view model:
public LoginViewModel()
{
LoginCommand = new Command(OnLoginClicked);
// m_pCrossPlatUtils = DependencyService.Get<ICrossPlatUtils>();
// RefreshPermissions();
Foo();
}
public void Foo()
{
ICallbackApp p = DependencyService.Get<ICallbackApp>();
p.Foo();
}
The answer these days is different than it used to be. Both iOS and Android are getting tighter about "bugging users for permissions". If you open the system OS permissions dialog to ask the user for permission, if they Deny them, then on successive runs of the app, or future usages of RequestPermissions( ), it WILL NOT SHOW the OS permissions dialog for those permissions that were denied previously! Yeah. That.
The only ways around this, if your app needs those permissions to run are:
Ask the user to reinstall the app
Bring up the app's settings pages and ask them to "fix it themselves", and then verify it is fixed when the user returns to the temporarily backgrounded app (backgrounded in order to show the App's settings page)
I think this is all true, I got it from a pretty reliable Xamarin source # MSFT....
Yes,in xamarin, we recommend you use Xamarin.Essentials: Permissions just as json said,which provides the ability to check and request runtime permissions.
This API uses runtime permissions on Android. Please ensure that Xamarin.Essentials is fully initialized and permission handling is setup in your app.
In the Android project's MainLauncher or any Activity that is launched Xamarin.Essentials must be initialized in the OnCreate method:
protected override void OnCreate(Bundle savedInstanceState)
{
//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
//...
}
To handle runtime permissions on Android, Xamarin.Essentials must receive any OnRequestPermissionsResult. Add the following code to all Activity classes:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
For more,, you can check document:https://learn.microsoft.com/en-us/xamarin/essentials/permissions?tabs=android .

How to implement an Alert box in xamarin forms android

I implemented a Dependency Service to display alert box in my xamarin forms app.My app crashes when I call the alert box in android.
Here is My code
Android.App.AlertDialog.Builder _dialog = new AlertDialog.Builder(Android.App.Application.Context);
AlertDialog _alertDialog = _dialog.Create();
_alertDialog.SetTitle("Unauthorized");
_alertDialog.SetMessage("Please login again to continue using the App);
_alertDialog.SetButton("OK", (c, ev) => { CloseApp(); });
_alertDialog.Show();
It throws an exception:-Unable to add window -- token null is not for an application in android.
How to fix this Please help me
Unable to add window -- token null is not valid; is your activity running?
You are using a Application context and you need to use an Activity based one.
So you need the current Activity's context within your Forms' dependancy class which you can obtain that via multiple methods; A static var on the MainActivity, using the "CurrentActivityPlugin", etc...
As a quick fix, add a static Context variable to your MainActivity class and set it in the OnResume override.
public static Context context;
protected override void OnResume()
{
context = this;
base.OnResume();
}
Then change your context to that static one:
Android.App.AlertDialog.Builder _dialog = new Android.App.AlertDialog.Builder(MainActivity.context);

Embedding Xamarin Forms in Native Xamarin app with MvvmCross 6.0

I'm trying to embed Forms XAML view into Xamarin Native application with MvvmCross 6.0.
I tried to reproduce effect of this solution but I got stuck on registering IMvxFormsViewPresenter. I also followed this tutorial.
I have simple MainContainerActivity with MainFragment (with corresponding MainViewModel in Core) that contains a button to Forms SettingsActivity/SettingsViewModel.
Full source can be found in the test repository.
Currently, I struggle with an exception thrown in base.OnCreate(bundle); while navigating to SettingsViewModel:
MvvmCross.Exceptions.MvxIoCResolveException has been thrown
Failed to resolve type MvvmCross.Forms.Presenters.IMvxFormsViewPresenter
I cannot find a way to register this Forms Presenter. I tried to do it in Setup but with no success.
I also tried to resolve MvxFormsAndroidViewPresenter in SplashActivity but Mvx.Resolve<IMvxViewPresenter>() as MvxFormsAndroidViewPresenter; yields null.
Do you have any idea what should I do to incorporate Forms views into MvvmCross 6.0 native application?
namespace MvvmCrossFormsEmbedding.Droid.Views
{
[Activity(Theme = "#style/AppTheme",
Label = "SettingsActivity")]
public class SettingsActivity : MvxFormsAppCompatActivity<SettingsViewModel>
{
public static SettingsActivity Instance { get; private set; }
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// #1 Initialize
Forms.Init(this, null);
SetContentView(Resource.Layout.activity_settings);
var toolbar = FindViewById<Toolbar>(Resource.Id.layout_toolbar);
SupportActionBar.Title = "Settings";
Instance = this;
// #2 Use it
var frag = new SettingsView().CreateFragment(this);
var ft = FragmentManager.BeginTransaction();
ft.Replace(Resource.Id.fragment_frame_layout, frag, "main");
ft.Commit();
}
}
}

Xamarin.UITest Backdoor with Splash Screen

I have an application that I'm attempting to put Xamarin UI Tests on. I need to Backdoor the app to bypass my login process.
My Backdoor method fires just fine.
[Activity(Label = "AppName", Icon = "#drawable/icon", Theme = "#style/Theme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
StartActivity(typeof(MainActivity));
}
[Java.Interop.Export("BackDoor")]
public void BackDoor()
{
var myActivity = {Magic code to get reference to the the instance of MainActivity goes here}
}
}
However its firing in my Splash screen and I need it get a reference to my actual MainActivity not my SplashActivity. How do I get a reference to the MainActivity in my BackDoor method?
Xamarin Backdoor Docs:
https://developer.xamarin.com/recipes/testcloud/start-activity-with-backdoor/
https://developer.xamarin.com/guides/testcloud/uitest/working-with/backdoors/
According to the guide for a backdoor method for Android, it can not return object type, only string, Java.Lang.String, or void. See: https://developer.xamarin.com/guides/testcloud/uitest/working-with/backdoors/
Don't you want to start the next Activity from the backdoor as in the guide? If so, just follow the guide you linked more closely.
Also, just double checked and returning object from the BackDoor method fails on build with a NullReferenceException. However, for "{Magic code to get reference to the the instance of MainActivity goes here}" you can do:
ActivityManager am = (ActivityManager)this.GetSystemService(Context.ActivityService);
var myActivity = am.GetRunningTasks(1)[0].TopActivity;
the myActivity will be a reference to the top most activity, but you can't return it from the BackDoor method anyway. You can return a string description of course. I do not know why you need a reference to the activity in your test code anyway as there is not much you can do with it in the test code.
How To Retrieve Current Activity
To retrieve the MainActivity, you can use #JamesMontemagno's CurrentActivityPlugin.
Add the Current Activity NuGet Package into your Xamarin.Android project, and then, in your Xamarin.Android Project, you can use the following line of code to retrieve the current activity and check that it is the MainActivity.
Activity currentActivity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity as MainActivity;
if (!(currentActivity is MainActivity))
throw new System.Exception("Current Activity is not MainActivity");
This plugin is open-sourced on GitHub.

Start specific view of Gluon App from a notification

I set up an alarm to show a corresponding Notification. The PendingIntent of the Notification is used to start the Gluon App main class. To show a View other than the homeView, I call switchView(otherView) in the postInit method. OtherView is shown, but without AppBar. While it's possible to make the AppBar appear, I wonder if this is the right approach.
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
switchView(READING_VIEW);
}
}
When triggering anything related to the JavaFX thread from another thread, we have to use Platform.runLater().
Yours is a clear case of this situation: the Android thread is calling some pending intent, and as a result, the app is started again.
This should be done:
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
Platform.runLater(() -> switchView(READING_VIEW));
}
}

Resources