Android Suporting older SDK where the method is not available - android

I've been looking for an answer but have not found a way to do it yet I hope somebody could point me in the right direction
I want support sdk8 and up, there is this method createInsecureRfcommSocketToServiceRecord
from the android.bluetooth.BluetoothDevice library that is supported on SDk10 and up only
Quick and dirty is make the minSDK=10 but I dont want to leave my users with older devices out in the cold
I've seen pretty involved(or should I say wacky) ways of attempting this , reflections??? but they all fail for me the simplest way I thought would be:
if( Build.VERSION.SDK_INT>=10)
{
BluetoothDevice device;
Class myC = ClassforName("android.bluetooth.BluetoothDevice")
Method myM = myC.getDeclaredMethod("createInsecureRfcommSocketToServiceRecord");
BluetoothSocket bb = (BluetoothSocket)myM.invoke(device, MYUUID);
}
But it throws a NoSuchExceptionMethod, so it looks like maybe the library has to have other name???? or how would you handle this??
Thanks in advance

If you don't want to increase your minSDK version you have either to wrap your calls with reflection ...
BluetoothDevice device;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
Class myC = ClassforName("android.bluetooth.BluetoothDevice")
Method myM = myC.getDeclaredMethod("createInsecureRfcommSocketToServiceRecord",
new Class[] { UUID.class } );
BluetoothSocket bb = (BluetoothSocket)myM.invoke(device, MYUUID);
}
... or you provide an (abstract) class android.bluetooth.BluetoothDevice that has only some empty method stubs. That allows you to compile your source without any errors. During run time the virtual machine will try to load that class from the system.
public abstract class BluetoothDevice {
BluetoothDevice () {
}
public void createInsecureRfcommSocketToServiceRecord(UUID uuid) {
}
}
The class has to be place in root-source/android/bluetooth. In any case it's important that you restrict any call to the right OS version (see my code above) otherwise you might get an NoSuchExceptionMethod -exception.
Finally: don't forget the signature (parameters) of the method (in getDeclaredMethod()).
Cheers!

You have to pass also the declared parameters
Class myC = ClassforName("android.bluetooth.BluetoothDevice")
Method myM = myC.getDeclaredMethod("createInsecureRfcommSocketToServiceRecord",UUID.class);

Related

Extent Report Issue Parallel testing

I have the following Reporting code:
public class Reporting {
private ExtentHtmlReporter extentHtmlReporter;
private static ThreadLocal<ExtentReports> extentReports = new ThreadLocal<>();
private static ThreadLocal<ExtentTest> extentTest = new ThreadLocal<>();
public synchronized ExtentTest createInstanceReport(String testCaseName) {
System.out.println(extentReports.get());
new File(Constants.userDir + "/Reports/").mkdirs();
// To generate report with name
extentHtmlReporter = new ExtentHtmlReporter(
Constants.userDir + "/Reports/" +
"ExecutionReport_" + new SimpleDateFormat(
Constants.date).format(new Date()) + ".html");
// Setting Document Title
extentHtmlReporter.config().setDocumentTitle("Demo");
// Setting Report Name
extentHtmlReporter.config().setReportName("Demo Automation");
// Setting Theme
extentHtmlReporter.config().setTheme(Theme.STANDARD);
// Setting Chart location
extentHtmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
// Setting Chart visibility
extentHtmlReporter.config().setChartVisibilityOnOpen(false);
// Setting Time stamp
extentHtmlReporter.config().setTimeStampFormat("yyyy-MM-dd HH:mm:ss");
// Setting append exist as true
extentHtmlReporter.setAppendExisting(true);
ExtentReports extentReports = new ExtentReports();
extentReports.attachReporter(extentHtmlReporter);
// Setting system info
extentReports.setSystemInfo("Name",
BaseTest.prop.getProperty(Constants.testerName));
extentReports.setSystemInfo("Environment",
BaseTest.prop.getProperty(Constants.environment));
extentReports.setSystemInfo("Browser",
BaseTest.prop.getProperty(Constants.browser));
Reporting.extentReports.set(extentReports); // Instead of using here extentReport thread like this, Can anyone suggest to use it directly
// Add test case name in report
ExtentTest extentTest = Reporting.extentTest.get();
extentTest = Reporting.extentReports.get().createTest(testCaseName);
Reporting.extentTest.set(extentTest);
// Assigning categories
extentTest.assignCategory(MultiFunction.getProp()
.getProperty(Constants.browser));
System.out.println(Reporting.extentReports.get());
System.out.println(Reporting.extentTest.get());
return extentTest;
}
public synchronized ExtentTest getExtentTest() {
return extentTest.get();
}
public synchronized ExtentReports getInstanceReport() {
return extentReports.get();
}
public synchronized void remove() {
extentReports.remove();
extentTest.remove();
}
}
I was trying parallel testing using TestNG (and will have to use Selenium grid and sauce in future). I execute 2 test cases then only one test case result is added in the report.
I have isolated the extentTest, extentReporter and WebDriver instances using threadPool.
Tried below with extentHtmlReporter instance:
1) Tried to make it static(no luck)
2) Tried to make it local (the same behaviour, getting only 1 test case result)
3) Tried as a non-static global variable ( no luck)
Could you suggest how to solve the above issue?
Please note: Only one report is generated. But when I tried to run parallel test cases in debug mode reports are generated for both the test case. I think because one test case gets over its killing some instance (when running in non-debug mode)
Also, I want to redesign the following place in my code:
For extentRpeort, I am using:
Reporting.extentReports.set(extentReports);
To add extentReport instance to my extentReport Thread.
Instead of adding like this I want to use it directly so as to reduce line of code.
If I understand correctly you have to generate Report from all executed TestNG cases.
However, from code which you shared, it is very visible that you will have some trouble with it. You are making a few critical mistakes and result are obvious:
For generating reports with TestNG I will suggest grabbing information about test execution from TestNG listener. Something like:
public final class TestNGListener extends TestListenerAdapter implements IInvokedMethodListener, ISuiteListener {
#Override
public void onStart(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_STARTED, context.getName()));
}
#Override
public void onFinish(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_FINISHED, context.getName()));
}
#Override
public void onTestStart(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_STARTED, getMethodName(result)));
}
#Override
public void onTestSuccess(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_SUCCESS, getMethodName(result)));
processTestResult(result);
}
#Override
public void onTestFailure(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_FAILED, getMethodName(result)));
}
You can't do everything in one method! You broke Single Responsibility Principle. Your createInstanceReport() is doing all jobs (setting report details, set system info, attach an executed test case to report) at one place. You have to redesign this logic to some logical separate operations. After redesigning your problem with the next line:
Reporting.extentReports.set(extentReports)
Could successfully disappear.
You have to consider a case, why you need to use exactly Extent, Reports Version 3. TestNG has test reports from the box. They are poor but they are presented out of the box. If you want just to improve it a little bit you could use ReportNG over TestNG.
It is quite easy to configure: Configuring ReportNG with TestNG for HTML Reports.
It isn't maintained, but it makes TestNG reports really eye candy and understandable.
Anyway, my suggestion is to use TestNGListener for getting info about test cases execution. And read more about good programming practice.
Work with TestNG/jUnit (or other runner framework that you are using) listener, here is a good example how to do it.
Do not put everything in a single class.
https://www.swtestacademy.com/extent-reports-version-3-reporting-testng/
The issue was with the flushing of extent report instance.
I was using ThreadLocal for storing extent report instance and was flushing the wrong instance.

Using internal android classes

I want to use an android internal class, com.android.internal.widget.LockPatternUtils .
I searched for examples and I got this:
LockPatternUtils lpu = new LockPatternUtils(this.getContentResolver());
However, i cant import, autimport does not appear and manually doing
import com.android.internal.widget.LockPatternUtils
doesnt work.
How is this handled? I think there was something about creating an interface but not sure about it
EDIT: I got this:
boolean patternLocked = android.provider.Settings.System.getInt(
getContentResolver(),Settings.System.LOCK_PATTERN_ENABLED, 0)==1;
That works for the pattern lock, but i cant know about the pin or password lock
Use reflection to access the internal Android method getActivePasswordQuality (line 350 of LockPatternUtils.java) and compare the int it returns to the int for any of the DevicePolicyManager Constants:
protected boolean isSecure() {
String LOCK_PATTERN_UTILS = "com.android.internal.widget.LockPatternUtils";
try {
Class<?> lockPatternUtilsClass = Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils = lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
Method method = lockPatternUtilsClass.getMethod("getActivePasswordQuality");
int lockProtectionLevel = Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils)));
// Then check if lockProtectionLevel == DevicePolicyManager.TheConstantForWhicheverLevelOfProtectionYouWantToEnforce, and return true if the check passes, false if it fails
}
catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
How is this handled?
Those classes are only available as part of full firmware builds, not from Android SDK apps.
You can not use Android internal classes, they do not come with the public SDK.
You can build your own SDK exposing them more or less as they do here How do I build the Android SDK with hidden and internal APIs available?
This is not recommended because you will have no guarantees.

Why does casting net.simonvt.numberpicker.NumberPicker to android.widget.NumberPicker work?

I have an app using NumberPickers. Since the app supports older versions of Android, where no NumberPicker widget had been available yet, I have to use an external library.
In my XML layout numberpicker_dialog.xml, I define the NumberPicker like this:
<net.simonvt.numberpicker.NumberPicker
android:id="#+id/interval_picker_1"
... />
Then in the code of my activity, I have:
import net.simonvt.numberpicker.NumberPicker;
private View intervalPickerDialogBody;
private NumberPicker intervalPicker1;
private android.widget.NumberPicker api11_intervalPicker1;
protected void onCreate(Bundle savedInstanceState) {
intervalPickerDialogBody = getLayoutInflater()
.inflate(R.layout.numberpicker_dialog, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// the casting in question happens here
api11_intervalPicker1 = (android.widget.NumberPicker)
intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
} else {
intervalPicker1 = (NumberPicker)
intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
}
}
It works, and really on Honeycomb and newer, android.widget.NumberPicker is used, on older versions, net.simonvt.numberpicker.NumberPicker is used.
But now that I am writing a thesis about the app using this, I wonder:
Why does casting net.simonvt.numberpicker.NumberPicker from the XML layout to android.widget.NumberPicker work when they in reality are different classes?
How come that only this casting makes the device use android.widget.NumberPicker on Honeycomb and newer when the NumberPicker that's in the XML layout is actually the one from the exetrnal library? When is an instance of android.widget.NumberPicker ever created?
The fact that android.widget.NumberPicker is used on Honeycomb and newer is supported by the fact the second line of the forecoming code snippet equals true on my 4.0.3 phone.
View tmp = intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
tmp instanceof android.widget.NumberPicker // pseudocode
(Thank you, Jules, this is a much better proof than the striked one with the arrows. :))
If anyone wonders how I know that android.widget.NumberPicker is actually used on Honeycomb and newer, it's because net.simonvt.numberpicker.NumberPicker doesn't display arrows above and below the NumberPicker as it was backported from 4.2 where the arrows have been removed. And when I try it on my device running 4.0.3, the arrows are there even though if it should display net.simonvt.numberpicker.NumberPicker, they wouldn't be.
I hope the question is understandable. Thanks for elaborating on the subject.
Interesting question. It seems to be because of what appears to me to be a bug in LayoutInflater. Here's the code used to decide what View class is used for a particular XML tag:
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor constructor = sConstructorMap.get(name);
Class clazz = null;
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
}
...
Before this is called, the prefix (i.e. everything up to and including the last dot in the tag name) is separated out and put into the prefix parameter, so for your XML, the call is:
createView ("NumberPicker", "net.simonvt.numberpicker.", ...);
This means that when the createView method checks in the cache for a previously-used constructor, as long as a NumberPicker has been used before it will return the system NumberPicker.
As a corollary to my belief that this is a bug, I would modify your code above to safeguard against the possibility that a future variant of Android will fix the bug by testing which type of object is returned by findViewById using the instanceof operator, rather than your current version number test, i.e.:
View tmp = intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
if (tmp instanceof android.widget.NumberPicker) { // Note 1 below
api11_intervalPicker1 = (android.widget.NumberPicker)
intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
} else {
intervalPicker1 = (NumberPicker)
intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
}
Note 1 is that I'm making the assumption that the instanceof check won't crash at run time if the class you're checking against doesn't exist. If it does, you'll have to reverse the order of the checks.

Is it safe to write Android compatible code this way?

is it safe to write such compatible code on Android?
if (Build.os.SDK_INT >= 11) {
newClass instance = new newClass();
....
}
else {
oldClass instance = new oldClass();
....
}
one of my colleagues argue with me that ClassNotFoundException might be thrown up when running the above code since ClassLoader is attempting to load newClass on an android os device which is below android 11. But I've tried couple times, and didn't see this happen.
After googling around for couple hours, I didn't find any information on how and when android default classLoader loads a specific class.
You should check the compatability like the following... It gives you more accurate than the above:
private static int currentApi = 0;
public static int getApiLevel() {
if (currentApi > 0) {
return currentApi;
}
if (android.os.Build.VERSION.SDK.equalsIgnoreCase("3")) {
currentApi = 3;
} else {
try {
Field f = android.os.Build.VERSION.class.getDeclaredField("SDK_INT");
currentApi = (Integer) f.get(null);
} catch (Exception e) {
return 0;
}
}
return currentApi;
}
you can alway use reflection to check if the class exists:
try {
Class.forName("yourclass")
} catch (ClassNotFoundExecption) {
oldClass instance = new oldClass();
}
Yes, this is safe to do on recent versions of Android. I want to say froyo and above, but it may have been even earlier than that. I don't recall for sure.
What happens is that dalvik performs a verification pass on the dex file at install time. For any classes/methods/fields that it can't resolve, it replaces those accesses with an instruction that throws a VerifyError.
In your example, if that code got loaded on, e.g. api 10, newClass instance = new newClass() would conceptually be replaced with throw new VerifYError(). So as long as that branch never gets executed at runtime, everything is good.
Short answer - don't do it.
Most VMs only load a class when it is absolutely needed. However a class loader is allowed to cache binary representation of classes beforehand[1].
Class loaders are allowed to cache binary representations of types,
load types early in anticipation of eventual use, or load types
together in related groups.
[1] - http://www.artima.com/insidejvm/ed2/lifetype2.html
[2] - http://developer.android.com/tools/extras/support-library.html
Edit - Have you checked if the class you need is available as part of the android support package ? [2]

Proper way to use reflection on inherited methods

I've used the TouchInterceptor class in the Google Music application in my application. This class allows you to drag and drop list items into different positions in the list.
The TouchInterceptor class makes a call to a method called smoothScrollBy. This method is only available in API 8+.
I want to target my application at API 7+, so I need to use reflection to execute smoothScrollBy only if it exists.
In the constructor for TouchInterceptor, I've added the following:
Method[] ms = this.getClass().getDeclaredMethods();
if (ms != null) {
for (Method m : ms) {
if (m != null && m.toGenericString().equals("smoothScrollBy")) {
Class[] parameters = m.getParameterTypes();
if (parameters != null && parameters.length == 1 && parameters[0].getName().equals("int")) {
mSmoothScrollBy = m;
}
}
}
}
This should find the smoothScrollBy method and assign it to a new member variable of TouchInterceptor called mSmoothScrollBy (Method).
I'm debugging through on an Android 2.2 (API 8) emulator and unfortunately the method is never found. My guess is getDeclaredMethods() does not return it in the array because smoothScrollBy is a method of AbsListView, which is inherited by ListView and ultimately TouchInterceptor.
I've tried casting this to AbsListView before calling getClass().getDeclaredMethods() with no success.
How can I properly get ahold of smoothScrollBy so I can invoke it when available?
Update:
I've also tried the following to no avail:
Method test = null;
try {
test = this.getClass().getMethod("smoothScrollBy", new Class[] { Integer.class });
}
catch (NoSuchMethodException e) {
}
It is because it is an inherited method. getDeclaredMethods() only retrieves methods declared within your class, not the methods of its superclasses. Although I have never actually done this, you should be able to call getSuperclass() until you find the class that declares the method (AbsListView) and get the method from it.
An easier answer might be just to check the version of the API though: Programmatically obtain the Android API level of a device?
I'm not sure, but I think if you target your application to API 7, then the method won't be found, cause it won't exist. You can target API 8 and list in your manifest that you only require API level 7.
Create a method called hasMethod(Class cls, String method) or similar which recursively calls itself up the inheritance hierarchy:
public boolean hasMethod(Class cls, String method) {
// check if cls has the method, if it does return true
// if cls == Object.class, return false
// else, make recursive call
return hasMethod(cls.getSuperclass(), method);
}
Thanks for your replies. I've solved the problem by doing the following:
try {
mSmoothScrollBy = this.getClass().getMethod("smoothScrollBy", new Class[] { int.class, int.class });
}
catch (NoSuchMethodException e) {
}
I had the parameter list of the method I was looking for incorrect.

Categories

Resources