Android & calling methods that current API doesnt have - android

If i want to get the external path like this, and device has Android 2.1 (api 7)
File f;
int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
if (sdkVersion >= 8) {
System.out.println(">=8");
f = getApplicationContext().getExternalFilesDir(null);
} else {
System.out.println("<=7");
f = Environment.getExternalStorageDirectory();
}
LogCat will display:
05-25 15:44:08.355: W/dalvikvm(16688): VFY: unable to resolve virtual method 12: Landroid/content/Context;.getExternalFilesDir (Ljava/lang/String;)Ljava/io/File;
, but app will not crush. I want to know what is VFY? Is there something in the virtual machine dalvik that checks if code inside a called method is valid? Because current proj was compiled agains Android 2.2 so Eclipse didn't complained.. but at runtime, i get LogCat entry
PS: i dont use method like this in really, i have Helper class which initialises a class for API<=7 or another for API>=8.. but still please answer!

Yes, VFY errors are logged from dex verifier in dalvik.
You are facing this issue because you are performing runtime checks for the SDK version and calling the API methods. The problem is even if the method call is inside the if(){} block which may never be executed in lower API levels, the symbolic information is present in the generated bytecode. If you need to perform platform specific function calls, you need to use reflection.
File f;
int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
if (sdkVersion >= 8) {
System.out.println(">=8");
try {
Method getExternalFilesDir = Context.class.getMethod("getExternalFilesDir", new Class[] { String.class } );
f = (File)getExternalFilesDir.invoke(getApplicationContext(), new Object[]{null});
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("<=7");
f = Environment.getExternalStorageDirectory();
}

Related

Fix warning NullPointerException in android

Below code gives a warning when I run Inspect code. How can I change it to fix the warning?
File contents = new File(context.getExternalFilesDir(null).getAbsolutePath(), "Contents");
if (!contents.exists()) {
contents.mkdirs();
}
Warning:
Method invocatiom 'getAbsolutePath' may produce 'NullPointerException'
and File mkdirs() is ignored
You can use boolean to get the result of mkdirs()
boolean isMkDirsSuccess = contents.mkdirs();
Log.e("TAG","This is the value of isMkDirsSuccess " + isMkDirsSuccess );
for NullPointerException you can use
File contents = new File(Objects.requireNonNull(context.getExternalFilesDir(null)).getAbsolutePath(), "Contents");
//requireNonNull needs min API = 19
Hope this will help!
From the docs:
Shared storage may not always be available, since removable media can be ejected by the user. Media state can be checked using Environment#getExternalStorageState(File).
You need to do some checking first:
File externalDir = context.getExternalFilesDir(null);
if(externalDir == null) {
throw new IllegalStateException("No External files directory found.");
}
if(Environment.getExternalStorageState(externalDir).equals(Environment.MEDIA_MOUNTED)) {
throw new IllegalStateException("External Storage not mounted correctly.");
}
File contents = new File(externalDir.getAbsolutePath(), "Contents");
if (!contents.exists()) {
contents.mkdirs();
}
You can replace the exceptions with flags, or logs or whatever your programme needs.
https://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)
https://developer.android.com/reference/android/os/Environment#getExternalStorageState()

Android Studio 0.9.2 to 0.9.3 error: toString() method cannot be referenced

I have just updated android studio release from 0.9.2 to 0.9.3 and opening an existing project (that was running with 0.9.2) the following error (red underline) appears in all '.toString' methods (f.i. Integer.toString): non static method toString() cannot be referenced from a static context. But the debug does not show anything and the app runs properly. Please any suggestion?
example:
private String read(String nomefile)
{
String dati=null;
try{
FileInputStream fin=openFileInput(nomefile);
int c;
String temp="";
while ((c=fin.read())!=-1)
{
temp=temp+Character.toString(((char)c));
}
dati=temp;
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return dati;
}
and '.toString' is red underlined with this annotation: 'non static method toString() cannot be referenced from a static context'
Upate:
replacing “Character.toString(...)” with “Character.valueOf(...)” seems all ok (no red underline). This means that toString method will be deprecated?
Try going to File > Invalidate Caches & Restart. I just upgraded and do not have this problem.
edit
It appears this is a known issue with 0.9.3. A permanent fix should be available in the next Canary release.
It's a common bug.
The fix is going to be delivered at 0.9.4 according this https://code.google.com/p/android/issues/detail?id=79420

unable to load owl file to android project using owl api

I have been trying to load an OWL file that I made in Protégé. I import OWL API 3.4.3 to my project and also passed the sample.owl file to raw folder, but when I try to load the OWL file, it doesn't work. There was no error but I am just getting this message
unfortunately, sampleproject has stopped
Here is the section of code am using. When I try the code in a standard Java environment it works without a problem.
OWLOntology localOntology = null;
int rID = resources.getIdentifier("com.example.cammclient1:raw/"+"sample", null, null);
InputStream input = resources.openRawResource(rID);
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
ontology = manager.loadOntologyFromOntologyDocument(input);
try {
for (OWLClass cls : localOntology.getClassesInSignature()) {
Log.d("class in the ontology", ((CharSequence) cls).toString());
}
TV1.setText("reading classes...............");
}
catch (Exception e) {
TV1.setText("Not successfull");
}
You are casting OWLClass instances to CharSequence and then calling toString() on it.
This will cause ClassCastException to be thrown - an OWLClass is not a string.
Just use cls.toString() instead, you will have the same result.
You are also swallowing the exception in the catch block. That's not helpful in diagnosing the issue, as it hides information by just saying "Not successful" without providing more information.

Is it possible to TextView#getMaxLines() on pre api-16 devices?

I used TextView#getMaxLines() in my application for a few weeks without incident.
Lint is now informing me that it's only available in API 16+ (#setMaxLines() is API 1+...), though (to the best of my knowledge) I haven't modified anything that would cause this sudden flag - my min-sdk has been 8 for a while, and I have files in my source control to prove it.
1) Why could lint be flagging this error randomly? (To be clear, I mean to say that it should have caught it initially - I'm not implying this is something that it shouldn't have flagged at all).
2) Is there any way to retrieve the maxLines for a TextView on pre-api 16 devices? I checked the source but couldn't devise a way to retrieve this value using the exposed methods on a 2.2 device.
A simpler solution was added to the support lib v4 inTextViewCompat
int maxLines = TextViewCompat.getMaxLines(yourtextView);
Check out this answer for some more informations.
You can use Reflection:
Field mMaximumField = null;
Field mMaxModeField = null;
try {
mMaximumField = text.getClass().getDeclaredField("mMaximum");
mMaxModeField = text.getClass().getDeclaredField("mMaxMode");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
if (mMaximumField != null && mMaxModeField != null) {
mMaximumField.setAccessible(true);
mMaxModeField.setAccessible(true);
try {
final int mMaximum = mMaximumField.getInt(text); // Maximum value
final int mMaxMode = mMaxModeField.getInt(text); // Maximum mode value
if (mMaxMode == 1) { // LINES is 1
text.setText(Integer.toString(mMaximum));
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
OR:
Maybe, the best way is keep maxLine value at values and set it value in xml, and get as int resource in code.
The code for that method simply doesn't exist on 2.2, so you can't use it directly of course.
On the other hand, I've run a diff on the two files and it seems as though the new 4.2.2 TextView isn't using any new APIs internally (this is based solely on its imports). You may be able to add it as a class in your project and use it instead of the inbuilt TextView across all version of Android.

Copy the shared preferences XML file from /data on Samsung device failed

There's an exporting feature in my application. It's just a copy operation since all my settings are store in shared preference.
I just copy the xml file from /data/data/package.name/shared_prefs/settings.xml to SD card. It works fine on my HTC desire. However, it might not work on Samsung devices, and i got the following error while I try to copy the file.
I/System.out( 3166): /data/data/package.name/shared_prefs/settings.xml (No such file or directory)
in the directory.
Anyone know how to fix it, or is there another simple way to store the shared preference ?
Thanks.
Never never never never never never never never never hardwire paths.
Unfortunately, there's no getSharedPreferenceDir() anywhere that I can think of. The best solution I can think of will be:
new File(getFilesDir(), "../shared_prefs")
This way if a device manufacturer elects to change partition names, you are covered.
Try this and see if it helps.
CommonsWare's suggestion would a be clever hack, but unfortunately it won't work.
Samsung does not always put the shared_prefs directory in the same parent directory as the getFilesDir().
I'd recommend testing for the existence of (hardcode it, except for package name):
/dbdata/databases/<package_name>/shared_prefs/package.name_preferences.xml and if it exists use it, otherwise fall back to either CommonsWare's suggestion of new File(getFilesDir(), "../shared_prefs") or just /data/data/<package_name>/shared_prefs/package.name_preferences.xml.
A warning though that this method could potentially have problems if a user switched from a Samsung rom to a custom rom without wiping, as the /dbdata/databases file might be unused but still exist.
More details
On some Samsung devices, such as the Galaxy S series running froyo, the setup is this:
/data/data/<package_name>/(lib|files|databases)
Sometimes there's a shared_prefs there too, but it's just Samsung's attempt to confuse you! Don't trust it! (I think it can happen as a left over from a 2.1 upgrade to 2.2, but it might be a left over from users switching roms. I don't really know, I just have both included in my app's bug report interface and sometimes see both files).
And:
/dbdata/databases/<package_name>/shared_prefs
That's the real shared_prefs directory.
However on the Galaxy Tab on Froyo, it's weird. Generally you have: /data/data/<package_name>/(lib|shared_prefs|files|databases)
With no /dbdata/databases/<package_name> directory, but it seems the system apps do have:
/dbdata/databases/<package_name>/yourdatabase.db
And added bonus is that /dbdata/databases/<package_name> is not removed when your app is uninstalled. Good luck using SharedPreferences if the user ever reinstalls your app!
Try using
context.getFilesDir().getParentFile().getAbsolutePath()
Best way to get valid path on all devices - run method Context.getSharedPrefsFile defined as:
/**
* {#hide}
* Return the full path to the shared prefs file for the given prefs group name.
*
* <p>Note: this is not generally useful for applications, since they should
* not be directly accessing the file system.
*/
public abstract File getSharedPrefsFile(String name);
Because of it hidden need use reflection and use fallback on fail:
private File getSharedPrefsFile(String name) {
Context context = ...;
File file = null;
try {
if (Build.VERSION.SDK_INT >= 24) {
try {
Method m = context.getClass().getMethod("getSharedPreferencesPath", new Class[] {String.class});
file = (File)m.invoke(context, new Object[]{name});
} catch (Throwable e) {
Log.w("App TAG", "Failed call getSharedPreferencesPath", e);
}
}
if (file == null) {
Method m = context.getClass().getMethod("getSharedPrefsFile", new Class[] {String.class});
file = (File)m.invoke(context, new Object[]{name});
}
} catch (Throwable e) {
Log.w("App TAG", "Failed call getSharedPrefsFile", e);
file = new File(context.getFilesDir(), "../shared_prefs/" + name + ".xml");
}
return file;
}
On some Samsungs implements like this:
public File getSharedPrefsFile(String paramString) {
return makeFilename(getPreferencesDir(), paramString + ".xml");
}
private File getPreferencesDir() {
synchronized (this.mSync) {
if (this.mPreferencesDir == null) {
this.mPreferencesDir = new File("/dbdata/databases/" + getPackageName() + "/", "shared_prefs");
}
File localFile = this.mPreferencesDir;
return localFile;
}
}
On other Android like this:
public File getSharedPrefsFile(String name) {
return makeFilename(getPreferencesDir(), name + ".xml");
}
private File getPreferencesDir() {
synchronized (mSync) {
if (mPreferencesDir == null) {
mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
}
return mPreferencesDir;
}
}
private File getDataDirFile() {
if (mPackageInfo != null) {
return mPackageInfo.getDataDirFile();
}
throw new RuntimeException("Not supported in system context");
}
After while Google change API for level 24 and later:
https://android.googlesource.com/platform/frameworks/base/+/6a6cdafaec56fcd793214678c7fcc52f0b860cfc%5E%21/core/java/android/app/ContextImpl.java
I've tested in Samsung P1010 with:
//I'm in a IntentService class
File file = this.getDir("shared_prefs", MODE_PRIVATE);
I got:
"/data/data/package.name/app_shared_prefs"
It works fine to me. I can run ffmpeg in this folder.
Look:
Context.getDir
You have to create the shared_prefs directory:
try{
String dir="/data/data/package.name/shared_prefs";
// Create one directory
boolean success = (new File(dir)).mkdirs();
if (success) {
// now copy the file
}
}catch (Exception e){//Catch exception if any
System.err.println("Error: " + e.getMessage());
}
Also... the package of your app is package.name? Make sure you are referring to the right package.

Categories

Resources