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

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 .

Related

Capture android TV remote keycode

My android app has a function to detect all the keycodes for TV remote.
I tried onKeyDown(up) API to detect when the user press the TV remote and override dispatchKeyEvent method too. It works except some keys : mute, volume, home, back. how can I detect those keys?
Thanks,
The system has the privilege to control those keys, so I have to find some source code to use.
I final resolved this problem. In general, AOSP dispose a led service (led controller):
public LedService() {
}
public IBinder onBind(Intent intent) {
this.handlePowerStateChanged(0);
return new LedService.LedServiceWrapper();
}
public void handlePowerStateChanged(int state) {
if (DEBUG) {
Log.d(TAG, "handlePowerStateChanged: " + state);
}
}
public void handleKeyEvent(KeyEvent event) {
if (DEBUG) {
Log.d(TAG, "handleKeyEvent: " + event);
}
}
This class use HAL to go down the level and communicate directly with system. I extend this class in my own class and use handleKeyEvent method.
Cheers,
Check the KeyEvent constants, and by the help of the following SO references.
Detect home button press in android
Check if back key was pressed in android?
Android: How to get app to remember if mute button has been pressed or not
Android - Volume Buttons used in my application

Xamarin.Android always getting previously selected Language and not current

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;
}
}

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));
}
}

Lock app with password

In WP application we need to provide user option to lock app with password.
As I understand WP app lifecycle, I need to put navigation to LockPage in App.Application_Activated, App.Application_Deactivated and start page, but I can not use NavigationService in App class...
I do not want to put navigation code to lock page in each other pages, or there is no other options?
I writed own solution, but may be it is not so elegant as it could be.
App locking logic: User enable app locking with password, we handling Application_Deactivated and Application_Closing events in App class and marking app as locked if user enabled this option. Then, on each page we should put check: is app currently locked and if it is, we should navigate to AppLockedWithPasswordPage. On AppLockedWithPasswordPage we need to check user`s password, if it is correct call NavigationService.GoBack().
So we need to do 6 steps:
You should choose where to save IsAppCurrentlyLocked (bool flag), AppLockPassword (string) and IsUserEnabledAppLockWithPassword (bool flag). I had chosen IsolatedStorageSettings
Create AppLockedWithPassword page, where you need to show TextBox and Button, do not forget to provide option for user to reset AppLock of course with deleting app data
AppLockedWithPasswordPage should prevent BackButton navigation, so preventing it:
// AppLockedWithPasswordPage
protected override void OnBackKeyPress(CancelEventArgs e)
{
// Preventing back key navigation
e.Cancel = true;
}
Check password on button click
// AppLockedWithPasswordPage
private void UnlockAppButton_Click(object sender, RoutedEventArgs e)
{
if (PasswordBox.Password.Equals(IsolatedStorageSettings["AppLockPassword"]))
{
NavigationService.GoBack();
}
else
{
// Say user, that password incorrect, etc...
}
}
In App class find Application_Deactivated (to handle app minimizing (windows button)) and Application_Closing (to handle when user closing app) methods, we should mark app as locked if user enabled this option when this events happens
private void SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking()
{
if ((bool)IsolatedStorageSettings["IsUserEnabledAppLockWithPassword"])
{
IsolatedStorageSettings["IsAppCurrentlyLocked"] = true;
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
And final step, on all pages you want to lock you should add check in OnNavigatedTo method which will navigate to AppLockedWithPasswordPage if app is currently locked
// Create some class, like PagesUtils or so on with check method
private static Uri uriToAppLockedWithPasswordPage = new Uri("pathToAppLockedWithPasswordPage", UriKind.Relative);
public static void NavigateToAppLockedWithPasswordPageIfAppLocked(PhoneApplicationPage page)
{
if ((bool)IsolatedStorageSettings["IsAppCurrentlyLocked"])
{
page.NavigationService.Navigate(uriToAppLockedWithPasswordPage);
}
}
// In each page you want to lock add
protected override void OnNavigatedTo(NavigationEventArgs e)
{
PagesUtils.NavigateToAppLockedWithPasswordPageIfAppLocked();
base.OnNavigatedTo();
}
P.S. of course real code is much better, this is just simple example, I hope it will help you
You should add the check in the Application_Launching and Application_Activated events.
The launching event for when the app is first opened and the activated one for when the user returns to the app after having left to do something else.
Have these events both set a flag and have the base page that all your pages inherit from check for this flag when navigated to. The check should be for if the flag is set, if it is, show the login prompt and then clear the flag after successful password entry.
This approach will handle FAS, FAR & deep linking, in addition to starting the app normally.
Beware Some choosers will trigger the activated event when they return to the app. Add extra handling for these as appropriate / if necessary.
Why not create a start page where the passwords is entered?
For instances you have your MainPage.xaml, create a InsertPasswordPage.xaml reference it on WMAppManifest as the start page:
<DefaultTask Name="_default" NavigationPage="InsertPasswordPage.xaml" />
And insert all the password logic on the InsertPasswordPage.xaml, when the user successfully logins just navigate to your main page ;)
EDIT: As Gambit said if the user pressed the back button he will return to the insert password page, but you can solve this by removing from the backstack the page after the user logged in.

how to disable location services in my WP7 app

I'm making a windows phone app which displays where the closest campus shuttles are (among other things). Windows Phone requires apps to allow the users to turn off location services within the app.
So, I added a toggle for it on a settings page, but it doesn't seem to do anything.
Here's the viewmodel where I declared the geocoordinatewatcher.
public MainViewModel()
{
geoWatcher = new GeoCoordinateWatcher();
if (geoWatcher.TryStart(false, TimeSpan.FromSeconds(30) )==false )
{ MessageBox.Show("The location services are disabled for this app. We can't detect the nearby stops. To turn location services back on, go to the settings page.", "Warning", MessageBoxButton.OK); }
}
private GeoCoordinateWatcher geoWatcher;
public GeoCoordinateWatcher GeoWatcher
{
get
{
return geoWatcher;
}
set
{
if (geoWatcher != value)
{
geoWatcher = value;
NotifyPropertyChanged("GeoWatcher");
}
if(geoWatcher.Status== GeoPositionStatus.Disabled)
{
geoWatcher.Stop();
}
}
}
and here's the bulk of the settings page
public SettingsPage()
{
InitializeComponent();
if (App.ViewModel.GeoWatcher.Status == GeoPositionStatus.Ready)
{
locToggle.IsChecked = true;
locToggle.Content = "On";
}
else
{
locToggle.IsChecked = false;
locToggle.Content = "Off";
}
}
private void toggleChecked(object sender, RoutedEventArgs e)
{
locToggle.Content = "On";
App.ViewModel.GeoWatcher.Start();
MessageBox.Show("this is the status " + App.ViewModel.GeoWatcher.Status.ToString(), "Info", MessageBoxButton.OK); //for debugging
}
private void toggleUnchecked(object sender, RoutedEventArgs e)
{
locToggle.Content = "Off";
App.ViewModel.GeoWatcher.Stop();
MessageBox.Show("this is the status " + App.ViewModel.GeoWatcher.Status.ToString(), "Info", MessageBoxButton.OK); //for debugging
}
When i turn the toggle off and click away from the Settings page, and go back to it, the toggle is re-enabled again.
I tried putting in a message box on the functions to debug but the status always says "Ready" and my app still uses the location services, even when I turn the toggle to "off".
Is there something I should be adding to the code so that the toggle will properly make my app stop using location services across my app if it's disabled on the settings page? Or should I be checking something else besides GeoPositionStatus? I couldn't figure out a way to make my app actually change the location services permissions or PositionStatus.
I looked at this page here, but am still confused since I followed the example at the bottom of the page, to no avail. I searched StackOverflow but I couldn't seem to find a similar question with WP. I also posted this on the AppHub forums though.
Thanks!
In your MainViewModel you need to check if they have allowed location services before you use the geocoordinatewatcher.
if(settings.LocationAllowed)
{all your code for using location}
You should probably take into account a few factors/points, most of which, you have. Anyway, you might find these helpful.
Your application settings toggle should only show when the location service is switched on on the device
GeoPositionStatus is just an Enum which contains the types of statuses.
StatusChanged is the event which is to be handled to check for changes in the device settings. See this.
You cannot change the device settings from the application.
Add event handlers before you call start on the watcher.

Resources