build for different targets conditionally - android

I have been building for API 14 so far. I'm trying to get ready now, so I downloaded and specified API 7 as my target. I have some compilation errors because a few api's I was using in API14 aren't available in API7.
I am wondering how I could put them in conditionals. Something on the lines of:
if (API_14)
{
if (mTextEdit.isEmpty()) {
// Do Something
}
} else if (API_7){
if (mTextEdit.matches("")) {
// Do the same thing
}
}
This has to be a compile time conditional switch because otherwise my code won't even compile.
I've heard before that pre-processors are not supported in Java, so I welcome suggestions on how best to manage my source which I'm targeting for multiple versions.
Edit:
I also am running into trouble with my state list drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_pressed="true"
android:drawable="#android:color/holo_orange_dark" />
<item android:drawable="#android:color/transparent" />
</selector>
Of course, hold_orange_dark isn't available in API7. I'd like to be able to manage those as well.

You can't do ifs, or you'll have a crash on older devices.
You have to use Java Reflection to use methods from more recent APIs conditionally.
An example: http://mobile.tutsplus.com/tutorials/android/java-reflection/

For your resources you can specify different directories for different platform versions. So you'd make a Values-v14 directory to have a copy of any resources that use anything from that API version and a Values directory so Values would be used by default and Values-v14 would only be used if the device was version 14.
As for doing it programmatically, you could try a Try-Catch like so-
try {
//Insert ICS stuff here
} catch(NoSuchMethodError e){
}
Or you could try this-
public static boolean isHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}

Related

SVG on Android below API 23 [duplicate]

I am working on an Android project and I chose <vector> to display icon because it is adaptable and dynamically, however, I just can run this app on devices running Android, which have API 21 or higher. My question is how can I use <vector> on lower Android version i.e. API 14 or kind of. Thanks!
<!-- drawable/ic_android_debug_bridge.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="48dp"
android:width="48dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#color/primaryColorDark"
android:pathData="M15,9A1,1 0 0,1 14,8A1,1 0 0,1 15,7A1,1 0 0,1 16,8A1,1 `0 0,1 15,9M9,9A1,1 0 0,1 8,8A1,1 0 0,1 9,7A1,1 0 0,1 10,8A1,1 0 0,1 9,9M16.12,4.37L18.22,2.27L17.4,1.44L15.09,3.75C14.16,3.28 13.11,3 12,3C10.88,3 9.84,3.28 8.91,3.75L6.6,1.44L5.78,2.27L7.88,4.37C6.14,5.64 5,7.68 5,10V11H19V10C19,7.68 17.86,5.64 16.12,4.37M5,16C5,19.86 8.13,23 12,23A7,7 0 0,0 19,16V12H5V16Z" /></vector>
With the support library 23.2, the true support for Vector Drawables has been provided all the way down to API v7.
It is recommended to disable the previous version of the support, which rendered PNG during build-time, by adding
// Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
to the build.gradle file.
The implementation is fairly simple. Just use the new srcCompat attribute on Drawables (under app namespace!):
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#drawable/ic_add" /> // <= this is new
Vector Drawables are also supported in cases like TextView's drawableLeft property.
Source: library announcement
However, I would still recommend something like Iconics library, AndroidSVG, or another font-icon or SVG solution for the full SVG-standand vector support.
VectorDrawable are supported pre-Lollipop via the Support Library, but the way to use them depends on the version of Support Library you have. And it may not work in all cases.
I've made this diagram to help (valid for Support Library 23.4.0 to - at least - 25.1.0).
I found solution! For those who search solution with TextView and other "android" namespace attributes.
First of all this is necessary:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
And in application class define this:
#Override
public void onCreate() {
super.onCreate();
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
Now you can use app:srcCompat="#drawable/ic_add" but if you try to use android:background= or android:drawableLeft= it will crash app with "Error inflating" exception.
We can create wrapped drawable ic_add_wrapped.xml for this vector:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_add"/>
</layer-list>
And now it will work with any property like drawableLeft or background. Just set android:drawableLeft="#drawable/ic_add_wrapped.xml".
WARNING! THIS IS A WORKAROUND SOLUTION. So you use it for your own risk.
Vector Drawables are now backward compatible, it's just a matter of upgrading your gradle version to 1.4.0-beta3 or higher, and upgrade your IDE :
We are also excited to offer backwards compatibility for your vector
assets in Android Studio 1.4. Once you have a vectorDrawable image in
your res/drawable, the Gradle plugin will automatically generate
raster PNG images for API level 20 and below during build time. This
means you only need to update and maintain your vector asset for your
app project and Android Studio can take care of image conversion
process.
http://android-developers.blogspot.com.uy/2015/09/android-studio-14.html
you need use android Support Repository 30+ if you using android studio
and need android support library 23.2.1+ if using Eclipse.
check your build.gradle (project) if using version 2.0+ add below code in your build.gradle (app)
// Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
and: if using version 1.5 add below in your build.gradle(app)
// Gradle Plugin 1.5
android {
defaultConfig {
generatedDensities = []
}
// This is handled for you by the 2.0+ Gradle Plugin
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
here is sample code for use vector icon:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#drawable/ic_add"
tools:ignore="MissingPrefix" />
or
<ImageButton
android:layout_width="wrap_content"
android:background="#android:color/transparent"
app:srcCompat="#drawable/camera"
tools:ignore="MissingPrefix"
android:layout_height="wrap_content"/>
Vector Drawables are also supported in cases like TextView's drawableLeft property. but it worked api 22+ to me and i still dont know how it will work for low api.
Also keep in mind if you want to be compatible below API 21:
you cannot use the android:background property in xml or View.setBackgroundResource() function. You need to use the View.setBackground().
you cannot use the svg-s in StateListDrawable xml-s or other xml drawables, you have to generate them programmatically.
you cannot use svg-s in case of notifications.
When you need to add VectorDrawable (created from SVG) programatically, you can do it like this:
icon = VectorDrawableCompat.create(resources, R.drawable.ic_map_black_24dp, null)
For lower version compatible,
add the below in gradle,
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
add the below code in onCreate() in your application class,
#Override
public void onCreate() {
super.onCreate();
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
In xml for imageView,
<ImageView
android:id="#+id/imageViewMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/transparent"
app:srcCompat="#drawable/ic_success"/>
If you want to change the image source programmatically use this,
imageView.setImageResource(R.drawable.ic_launcher);
I had an issue where my vector images would show up but would be completely black, this was due to an issue where you can't reference color resources in the vector xml file.
So instead of #color/primaryColorDark you have to use the actual color e.g. #212121
Just to make #2Dee's answer complete:
VectorDrawable cannot be used on API 20 and lower. The official method generates pngs, which then become BitmapDrawables, which breaks the idea of vector graphics.
Personally I prefer svg's over xml vectors:
such graphics can be exported right from Illustrator or other popular software
svg supports transformations, text, masks and other things properly
true scallable vector graphics on all platforms
smaller size and faster builds
To use vector graphics you can try https://github.com/BigBadaboom/androidsvg . It's an svg reader and svg-compatible ImageView.
If you are using Android Studio 3.0.0 you can set
android.enableAapt2=false
in gradle.properties
https://www.reddit.com/r/androiddev/comments/6mj8di/android_studio_30_canary_6_released/
You can use programatically ..to set drawableLeft to your editText or textView
like
Drawable tick_drawable = VectorDrawableCompat.create(getResources(), R.drawable.green_tick, null);
if (tick_drawable != null) {
tick_drawable.setBounds(0, 0, tick_drawable.getIntrinsicWidth(),tick_drawable.getIntrinsicHeight());
}
And to drawable left like this..
editText.setCompoundDrawables( tick_drawable , null, null, null );
after setting vectorDrawables.useSupportLibrary = true in gradle default and AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); in activity on create
To avoid crash with android:drawableleft in Textview, set drawble left to the textview programitically
for examlple:
textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movie, 0, 0, 0);
I also found the same issue. And I did:
vectorDrawables.useSupportLibrary = true
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movie, 0, 0, 0);
It is good. But after that, I got an error that the resource not found.
So I found it's better to add a vector image according to the SDK version.
Finally, this solution became good is for me:-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
textview.setCompoundDrawablesWithIntrinsicBounds(null, null, AppCompatResources.getDrawable(this, R.drawable.movie), null);
} else {
textview.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.movie, 0);
}
I hope, it will help someone.

Setting a button background resource with a .png from internal storage

I have a button that I want to set the background of using a png file from internal storage. For android api 16 and up, this works fine:
filePath = getActivity().getFileStreamPath(colorCodes.get(i-1));
temp.setBackground(Drawable.createFromPath(filePath.toString()));
When running on an android tablet with 4.0.4, this part crashes the app with a nosuchmethod error (setBackground). After a little research, I see that setBackground is only available for api 16+. After looking around on SO and a few other places, it looks like I need to use setBackgroundDrawable (deprecated) or setBackgroundResource. I tried this:
filePath = getActivity().getFileStreamPath(colorCodes.get(i-1));
if (android.os.Build.VERSION.SDK_INT < 16) {
temp.setBackgroundDrawable(Drawable.createFromPath(filePath.toString()));
} else {
temp.setBackground(Drawable.createFromPath(filePath.toString()));
}
When logging it out, it shows that setBackgroundDrawable is running and not setBackground, but I get the same nosuchmethod error (setBackground).
The other option is setBackgroundResource, but it accepts an int and not a drawable. Can I convert from drawable to int for this purpose?
What can I do here to set the background of the button to a file in internal storage for APIs < 16?
Thanks.
***EDIT - ok, this is working. just missed a little part elsewhere in the code that had the same problem. However, is using a deprecated method really the only way?
Deprecation is a status applied to a computer software feature,
characteristic, or practice indicating it should be avoided, typically
because of it being superseded. The term is also sometimes used for a
feature, design, or practice that is permitted but no longer
recommended in other areas, such as hardware design or compliance to
building codes. (source link)
Now we can answer your question.
Before API level 16 there is a method named setBackgroundDrawable. After API Level 16 google decided to write a new method setBackground for same purpose and recommend us to use new method. (Reason of this may be found by googling.)
You can use setBackgroundDrawable method for all api levels. There aren't any constraint for this. But using new method setBackground is recommended after API Level 16.
But you can only use setBackground method for devices which is running on API Level 16 or higher. So if you only implement setBackground method in your code, you are going to get MethodNotFoundException for devices which run below API Level 16.
To sum up; it is a best practice(for me it is a must) to use new methods then deprecated ones with supportted api version check such as;
if (android.os.Build.VERSION.SDK_INT < 16) {
temp.setBackgroundDrawable(Drawable.createFromPath(filePath.toString()));
} else {
temp.setBackground(Drawable.createFromPath(filePath.toString()));
}
I am not quite sure whether it is the only way to achieve this but in my opinion it is the correct one. Because the annotation #Deprecated defines the method to be superseded (in most cases) it automatically implies you can (I would even say should) use it to address older versions which are the targeted versions of this method.

setBackgroundDrawable() is API 16 and my app requires a minimum API 9 I get no error when building

I have my app that requires SDK 9+ with code containing setBackgroundDrawable() which is API level 16. I did not get any error while coding or building the apk. but I got about 50 reports of this error happening in google analytics and a few reports in my developers console.
When I run the lint checker It also doesn't warn me. I am using eclipse. Is there a reason why it doesn't fail to compile like usually when you add a method that's not supported by the minimum API or is it simply an eclipse bug?
First of all you get no error when building since you probably are building with SDK 16+ and the method is there. But if you install the apk to a 2.1 Android phone it will throw a MethodNotFound Exception. So in the future ALWAYS install your apk on a min-target device to see if you didn't forget something. Min-Target basically is only a filter for the PLAY store (and for lint warnings, etc.)
AFAIK moving from imageView.setBackground(...) to imageView.setBackgroundDrawable(...) was just an api style design choice. So if you look at the source of Android SDK 18 you will see:
/**
* Set the background to a given Drawable, or remove the background. If the
* background has padding, this View's padding is set to the background's
* padding. However, when a background is removed, this View's padding isn't
* touched. If setting the padding is desired, please use
* {#link #setPadding(int, int, int, int)}.
*
* #param background The Drawable to use as the background, or null to remove the
* background
*/
public void setBackground(Drawable background) {
//noinspection deprecation
setBackgroundDrawable(background);
}
So for now its absolutely irrelevant if you use one or the other - but of course this COULD change (but it's unlikely it does in the future since it would break nearly every app done before SDK 16) - basically it's fine to use setBackground() even on SDK 18+
So if you want to be on the future-proof but ugly side you could use a version fork depicted by the other answers
if(Build.VERSION.SDK_INT >= 16) {
//new code
} else {
//deprecated code
}
Just one thing, and maybe this is a personal style preference, I would not suppress Lint warnings with annotations like this:
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
I like to keep the warnings since maybe later if I want to refactor/move to higher SDK I could easily get rid of these ugly switches.
Update:
Google's v4 support library contains helper classes for sdk boiler plate code. In this instance you would use:
ViewCompat.setBackground(view,drawable);
which handles the SDK check for you.
Seems to be a bug in eclipse or your eclipse is not working perfectly. But you can try project clean and try. But code wise you can try something like this:
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
public void setImage(ImageView imageView, BitmapDrawable bd) {
if(Build.VERSION.SDK_INT > 16) {
imageView.setBackground(bd);
} else {
imageView.setBackgroundDrawable(bd);
}
}
You can call this function along with ImageView and bitmap drawable.

Conditional compiling in Android?

Is there any kind of conditional compiling for Android?
I had to make my project for Android 3 (API 11) just because ExifInterface has almost no useful attributes in Android 2.3 (API 10), despite the fact that it appeared in API 5 (!!??). I don't want to restrict my app to ICS users.
Thanks!
You can check dynamically the current API version of the device and do different stuff depending on that:
if(Build.VERSION.SDK_INT < 14) {
// Crappy stuff for old devices
}
else {
// Do awesome stuff on ICS
}
But be careful that if you need to instantiate classes that are not available for all APIs then you should do it in a runnable or in a separate wrapper class, e.g:
if(Build.VERSION.SDK_INT < 14) {
// Crappy stuff for old devices
}
else {
// Do awesome stuff on ICS
new Runnable() {
new AmazingClassAvailableOnICS();
(...)
}.run();
}
import android.annotation.TargetApi;
and then use annotations:
#TargetApi(11)
public void methodUsesAPI11()
{
...
Using this trick does a very simple thing: it allows compiling some code which contains API level 11 calls (classes, methods, etc) and still set android:minSdkVersion="8" in the manifest. Nothing more, nothing else.
The rest is up to you. You must check platform version before you call methodUsesAPI11() or you handle exceptions in order to prevent app crash and perform other action on older platforms.
Checking Build.VERSION.SDK_INT or using annotations should suffice, however, this link I'd bookmarked might be relevant to your case:
http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html?m=1
You can use what they describe there to have classes that may not be compatible, but will never be loaded. It's not conditional compilation, but it may be what you need, however, it is a bit more complex.

How to detect HTC Sense?

Can I somehow detect if my app is running on HTC Sense?
More generally, the problem is, that I have a Button with custom drawable. It's something very similar to the account switcher in the top right of Gmail app. When pressed or focused, the button has orange highlight. But that doesn't look nice on HTC Sense - because the standard highlight color there is green.
Lets see android.os.Build strings I am not sure what the HTC folks use a combination to indicate a HTC sense device..
Here's a link suggesting a way to detect HTC Sense on the device, about 1/3 the way down the discussion. I've tested on Desire and Desire Z.
Code is below (from a user: David):
private static final String SENSE_UI_LAUNCHER_NAME =
"com.htc.launcher.Launcher";
private static Boolean senseUI;
public static final boolean isSenseUI(Context context) {
if (senseUI == null) {
senseUI = false;
PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> list = packageManager.queryIntentActivities(
intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : list) {
if (info.activityInfo != null
&& SENSE_UI_LAUNCHER_NAME
.equals(info.activityInfo.name)) {
senseUI = true;
break;
}
}
}
return senseUI;
}
I think that Android has provided a better way for you to solve this than doing a check for Sense, and it will work for every device manufacturer. HTC isn't the only one who has changed the colors on the selectors. I think Sony Ericsson has a transparent white/blue, Motorola changed it to red in their MotoBlur UI, and Garmin-Asus changed it to blue in theirs just to name a few.
What you should do is override the android:background attribute on your image button and use a drawable of your own instead of relying on the framework's background
drawable. If you're not already familiar with them, you'll probably also want to take a look at Selectors when you create your custom background, so you still get the pressed/selected/not-selected color cues.
If you have multiple buttons that you want to do this on, you may want to use styles and themes to help out with this. The two primary places you'll want to reference from the Android documentation are "Applying Styles and Themes" and "Style Resources"
Hope that helps!
I think the following approach should be followed - we define android:background, something like
<?xml version="1.0" encoding="UTF-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/pressed_application_background" />
<item android:state_focused="true" android:state_window_focused="true" android:drawable="#drawable/focused_application_background" />
<item android:state_focused="true" android:state_window_focused="false" android:drawable="#android:color/transparent" />
</selector>
But here we should refer to some standard resources, not to the images of our application. Just need to find out which ids they have.
This is a bit late, but I think this solution might work on some people cases.
I tried to use Build.MANUFACTURER to check if it is a HTC device. This solution doesnt work on some devices and we can ensure that that devices is running Sense anyway
If you notice, you may see Play Store needs to check what features available on the phone to display correct apps, and HTC Sense is one of the features!
To get all available features:
public FeatureInfo[] getSystemAvailableFeatures(Context context) {
FeatureInfo[] features = context.getPackageManager().getSystemAvailableFeatures();
for (FeatureInfo f : features) {
Log.d("Features", "feature " + f.name);
}
return features;
}
This will return something like these:
com.example D/Features﹕ Feature android.hardware.wifi
com.example D/Features﹕ Feature android.hardware.location.network
com.example D/Features﹕ Feature com.sec.android.mdm
com.example D/Features﹕ Feature android.hardware.location
com.example D/Features﹕ Feature android.hardware.sensor.gyroscope
com.example D/Features﹕ Feature android.hardware.screen.landscape
com.example D/Features﹕ Feature com.htc.software.Sense5.0
Notice the last line! You can use it to check HTC sense version
Hope it helps

Categories

Resources