Appium - Unable to locate android element using xpath - android

I have to click the ALLOW, to give the app permissions on Android 7.0.
I Tried the following xpath
//android.widget.Button[contains(#resource-id,'com.android.packageinstaller:id/permission_allow_button')]
also tried //android.widget.Button[#text='ALLOW']
Getting error :
No Such element exception.

You can try it:
driver.findElement(By.id("com.android.packageinstaller:id/permission_allow_button")).click();
or
driver.findElement(By.Name("Allow")).click();
or
driver.findElement(new By.ByName("Allow")).click();
It's working fine for me.

Since, App requesting for permission. You can allow permission directy using Desired Capabilities.
Try this:
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability("autoGrantPermissions", "true");
Check this also.

First of all, you need to locate the alert, whether it is there or not?
So what you can do you is you can create the list, which will check whether alert is present or not? And then if the alert is present, it will click on ALLOW button.
I am using PageObjectFactory Pattern so the following is my code which works fine for me:
#AndroidFindBy(xpath = "//android.widget.Button[#text='ALLOW']")
private List<MobileElement> alert;
#AndroidFindBy(xpath = "//android.widget.Button[#text='ALLOW']")
private MobileElement allowAlert;
if (!alert.isEmpty()) {
try {
// do something
} catch (InterruptedException e) {
e.printStackTrace();
}
waitForElement(appiumDriver, allowAlert).click();
}

Related

Appium/wdio - unable to dismiss location permissions alert on Android

I'm using webdriver.io to write a suite of Appium tests for a hybrid Cordova App.
"appium": "^1.10.0",
"wdio-appium-service": "^0.2.3",
"wdio-jasmine-framework": "^0.3.8",
"webdriverio": "^4.14.1",
In one of my tests I am trying to programmatically accept the Location Permissions native modal presented on App launch on top of the WebView.
I can easily do so in iOS using browser.alertAccept() but the latter does not seem to work for Android.
I also tried to switch to the native context to dismiss it but had no luck.
function dismissLocationPermissions() {
if (browser.isAndroid) {
new WebView().switchToContext(0);
browser.pause(2000);
const ANDROID_ACCEPT_ALERT_SELECTOR = "//*[#class='android.widget.Button'][2]";
// OR '*//android.widget.Button[#text="ALLOW"]' ?
$(ANDROID_ACCEPT_ALERT_SELECTOR).click();
WebView().switchToContext(1);
} else {
browser.alertAccept();
}
}
How can I dismiss the native alert on Android using wdio?
There is autoGrantPermissions DesiredCapability, if you set it to true - Appium will automatically determine which permissions are required by your application and grant them to the application during the installation procedure.
The capability is set to false by default so you need to explicitly set it like:
DesiredCapabilities dc = new DesiredCapabilities();
dc.setCapability(MobileCapabilityType.NO_RESET, false);
dc.setCapability(AndroidMobileCapabilityType.AUTO_GRANT_PERMISSIONS, true);
driver = new AndroidDriver<>(url, dc);
More information:
Appium Desired Capabilities
Application Setup
The permission dialog is native, so you don't need to switch to the webview to dismiss it.
There is an android id associated with the allow/deny permission button, so you can use that instead of the identifier you have used.
The id for allow button is: com.android.packageinstaller:id/permission_allow_button .
The id for deny button is: com.android.packageinstaller:id/permission_deny_button

how to handle alerts in android using appium

How do I handle alerts in an Android web application using Appium server (1.0.1) and the Android SDK?
The below code is not working on android:
driver.switchTo().accept().alert();
Error message:
> -modal window does not get closed
You need to get the Alert before you try and accept it
This is code from some of the Appium Java Client Tests:
wait.until(ExpectedConditions.alertIsPresent());
Alert alert = driver.switchTo().alert();
alert.accept();
This should work most of the time.
If accept() isn't working, replace the driver.switchTo().alert(); and alert.accept(); with code to find the button and then click it.
If it's not finding the button wrap findElementBy(Method) code in a try/retry block, and then click on it.
The best way is to use the appium inspector. Click on the element and copy the resource-id from it. Use this resource id in findElement(By.id()) method.
For me resource-id: android:id/button1
((AndroidDriver) driver).findElement(By.id("android:id/button1")).click();
This is for Android. For regular use you can use
driver.findElement(By.id("android:id/button1")).click();
Some alerts may be native Android's alerts, not generated by a browser. In this case the following code:
Alert alert = driver.switchTo().alert(); alert.accept();
may throw:
WebDriverException: unknown error: unhandled inspector error: {"code":-32603,"message":"No JavaScript dialog to handle"}
To handle such alert, just switch to the native application context, make required actions, and then switch back to the browser:
AppiumDriver<WebElement> appiumDriver = (AppiumDriver<WebElement>) webDriver;
String currentContext = appiumDriver.getContext();
appiumDriver.context("NATIVE_APP");
// actions within the alert
appiumDriver.findElements(By.xpath(OK_BUTTON_LOCATOR)).click(); // put locator instead of OK_BUTTON_LOCATOR
appiumDriver.context(currentContext);
// continue working
WebElement btn = driver.findElement(By.xpath("//android.widget.Button[#content-desc='OK']"));
TouchAction act = new TouchAction(driver);
act.tap(241,320).perform();
(241,320) these are X & Y c ordinates of alert
This work perfectly for me
Appium comes with a default capability to accept, dismiss alerts
capabilities.SetCapability("autoAcceptAlerts", true);
capabilities.SetCapability("autoDismissAlerts", true);
If the alert display on Ui , taking more time to display when we need to wait ..we can use fluent wait instead of this..
So an updated answer on this is this: an AlertDialog is a system level element, so clicking on accept button you should use:
androidDriver.findElementById("android:id/button1").click()
else for cancel do this:
androidDriver.findElementById("android:id/button2").click()
// first check alert is present or not
public boolean isAlertPresent() {
try {
mobDriver.switchTo().alert();
log.info("ALERT IS PRESENT !! ");
return true;
} catch (Exception e) {
log.info("ALERT IS NOT PRESENT !! ");
return false;
}
}
public void mobileAlertHandle() {
if (isAlertPresent()) {
Alert alert = mobDriver.switchTo().alert();
alert.accept();
}
}
if this does not work then inspect your element and try with id or name
ex: mobDriver.findElementById("android:id/button2").click()
Please use the below code, Add some wait before clicking on OK Button.
After that pass the xpath of you OK Button.
synchronized (driver)
{
driver.wait(2000);
}
driver.context(NATIVE_APP);
driver.findElementByXPath{("//android.widget.Button[#resourceid=
‘android:id/button1’]").click();

How to get selected text in Android WebView using reflection?

I'm trying to get the selected text of my WebView in Android. I know Android does not let us to get this using the right ways.
One solution I've found in the internet is using reflection. This is the code I'm using:
Region result = null;
try {
Object[] params = null;
Method nativeGetSelection = WebView.class.getDeclaredMethod("nativeGetSelection");
nativeGetSelection.setAccessible(true);
result = (Region)nativeGetSelection.invoke(this, params);
} catch (Exception e) {
e.printStackTrace();
}
But I am getting NoSuchMethodException. But the Android WebView has the desired method (nativeGetSelection). How you can see here
So why is this happening?
Don't use reflection to get at private APIs. This will not work on Android 4.4 (KitKat) regardless of your minSdk/targetSdk because that API is simply not there.

Use android RootTools library to gain permission android.permission.STATUS_BAR

I am trying to code an app that calls a method in android StatusBarManager. I did it the same way as the hack to expand the android status bar:
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method expand = statusbarManager.getMethod("disable", new Class[] { int.class });
expand.invoke(service, 0x00200000);
} catch (Exception e) {
e.printStackTrace();
System.out.println("DISABLE HOME ERROR");
}
Except the method I want to call is disable() instead of expand().
However, I am getting a security exception "neither and user nor the current process has permission android.permission.STATUS_BAR.
I tried adding it to the manifest but because it is a system apps permission, it only works if I copy my app to /system/app.
I messed around with RootTools http://code.google.com/p/roottools/ but couldn't figure out how to set the permissions. I tried setPermissions(0) and setUserPermissions("android.permissions.status_bar") but none works. I am not really sure how to use this library.
Any help would be greatly appreciated!

How to programatically hide Caller ID on Android

on Android phones, under Call -> Additional settings -> Caller ID
it is possible to hide your caller ID. I want to do that programatically from my code, but was not able to find a way to do that.
I searched through
android.provider
android.telephony
for 2.1 release and was not able to find it.
Has anybody successfully solved this issue?
Thanks in advance. Best regards.
Here I will describe two approaches I tried.
1.) It is possible to display Additional Call Settings screen from your application. Although it looks like it is part of the Settings application, that is not true. This Activity is part of the Native Phone Application, and it may be approached with the following intent:
Intent additionalCallSettingsIntent = new Intent("android.intent.action.MAIN");
ComponentName distantActivity = new ComponentName("com.android.phone", "com.android.phone.GsmUmtsAdditionalCallOptions");
additionalCallSettingsIntent.setComponent(distantActivity);
startActivity(additionalCallSettingsIntent);
Then user has to manually press on the CallerID preference and gets radio button with 3 options.
This was not actually what I wanted to achieve when I asked this question. I wanted to avoid step where user has to select any further options.
2.) When approach described under 1.) is executed in the Native Phone Application, function setOutgoingCallerIdDisplay() from com.android.internal.telephony.Phone has been used.
This was the basis for the next approach: use Java Reflection on this class and try to invoke the function with appropriate parameters:
try
{
Class <?> phoneFactoryClass = Class.forName("com.android.internal.telephony.PhoneFactory");
try
{
Method getDefaultPhoneMethod = phoneFactoryClass.getDeclaredMethod("getDefaultPhone");
Method makeDefaultPhoneMethod = phoneFactoryClass.getMethod("makeDefaultPhone" , Context.class);
try
{
makeDefaultPhoneMethod.invoke(null, this);
Object defaultPhone = getDefaultPhoneMethod.invoke(null);
Class <?> phoneInterface = Class.forName("com.android.internal.telephony.Phone");
Method getPhoneServiceMethod = phoneInterface.getMethod("setOutgoingCallerIdDisplay", int.class, Message.class);
getPhoneServiceMethod.invoke(defaultPhone, 1, null);
}
catch (InvocationTargetException ex)
{
ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
ex.printStackTrace();
}
}
catch (NoSuchMethodException ex)
{
ex.printStackTrace();
}
}
catch (ClassNotFoundException ex)
{
ex.printStackTrace();
}
Firstly I tried just to use getDefaultPhone(), but I get RuntimeException
"PhoneFactory.getDefaultPhone must be called from Looper thread"
Obviously, issue lies in the fact that I tried to call this method from the Message Loop that was not the Native Phone App one.
Tried to avoid this by making own default phone, but this was a security violation:
ERROR/AndroidRuntime(2338): java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.provider.Telephony.SPN_STRINGS_UPDATED from pid=2338, uid=10048
The only way to overcome (both of) this would be to sign your app with the same key as the core systems app, as described under
Run secure API calls as root, android
I'm not sure if this is a global feature, but Australian phones can hide their number by prefixing the caller's number with #31# or 1831. This may not be the perfect solution, but a prefix like this could possibly work for your requirements during coding.

Categories

Resources