Appium: automatically handling the iOS location permissions popup - appium

I need to be able to either prevent the location permissions popup from occurring or have it handled by Appium/WebDriver.
It looks like the Appium settings API supports these two settings for this purpose:
appium:autoAcceptAlerts
and
acceptAlertButtonSelector
We're using Java and unless I'm mistaken, settings have to be applied via a driver instance, something like this:
driver.setSetting("appium:autoAcceptAlerts",true);
The problem that I have is that when creating the driver instance, we have to pass in the capabilities that we require. In practice, this seems to mean that by the time the instance has been created, the application has already launched and the location permissions popup is already on display before we can set the appropriate settings.
I'm sure I must be missing something so I'd appreciate it if anyone can point out my mistake
TIA,
Mike

You can Try this in your capabilities setting:
capabilities.setCapability("autoAcceptAlerts", "true"); // for auto alerts
capabilities.setCapability("autoGrantPermissions", "true"); // for auto premission
or you can try manual handle with this:
custom element when you needed with alert displayed
public void alertAction(String action) {
By element;
switch (action.toUpperCase()) {
case "DONT ALLOW":
element = MobileBy.id("Don’t Allow");
break;
case "ALLOW":
element = MobileBy.id("Allow");
break;
case "ASK APP NOT TO TRACK":
element = MobileBy.id("Ask App Not to Track");
break;
case "OK":
element = MobileBy.id("OK");
break;
case "TONTON SEKARANG":
element = MobileBy.id("Tonton Sekarang");
break;
case "CANCEL":
element = MobileBy.id("CANCEL");
break;
case "LOGOUT":
element = MobileBy.id("LOG OUT");
break;
default:
throw new IllegalStateException("Unexpected value: " + action.toUpperCase());
}
if (isPresent(element)) {
clickOn(element);
}
}
this work for me on appium version 1.21.0, and iOS version 15

Related

Xamarin Forms: How to check if GPS is on or off in Xamarin ios app?

Whenever I am opening my app I need to check the GPS is on or off. If the GPS is off, I need to redirect the user to the location settings page. I have done the android part using the dependency service like below.
ILocSettings
public interface ILocSettings
{
void OpenSettings();
bool isGpsAvailable();
}
Android implementation
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.Droid.Services
{
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
Android.Locations.LocationManager manager = (Android.Locations.LocationManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.LocationService);
if (!manager.IsProviderEnabled(Android.Locations.LocationManager.GpsProvider))
{
//gps disable
value = false;
}
else
{
//Gps enable
value = true;
}
return value;
}
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
}
Finally from the shared project called like below:
//For checking the GPS Status
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
//For opening the location settings
DependencyService.Get<ILocSettings>().OpenSettings();
For ios how I can I do the same features? I tried like below:
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.iOS.Serivces
{
class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
//how to check the GPS is on or off here
}
public void OpenSettings()
{
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}
}
}
Location settings page opening on ios simulators, but don't know how to check the GPS status.
Update1
I have tried the CLLocationManager code and it is not working as expected. It returns true always even if the location is off or on.
OpenSettings() function code (UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));) is also not working as expected, it is redirecting to some other page, I need to open the location settings page if the GPS is off.
Also, I am requesting location permission like below:
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
In android, location permission is asking, but in ios, no permissions are asking.
Update2
I have tried the new codes and getting false value always as GPS status. I have added all the location permission on the info.plist like below:
But location permission is not asking when running the app (not even a single time). I have tried Permissions.LocationWhenInUse instead of Permissions.LocationAlways, but no luck.
Update 3
Following is my complete flow for checking location permission, checking GPS status, and open settings. The permission status value is always Disabled.
//Requesting permission like below:
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
if (status == PermissionStatus.Granted)
{
//Then checking the GPS state like below
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
if (!gpsStatus)
{
//show a message to user here for sharing the GPS
//If user granted GPS Sharing, opening the location settings page like below:
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
I have tried the below 2 codes for requesting or checking permission. In both cases, the status value is Disabled. If I uninstall the app and reinstall it again, getting the same status and not showing any permission pop-up window.
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
Unlike the Android system, iOS can set the GPS switch separately, and can only get the status of whether the location service is turned on. The rest of the specific positioning method will be left to the iOS system to choose.
At the beginning, we need to have a look at the status of location in iOS:
CLAuthorizationStatus Enum
UIApplicationOpenSettingsURLString: Used to create a URL that you can pass to the openURL: method. When you open the URL built from this string, the system launches the Settings app and displays the app’s custom settings, if it has any.
From now, iOS only support this way to displays the app’s custom settings. There are two helpful discussion, you could have a look. How to jump to system setting's location service on iOS10? and Open Location Settings Not working in ios 11 Objective c?.
If it is redirecting to some other page as follows:
That means your app not do any settings about the location service after installing the app . Therefore, you not need to open the setting page, because it will not show the location service bellow the setting page of your app. Now the CLAuthorizationStatus should be NotDetermined. You could use CLLocationManager.RequestWhenInUseAuthorization to request the permission, the
popup window of location service will show for customer to choose inside the app.
If customer select Don't Allow first time, that means next time opening the app to check the location service that will show Denied status. Now you will need to use UIApplicationOpenSettingsURLString to open the settings page and will see the location service inside the app’s custom settings list.
At last, the final code of LocationShare is as follows:
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
if ( CLLocationManager.LocationServicesEnabled )
{
if(CLLocationManager.Status == CLAuthorizationStatus.Authorized || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedWhenInUse)
{//enable
value = true;
}
else if (CLLocationManager.Status == CLAuthorizationStatus.Denied)
{
value = false;
OpenSettings();
}
else{
value = false;
RequestRuntime();
}
}
else
{
//location service false
value = false;
//ask user to open system setting page to turn on it manually.
}
return value;
}
public void RequestRuntime()
{
CLLocationManager cLLocationManager = new CLLocationManager();
cLLocationManager.RequestWhenInUseAuthorization();
}
public void OpenSettings()
{
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}
}
Similarly, if CLAuthorizationStatus is Denied (the same as status == PermissionStatus.Unknown in Forms), the following code will not work in Forms.
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
It only works when CLAuthorizationStatus is NotDetermined. And you'd better request Permissions.LocationWhenInUse instead of Permissions.LocationAlways, this should be the better recommanded option.
============================Update #2================================
I have modified the above code, and you will see that if CLLocationManager.LocationServicesEnabled is false, we only can ask user to redirect to the system setting page to turn on the service manually. Because from iOS 10, iOS not supports to navigate to system setting page from non-system app.
============================Update #3======================================
If location service is enabled, when using UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString)); method you will see the similar screenshot as follows:
The Loaction option will show in the list.

using AUScheduleParameterBlock from a host on AUAudioUnit v3

I am new AUAudioUnits and trying to get an understanding of how to use the v3 API. I am trying to schedule a parameter change on a kAudioUnitSubType_MultiChannelMixer AUAudioUnit so that I can ramp a gain change over time.
I am able to set the gain directly with a slider and the change takes affect immediately like this:
/*...*/
// Inside my ViewController
#IBAction func slider1Changed(sender: UISlider) {
player.gainParameter.setValue(sender.value, originator: nil)
}
// Inside my AudioPlayerClass
guard let parameterTree = self.multichannelMixer.parameterTree else {
print("Param Tree")
return
}
self.gainParameter = parameterTree.parameterWithID(
kMultiChannelMixerParam_Volume,
scope: kAudioUnitScope_Input,
element: 0)
but when I try to do this using the scheduleParameterBlock by adding this to the above AudioPlayerClass I would expect the gain to ramp from 1 to 0 over 10 seconds but there is no change:
let scheduleMixerParamBlock = self.multichannelMixer.scheduleParameterBlock
let rampTime = AUAudioFrameCount(10.secondsToSampleFrames)
scheduleMixerParamBlock(AUEventSampleTimeImmediate, rampTime, self.gainParameter.address, 0)
Examples I have seen of it working in the apple examples include code such as this (without the dispatch_async part):
parameterTree.implementorValueObserver = {param, value in
dispatch_async(dispatch_get_main_queue()) {
print(param, value)
}
scheduleMixerParamBlock(AUEventSampleTimeImmediate, rampTime, param.address, value)
}
When I run this and change the gain parameter with the slider then the block is run and the param and value are printed to the console with the correct looking values but the gain is not changed in the actual audio.
These examples I have seen are also on custom AUAudioUnits where the implementor has direct access to the dspKernel function so I might be missing something crucial there.
The other alternative I have to calculate a series of ramp values and then set the gain parameter directly for each value but since the scheduleParameterBlock is there is seems like I should be able to use that. Any help would be great. Thanks
You have to look at the AURenderEvent head in the internalRenderBlock which is operating on the audio render thread. Your scheduled parameter events will appear there for you to respond to.
For example, pass the head to this function:
void doEvents(AURenderEvent const* event)
{
while (event != nullptr) {
switch (event->head.eventType) {
case AURenderEventParameter:
doParameterEvent(event->parameter);
break;
default:
break;
}
event = event->head.next;
}
}
void doParameterEvent(AUParameterEvent const &event) {
switch (event.parameterAddress) {
case FilterParameterAddressFoo:
doFoo();
break;
case FilterParameterAddressBar:
doBar();
break;
default:
break;
}
}

Read camera permission for iOS in Xamarin

I have an iOS app developed in Xamarin. When the app does not have permission to access the microphone, if the user tries to access the microphone from the app, I check the settings using AVAudioSession.SharedInstance().RequestRecordPermission (delegate(bool granted)) and display a message.
Now I need to do the same if the app does not have permission to access the camera. I need to check if permission is granted for camera and display a message accordingly. How can I do this?
I have got the answer. Here is what I have done:
AVCaptureDevice.RequestAccessForMediaType (AVMediaType.Video, (bool isAccessGranted) => {
//if has access
if(isAccessGranted)
{
//do something
}
//if has no access
else
{
//show an alert
}
});
Did you checked this answer?
Detect permission of camera in iOS
I think that's the solution you are looking for :).
EDIT:
Here is the highest voted answer's code ported to C#
// replace the media type to whatever you want
AVAuthorizationStatus authStatus = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);
switch (authStatus)
{
case AVAuthorizationStatus.NotDetermined:
break;
case AVAuthorizationStatus.Restricted:
break;
case AVAuthorizationStatus.Denied:
break;
case AVAuthorizationStatus.Authorized:
break;
default:
throw new ArgumentOutOfRangeException();
}

How to autocomplete enum switch using Xcode?

Let say you have an enum:
typedef enum {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
} DayOfWeek;
when writing a switch statement, a Xcode is trying to help with switch statement snippet:
and then:
great, but I need to list all enums:
DayOfWeek day = ...; // a day of week
switch (day) {
case Sunday:
break;
case Monday:
break;
case Tuesday:
break;
case Wednesday:
break;
case Thursday:
break;
case Friday:
break;
case Saturday:
break;
default:
break;
}
Unfortunately, have to do it manually :( Is there any known snippet to populate all cases?
Of course I saw some answers 3 years old states that it is impossible, but maybe something changed since that time? Any ideas?
Short Answer :
Use Editor > Refactor > Add Missing Switch Cases
Convenient way :
Open Preferences by pressing [command] + [,] in Xcode.
Go to Key Bindings tab and set a shortcut for the command Refactor > Add Missing Switch Cases.
Input the shortcut in editor. Be sure the cursor is on switch letters.
Saw your question and decided to build an Xcode plugin that does exactly that.
Check it out: https://github.com/stefanceriu/SCXcodeSwitchExpander
in Xcode 10 do the following
add switch...case template and set your enum type instead of self so that xcode understands what it should autocomplete
move cursor inside word switch and go to menu > editor > refactor > expand switch cases
This is what you will get as result
Unfortunately this is not possible with Xcode out of the box. File a bug report and select the request an enhancement option.
https://bugreport.apple.com
I actually did this myself lately, and they gauge the severity and importance of bugs/enhancements by the number of duplicates they receive for a given issue - hence one of the reasons for not being able to search the tracker.
Just hit "Fix" button and the cases will appear:
While Xcode won't autocomplete the entire switch statement, there are tricks to speeding up writing them. This is what I've been doing lately:
Start the switch autocomplete, and type the expression
Copy the case block and paste it enough times to cover all the enum constants (actually, I just paste more than I need and delete later, rather than counting)
Move the cursor to before the first constant placeholder
Press TAB, then the first letter of the constant, then Enter to accept the first autocomplete suggestion
Repeat TAB, letter, ENTER for the rest. Autocomplete won't suggest constants you've already used in the switch, so the list narrows with each use.
Of course, it really helps if all the constants start with the same letter. In your case, consider naming them:
typedef enum {
dayOfWeekSunday,
dayOfWeekMonday,
dayOfWeekTuesday,
dayOfWeekWednesday,
dayOfWeekThursday,
dayOfWeekFriday,
dayOfWeekSaturday
} enumDayOfWeek;
Hey I made an Xcode 8 Source Editor Extension for that in Swift.
https://github.com/timaktimak/SwitchCaseGenerator
Hope it is helpful!
It's not using Xcode, but you can very quickly burp out processed snippets with any scripting language. Throw it as a plugin in your favourite text editor too (e.g Textmate).
I have a ~/bin folder which i place helpers like this and chmod +x them so i can use anytime like this:
enumify.pl ~/tmp.txt
or even better use the command line pasteboard tools:
pbpaste | enumify.pl
#! /usr/bin/perl
#enumify.pl
#reads from STDIN or use filename as first arg
#on OS X combine with pbpaste e.g pbpaste | enumify.pl
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
my #lines;
$filename = $ARGV[0];
if (length($filename) > 0) {
{ local *FILE; open FILE, "<$filename"; #lines = <FILE>; close FILE }
} else {
#lines = <STDIN>;
}
if (scalar #lines) {
my $target = "switch(<#switchvar#>) {\n";
my $enumlock = 0;
foreach (#lines) {
if ($enumlock == 0 && m/enum/) {
$enumlock = 1;
}
elsif ( $enumlock == 1) {
$_ = trim($_);
if (!(m/[{}]/) && (length($_)>0)) {
$_ =~ s/,//;
$target = "$target case $_:\nbreak;\n";
}
}
}
$target = "$target default:\nbreak;\n}\n";
if ( $enumlock == 1) {
print $target;
}
}
It turns
typedef enum {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
} DayOfWeek;
into
switch(<#switchvar#>) {
case Sunday:
break;
case Monday:
break;
case Tuesday:
break;
case Wednesday:
break;
case Thursday:
break;
case Friday:
break;
case Saturday:
break;
default:
break;
}
You can just right click the switch case itself and select Refactor > Expand Switch Cases and Xcode will fill in all cases that match the option. Confirmed working in Xcode version 12.0.
As of Xcode 13 (I think) it's finally built in!
Just start typing switch yourEnumName, and while you're in the middle of typing yourEnumNa... look for the autocomplete ellipsis:
I missed it at first because if you type the entire name, the top result becomes something else, leaving you to do things manually.

Design pattern for waiting for user interaction in iOS?

I'm developing a BlackJack game for iOS. Keeping track of the current state and what needs to be done is becoming difficult. For example, I have a C++ class which keeps track of the current Game:
class Game {
queue<Player> playerQueue;
void hit();
void stand();
}
Currently I'm implementing it using events (Method A):
- (void)hitButtonPress:(id)sender {
game->hit();
}
void Game::hit() {
dealCard(playerQueue.top());
}
void Game::stand() {
playerQueue.pop();
goToNextPlayersTurn();
}
as more and more options are added to the game, creating events for each one is becoming tedious and hard to keep track of.
Another way I thought of implementing it is like so (Method B):
void Game::playersTurn(Player *player) {
dealCards(player);
while (true) {
string choice = waitForUserChoice();
if (choice == "stand") break;
if (choice == "hit")
dealCard(player);
// etc.
}
playerQueue.pop();
goToNextPlayersTurn();
}
Where waitForUserChoice is a special function that lets the user interact with the UIViewController and once the user presses a button, only then returns control back to the playersTurn function. In other words, it pauses the program until the user clicks on a button.
With method A, I need to split my functions up every time I need user interaction. Method B lets everything stay a bit more in control.
Essentially the difference between method A and B is the following:
A:
function A() {
initialize();
// now wait for user interaction by waiting for a call to CompleteA
}
function CompleteA() {
finalize();
}
B:
function B() {
initialize();
waitForUserInteraction();
finalize();
}
Notice how B keeps the code more organized. Is there even a way to do this with Objective-C? Or is there a different method which I haven't mentioned recommended instead?
A third option I can think of is using a finite state machine. I have heard a little about them, but I'm sure if that will help me in this case or not.
What is the recommended design pattern for my problem?
I understand the dilemma you are running into. When I first started iOS I had a very hard time wrapping my head around relinquishing control to and from the operating system.
In general iOS would encourage you to go with method A. Usually you have variables in your ViewController which are set in method A(), and then they are checked in CompleteA() to verify that A() ran first etc.
Regarding your question about Finite State Machines, I think that it may help you solve your problem. The very first thing I wrote in iOS was a FSM (there for this is pretty bad code) however you can take a look here (near the bottom of FlipsideViewController.m:
https://github.com/esromneb/ios-finite-state-machine
The general idea is that you put this in your .h file inside an #interface block
static int state = 0;
static int running = 0;
And in your .m you have this:
- (void) tick {
switch (state) {
case 0:
//this case only runs once for the fsm, so setup one time initializations
// next state
state = 1;
break;
case 1:
navBarStatus.topItem.title = #"Connecting...";
state = 2;
break;
case 2:
// if something happend we move on, if not we wait in the connecting stage
if( something )
state = 3;
else
state = 1;
break;
case 3:
// respond to something
// next state
state = 4;
break;
case 4:
// wait for user interaction
navBarStatus.topItem.title = #"Press a button!";
state = 4;
globalCommand = userInput;
// if user did something
if( globalCommand != 0 )
{
// go to state to consume user interaction
state = 5;
}
break;
case 5:
if( globalCommand == 6 )
{
// respond to command #6
}
if( globalCommand == 7 )
{
// respond to command #7
}
// go back and wait for user input
state = 4;
break;
default:
state = 0;
break;
}
if( running )
{
[self performSelector:#selector(tick) withObject:nil afterDelay:0.1];
}
}
In this example (modified from the one on github) globalCommand is an int representing the user's input. If globalCommand is 0, then the FSM just spins in state 4 until globalCommand is non zero.
To start the FSM, simply set running to 1 and call [self tick] from the viewController. The FSM will "tick" every 0.1 seconds until running is set to 0.
In my original FSM design I had to respond to user input AND network input from a windows computer running it's own software. In my design the windows PC was also running a similar but different FSM. For this design, I built two FIFO queue objects of commands using an NSMutuableArray. User interactions and network packet would enqueue commands into the queues, while the FSM would dequeue items and respond to them. I ended up using https://github.com/esromneb/ios-queue-object for the queues.
Please comment if you need any clarification.

Resources