How to get selected text in Android WebView using reflection? - android

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.

Related

Appium - Unable to locate android element using xpath

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

BeanShell Android Issue. Unable to start activity remotely

Good day. The main amazing thing about the BeanShell is the idea that i can control what i want to be done dynamically from the server and i thought it would be amazing.
Although i never succeded in achieving that and seems no one else tried to start activity from the beanshell either.
Here how it goes. I simply want to pass the code from the server side to the Android,Android is going to evaluate that code within interpreter and run that.
The issue is that i am getting the exception from BeanShell no matter what i try.
The code from server side is the next.
$response['method'] = "import my.some.name.*;"
. "startActivity(new Intent(this,MyProfile.class))";
The code for Android is the next.
try {
String responseBody = response.body().string();
JSONObject jsonObject = new JSONObject(responseBody);
String method = jsonObject.optString("method");
Interpreter interpreter = new Interpreter();
try {
Object res = interpreter.eval(method);
} catch (EvalError evalError) {
evalError.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
But i am getting the next exception from the BeanShell
Sourced file: inline evaluation of: ``import my.some.name.*;startActivity(new Intent(this,MyProfile.class));'' : Class: MyProfile not found in namespace : at Line: 1 : in file: inline evaluation of: ``import my.some.name.*;startActivity(new Intent(this,MyProfile.class));'' : MyProfile
Any ideas what is going on?
Just in case if anyone needs the same solution i am posting for everyone to know.
Here how it goes.
Firstly you need to know that whatever you are trying to do on the server side remember that the BeanShell actually does not know anything about the String code you are passing itself,as it is going to interpret it just like a code out of box so with the help of CommonWare hint about full name path i managed to get it working.
So first step to do is to initialize the Interpreter.
Basic initialization goes like this :
String responseBody = response.body().string();
JSONObject jsonObject = new JSONObject(responseBody);
String method = jsonObject.optString("method");
Interpreter interpreter = new Interpreter();
try {
interpreter.set("context",getApplicationContext());
Object res = interpreter.eval(method);
} catch (EvalError evalError) {
evalError.printStackTrace();
}
Take a very attentive notice about the context as it was my main issue going back and forth as at the moment when i succeded to actually force BeanShell recognize my classes,the BeanShell started to throw Method not found exception about the startActivity() so by thinking logically we can assume that we would set the context as activity as the parent one for our remote methods and start evaluating everything from the context. So here how the remote code is looking.
$response['method'] = "import ink.va.activities;"
. "import android.content.Intent;"
. "import android.content.*;"
. "context.startActivity(new android.content.Intent(context, my.package.name.MyProfile.class));";
The most important things to notice here.
• We are importing everything possible for BeanSherll to recognize our classes,even if they are Android-Build,no matter,still we need to import them.
• If you are going to use any class,then as CommonWare noticed out,you MUST specify the full path to that Class E.G my.package.name.MyProfile.class.
• As i was getting Command Not Found i started to think about the context.startActivity() as i have defined the context beforehand in BeanShell as my parent from which i am going to use methods and Woala,everything worked like a charm!
Possible Problems
I don't know a lot about BeanShell, but there's a couple of issues here
You can import a class (in a compiled language) at runtime
You're trying to do the equivalent of Reflection (but aren't doing any)
Security. No user would consent to you having control to open a screen on their app remotely
Presumably BeanShell is supposed to do the reflection under the covers, but in an case you won't be able to do the import.
Possible solutions
The class/activity using the library should import everything (I'm not sure if a compiler will even retain this)
You can use reflection directly, with things like "method from name". The downside is it's very limited what code you can send from the server unless you handle a myriad of cases.
You could only send names/commands; to specific endpoints in your java app (this is what I recommend) and plan the actions you want ahead of time
$response['method'] = "my.some.name.MyProfile";
JSONObject jsonObject = new JSONObject(response.body().string());
String nameParam = jsonObject.optString("method");
Class<? extends Activity> clazz = Class.forName(nameParam); //wrap with try
startActivity( new Intent(this, clazz) )

How to call dumpNativeHeap api?

I find that there is an API dumpNativeHeap in android/os/Debug.java which has its implementation in native. How am i supposed to call this API? I tried calling as Debug.dumpNativeHeap(fd); but got an error saying the method does not exists.
dumpNativeHeap is not a public API. Use dumpHprofData instead.
As #segfault says, it's not part of the public API. However, if you're just wanting to use it for debugging, you can use reflection to access it. It's not recommended to do this in a production application, though.
try {
Method dumpNativeHeap = Debug.class.getDeclaredMethod("dumpNativeHeap", FileDescriptor.class);
dumpNativeHeap.setAccessible(true);
dumpNativeHeap.invoke(null, myFileDescriptor);
} catch (NoSuchMethodException e) {
// Shouldn't happen unless the method gets removed in a future API
}

Add statements for higher version?

I am using 1.6 i.e. API 4 to build my application. There are couple of commands that are supported by higher versions. I want to write those commands and make application more compatible for higher versons. Like, I use Tabs. I want to use setLeftStripDrawable and setRightStripDrawable, but they are supported by API 8.
I write something like :
// I want these lines to come into affect only if the device SDK is greater than 7 as SDK of below 7, doesn't support these methods.
if (android.os.Build.VERSION.SDK_INT > 7) {
tw.setLeftStripDrawable(R.drawable.tab_selected_bar_left_v4); // TabWidget
}
EDIT : I want to set setLeftStripDrawable to the tabs used in my app. In my manifest I have uses-sdk android:minSdkVersion="4". If I write the lines as above and compile it in 2.3, it compiles successfully. When I run in 1.6 I get "java.lang.VerifyError". If I remove those liens and again run in 1.6, it works properly.
What should I do to execute those lines only if the device SDK api is > 7, and if it is less than that then those lines should not come under any affect ?
Any clue ?
if (Build.VERSION.SDK_INT > 7) {
...
}
I think you should use something like this. I did this by heart, so there might be some errors.
try {
Method twMethod = TabWidget.class.getMethod("setLeftStripDrawable", new Class[] { int.class });
twMethod.invoke(tw, R.drawable.yourdrawable);
} catch (NoSuchMethodException e) {
/* not supported */
} catch (IllegalArgumentException e) {
/* wrong class provided */
} catch (IllegalAccessException e) {
/* Java access control has denied access */
} catch (InvocationTargetException e) {
/* method has thrown an exception */
}
You can try looking at Android Reflection. I have not used it yet myself, but as far as i understand, you can test for classes and methods that you know the name of. Then you can instantiate and use them.
You can read some of the basics here: http://www.tutorialbin.com/tutorials/85977/java-for-android-developers-reflection-basics
Here's some sample Android code, using reflection, that does something similar. It calls the getRotation() method from the Display class; the method only exists in SDK 8+. I've used it in one of my apps and it works:
//I want to run this: displayrotation = getWindowManager().getDefaultDisplay().getRotation();
//but the getRotation() method only exists in SDK 8+, so I have to call it in a sly way, using Java "reflection"
try{
Method m = Display.class.getMethod("getRotation", (Class[]) null);//grab the getRotation() method if it exists
//second argument above is an array containing input Class types for the method. In this case it takes no inputs.
displayrotation = (Integer) m.invoke(getWindowManager().getDefaultDisplay(),(Object[]) null);
//again, second argument is an array of inputs, in this case empty
}catch(Exception e){//if method doesn't exist, take appropriate alternate actions
Log.w("getRotation","old OS version => Assuming 90 degrees rotation");
displayrotation = Surface.ROTATION_90;
}

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