Soft keyboard not present, cannot hide keyboard - Appium android - android

I am getting following exception:
org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. (Original error: Soft keyboard not present, cannot hide keyboard) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 368 milliseconds
I am using driver.hideKeyboard() to hide soft input keyboard that is open on the screen.How to ensure that keyboard is open before hiding it? OR any other workaround?

I also get this error, i correct it by using the following code in the setUp method :
capabilities.setCapability("unicodeKeyboard", true);
capabilities.setCapability("resetKeyboard", true);
You can check answers here :
Keyboard in Android physical device isn’t always hidden while using Appium

Use adb command to check whether keyboard has popped up or not
adb shell dumpsys input_method | grep mInputShown
Output : mShowRequested=true mShowExplicitlyRequested=false mShowForced=false mInputShown=true
if mInputShown=true then yes software keyboard has popped up. Then use driver.pressKeyCode(AndroidKeyCode.BACK);
One way to do using java is
Process p = Runtime.getRuntime().exec("adb shell dumpsys input_method | grep mInputShown");
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String outputText = "";
while ((outputText = in.readLine()) != null) {
if(!outputText.trim().equals("")){
String keyboardProperties[]=outputText.split(" ");
String keyValue[]=keyboardProperties[keyboardProperties.length-1].split("=");
String softkeyboardpresenseValue=keyValue[keyValue.length-1];
if(softkeyboardpresenseValue.equalsIgnoreCase("false")){
isKeyboardPresent=false;
}else{
isKeyboardPresent=true;
}
}
}
in.close();
PS: Please do not use driver.navigate().back() as its behavior may not be same on all devices.

Related

Appium pressKeyCode generate IME action

In Appium release 1.8.1 the said it can generate IME actions.
Appium 1.8.1 Release
How can I generate IME event enter with action ID 5 / Create action Done or Next.
Also is it possible to fire it via ADB?
I know I can fire the command adb shell input keyevent 66 to get enter event. What I want is to add to this command actionId.
This is unit test written by Appium for generating IMEAction.
This test is written for APIDemos.apk which can be found here
public class KeyCodeTest {
final By PRESS_RESULT_VIEW = By.id("io.appium.android.apis:id/text");
final Activity activity;
#Test
public void pressKeyAndGenerateIMEActionTest() {
activity = new Activity(driver.getCurrentPackage(), ".text.KeyEventText");
driver.startActivity(activity);
driver.pressKey(new KeyEvent()
.withKey(AndroidKey.ENTER)
.withFlag(KeyEventFlag.SOFT_KEYBOARD)
.withFlag(KeyEventFlag.KEEP_TOUCH_MODE)
.withFlag(KeyEventFlag.EDITOR_ACTION));
final String state = driver.findElement(PRESS_RESULT_VIEW).getText();
// This event won't update the view
assertTrue(state.isEmpty());
}
I believe if we can run this, it should show adb command details in appium server logs
The definition of different KeyEventFlag can be read here
You can use driver.pressKeyCode(int key, Integer metastate) to generate IME actions.
Key codes can be found here: io.appium.java_client.android.AndroidKeyCode
Make sure you're using UIAutomator2 by doing desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
To do it via adb you could run a shell command or use appium's relaxed security (run your appium with CL arg --relaxed-security) and mobile: shell command.
Eg: driver.executeScript("mobile:shell", "adb shell input keyevent 66");
Here's a full example:
#Test
public void testKeyEvent() {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("deviceName", "Android Emulator");
capabilities.setCapability("automationName", "UiAutomator2");
capabilities.setCapability("appPackage", "com.your.app.package");
capabilities.setCapability("appActivity", ".your.Activity");
AndroidDriver driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), capabilities);
// Press enter
driver.pressKeyCode(AndroidKeyCode.ENTER);
// Press Editor action
driver.pressKeyCode(AndroidKeyCode.FLAG_EDITOR_ACTION);
// Send adb command
driver.executeScript("mobile:shell", "adb shell input keyevent 66");
// Switch ime using adb shell
driver.executeScript("mobile:shell", "adb shell ime set 5");
}

Live output to wx.TextArea from stdout

I have code that prints out the output of adb logcat into a wx.TextArea box, this all works great, click the button and logcat prints out, it prints out more when phone taps are made and everything works fine.
toolsDir and pkgName are both strings.
params = [toolsDir + "\\adb.exe", "logcat"]
p = Popen(params, stdout=subprocess.PIPE, bufsize=1)
for line in p.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
However I have adapted this code to only print out the logs that are for a particular app, this is done using the windows 'findstr' function, adb logcat | findstr myApp. The code below initially works, but then stops, and doesn't display anything else whatever happens, button tapped, app closed etc.
Its like the buffer has reached the end and doesn't process any further events.
args = [toolsDir + '\\adb.exe', 'logcat']
args2 = ['findstr', pkgName]
process_adb = subprocess.Popen(args, stdout=subprocess.PIPE, shell=False)
process_fs = subprocess.Popen(args2, stdin=process_adb.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
for line in process_fs.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
How come the bottom code, the one that filters the app name, stops printing out real time logs, but the top doesn't? I'm guessing its something to do with the piping of one command to another.
You can use Python to filter lines:
if pkgName in line:
BTW: it can be problem with two processes because probably second waits for EOF (end of file) or other signal (ie. closed pipe) but its stdin is open all the time waiting for new data from first process..
Check this: python-subprocess-interaction-why-does-my-process-work-with-popen-communicate

Xamarin: IsDebuggerConnected returns incorrect value

This picture is self explanatory I think.
Anyone know why this might be happening, and if I can fix it?
Android.OS.Debug.IsDebuggerConnected only returns true when the process has the Java/jdb debugger connected. Use System.Diagnostics.Debugger.IsAttached to detect when Mono/Xamarin Studio debugger is connected..
You can verify the above statement with the following steps:
1.
Create a new Xamarin.Android app named DebugTest with the following source code:
[Activity (Name="com.companyname.debugtestapp.MainActivity", Label = "DebugTestApp", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
string androidConnected = Android.OS.Debug.IsDebuggerConnected ? "JDB Debugger" : "";
string monoConnected = System.Diagnostics.Debugger.IsAttached ? "Mono Debugger" : "";
button.Text = "Debugging mode: " + androidConnected + " | " + monoConnected;
};
}
}
Some important things to note:
Override the default name of the activity using Name=com.companyname.debugtestapp.MainActivity so it doesn't use the MD5 sum of the assembly name (Explained here).
The button will display if it detects the debugger that is connected. Either JDB or Mono.
2.
Start debugging the app via Xamarin Studio. Click the button to see:
"Debugging mode: | Mono Debugger"
This means we have detected the Mono debugger.
3.
Kill the app.
4.
Connect the Java debugger to the app via the following set of terminal commands:
# Start the main activity via adb. This will open it in debug mode, meaning it will wait until a debugger is connected before proceeding.
adb -d shell am start -D -n "com.companyname.debugtestapp/com.companyname.debugtestapp.MainActivity"
# Discover the Java Debugger port. Copy the number outputted here...
adb jdwp
# Forward the debugger port to JDB. Replace '7602' with the port number outputted by the command above.
adb forward tcp:8000 jdwp:7602
# Start the Java debugger...
jdb -attach 127.0.0.1:8000
You should see:
Initializing jdb ...
>
This specifies the Java debugger is connected.
On your phone, click the button. You should see the text **Debugging mode: JDB Debugger | **.

How to launch android app through python

I want to run any app (say Settings) after rebooting tablet. Can I use os.system or do I have to use other methods.
import os,time
for i in range(0,3):
os.system("adb reboot")
time.sleep(60)
Yes, you can use os.system to execute ADB commands. If you want to validate the command executed successfully, take a look at the check_output(...) function which is apart of the subprocess library. This code snipet is how I choose to implement the check_output function. For the full code look here.
def _run_command(self, cmd):
"""
Execute an adb command via the subprocess module. If the process exits with
a exit status of zero, the output is encapsulated into a ADBCommandResult and
returned. Otherwise, an ADBExecutionError is thrown.
"""
try:
output = check_output(cmd, stderr=subprocess.STDOUT)
return ADBCommandResult(0,output)
except CalledProcessError as e:
raise ADBProcessError(e.cmd, e.returncode, e.output)
To launch an application you can use the command am start -n yourpackagename/.activityname. To launch the Settings App, run adb shell am start -n com.android.settings/com.android.settings.Settings. This stackoverflow question shows you in detail the options you can use to start the application via a command line intent.
Other tips:
I created an ADB wrapper written in python along with a few other python utilities that may aid in what you are trying to accomplish. For example, instead of calling time.sleep(60) to wait for the reboot, you use adb to poll the status of the property sys.boot_completed and once the property is set the device has finished booting and you can launch any application. Below is a reference implementation you can use.
def wait_boot_complete(self, encryption='off'):
"""
When data at rest encryption is turned on, there needs to be a waiting period
during boot up for the user to enter the DAR password. This function will wait
till the password has been entered and the phone has finished booting up.
OR
Wait for the BOOT_COMPLETED intent to be broadcast by check the system
property 'sys.boot_completed'. A ADBProcessError is thrown if there is an
error communicating with the device.
This method assumes the phone will eventually reach the boot completed state.
A check is needed to see if the output length is zero because the property
is not initialized with a 0 value. It is created once the intent is broadcast.
"""
if encryption is 'on':
decrypted = None
target = 'trigger_restart_framework'
print 'waiting for framework restart'
while decrypted is None:
status = self.adb.adb_shell(self.serial, "getprop vold.decrypt")
if status.output.strip() == 'trigger_restart_framework':
decrypted = 'true'
#Wait for boot to complete. The boot completed intent is broadcast before
#boot is actually completed when encryption is enabled. So 'key' off the
#animation.
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
print 'wait for animation to start'
while status == 'stopped':
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
print 'waiting for animation to finish'
while status == 'running':
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
else:
boot = False
while(not boot):
self.adb.adb_wait_for_device(self.serial)
res = self.adb.adb_shell(self.serial, "getprop sys.boot_completed")
if len(res.output.strip()) != 0 and int(res.output.strip()) is 1:
boot = True

adb shell input unicode character

Knowing the basic key mappings described in ADB Shell Input Events I get the emulation of text input and special keys working quite well. But what about Unicode characters? For instance I want to use umlauts from the German QWERTZ keyboard layout.
This gets me:
$ adb shell input text ö
Killed
So it seems to crash and
adb shell input text \xFC
prints xFC on the input. I have tried to the the events with getevent but I haven't found a direct mapping, I've also looked into the keymapping file /system/usr/keylayout/Qwerty.kl
I believe the only possibility is via the clipboard, but as pointed out in the question Pasting text into Android emulator clipboard using adb shell it seems to be unknown how to use it for Android Ice Cream Sandwich or later..
I wrote a virtual keyboard that accept broadcast intent, so you can send unicode characters to the editText view via adb.
for e.g.
adb shell am broadcast -a ADB_INPUT_TEXT --es msg "你好嗎! Hello!"
Here is the github project:
https://github.com/senzhk/ADBKeyBoard
Hope this little project would help.
Actually ADBKeyBoard is very good! Thanks for Eric Tang !
Some useful extensions for comfortable usage:
Switch to ADBKeyBoard from adb:
adb shell ime set com.android.adbkeyboard/.AdbIME
Check your available le virtual keyboards:
ime list -a
Use simple quote characters -not double as in example above- if your shell not accepts "!" (explanation sign)
adb shell am broadcast -a ADB_INPUT_TEXT --es msg 'Accented characters here'
Switch back to original virtual keyboard: (swype in my case...)
adb shell ime set com.nuance.swype.dtc/com.nuance.swype.input.IME
Use adb over wifi to simplify your life... :)
input won't work because it can only send single key event through the virtual keyboard (check the source code if you don't know what I mean).
I think the only way left is using Instrumentation. I guess you can create a test for your Activity and then do something like this:
final Instrumentation instrumentation = getInstrumentation();
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
final KeyEvent altDown = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_GRAVE, 1, KeyEvent.META_ALT_LEFT_ON);
final KeyEvent altUp = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_GRAVE, 1, KeyEvent.META_ALT_LEFT_ON);
instrumentation.sendKeySync(altDown);
instrumentation.sendCharacterSync(KeyEvent.KEYCODE_A);
instrumentation.sendKeySync(altUp);
instrumentation.sendKeySync(altDown);
instrumentation.sendCharacterSync(KeyEvent.KEYCODE_E);
instrumentation.sendKeySync(altUp);
instrumentation.sendKeySync(altDown);
instrumentation.sendCharacterSync(KeyEvent.KEYCODE_I);
instrumentation.sendKeySync(altUp);
instrumentation.sendKeySync(altDown);
instrumentation.sendCharacterSync(KeyEvent.KEYCODE_O);
instrumentation.sendKeySync(altUp);
instrumentation.sendKeySync(altDown);
instrumentation.sendCharacterSync(KeyEvent.KEYCODE_U);
instrumentation.sendKeySync(altUp);
This will send the modified keys: àèìòù
update 2022
https://stackoverflow.com/a/71367206/236465 shows another solution using AndroidViewClient/culebra and CulebraTester2-public backend.

Categories

Resources